Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Smoothie by
PID_Autotuner.cpp
00001 #include "PID_Autotuner.h" 00002 #include "Kernel.h" 00003 #include <cmath> // std::abs 00004 00005 #define DEBUG_PRINTF s->printf 00006 00007 PID_Autotuner::PID_Autotuner() 00008 { 00009 t = NULL; 00010 s = NULL; 00011 lastInputs = NULL; 00012 peaks = NULL; 00013 tick = false; 00014 tickCnt= 0; 00015 } 00016 00017 void PID_Autotuner::on_module_loaded() 00018 { 00019 tick = false; 00020 THEKERNEL->slow_ticker->attach(20, this, &PID_Autotuner::on_tick ); 00021 register_for_event(ON_IDLE); 00022 register_for_event(ON_GCODE_RECEIVED); 00023 } 00024 00025 void PID_Autotuner::begin(TemperatureControl *temp, float target, StreamOutput *stream, int ncycles) 00026 { 00027 noiseBand = 0.5; 00028 oStep = temp->heater_pin.max_pwm(); // use max pwm to cycle temp 00029 nLookBack = 5 * 20; // 5 seconds of lookback 00030 lookBackCnt= 0; 00031 tickCnt= 0; 00032 00033 if (lastInputs != NULL) delete[] lastInputs; 00034 lastInputs = new float[nLookBack+1]; 00035 t = temp; 00036 s = stream; 00037 00038 t->heater_pin.set(0); 00039 t->target_temperature = 0.0; 00040 00041 target_temperature = target; 00042 requested_cycles = ncycles; 00043 00044 if (peaks != NULL) delete[] peaks; 00045 peaks = new float[ncycles]; 00046 00047 for (int i = 0; i < ncycles; i++) { 00048 peaks[i] = 0.0; 00049 } 00050 00051 peakType = 0; 00052 peakCount = 0; 00053 justchanged = false; 00054 00055 float refVal = t->get_temperature(); 00056 absMax = refVal; 00057 absMin = refVal; 00058 output= oStep; 00059 t->heater_pin.pwm(oStep); // turn on to start heating 00060 00061 s->printf("%s: Starting PID Autotune, %d max cycles, M304 aborts\n", t->designator.c_str(), ncycles); 00062 } 00063 00064 void PID_Autotuner::abort() 00065 { 00066 if (!t) 00067 return; 00068 00069 t->target_temperature = 0; 00070 t->heater_pin.set(0); 00071 t = NULL; 00072 00073 if (s) 00074 s->printf("PID Autotune Aborted\n"); 00075 s = NULL; 00076 00077 if (peaks != NULL) 00078 delete[] peaks; 00079 peaks = NULL; 00080 if (lastInputs != NULL) 00081 delete[] lastInputs; 00082 lastInputs = NULL; 00083 } 00084 00085 void PID_Autotuner::on_gcode_received(void *argument) 00086 { 00087 Gcode *gcode = static_cast<Gcode *>(argument); 00088 00089 if ((gcode->has_m) && (gcode->m == 304)) 00090 abort(); 00091 } 00092 00093 uint32_t PID_Autotuner::on_tick(uint32_t dummy) 00094 { 00095 if (t) 00096 tick = true; 00097 tickCnt += 1000/20; // millisecond tick count 00098 return 0; 00099 } 00100 00101 /** 00102 * this autopid is based on https://github.com/br3ttb/Arduino-PID-AutoTune-Library/blob/master/PID_AutoTune_v0/PID_AutoTune_v0.cpp 00103 */ 00104 void PID_Autotuner::on_idle(void *) 00105 { 00106 if (!tick) 00107 return; 00108 00109 tick = false; 00110 00111 if (t == NULL) 00112 return; 00113 00114 if(peakCount >= requested_cycles) { 00115 finishUp(); 00116 return; 00117 } 00118 00119 float refVal = t->get_temperature(); 00120 00121 if (refVal > absMax) absMax = refVal; 00122 if (refVal < absMin) absMin = refVal; 00123 00124 // oscillate the output base on the input's relation to the setpoint 00125 if (refVal > target_temperature + noiseBand){ 00126 output= 0; 00127 //t->heater_pin.pwm(output); 00128 t->heater_pin.set(0); 00129 } else if (refVal < target_temperature - noiseBand) { 00130 output= oStep; 00131 t->heater_pin.pwm(output); 00132 } 00133 00134 bool isMax = true, isMin = true; 00135 00136 // id peaks 00137 for (int i = nLookBack - 1; i >= 0; i--) { 00138 float val = lastInputs[i]; 00139 if (isMax) isMax = refVal > val; 00140 if (isMin) isMin = refVal < val; 00141 lastInputs[i + 1] = lastInputs[i]; 00142 } 00143 00144 lastInputs[0] = refVal; 00145 00146 if (lookBackCnt < nLookBack) { 00147 lookBackCnt++; // count number of times we have filled lastInputs 00148 //we don't want to trust the maxes or mins until the inputs array has been filled 00149 return; 00150 } 00151 00152 if (isMax) { 00153 if (peakType == 0) peakType = 1; 00154 if (peakType == -1) { 00155 peakType = 1; 00156 justchanged = true; 00157 peak2 = peak1; 00158 } 00159 peak1 = tickCnt; 00160 peaks[peakCount] = refVal; 00161 00162 } else if (isMin) { 00163 if (peakType == 0) peakType = -1; 00164 if (peakType == 1) { 00165 peakType = -1; 00166 peakCount++; 00167 justchanged = true; 00168 } 00169 00170 if (peakCount < requested_cycles) peaks[peakCount] = refVal; 00171 } 00172 00173 // we need to ignore the first cycle warming up from room temp 00174 00175 if (justchanged && peakCount > 2) { 00176 if(peakCount == 3) { // reset min to new min 00177 absMin= refVal; 00178 } 00179 //we've transitioned. check if we can autotune based on the last peaks 00180 float avgSeparation = (std::abs(peaks[peakCount - 1] - peaks[peakCount - 2]) + std::abs(peaks[peakCount - 2] - peaks[peakCount - 3])) / 2; 00181 s->printf("Cycle %d: max: %g, min: %g, avg separation: %g\n", peakCount, absMax, absMin, avgSeparation); 00182 if (peakCount > 3 && avgSeparation < 0.05 * (absMax - absMin)) { 00183 DEBUG_PRINTF("Stabilized\n"); 00184 finishUp(); 00185 return; 00186 } 00187 } 00188 00189 justchanged = false; 00190 00191 if ((tickCnt % 1000) == 0) { 00192 s->printf("%s: %5.1f/%5.1f @%d %d/%d\n", t->designator.c_str(), t->get_temperature(), target_temperature, output, peakCount, requested_cycles); 00193 DEBUG_PRINTF("lookBackCnt= %d, peakCount= %d, absmax= %g, absmin= %g, peak1= %lu, peak2= %lu\n", lookBackCnt, peakCount, absMax, absMin, peak1, peak2); 00194 } 00195 } 00196 00197 00198 void PID_Autotuner::finishUp() 00199 { 00200 //we can generate tuning parameters! 00201 float Ku = 4*(2*oStep)/((absMax-absMin)*3.14159); 00202 float Pu = (float)(peak1-peak2) / 1000; 00203 s->printf("\tKu: %g, Pu: %g\n", Ku, Pu); 00204 00205 float kp = 0.6 * Ku; 00206 float ki = 1.2 * Ku / Pu; 00207 float kd = Ku * Pu * 0.075; 00208 00209 s->printf("\tTrying:\n\tKp: %5.1f\n\tKi: %5.3f\n\tKd: %5.0f\n", kp, ki, kd); 00210 00211 t->setPIDp(kp); 00212 t->setPIDi(ki); 00213 t->setPIDd(kd); 00214 00215 s->printf("PID Autotune Complete! The settings above have been loaded into memory, but not written to your config file.\n"); 00216 00217 00218 // and clean up 00219 t->target_temperature = 0; 00220 t->heater_pin.set(0); 00221 t = NULL; 00222 s = NULL; 00223 00224 if (peaks != NULL) 00225 delete[] peaks; 00226 peaks = NULL; 00227 00228 if (lastInputs != NULL) 00229 delete[] lastInputs; 00230 lastInputs = NULL; 00231 }
Generated on Tue Jul 12 2022 20:09:02 by
1.7.2
