Added PID autotune. (experimental)

M303 Starts autotune. Wait till the Kp Ki and Kd constants are printed.
Put these values in Configuration.h
This commit is contained in:
Erik van der Zalm 2012-03-08 21:43:21 +01:00
parent 116dc86b8a
commit c077316b2b
5 changed files with 274 additions and 169 deletions

View file

@ -109,6 +109,7 @@
// M501 - reads parameters from EEPROM (if you need reset them after you changed them temporarily). // M501 - reads parameters from EEPROM (if you need reset them after you changed them temporarily).
// M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to. // M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to.
// M503 - print the current settings (from memory not from eeprom) // M503 - print the current settings (from memory not from eeprom)
// M303 - PID relay autotune S<temperature> sets the target temperature. (default target temperature = 150C)
//Stepper Movement Variables //Stepper Movement Variables
@ -1197,6 +1198,13 @@ void process_commands()
allow_cold_extrudes(true); allow_cold_extrudes(true);
} }
break; break;
case 303: // M303 PID autotune
{
float temp = 150.0;
if (code_seen('S')) temp=code_value();
PID_autotune(temp);
}
break;
case 400: // finish all moves case 400: // finish all moves
{ {
st_synchronize(); st_synchronize();

View file

@ -734,7 +734,7 @@
#define encrot2 3 #define encrot2 3
#define encrot3 1 #define encrot3 1
#define SDCARDDETECT -1
//bits in the shift register that carry the buttons for: //bits in the shift register that carry the buttons for:
// left up center down right red // left up center down right red
#define BL_LE 7 #define BL_LE 7

View file

@ -62,7 +62,7 @@ int current_raw_bed = 0;
//=========================================================================== //===========================================================================
//=============================private variables============================ //=============================private variables============================
//=========================================================================== //===========================================================================
static bool temp_meas_ready = false; static volatile bool temp_meas_ready = false;
static unsigned long previous_millis_bed_heater; static unsigned long previous_millis_bed_heater;
//static unsigned long previous_millis_heater; //static unsigned long previous_millis_heater;
@ -132,7 +132,94 @@ static unsigned long previous_millis_bed_heater;
//=========================================================================== //===========================================================================
//============================= functions ============================ //============================= functions ============================
//=========================================================================== //===========================================================================
void PID_autotune(float temp)
{
float input;
int cycles=0;
bool heating = true;
soft_pwm[0] = 255>>1;
unsigned long temp_millis = millis();
unsigned long t1=temp_millis;
unsigned long t2=temp_millis;
long t_high;
long t_low;
long bias=127;
long d = 127;
float Ku, Tu;
float Kp, Ki, Kd;
float max, min;
SERIAL_ECHOLN("PID Autotune start");
for(;;) {
if(temp_meas_ready == true) { // temp sample ready
CRITICAL_SECTION_START;
temp_meas_ready = false;
CRITICAL_SECTION_END;
input = analog2temp(current_raw[0], 0);
max=max(max,input);
min=min(min,input);
if(heating == true && input > temp) {
if(millis() - t2 > 5000) {
heating=false;
soft_pwm[0] = (bias - d) >> 1;
t1=millis();
t_high=t1 - t2;
max=temp;
}
}
if(heating == false && input < temp) {
if(millis() - t1 > 5000) {
heating=true;
t2=millis();
t_low=t2 - t1;
if(cycles > 0) {
bias += (d*(t_high - t_low))/(t_low + t_high);
bias = constrain(bias, 20 ,235);
if(bias > 127) d = 254 - bias;
else d = bias;
SERIAL_PROTOCOLPGM(" bias: "); SERIAL_PROTOCOL(bias);
SERIAL_PROTOCOLPGM(" d: "); SERIAL_PROTOCOL(d);
SERIAL_PROTOCOLPGM(" min: "); SERIAL_PROTOCOL(min);
SERIAL_PROTOCOLPGM(" max: "); SERIAL_PROTOCOLLN(max);
if(cycles > 2) {
Ku = (4.0*d)/(3.14159*(max-min)/2.0);
Tu = ((float)(t_low + t_high)/1000.0);
Kp = 0.6*Ku;
Ki = 2*Kp/Tu;
Kd = Kp*Tu/8;
SERIAL_PROTOCOLPGM(" Kp: "); SERIAL_PROTOCOLLN(Kp);
SERIAL_PROTOCOLPGM(" Ki: "); SERIAL_PROTOCOLLN(Ki);
SERIAL_PROTOCOLPGM(" Kd: "); SERIAL_PROTOCOLLN(Kd);
}
}
soft_pwm[0] = (bias + d) >> 1;
cycles++;
min=temp;
}
}
}
if(input > (temp + 20)) {
SERIAL_PROTOCOLLNPGM("PID Autotune failed !!!, Temperature to high");
return;
}
if(millis() - temp_millis > 2000) {
temp_millis = millis();
SERIAL_PROTOCOLPGM("ok T:");
SERIAL_PROTOCOL(degHotend(0));
SERIAL_PROTOCOLPGM(" @:");
SERIAL_PROTOCOLLN(getHeaterPower(0));
}
LCD_STATUS;
}
}
void updatePID() void updatePID()
{ {
#ifdef PIDTEMP #ifdef PIDTEMP

View file

@ -1,162 +1,165 @@
/* /*
temperature.h - temperature controller temperature.h - temperature controller
Part of Marlin Part of Marlin
Copyright (c) 2011 Erik van der Zalm Copyright (c) 2011 Erik van der Zalm
Grbl is free software: you can redistribute it and/or modify Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Grbl is distributed in the hope that it will be useful, Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>. along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef temperature_h #ifndef temperature_h
#define temperature_h #define temperature_h
#include "Marlin.h" #include "Marlin.h"
#include "planner.h" #include "planner.h"
#ifdef PID_ADD_EXTRUSION_RATE #ifdef PID_ADD_EXTRUSION_RATE
#include "stepper.h" #include "stepper.h"
#endif #endif
// public functions // public functions
void tp_init(); //initialise the heating void tp_init(); //initialise the heating
void manage_heater(); //it is critical that this is called periodically. void manage_heater(); //it is critical that this is called periodically.
//low leven conversion routines //low leven conversion routines
// do not use this routines and variables outsie of temperature.cpp // do not use this routines and variables outsie of temperature.cpp
int temp2analog(int celsius, uint8_t e); int temp2analog(int celsius, uint8_t e);
int temp2analogBed(int celsius); int temp2analogBed(int celsius);
float analog2temp(int raw, uint8_t e); float analog2temp(int raw, uint8_t e);
float analog2tempBed(int raw); float analog2tempBed(int raw);
extern int target_raw[EXTRUDERS]; extern int target_raw[EXTRUDERS];
extern int heatingtarget_raw[EXTRUDERS]; extern int heatingtarget_raw[EXTRUDERS];
extern int current_raw[EXTRUDERS]; extern int current_raw[EXTRUDERS];
extern int target_raw_bed; extern int target_raw_bed;
extern int current_raw_bed; extern int current_raw_bed;
#ifdef BED_LIMIT_SWITCHING #ifdef BED_LIMIT_SWITCHING
extern int target_bed_low_temp ; extern int target_bed_low_temp ;
extern int target_bed_high_temp ; extern int target_bed_high_temp ;
#endif #endif
extern float Kp,Ki,Kd,Kc; extern float Kp,Ki,Kd,Kc;
#ifdef PIDTEMP #ifdef PIDTEMP
extern float pid_setpoint[EXTRUDERS]; extern float pid_setpoint[EXTRUDERS];
#endif #endif
// #ifdef WATCHPERIOD // #ifdef WATCHPERIOD
extern int watch_raw[EXTRUDERS] ; extern int watch_raw[EXTRUDERS] ;
// extern unsigned long watchmillis; // extern unsigned long watchmillis;
// #endif // #endif
//high level conversion routines, for use outside of temperature.cpp //high level conversion routines, for use outside of temperature.cpp
//inline so that there is no performance decrease. //inline so that there is no performance decrease.
//deg=degreeCelsius //deg=degreeCelsius
FORCE_INLINE float degHotend(uint8_t extruder) { FORCE_INLINE float degHotend(uint8_t extruder) {
return analog2temp(current_raw[extruder], extruder); return analog2temp(current_raw[extruder], extruder);
}; };
FORCE_INLINE float degBed() { FORCE_INLINE float degBed() {
return analog2tempBed(current_raw_bed); return analog2tempBed(current_raw_bed);
}; };
FORCE_INLINE float degTargetHotend(uint8_t extruder) { FORCE_INLINE float degTargetHotend(uint8_t extruder) {
return analog2temp(target_raw[extruder], extruder); return analog2temp(target_raw[extruder], extruder);
}; };
FORCE_INLINE float degTargetBed() { FORCE_INLINE float degTargetBed() {
return analog2tempBed(target_raw_bed); return analog2tempBed(target_raw_bed);
}; };
FORCE_INLINE void setTargetHotend(const float &celsius, uint8_t extruder) { FORCE_INLINE void setTargetHotend(const float &celsius, uint8_t extruder) {
target_raw[extruder] = temp2analog(celsius, extruder); target_raw[extruder] = temp2analog(celsius, extruder);
#ifdef PIDTEMP #ifdef PIDTEMP
pid_setpoint[extruder] = celsius; pid_setpoint[extruder] = celsius;
#endif //PIDTEMP #endif //PIDTEMP
}; };
FORCE_INLINE void setTargetBed(const float &celsius) { FORCE_INLINE void setTargetBed(const float &celsius) {
target_raw_bed = temp2analogBed(celsius); target_raw_bed = temp2analogBed(celsius);
#ifdef BED_LIMIT_SWITCHING #ifdef BED_LIMIT_SWITCHING
if(celsius>BED_HYSTERESIS) if(celsius>BED_HYSTERESIS)
{ {
target_bed_low_temp= temp2analogBed(celsius-BED_HYSTERESIS); target_bed_low_temp= temp2analogBed(celsius-BED_HYSTERESIS);
target_bed_high_temp= temp2analogBed(celsius+BED_HYSTERESIS); target_bed_high_temp= temp2analogBed(celsius+BED_HYSTERESIS);
} }
else else
{ {
target_bed_low_temp=0; target_bed_low_temp=0;
target_bed_high_temp=0; target_bed_high_temp=0;
} }
#endif #endif
}; };
FORCE_INLINE bool isHeatingHotend(uint8_t extruder){ FORCE_INLINE bool isHeatingHotend(uint8_t extruder){
return target_raw[extruder] > current_raw[extruder]; return target_raw[extruder] > current_raw[extruder];
}; };
FORCE_INLINE bool isHeatingBed() { FORCE_INLINE bool isHeatingBed() {
return target_raw_bed > current_raw_bed; return target_raw_bed > current_raw_bed;
}; };
FORCE_INLINE bool isCoolingHotend(uint8_t extruder) { FORCE_INLINE bool isCoolingHotend(uint8_t extruder) {
return target_raw[extruder] < current_raw[extruder]; return target_raw[extruder] < current_raw[extruder];
}; };
FORCE_INLINE bool isCoolingBed() { FORCE_INLINE bool isCoolingBed() {
return target_raw_bed < current_raw_bed; return target_raw_bed < current_raw_bed;
}; };
#define degHotend0() degHotend(0) #define degHotend0() degHotend(0)
#define degTargetHotend0() degTargetHotend(0) #define degTargetHotend0() degTargetHotend(0)
#define setTargetHotend0(_celsius) setTargetHotend((_celsius), 0) #define setTargetHotend0(_celsius) setTargetHotend((_celsius), 0)
#define isHeatingHotend0() isHeatingHotend(0) #define isHeatingHotend0() isHeatingHotend(0)
#define isCoolingHotend0() isCoolingHotend(0) #define isCoolingHotend0() isCoolingHotend(0)
#if EXTRUDERS > 1 #if EXTRUDERS > 1
#define degHotend1() degHotend(1) #define degHotend1() degHotend(1)
#define degTargetHotend1() degTargetHotend(1) #define degTargetHotend1() degTargetHotend(1)
#define setTargetHotend1(_celsius) setTargetHotend((_celsius), 1) #define setTargetHotend1(_celsius) setTargetHotend((_celsius), 1)
#define isHeatingHotend1() isHeatingHotend(1) #define isHeatingHotend1() isHeatingHotend(1)
#define isCoolingHotend1() isCoolingHotend(1) #define isCoolingHotend1() isCoolingHotend(1)
#endif #endif
#if EXTRUDERS > 2 #if EXTRUDERS > 2
#define degHotend2() degHotend(2) #define degHotend2() degHotend(2)
#define degTargetHotend2() degTargetHotend(2) #define degTargetHotend2() degTargetHotend(2)
#define setTargetHotend2(_celsius) setTargetHotend((_celsius), 2) #define setTargetHotend2(_celsius) setTargetHotend((_celsius), 2)
#define isHeatingHotend2() isHeatingHotend(2) #define isHeatingHotend2() isHeatingHotend(2)
#define isCoolingHotend2() isCoolingHotend(2) #define isCoolingHotend2() isCoolingHotend(2)
#endif #endif
#if EXTRUDERS > 3 #if EXTRUDERS > 3
#error Invalid number of extruders #error Invalid number of extruders
#endif #endif
int getHeaterPower(int heater); int getHeaterPower(int heater);
void disable_heater(); void disable_heater();
void setWatch(); void setWatch();
void updatePID(); void updatePID();
FORCE_INLINE void autotempShutdown(){ FORCE_INLINE void autotempShutdown(){
#ifdef AUTOTEMP #ifdef AUTOTEMP
if(autotemp_enabled) if(autotemp_enabled)
{ {
autotemp_enabled=false; autotemp_enabled=false;
if(degTargetHotend(ACTIVE_EXTRUDER)>autotemp_min) if(degTargetHotend(ACTIVE_EXTRUDER)>autotemp_min)
setTargetHotend(0,ACTIVE_EXTRUDER); setTargetHotend(0,ACTIVE_EXTRUDER);
} }
#endif #endif
} }
#endif
void PID_autotune(float temp);
#endif

View file

@ -315,19 +315,18 @@ void MainMenu::showStatus()
static int olddegHotEnd0=-1; static int olddegHotEnd0=-1;
static int oldtargetHotEnd0=-1; static int oldtargetHotEnd0=-1;
//force_lcd_update=true; //force_lcd_update=true;
if(force_lcd_update||feedmultiplychanged) //initial display of content if(force_lcd_update) //initial display of content
{ {
feedmultiplychanged=false;
encoderpos=feedmultiply; encoderpos=feedmultiply;
clear(); clear();
lcd.setCursor(0,0);lcdprintPGM("\002123/567\001 "); lcd.setCursor(0,0);lcdprintPGM("\002---/---\001 ");
#if defined BED_USES_THERMISTOR || defined BED_USES_AD595 #if defined BED_USES_THERMISTOR || defined BED_USES_AD595
lcd.setCursor(10,0);lcdprintPGM("B123/567\001 "); lcd.setCursor(10,0);lcdprintPGM("B---/---\001 ");
#endif #endif
} }
int tHotEnd0=intround(degHotend0()); int tHotEnd0=intround(degHotend0());
if((abs(tHotEnd0-olddegHotEnd0)>1)||force_lcd_update) //>1 because otherwise the lcd is refreshed to often. if((tHotEnd0!=olddegHotEnd0)||force_lcd_update)
{ {
lcd.setCursor(1,0); lcd.setCursor(1,0);
lcd.print(ftostr3(tHotEnd0)); lcd.print(ftostr3(tHotEnd0));
@ -379,8 +378,15 @@ void MainMenu::showStatus()
lcdprintPGM("Z:");lcd.print(ftostr52(current_position[2])); lcdprintPGM("Z:");lcd.print(ftostr52(current_position[2]));
oldzpos=currentz; oldzpos=currentz;
} }
static int oldfeedmultiply=0; static int oldfeedmultiply=0;
int curfeedmultiply=feedmultiply; int curfeedmultiply=feedmultiply;
if(feedmultiplychanged == true) {
feedmultiplychanged == false;
encoderpos = curfeedmultiply;
}
if(encoderpos!=curfeedmultiply||force_lcd_update) if(encoderpos!=curfeedmultiply||force_lcd_update)
{ {
curfeedmultiply=encoderpos; curfeedmultiply=encoderpos;
@ -391,12 +397,14 @@ void MainMenu::showStatus()
feedmultiply=curfeedmultiply; feedmultiply=curfeedmultiply;
encoderpos=curfeedmultiply; encoderpos=curfeedmultiply;
} }
if((curfeedmultiply!=oldfeedmultiply)||force_lcd_update) if((curfeedmultiply!=oldfeedmultiply)||force_lcd_update)
{ {
oldfeedmultiply=curfeedmultiply; oldfeedmultiply=curfeedmultiply;
lcd.setCursor(0,2); lcd.setCursor(0,2);
lcd.print(itostr3(curfeedmultiply));lcdprintPGM("% "); lcd.print(itostr3(curfeedmultiply));lcdprintPGM("% ");
} }
if(messagetext[0]!='\0') if(messagetext[0]!='\0')
{ {
lcd.setCursor(0,LCD_HEIGHT-1); lcd.setCursor(0,LCD_HEIGHT-1);
@ -404,7 +412,6 @@ void MainMenu::showStatus()
uint8_t n=strlen(messagetext); uint8_t n=strlen(messagetext);
for(int8_t i=0;i<LCD_WIDTH-n;i++) for(int8_t i=0;i<LCD_WIDTH-n;i++)
lcd.print(" "); lcd.print(" ");
messagetext[0]='\0'; messagetext[0]='\0';
} }