Why reset the errorSum when the output reaches the maximum value?
I'm trying to use MiniPID as a temperature controller. I measure the current temperature in °C, and drive a PWM (with a value between 0 and 400) relay to heat the space.
For now, I set arbitrary settings for P, I and D, just to see how the system reacts.
I initialize my pid :
pid = new MiniPID(1.0, 0.1, 0.1); pid->setOutputLimits(0.0, 400.0);
and call getOutput() in a loop:
auto out = pid->getOutput(0.0, 20.0);
The value of 'out' increases each time getOutput() is called (2.0, 4.0, 6.0,...) until it reaches the maximum value (400). The next value will be 4, while I thought it would return 400 forever, until the current value increases (which won't happen in this example, of course).
I think it happens on line 240 of MiniPID.cpp, when errorSum is set to the current error. As the error is lower, the PID returns a lower value.
In my case, it would mean that the setting of the heater would increase up to the max value, then set to nearly 0, and increase again.
Is this behavior expected? Is my understanding of MiniPID correct? Should I use it another way?
Thanks for your help!
JF
Happy to clarify! All the I term windup prevention is something not present in most controllers, so it's not surprising you haven't seen it. As I haven't seen it a lot, it's also possible it's implemented in a way that's detrimental to some use cases or operation.
Most of my use cases are P-term driven, which is the case I test a lot and optimize for. The reason I reset it to error is so that it transitions smoothly from being P driven to being PI driven, as the system approaches a setpoint after a change. When the primary value is the P term, then this line actually keeps the I term error for the entire duration of the setpoint change, making the function basically out = P*error + I*error . As you approach a setpoint, P starts reducing, and at some point I can increase, operating as a normal PID.
The upshot is that this wildly reduces overshoot (by reducing the general value of the I term), and makes it easier to tune, while allowing a more aggressive I term (since there's no windup to cause problems).
I haven't tested this a lot on I driven systems though, as I don't have any lying around. I can see how the I term would be such a significant part of the output in the general case, and this reset would cause problems there. I have an idea though: Try changing line #239 to this:
if(minOutput!=maxOutput && !bounded(output-Ioutput, minOutput,maxOutput) ){
or maybe a raw P term like this, which will prevent a D spike from causing a reset on one loop.
if(minOutput!=maxOutput && !bounded(Poutput, minOutput,maxOutput) ){
That should resolve your issue by properly validating that the P or PD terms alone are what's causing the push over the max output. This better maps my assumption (a PD driven system) into that code. Let me know if this resolves the issue you're seeing, and if so I'll patch it in.
On a similar vein, I believe the ramp rate functionality will encounter a similar bug for I driven systems on line #247.
Rather than a reset, I think the appropriate action in this case is to only add error and errorSum if the signs are different. Perhaps something like this:
if( Float.signum(error) != Float.signum(errorSum)) errorSum+= error;
Not adding it when ramped is an option, but it'll cause a lockup on pure-I systems. I suspect it would also cause weird quirks in low-P high-I systems, like refusing to approach the setpoint as the I term backs off, and the error slowly starts to decrease until it crosses zero and flips the sign.
@tekdemo Thank you very much for the clarification! I implemented your second solution, and now, the integrator works as I expected it. I'll continue my tuning and tests with this modification. However, I don't know if this modification will have impact on other projects (yours, for example) using MiniPID. I don't use the ramp rate functionality (for now, at least), but thanks for the hint about this issue!