Wednesday, December 24, 2008
I started writing the software to the drive my temperature controlled oven thinking it was going to be a fairly easy process: Just whack in a PID controller, tune the constants a little, Voila!
Not so much.
Some of this is undoubtedly my inexperience, some of it is the extreme time lag between action and result, and some of it is the fairly exacting requirements I placed.
The model is something like: The heating element heats up ~ 0.6 Celsius per second that the power is applied. It cools at around r= ~0.007 (ie. It cools at roughly 0.7% of the difference between it's temperature and ambient temperature per second)
The heat transfer rate between the element and the board has r = ~0.085
This means the temperature as measured at the thermocouple is heavily lagged behind the applied power. Something like 12 - 15 seconds lagged.
This means that it's very difficult to get good performance out of the PID controller. The huge lag means that overshoot is difficult to control without high Kd constants. This would be ok, but for the relatively large noise in the temperature measurement. Quantization noise alone is ~ 1% and there's another 2% in measurement noise.
This could be reduced by low-pass filtering the thermocouple measurements but the sample rate is only about 5Hz. This means that to get the noise down to ~0.5% by straight averaging would need to average over ~ 15 data points which means 1.5 seconds of measurement lag into a system that already has extreme lag. :(
The first graph above shows my attempts at straight PID control. Keeping Kd low enough to avoid crazy noise in the PWM output means pushing Kp fairly low as low. The result is nice and stable about 50 degrees, and horribly over-damped at 120 degress.
I spent far too long fighting this, tweaking constants back and forth, generally wasting time.
Eventually, I gave up on the straight PID approach, took a deep breath and implemented a Kalman filter to be able to accurately estimate the current element temperature from the lagged thermocouple measurements and the known inputs.
This proved to be more than a little hairy: My maths is extremely rusty, the kalman filter has lots of matrix math, and I'm writing it all in C (it's running on an embedded board). It took me a couple of hours just to understand what was going on with the state update equations! (I may do a later post with some more detail on the maths).
Oh, and the modelled process is non-linear so I had to move it all to log space to linearize it so the kalman filter can operate.
The result is the second graph. The red line is what I care about, and you can see it's doing much better. It quickly gets to the setpoint (orange line), and then follows it fairly accurately (albeit lagged). The green line is the heating element. It deliberately overshoots the setpoint to improve the speed at which the measured temperature gets to the setpoint.
I'm pretty happy with this; I could just leave it like this.
But I probably won't. :) In the spirit that nothing suceeds quite like overkill, I'll probably attempt to write a LQR (essentially, it inverts the kalman filter to be able to predict which needs to be done). This would remove most of the lag between the measured temperature and the setpoint.