Thermostat with PID controller

Now my settings are as follows :

And I have this curve on the climate history.

The temperature is measured by a Popp Mold Zwave sensor (battery powered), reporting temperature as soon as it sees a 0.1°C change, and minimum every 15mn if the temperature doesn’t change.

As you can see, after 4pm it started to heat while temperature was higher than setting, and when the target became 24°C, it didn’t react. I had to add 0.5°C to the setting to make it switch on.

The temperature reading with irregular intervals could be tricky. Maybe first try without kd.

The keep_alive is set once per hour. I think a regular control update will be better and I personally use each minute, thus three PID controller updates per pwm interval.

You could give it a try with these settings. I would expect that it would stabilise slow: ki is low and would need quite some time to stabilise before the temperature oscillations are gone. After activation first the kp will be dominant which will determine on-off signal and meanwhile the integral part for ki will build-up to define the ‘mean’ required ‘on time’. Hereafter it should behave more stable.

Could you give this a try and present the output graph?

    keep_alive: #time before update of PID controller
      seconds: 60
    kp : 10
    ki : 0.001
    kd : 0
    
    #duration of cycle in seconds
    pwm : 180
    #
    difference : 100

Removed , my mistake

Thanks for your help, I think I’m moving forward thanks to your support.

I set the thermostat as you suggested, and then this morning it started to modulate the heater activity, but with very fast on/off sequences.

So at 7am I modified the pwm to 900 seconds, and the min_cycle_duration and keep_alive parameters to 5 minutes (300 seconds, third of pwm), and it slows down the cycling.

However, it seems it still doesn’t deliver sufficient power to maintain the 24°C target.

I don’t understand how this pulse width modulation is working. In my experience, a pulse width modulating device is defined by a modulation frequency and pulse width limits (minimum 10% to maximum 90% on, in example). Then it sets the pulse width depending on the error between input and output.

But on this thermostat, it seems the pulse are always 5mn ON, and 15mn OFF. And there is no modulation.

Nice to see improvement.

The KP part sets the pwm on time based on the error, the ki is the integral of that error over time. Thus when the error is present for a longer period the integral increases which will extend the on duration of the pwm. This results in a lower error and thereby lower on duration by the KP part.

In this case the ki seems to be too low, you could try it to 0.05.

Hi, with Ki = 0.05, it clearly modulates.
The spike @21°C corresponds to the reboot to set Ki. I then manually set the temperature to 23°C after boot and waited for the schedule to rise to 24°C. There is no anticipation of the temperature crossing the setting, so missing derivative coefficient. It still enables the heater while the temperature is above the setting.

The integral builds up when the temperature is less than the setpoint and the longer and the lower it is below setpoint it increases the integral part which keeps the switch longer ‘on’. The integral contribution will reduce when the temperature is above setpoint. The longer and higher above setpoint the more the integral will reduce.

In normal operation the integral part will fill slowly and the temperature reaches the setpoint gradually and as it reaches setpoint the integral part will not increase anymore as the error is very small.

The pi settings do need some tuning. I expect the overshoot what you see may result in an oscillation and dampen out. For example see here schematic effects pi settings. So by keeping the setpoint constant for a longer period the oscillation will start and you will be able to identify the part (KP or KI) which has to change (probably to lower).

After testing and improved KP and KI setting, only then I would start to see how it behaves to scheduling the setpoint by first letting the temperature stabilise at setpoint and then an adjustment to a new setpoint and keep this setpoint also for a while to see how it stabilises. Based in these result some further adjustments may be needed.

Hi guys, I see you are struggling with the PID control for the thermostat. I am the developer of the free pidtuner tool, I can answer any questions you might have regarding PID tuning.

First I would like to point you to this FAQ regarding PID tuning with the tool:

Then I would recommend that you use the canonical arduino PID implementation from Brett Beauregard:

Some PID implementation make some weird additions to the basic algorithm takes helps some people to wrap their heads around manual tuning, but you really don’t need that if you use a tuning tool. Furthermore, most tools assume a standard PID implementation.

You don’t really need to be math experts or have years of experience with PIDs to have a good PID tuning, as long as you use a tool correctly, that’s what tools are for.

Hope this information is useful.

1 Like

Thanks @pidtuner for the FAQ, sure it will help.
My issue is to understant what to provide to the PID tuner. I need to tune the PID for the heating in my bathroom, but the input of the smart thermostat component is a set point in °C while the output of the component is ON/OFF periods.
You mention in the FAQ that the variables should be taken with PID OFF, and generate the step without changing the set point. But how can I make a step in the process output of a thermostat without changing the set point ?

If the output of the PID is directly ON/OFF, then what you need to do is a series of ON and OFF periods long enough that the temperature dynamics (°C measurement) can be observed in the data with good enough precision (as stated in the FAQ).

