1 #include "PID_Autotuner.h"
3 #include <cmath> // std::abs
4 #include "SlowTicker.h"
7 #define DEBUG_PRINTF s->printf
9 PID_Autotuner::PID_Autotuner()
19 void PID_Autotuner::on_module_loaded()
22 THEKERNEL
->slow_ticker
->attach(20, this, &PID_Autotuner::on_tick
);
23 register_for_event(ON_IDLE
);
24 register_for_event(ON_GCODE_RECEIVED
);
27 void PID_Autotuner::begin(TemperatureControl
*temp
, float target
, StreamOutput
*stream
, int ncycles
)
30 oStep
= temp
->heater_pin
.max_pwm(); // use max pwm to cycle temp
31 nLookBack
= 5 * 20; // 5 seconds of lookback
35 if (lastInputs
!= NULL
) delete[] lastInputs
;
36 lastInputs
= new float[nLookBack
+1];
41 t
->target_temperature
= 0.0;
43 target_temperature
= target
;
44 requested_cycles
= ncycles
;
46 if (peaks
!= NULL
) delete[] peaks
;
47 peaks
= new float[ncycles
];
49 for (int i
= 0; i
< ncycles
; i
++) {
57 float refVal
= t
->get_temperature();
61 t
->heater_pin
.pwm(oStep
); // turn on to start heating
63 s
->printf("%s: Starting PID Autotune, %d max cycles, M304 aborts\n", t
->designator
.c_str(), ncycles
);
66 void PID_Autotuner::abort()
71 t
->target_temperature
= 0;
76 s
->printf("PID Autotune Aborted\n");
82 if (lastInputs
!= NULL
)
87 void PID_Autotuner::on_gcode_received(void *argument
)
89 Gcode
*gcode
= static_cast<Gcode
*>(argument
);
91 if ((gcode
->has_m
) && (gcode
->m
== 304))
95 uint32_t PID_Autotuner::on_tick(uint32_t dummy
)
99 tickCnt
+= 1000/20; // millisecond tick count
104 * this autopid is based on https://github.com/br3ttb/Arduino-PID-AutoTune-Library/blob/master/PID_AutoTune_v0/PID_AutoTune_v0.cpp
106 void PID_Autotuner::on_idle(void *)
116 if(peakCount
>= requested_cycles
) {
121 float refVal
= t
->get_temperature();
123 if (refVal
> absMax
) absMax
= refVal
;
124 if (refVal
< absMin
) absMin
= refVal
;
126 // oscillate the output base on the input's relation to the setpoint
127 if (refVal
> target_temperature
+ noiseBand
){
129 //t->heater_pin.pwm(output);
130 t
->heater_pin
.set(0);
131 } else if (refVal
< target_temperature
- noiseBand
) {
133 t
->heater_pin
.pwm(output
);
136 bool isMax
= true, isMin
= true;
139 for (int i
= nLookBack
- 1; i
>= 0; i
--) {
140 float val
= lastInputs
[i
];
141 if (isMax
) isMax
= refVal
> val
;
142 if (isMin
) isMin
= refVal
< val
;
143 lastInputs
[i
+ 1] = lastInputs
[i
];
146 lastInputs
[0] = refVal
;
148 if (lookBackCnt
< nLookBack
) {
149 lookBackCnt
++; // count number of times we have filled lastInputs
150 //we don't want to trust the maxes or mins until the inputs array has been filled
155 if (peakType
== 0) peakType
= 1;
156 if (peakType
== -1) {
162 peaks
[peakCount
] = refVal
;
165 if (peakType
== 0) peakType
= -1;
172 if (peakCount
< requested_cycles
) peaks
[peakCount
] = refVal
;
175 // we need to ignore the first cycle warming up from room temp
177 if (justchanged
&& peakCount
> 2) {
178 if(peakCount
== 3) { // reset min to new min
181 //we've transitioned. check if we can autotune based on the last peaks
182 float avgSeparation
= (std::abs(peaks
[peakCount
- 1] - peaks
[peakCount
- 2]) + std::abs(peaks
[peakCount
- 2] - peaks
[peakCount
- 3])) / 2;
183 s
->printf("Cycle %d: max: %g, min: %g, avg separation: %g\n", peakCount
, absMax
, absMin
, avgSeparation
);
184 if (peakCount
> 3 && avgSeparation
< 0.05 * (absMax
- absMin
)) {
185 DEBUG_PRINTF("Stabilized\n");
193 if ((tickCnt
% 1000) == 0) {
194 s
->printf("%s: %5.1f/%5.1f @%d %d/%d\n", t
->designator
.c_str(), t
->get_temperature(), target_temperature
, output
, peakCount
, requested_cycles
);
195 DEBUG_PRINTF("lookBackCnt= %d, peakCount= %d, absmax= %g, absmin= %g, peak1= %lu, peak2= %lu\n", lookBackCnt
, peakCount
, absMax
, absMin
, peak1
, peak2
);
200 void PID_Autotuner::finishUp()
202 //we can generate tuning parameters!
203 float Ku
= 4*(2*oStep
)/((absMax
-absMin
)*3.14159);
204 float Pu
= (float)(peak1
-peak2
) / 1000;
205 s
->printf("\tKu: %g, Pu: %g\n", Ku
, Pu
);
208 float ki
= 1.2 * Ku
/ Pu
;
209 float kd
= Ku
* Pu
* 0.075;
211 s
->printf("\tTrying:\n\tKp: %5.1f\n\tKi: %5.3f\n\tKd: %5.0f\n", kp
, ki
, kd
);
217 s
->printf("PID Autotune Complete! The settings above have been loaded into memory, but not written to your config file.\n");
221 t
->target_temperature
= 0;
222 t
->heater_pin
.set(0);
230 if (lastInputs
!= NULL
)