Open Traumflug opened 10 years ago
If you follow the thermistortable branch, you can see that I implemented the logarithm algorithm used in the HP-35 pocket calculator. Remember this one? It was the first pocket calculator coming with trigonometric functions, leading to the end of slide rule usage.
It's an algorithm working without any function calls already, so it should be possible to implement it as preprocessor macro. Compared to avr-libc's implementation (2500 CPU cycles on average), performance is poor, so far (14'000 CPU cycles on average), but I'm positive I can improve this further by using more integer math and also adjusting better for binary numbers (base 2). The HP-35 used BCD (base 10).
OK, I'm back in the vincinity of a table lookup when doing direct calculation:
LED on time minimum: 1646 clock cycles,
LED on time maximum: 2714 clock cycles,
LED on time average: 2486,61 clock cycles,
One issue left is, results are plain wrong near room temperature. Should be fixable.
This new int_log() is just 1650 clock cycles on average :-)
Now that we have auto-generated thermistor tables this issue here certainly needs much less a solution.
But the last few days/weeks I built the ISTAtrol, which uses thermistors, too, but certainly doesn't have the room to do interpolated table lookups. The used ATtiny has 2 kB Flash, 1.7 kB of which is occupied by V-USB. So I had to get away with much less code.
Lesson # 1 learned there was that it's a mistake to calculate a Celsius value from every thermistor reading. Regulating algorithms work against raw thermistor readings just as well. Accordingly, translating from Celsius to thermistor reading and back should be part of the user interface. On ISTAtrol that's done on the host, in Teacup this could be done as part of processing M104/M105, which is used rather rarely.
Lesson # 2 is that a simple predictive integrating term regulates temperature just fine. It goes as following:
t0 is the last temp reading. t1 is the current temp reading. td = t1 - t0 is the change in temperature.
Now the algorithm simply predicts the future by extrapolating the recent temperature change:
tf = t1 + 4 * td = t1 + 4 * (t1 - t0) (where 4 is some kind of 'sensitivity' factor; can be another number, too).
If tf, the future temperature, lies within the allowed hysteresis of the target temperature, do nothing. If it lies outside, raise or lower heating power by an incremental step accordingly.
This algorithm works surprisingly well. Is temperature totally off, full heating power is reached quickly. As it gets closer to the target, power is reduced already, to keep temperature pretty much on the spot, closer than allowed hysteresis. It commands heating power changes as soon as the temperature starts to fall due to e.g. a hotend starting extruding, before the hysteresis corridor is left.
Algorithm can be seen in main() in main.c: https://github.com/Traumflug/ISTAtrol/blob/master/firmware/main.c, line 498 to 514. In this case, motor_open() is an incremental increase in heating power, motor_close() the opposite.
Lesson # 3 is that an exponential moving average can be as simple as
uint32_t temp_eight = 8 * 100; // eight times 'temp'
uint16_t temp = 100; // smoothed temp reading
uint16_t temp_reading = 0; // raw temp reading from ADC
// Smooth in a new reading.
temp_eight -= temp;
temp_eight += temp_reading;
temp = (temp_eight + 4) / 8; // '+ 4' for rounding
No need for storing or recalculating a history all the time, no multiplication, no float, dividing by 8 is a bit-shift operation. For a more extended explanation see here: http://reprap-diy.com/moving_average_on_avrs
L # 3 I'm using often for fast smoothing excel tables.
with smooth = (new_val + (x - 1) * old_smooth) / x
... which is exactly the same for x = 8, except that x * old_smooth
isn't stored for re-use.
shake hands
shake ;)
tf = t1 + 4 * td = t1 + 4 * (t1 - t0) (where 4 is some kind of 'sensitivity' factor; can be another number, too).
Your sensitivity factor is more a timing value. td is the temperature difference over a specific time. In units delta_T/s. The smoothing factor gives us then the temperature we need in [s].
Yes, exactly.
Still it mostly acts as sensitivtiy, because this prediction into the future is a straight vector. The longer this vector is, the more likely the prediction leaves the hysteresis band, the more likely it triggers a reaction.
Example:
You are right. Thanks for pointing this out. It's not an exact prediction. Depending on how fast your reader is and how stable, this is also a good thing for reprap-printers.
I've taken that code some month ago into Marlin4Due. It is very easy to adjust and very stable. PID is much harder to get good values. Especially when you don't have a autotune.
L # 2 and # 3 can be found here: https://github.com/Traumflug/Teacup_Firmware/tree/temp_prediction
don't waste your time in this code. I made something big wrong...
An update: https://github.com/Traumflug/Teacup_Firmware/commit/af324df67 This is close to the idea of Willy, some years ago. https://gist.github.com/Wurstnase/50e10248218fd5b1eaac
Currently the firmware uses 14.2 fix-point for the temperature. With the idea of the temp prediction and a jump of temperature of 1bit that becomes an issue because we scale this with 10ms. Scaling this to 1s we have then a jump of 25° in one second. This is not easy / accurate to control.
The idea is to use 11.5 fix-point. In that case 1bit only makes a jump of ~3.1°. Any pro or contra?
With 2^11 we can smooth it with factor 4 without overflow up to 512°.
These 1-bit-jumps mean to "jump" applied heater power, not the temperature reading.
Also suggested above is to not control Celsius readings, but raw ADC readings. This removes the need to do an expensive ( ~2000 clock ticks) ADC -> Celsius conversion every few milliseconds. Instead one would do the opposite conversion on a temp_set() (M104, M140) only once.
argh Yet another branch. Couldn't hold me back, though.
The goal is to get rid of TemperatureTable.h in favour of adding these parameters in config.h. No precomputed table, just the parameters.
So far I've implemented this (a bit messy) using floating point math. Performance impact isn't as bad as I thought. With ThermistorTable.double.h:
Calculated fully with floating point math:
All integer math, except log():
With all of this, precision is very close to that of using the table, probably a bit better. No surprise, the challenge is calculating log() with integers. I've found http://www.jacques-laporte.org/Logarithm_1.htm and http://www.quinapalus.com/efunc.html , both claim to do such things.
It's on the thermistortable branch.