This is assuming:

  • That the PID inside the thermostat component indeed uses the °C setpoint and °C measurement to calculate the error that is fed into the PID block.

  • That the PID indeed outputs ON/OFF directly.

  • That the PID takes standard Kp, Ki and Kd (or Kp, Ti and Td) tuning parameters.

Then what you have to put into the tool is the ON/OFF signal as input, the °C measurement as output and the time in seconds.

The PID control implemented here is quite similar except one critical item. Here in the code the kd is switched in sign so the derative is subtracted!

Anyway, there are some issues in the smart thermostat code. I had a look to the pidcontroler.PIDAutotune class, for example and it requires the following parameters at init :

def __init__(self, setpoint, out_step=10, sampletime=5, lookback=60,
                 out_min=float('-inf'), out_max=float('inf'), noiseband=0.5, time=time):

with the description of the parameters :

    Args:
        setpoint (float): The target value.
        out_step (float): The value by which the output will be
            increased/decreased when stepping up/down.
        sampletime (float): The interval between run() calls.
        loockback (float): The reference period for local minima/maxima.
        out_min (float): Lower output limit.
        out_max (float): Upper output limit.
        noiseband (float): Determines by how much the input value must
            overshoot/undershoot the setpoint before the state changes.
        time (function): A function which returns the current time in seconds.

And when looking at how it is called by the SmartThermostat class, I can see this :

self.pidAutotune = pid_controller.PIDAutotune(self._target_temp, self.difference,
            self._keep_alive.seconds, self._keep_alive.seconds, self.minOut, self.maxOut,
            noiseband, time.time)

You can see that both sampletime and lookback are set to the same value self._keep_alive.seconds ! setting the same value for both will lead to a buffer of only 1 value, while the autotuner needs a lot of values to be able to see at least 1.5 cycles with the min and max values inside.
Will try to analyze the full code and see how I can improve it to my needs, but I think I’ll have to change some input parameters (the keep_alive becoming a sampletime and define a separated parameter for PID lookback).

Another issue found.
Line 504 in climate.py there are typo issues that would lead to an error if the control_output (output of PID) is negative

await self.pwm_switch(self.pwm * self.control_output / self.minOut, self.pwm * self.minOut / self.control_outpu, time.time() - self.time_changedt)

I’m building my own repo with the code I fixed, may share it when working fine.
By setting the debug level in the logger I can get much better understanding of the issues. With +2°C change in setpoint, the control_output was only 14% with Kp = 10, that’s why the system was much too slow to react. by using Kp = 100 now the heater immediately switches ON.
Now let’s start the tuning.

Yes, you need to find a way to record all the necessary data points to use with the tool.

The “sampletime” must be fast enough to capture the evolution of temperature as stated in the FAQ, but given that temperature dynamics are slow by nature, default values should be OK.

Good to see you are double checking your PID implementation, I cannot stress enough that a standard PID implementation is necessary (with basic tweaks, like anti-wind up). But as I said, the Arduino implementation is battle tested and highly recommended. Take a look at it, the blog post explain the why and how of every part of the PID code:

Very nice blog, thanks for the link. It confirms the PID code was good, but the use in the climate.py was incorrect.
However I had to change the way the PID is working, to replace the fixed sampling period with variable dt, as the temperature sensor is battery powered. But it makes difficult the PWM management.

What you might try is to change the keep_alive to the minimum refresh interval of your sensor. If I remember it correctly you mentioned it was 10 minutes?

One of the things I like in a Thermostat is to be able to tell it at what time I want a given temperature, and then it will start heating when needed. So a while earlier in deep winter than at the start of the heating season.

I think it is predictable with a 1st order approach/linear approach. While the system is not heating, we can observe how fast the temperature drops. And when the system is heating, we observe how fast it heats, and we can suppose that this is the speed for the current cold.

I think the “fall time” is sufficiently linear with -LossWatts and the “rise time” is sufficiently linear with HeatWatts-LossWatts=NetHeatWatts. Now when we know “NetHeatWatts” from the previous heat cycle, we can already get an idea of the time needed to heat from say 18°C to 20°C and therefore determine when to start heating to get 20°C at 6:30 for instance. It does not have to be precise to the second, a warm radiator is always nicer than a cold one, at the same temperature.

It’s not dead easy to do, but I think it’s doable.

1 Like

It seems I found the right settings…

  kp : 50
  ki : 0.001
  kd : 150000
  pwm: 900
  keep_alive: 00:15:00

There is an issue, still. During my daughter’s bath, the room temperature increased over the set point, and when it decreased the PID output was too low to slow down the ramp down and stabilize around the set point, it undershoot of 1°C before recovering.

I’d like to plot the P, I and D values computed by the controller, but I don’t know how to expose them as attributes in the climate entity or as dedicated sensor’s in Home Assistant. I can’t find docs on developing for HACS.