CapnBry / HeaterMeter

HeaterMeter and LinkMeter Arduino BBQ Controller
https://tvwbb.com/forums/heatermeter-diy-bbq-controller.85/
MIT License
503 stars 83 forks source link

Dynamic Integrator Cutback #26

Closed R-Burk closed 8 years ago

R-Burk commented 8 years ago

Amount the integrator gets cut is dependent on how fast temperature is moving. Formula is a second order Butterworth filter ( https://en.wikipedia.org/wiki/Butterworth_filter). Values were determined from PID simulation and real cook testing.

R-Burk commented 8 years ago

I ran into a frustration when trying various PID tuning rules. When I had very good values then 1/2 cutback would kill the work. Other times I was wishing the cutback would be more harsh to keep me from a severe overshoot.

CapnBry commented 8 years ago

Oh I love this idea! I have a couple of problems with it.

Implementation - It adds 122 bytes to do a single calculation. We can optimize this though.

Function - Here's where I have the biggest problem. If the temperature has changed 1 degree in the past two minutes, 83% of pidI is retained. That's fine, 2 degrees in 2 minutes = 55%. 3 degrees in 2 minutes 35%... It is dampening waaaaay too much. In that at 5 degrees per minute (which is a pretty slow climb still), it cuts it to just 5% of its value which is almost effectively zeroing it out. The calculation effectively only works on a small range of input data.

I do see value in this but I think it overdampens the integral sum. What about this:

        const float PID_INTEGRAL_CUT_FACTOR = 5.0f;
        float deriv = Probes[TEMP_CTRL]->Temperature - Probes[TEMP_CTRL]->TemperatureAvg;
        _pidCurrent[PIDI] *= PID_INTEGRAL_CUT_FACTOR / (PID_INTEGRAL_CUT_FACTOR + deriv);

image

The graph is for the cut factor of 5. I think the value should be higher maybe closer to 10 but I'd need to experiment. It cuts the output a lot less than your filter does.

CapnBry commented 8 years ago

Looking at this and running a few grill startups during the day today, I get around a 4-5F delta T when the pit temperature is reached. It definitely can use more cutback than my code above uses, which I realized also doesn't work when decreasing the setpoint.

I'm not sure the PIDI is handled right in other places in the code though. If we're lowering the temperature, the PIDI is set to 0 on the setpoint change and then goes negative until we reach setpoint. This seems wrong, the sum should never be negative because assuming we're right at temp, the sum should roughly equal the PID output value needed to maintain that temp.

I might want to constrain the integrated sum to keep it from going negative, but that raises even more questions. Like should the sum scale, and what temperature units should it use when scaling, and if the sum should be allowed to decrease when the PID output is 0 (which might even eliminate the need for this cutback).

R-Burk commented 8 years ago

The formula you have was actually my first consideration. I went to the second order because it punished higher heat up rates faster and was gentler at lower values. Probably is no perfectly correct answer.

Ironic you mention using a value of 10 as I have flip-flopped using that value quite often as it's gentler. I really don't want to add more values to play with in the configuration panel. Maybe should the advanced stuff like overlocking warnings in video drivers.

Trying to figure out why your saying the deriv is over 2 minutes though. TemperatureAvg is a 30 point moving average ie 30 seconds. I was doing a multiply and then divide by 2 which was redundant. So if we assume those imaginary 2 is there but factored out then we using temperature over a 1 minute period

R-Burk commented 8 years ago

I saw this the other day when I was doing a pull against your master

@@ -686,7 +689,6 @@ void GrillPid::setSetPoint(int value)
  _setPoint = value;
  _pitStartRecover = PIDSTARTRECOVER_STARTUP;
  _manualOutputMode = false;
  _pidCurrent[PIDI] = 0.0f;
  LidOpenResumeCountdown = 0;
}

I left the reset line out as I wasn't sure of the use case to add it. I did like the one where if the I term is 0 them make _pidCurrent[PIDI] = 0.

Maybe do a cutback when decreasing setpoint something like:

if (value < _setPoint) { _pidCurrent[PIDI] *= value/_setPoint'} 
_setPoint = value; 

On an up setpoint your going to need more integrator less

If your talking about doing Backtracking Anti-Windup then I've tried that. Also tried changing the constraints on the current Conditonal Anti-Windup from 0 < _lastOutput < 100 ( current) to -1 < control < 101. Neither change made very little difference for me

CapnBry commented 8 years ago

Trying to figure out why your saying the deriv is over 2 minutes though

Yup you're right about this, the fan average is over 2 minutes (120 periods), the temp is only 30 periods. I got them mixed up in my head for some reason.

I've done a lot of testing with trying various formulas for the cutback and they all seem to work about the same. Generally my deriv is around 4 degrees on startup and the sum cutback doesn't make too much difference because the ceramic grill is cold so it actually needs a little more oomph to maintain the setpoint. Going the other direction almost required different code because the sum needed to scale up a little, but also did not have a tremendous effect. I also tried using a constant * deriv * Kd. I feel like we're tuning the algorithm to specifically to match our grills though and anything we come up with might not benefit other setups and may actually make things worse the more we try to fudge it.

So I am going to say we'll hold off on this for a while and if you can come up with some other ideas, maybe bring them up on the forum and we can try them out. I did remove the PIDI = 0 when changing setpoints which may be of benefit, and other code to prevent PIDI from going negative as well (which it would have when lowering the setpoint, it would be set to to 0 then turned way negative waiting for the temp to drop). I'm going to close this and we can discuss it after v13 if you still think there's merit.