I'm working on the air flow system for my smoker, well described https://www.avrfreaks.net/forum/d...
I've put together my 5VDC micro-fan, PT100 temperature sensor with conditioning to 0-5VDC input, and an appropriate re-purposed AVR board. The pieces appear to work well.
In "closing the loop", I thought I might as well do it right and use PID. As right now it is still on the bench, I have no solid idea of what the "time constant" of the "plant" is, and similar rigorous control engineering items. (For background, if I open up another of the four intake holes the [mostly] charcoal fire gets hotter such that the simple grill thermometer starts rising in a minute or two. So surprisingly fast.)
I adapted the code from Atmel's app note
AVR221: Discrete PID controller
and it is reacting as expected on the bench. [As written it takes 40% of Mega48 code space. About half of that for the PID routines and the other half being 32-bit arithmetic operations which were not otherwise in the app. I may make a 16-bit "lite" version for later re-use. After going through the below, small numbers are fine for this and many other AVR8 apps. After all, one typically only starts with a 10-bit or less ADC value, and ends up with a control parameter of 7- (e.g., whole percent) to maybe 10- or 12-bits for PWM or DAC.]
When the PID routine is called with the setpoint/desired and measurement/"process" values, the result is a 16-bit signed integer. Makes sense--trivial example with a P gain of 1 and measured value 10 below setpoint, the result is 10. Measured value 10 above setpoint, result is -10. And so on adding I and D terms.
1) Setpoint and measurement need to be in the same units. Makes sense for a general-purpose routine, but often isn't that way in a real app. Not really a problem for me in this app and no real question here, but I posted it as a discussion point for comment.
2) "Style" of the routine. The max values [internal and result] are more or less hard coded to avoid overflow and runaway. Again for comment: Shouldn't these be exposed with an API to allow limits less than INT_MAX and similar?
3) Shouldn't there be a deadband provision somewhere? Or is this done outside of the GP PID engine? [Perhaps I should be starting with a different PID "library" with all this stuff?]
4) Now the "real" question. Led into by the mention of "units" in 1).
What is the best way to scale the return value to my needs?
Let's work through a simple example, based on the above "Measured value 10 above setpoint, result is -10." Great; makes sense. In my smoker, that means that the measured temperature is 235 degrees F and the setpoint/desired temperature is 225 degrees F.
But the input to my "plant" sets the speed (or duty) of the fan. Let's say I want a value 0% to 100% to PWM the fan. I can't go lower than 0%/off.
How do I take the output of the PID routine, -10, and apply that to the fan percentage? For sake of discussion, the fan could currently be running at 25% or 52%, right? It just needs to be cut back a bit...