esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
290 stars 36 forks source link

Changing set-point in PID-controller affects derivative #5145

Closed Newspaperman57 closed 7 months ago

Newspaperman57 commented 11 months ago

The problem

Changing the set-point in the PID-controller caused the derivative term to see a change, and pull the output hard in the direction the change was in.

I'm working with 30 seconds of sampling, so even though the error in the derivative term was only for 1 sample, it strongly affects the output for 30 seconds.

In this example i first changed the setpoint 1 degree down, and then 40 seconds later 1 degree up. Notice how the derivative term gets low for 30 seconds and shutting the output completely off (Which actually isn't affecting anything in this example since P also pulls the output to 0), and then settles when the anomaly exits the running average. But when i increase the setpoint by 1 degree, it again goes very high, causing the output to be 100% for 30 seconds, which in my case is enough to overshoot the target by 5-10 degrees (Allowable temperature-swing is 2-3 degrees in my case, which the PID is tuned for)

image

Which version of ESPHome has the issue?

2023.11.3

What type of installation are you using?

Home Assistant Add-on

Which version of Home Assistant has the issue?

No response

What platform are you using?

ESP8266

Board

NodeMCU

Component causing the issue

pid

Example YAML snippet

No response

Anything in the logs that might be useful for us?

No response

Additional information

I have already identified and fixed the problem:

https://github.com/esphome/esphome/pull/4737

Using the fixed code the derivative no longer changes, only the proportional: image

nagyrobi commented 11 months ago

Can you please post your pid climate config (including tuned values)?

Newspaperman57 commented 11 months ago

Thank you for your comment.

Note that i have identified the problem in the code and submitted a PR back in April that went unnoticed: https://github.com/esphome/esphome/pull/4737

I don't believe the tuned values i'm using are relevant for this bug, but to give an idea of my use case i was using a dallas probe and a slow_pwm output with a period of 1s on a repurposed 3d-printer bed for keeping newly hatched chicks warm. The heater was quite overpowered for this task, only using 5-7% dutycycle most of the time. 10 seconds at full power meant a temperature-increase of more than 10 degrees and taking up to a minute to cool down again, potentially hurting the chicks.

I have quite a lot of experience with PID-controllers from my education as software-engineer, custom built 3d-printers, stabilization in hobby-grade quadcopters, and other diy projects using arduinos and esp's before i started using homeassistant, so i'm very confident in the tuned values being correct for my setup. I was not experiencing oscillation, and only saw overshoot when changing the target temperature, which i identified as being caused by the derivative reacting erroneously to the change.

Here is an untested example-config from the documentation of the modules, using a dallas temperature probe and a slow_pwm output with a period of 1s:

# Example configuration entry
dallas:
  - pin: 23

# Individual sensors
sensor:
  - platform: dallas
    address: 0x1c0000031edd2a28
    name: "Livingroom Temperature"
    id: temperature_sensor

# Example configuration entry
output:
  - platform: slow_pwm
    pin: D1
    id: heater
    period: 1s

# Example configuration entry
climate:
  - platform: pid
    name: "PID Climate Controller"
    sensor: temperature_sensor
    default_target_temperature: 21°C
    heat_output: heater
    control_parameters:
      kp: 0.49460
      ki: 0.00487
      kd: 12.56301
      output_averaging_samples: 5      # smooth the output over 5 samples
      derivative_averaging_samples: 5  # smooth the derivative value over 10 samples
nagyrobi commented 11 months ago

Thank you, your use case makes the circumstances you found this bug credible.

stijnghesquiere commented 7 months ago

It's actually not a bug at all. Let me explain: the PID implementation in ESPHOME is a type-a PID. This has the error in the Proportional, the Integral and the Derivate term. That works fine... unless the setpoint is changed and affects the error and subsequently the D-term. As you found out. Hence this type of PID implementation isn't used often in inmdustrial settings where the setpoint needs to be changable. So your 'fix' is actually a kind of add-on to something that's inherent to the type-a. Much better would have been a type-b (error replaced with the Process Variable) or the best option a type-c (both teh error removed from the P and the D term). A good explanation is here: https://apmonitor.com/pdc/index.php/Main/ProportionalIntegralDerivative I haven't found time to add a type-c to teh esphome code. It isn't that hard.

Newspaperman57 commented 7 months ago

Hi Didn't realise this was still open. My merge request was merged 3 months ago fixing this, making it a C-type PID implementation, as i believe most users of ESPHome would expect it to be.

SP was already removed from the Proportional, but not Derivative, so the actual implementation didn't match any of the 3 types, and since the industry (and hobbyspace) is almost exclusively using Type B/C, i would still consider a proper A-implementation a bug.

stijnghesquiere commented 7 months ago

Thanks for the answer! I agree, we should have a type B/C. But.... I'm a bit confused when looking at the code in https://github.com/Newspaperman57/esphome/blob/dev/esphome/components/pid/pid_controller.cpp

void PIDController::calculate_proportional_term_() {
  // p(t) := K_p * e(t)
  proportional_term_ = kp_ * error_;

So the setpoint is still in it (error_ = setpoint - process_value;)

And for the derivative it's only removed temporary when the setpoint is changed. So still basically a type A.

We should put the Process Variable (PV) in both parts instead of the error based on the setpoint and the process_value: proportional_term_ = kp_ * (process_value - process_value_min1); and derivative = (process_value - 2*process_value_min1 + process_value_min2) / dt_;

Or am I looking at the wrong code part?

Newspaperman57 commented 7 months ago

After looking through the code and the link you provided again, it would seem this is a Positional/Discrete PID Controller, not of the Velocity form. Honestly i wasn't looking at equations, i just saw a problem and fixed it... The last time i was reading up on equations in control systems theory was probably the night before an exam at university a number of years ago, so i am a bit rusty on the details. My change looks to be what the page describes as avoiding a "derivative kick" by using the delta process variable instead of the delta error, which i accomplished by removing changes to the setpoint from the error. That's probably not the prettiest way of doing it, but my goal was more to make as few changes as possible to avoid inroducing more errors and breaking something else, than to make it pretty.

I've been happy with the current algorithm, i just used it last month to keep a consistent temperature inside a chicken brooder box for 22 days to hatch some chicks, and it kept the temperature at an impressive +/- 2 steps of the temperature-probe i was using (dallas sensor, each step was 0.0625c), with the output fluctuating between 11% and 13% controlling a 12v halogen bulb from an old car, and a small overshoot of 0.5 celsius when heating back up after opening the box, quickly heating up the eggs again, without overheating them :)

stijnghesquiere commented 7 months ago

Thanks for replying! I really appreciate your input! Indeed, your fix solves the derivative kick as that can well... kick hard. The current type A implementation works, certainly if one doesn't change the setpoint or if the system used isn't reactig very fast. Hopefully I can find time to implement a pid B/C for esphome. It would be great if one can choose between the different PID types and see what works best for the situation.

nagyrobi commented 7 months ago

Yes. I need to change setpoints a few times a day. For porper operation, at each setpoint change I need reset the integral (luckily there's an action for that - made an automation so that at setpoint change the integral gets reset...) otherwise it's taking about 1 hour to start considering the change...