Help understanding Integral contribution

I want to return to something that has been annoying me for a while now, in that my beer never seems to be able to reach my setpoint, but always tends towards 0.1 or 0.2 degrees above it, and holds.

I haven’t really chased this up and 0.1 to 0.2 is really nothing in the grand scheme of things, but I would just like to understand what is going on, just out of curiosity.

It seems to be because the Integrals never really contributes much.

The below graph is one that illustrates this over the last couple of days of my current fermentation, but is pretty typical of what I have seen over the last year. The settings that I have now provide pretty consistent results and I am generally happy.

Current logs of my setup

I’m using a dynamic fridge setpoint, but I want to focus on the Beer PID

Current values are Kp=5, Ti=4h, Td=15m

Here is a graph of the PID in the last hour

You can see that in this time, the error has been largely steady at -0.28c, it is my understanding that the Ti should also reach Kp * -0.28c after Ti, which is 4 hours. The above graph is only an hour long, but I would expect it to make some amount towards the error, but it is not doing that.

If I remove all but the PID and Output targets, you can see that the I tends below 0 for a while, then regresses back.

What I also don’t understand how all of this influences the output value as the sawtooth nature does not really align with any of the rest of it. I assume this value is how it is interacting with the

I have had a look at the PID code, where I can see that the increase is (m_p + m_d)/m_kp, so the derivative is impacting the accumulation of I

    decltype(m_integral) integral_increase = 0;
    if (m_ti != 0 && m_kp != 0 && !m_boilModeActive) {
        integral_increase = cnl::quotient(m_p + m_d, m_kp);
        m_integral += integral_increase;
        m_i = m_integral * safe_elastic_fixed_point<4, 27>(cnl::quotient(m_kp, m_ti));
    } else {
        m_integral = integral_t{0};
        m_i = 0;

Incase you are interested, here are the same graphs, over the last day to show more long term behaviour

Disregard the flat spot between 10:30am and 12:30, I had some unrelated wifi issues.

I assume Kp to be -5, given that it’s a cooler?

The sawtooth is somewhat typical behavior for a system where I is not high enough to maintain steady state. It’s keeping temperature by tapping the P until error is at 0 again.

The solution to this, and the inner workings of the PID code is a bit beyond my rudimentary knowledge.
@Elco will be along when he has some time to spare.

This is the Beer PID driving a fridge setpoint, those are the current settings.

No issues on the timing of this, I rarely see it go beyond 0.5c which is not impacting quality. I am just being pedantic.

What are the settings for the fridge cool PID?

While dynamic fridge setpoints are completely functional (and kind of fun to play with), I do have to mention that we’ve found that direct beer control works just fine. Both approaches can be tuned to the minimum sensor resolution of 0.05*C.

Kp=-25, Ti=6h, Td=5m

I might try out direct control after this brew, maybe if it is simpler, it will work better.

Adding both the differential and the proportional part to the integral helps with reducing the overshoot caused by integrator windup.

It shouldn’t affect steady-state much, because the derivative is zero. The average of the derivative will also be zero in a non-steady-state scenario.

The derivative part is limited to only counteract the proportional part, this could introduce a bias.

I think, in this case, the problem is not in the beer PID, but in the fridge PID. It is not averaging around the setpoint. Probably due to integrator anti-windup.

Do you have a graph of the fridge PID?
I think some extra filtering on the fridge setpoint might help. Maybe even some on the beer setpoint to reduce the effect of a single bit flip of the sensor.