8 stage PID control

Hi
I am new on this site so please feel free to correct me.

I have some specific problems which I couldn’t solve.

I wrote this code but I found pretty unusual behavior.

            if (pid_value > 90 ) {
                power_stage = 7;  // 100%
            } else if (pid_value > 72 && pid_value <= 90) {
                power_stage = 6;  // 90%
            } else if (pid_value > 62 && pid_value <= 70) {
                power_stage = 5;  // 70%
            } else if (pid_value > 52 && pid_value <= 60) {
                power_stage = 4;  // 60%
            } else if (pid_value > 42 && pid_value <= 50) {
                power_stage = 3;  // 50%
            } else if (pid_value > 32 && pid_value <= 40) {
                power_stage = 2;  // 40%
            } else if (pid_value > 12 && pid_value <= 30) {
                power_stage = 1;  // 30%
            } else if (pid_value <= 10) {
                power_stage = 0;  // 0% (izklop)
            }

For some reason, on the working driver for the Heat pump, I get strange results for values between 10-12 and 30-32.
Sometimes I get power_stage 7 for pid_value from 10-12
Can anybody explain why?

image

This is a graph drawn by the Home Assistant
blu is power_stage
orange is pid_value
Brown is temperature
Other colors are P I D values

Your code is missing case 10-12

There are holes between all the cases. All for 2 pid value difference between upper and lowe bound.

The code you wrote while I was replying wasn’t so bad, the one few seconds later is full of undefined zones. What are you trying to do?

I was trying to solve the debouncing problem when the pid value is between two stages.

And sorry for the late response.

But now, between each case there is no value for power_stage, and this is exactly where your problems happen. And you do not show what happens with the value when it isn’t defined.

Leaving undefined zones doesn’t look a good solution for debounce.
Did you resolve your problem?

I believe you could get what you need just with Esphome built in filters,
maybe calibrate_linear and delta.

I like to add deadband
If pid value is aroound switching point lets assuming pid is 11 i voud like to svitch to stage 2 when pid raise to 12
And switch down to stage 1 when pid drops below 10. And so on for every stage switch.
In that case there would be no quck switching between stages.

Delta filter does that.

Can you please explain
I am not familiar with it

Can you type sample code

just use filters on your sensor:

filters:

  - delta: 2.0
  - calibrate_linear:
      - 0.0 -> 0
      - 90.0 -> 7
  - clamp:
      min_value: 0
      max_value: 7
  - round: 0

Hi
Thanks for your suggestion.
I did approach with this approach but not with filters I get strange behaviour with that.
I add code to my PID calculation.
I am sure that another way could do it but this way is simpler to understand for me.
So thanks for pointing me in the right direction.

      - lambda: |-
          static float I_integral = 0;
          static float previous_odklon = 0;
          static float previous_pid_value = 0;  // Prejšnji PID izhod
          int power_stage;

          // Preveri, ali je toplotna črpalka vklopljena (VklopHP)
          if ((id(HP_ON_OFF_fiz).state == HIGH || id(HP_ON_OFF_virt).state == HIGH) && id(Delov_hp_txt).state != "PowerOFF" && id(MV_TG_gipo).state == LOW) {

            float Trenutna_temp_HP = id(TempHP).state;
            // Preveri, da Trenutna_temp_HP ni NaN
            if (isnan(Trenutna_temp_HP)) {
              ESP_LOGE("PID", "TempHP je NaN, preskočam izračune.");
              power_stage = 0;
              return;  // Izpusti ostalo kodo
            }
            float setpoint = id(NastavitevTempHp).state;  // Preberi setpoint iz number entitete
            // Preveri, da setpoint ni NaN
            if (isnan(setpoint)) {
              ESP_LOGE("PID", "Setpoint je NaN, preskočam izračune.");
              return;  // Izpusti ostalo kodo
            }
            float odklon = setpoint - Trenutna_temp_HP;
            // Preberi vrednosti P, I, D komponent iz number entitet
            float kp = id(kp_value).state;
            float ki = id(ki_value).state;
            float kd = id(kd_value).state;

            // P komponenta
            float P_proportional = odklon * kp;

            // I komponenta
            I_integral += odklon * ki;
            if (I_integral > 100) I_integral = 100;  // Omejitev I_integrala
            if (I_integral < -10) I_integral = -10;

            // D komponenta
            float D_derivative = (odklon - previous_odklon) * kd;
            previous_odklon = odklon;

            // Končni izračun PID
            float pid_value = P_proportional + I_integral + D_derivative;
            // Omeji vrednosti PID (med 0 in 100)
            if (pid_value > 100) pid_value = 100;
            if (pid_value < 0) pid_value = 0;
            // Pretvorba PID vrednosti v 8 stopenj moči

            // Posodobi PID vrednost samo, če se spremeni za več kot 1
            if (fabs(pid_value - previous_pid_value) > 0.5) {
              previous_pid_value = pid_value;  // Posodobi prejšnji PID izhod
            } else {
              pid_value = previous_pid_value;  // Ohrani prejšnji PID izhod
            }

  
            int power_stage = 0;
            if (pid_value >= 90) {
              power_stage = 7;  // 100%
            } else if (pid_value >= 70) {
              power_stage = 6;  // 90%
            } else if (pid_value >= 60) {
              power_stage = 5;  // 70%
            } else if (pid_value >= 50) {
              power_stage = 4;  // 60%
            } else if (pid_value >= 40) {
              power_stage = 3;  // 50%
            } else if (pid_value >= 30) {
              power_stage = 2;  // 40%
            } else if (pid_value >= 10) {
              power_stage = 1;  // 30%
            } else {
              power_stage = 0;  // 0% (izklop)
            }