From 7787498eda8955288e99a18d02581a425203bac5 Mon Sep 17 00:00:00 2001 From: Chris Giacofei Date: Fri, 26 Apr 2024 08:42:17 -0400 Subject: [PATCH] incorporate tuning into main library. Not yet tested. --- PID_AutoTune_v0.cpp | 196 ++++++++++++++++++++++++++++++++++++++++++++ PID_AutoTune_v0.h | 55 +++++++++++++ PID_v1.cpp | 195 ++++++++++++++++++++++++++++++++++++++++--- PID_v1.h | 50 +++++++++-- 4 files changed, 476 insertions(+), 20 deletions(-) create mode 100644 PID_AutoTune_v0.cpp create mode 100644 PID_AutoTune_v0.h diff --git a/PID_AutoTune_v0.cpp b/PID_AutoTune_v0.cpp new file mode 100644 index 0000000..43decdc --- /dev/null +++ b/PID_AutoTune_v0.cpp @@ -0,0 +1,196 @@ +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include "PID_AutoTune_v0.h" + + +PID_ATune::PID_ATune(double* Input, double* Output) +{ + input = Input; + output = Output; + controlType =0 ; //default to PI + noiseBand = 0.5; + running = false; + oStep = 30; + SetLookbackSec(10); + lastTime = millis(); + +} + + + +void PID_ATune::Cancel() +{ + running = false; +} + +int PID_ATune::Runtime() +{ + //~ justevaled=false; + if(peakCount>9 && running) + { + running = false; + FinishUp(); + return 1; + } + unsigned long now = millis(); + + if((now-lastTime)absMax)absMax=refVal; + if(refValsetpoint+noiseBand) *output = outputStart-oStep; + else if (refVal=0;i--) + { + double val = lastInputs[i]; + if(isMax) isMax = refVal>val; + if(isMin) isMin = refVal2) + { //we've transitioned. check if we can autotune based on the last peaks + double avgSeparation = (abs(peaks[peakCount-1]-peaks[peakCount-2])+abs(peaks[peakCount-2]-peaks[peakCount-3]))/2; + if( avgSeparation < 0.05*(absMax-absMin)) + { + FinishUp(); + running = false; + return 1; + + } + } + atune_peak_change=false; + return 0; +} +void PID_ATune::FinishUp() +{ + *output = outputStart; + //we can generate tuning parameters! + Ku = 4*(2*oStep)/((absMax-absMin)*3.14159); + Pu = (double)(peak1-peak2) / 1000; +} + +double PID_ATune::GetKp() +{ + return controlType==1 ? 0.6 * Ku : 0.4 * Ku; +} + +double PID_ATune::GetKi() +{ + return controlType==1? 1.2*Ku / Pu : 0.48 * Ku / Pu; // Ki = Kc/Ti +} + +double PID_ATune::GetKd() +{ + return controlType==1? 0.075 * Ku * Pu : 0; //Kd = Kc * Td +} + +void PID_ATune::SetOutputStep(double Step) +{ + oStep = Step; +} + +double PID_ATune::GetOutputStep() +{ + return oStep; +} + +void PID_ATune::SetControlType(int Type) //0=PI, 1=PID +{ + controlType = Type; +} +int PID_ATune::GetControlType() +{ + return controlType; +} + +void PID_ATune::SetNoiseBand(double Band) +{ + noiseBand = Band; +} + +double PID_ATune::GetNoiseBand() +{ + return noiseBand; +} + +void PID_ATune::SetLookbackSec(int value) +{ + if (value<1) value = 1; + + if(value<25) + { + nLookBack = value * 4; + sampleTime = 250; + } + else + { + nLookBack = 100; + sampleTime = value*10; + } +} + +int PID_ATune::GetLookbackSec() +{ + return nLookBack * sampleTime / 1000; +} diff --git a/PID_AutoTune_v0.h b/PID_AutoTune_v0.h new file mode 100644 index 0000000..6e8ff19 --- /dev/null +++ b/PID_AutoTune_v0.h @@ -0,0 +1,55 @@ +#ifndef PID_AutoTune_v0 +#define PID_AutoTune_v0 +#define LIBRARY_VERSION 0.0.1 + +class PID_ATune +{ + + + public: + //commonly used functions ************************************************************************** + PID_ATune(double*, double*); // * Constructor. links the Autotune to a given PID + int Runtime(); // * Similar to the PID Compue function, returns non 0 when done + void Cancel(); // * Stops the AutoTune + + void SetOutputStep(double); // * how far above and below the starting value will the output step? + double GetOutputStep(); // + + void SetControlType(int); // * Determies if the tuning parameters returned will be PI (D=0) + int GetControlType(); // or PID. (0=PI, 1=PID) + + void SetLookbackSec(int); // * how far back are we looking to identify peaks + int GetLookbackSec(); // + + void SetNoiseBand(double); // * the autotune will ignore signal chatter smaller than this value + double GetNoiseBand(); // this should be acurately set + + double GetKp(); // * once autotune is complete, these functions contain the + double GetKi(); // computed tuning parameters. + double GetKd(); // + + private: + void FinishUp(); + bool isMax, isMin; + double *input, *output; + double setpoint; + double noiseBand; + int controlType; + bool running; + unsigned long peak1, peak2, lastTime; + int sampleTime; + int nLookBack; + int peakType; + double lastInputs[101]; + double peaks[10]; + int peakCount; + bool atune_peak_change; + //~ bool justevaled; + double absMax, absMin; + double oStep; + double outputStart; + double Ku, Pu; + +}; +#endif + diff --git a/PID_v1.cpp b/PID_v1.cpp index cb6637c..4c963b9 100644 --- a/PID_v1.cpp +++ b/PID_v1.cpp @@ -11,7 +11,7 @@ #include "WProgram.h" #endif -#include +#include "PID_v1.h" /*Constructor (...)********************************************************* * The parameters specified here are those for for which we can't set up @@ -24,9 +24,8 @@ PID::PID(double* Input, double* Output, double* Setpoint, myInput = Input; mySetpoint = Setpoint; inAuto = false; - - PID::SetOutputLimits(0, 255); //default output limit corresponds to - //the arduino pwm limits + //Tuner = new PID_ATune(Input, Output); + PID::SetOutputLimits(0, 100); //Arduino PWM limits 0-255 SampleTime = 100; //default Controller Sample Time is 0.1 seconds @@ -94,6 +93,110 @@ bool PID::Compute() else return false; } +int PID::ComputeTune() { + //~ justevaled=false; + if(peakCount>9 && running) + { + running = false; + FinishUp(); + return 1; + } + unsigned long now = millis(); + + if((now-lastTime)absMax)absMax=refVal; + if(refVal*mySetpoint+noiseBand) *myOutput = outputStart-oStep; + else if (refVal<*mySetpoint-noiseBand) *myOutput = outputStart+oStep; + + + //bool isMax=true, isMin=true; + isMax=true;isMin=true; + //id peaks + for(int i=nLookBack-1;i>=0;i--) + { + double val = lastInputs[i]; + if(isMax) isMax = refVal>val; + if(isMin) isMin = refVal2) + { //we've transitioned. check if we can autotune based on the last peaks + double avgSeparation = (abs(peaks[peakCount-1]-peaks[peakCount-2])+abs(peaks[peakCount-2]-peaks[peakCount-3]))/2; + if( avgSeparation < 0.05*(absMax-absMin)) + { + FinishUp(); + running = false; + return 1; + + } + } + atune_peak_change=false; + return 0; +} + +void PID::FinishUp() +{ + *myOutput = outputStart; + //we can generate tuning parameters! + Ku = 4*(2*oStep)/((absMax-absMin)*3.14159); + Pu = (double)(peak1-peak2) / 1000; +} + /* SetTunings(...)************************************************************* * This function allows the controller's dynamic performance to be adjusted. * it's called automatically from the constructor, but tunings can also @@ -172,14 +275,25 @@ void PID::SetOutputLimits(double Min, double Max) * when the transition from manual to auto occurs, the controller is * automatically initialized ******************************************************************************/ -void PID::SetMode(int Mode) +void PID::Mode(int Mode) { - bool newAuto = (Mode == AUTOMATIC); + bool newAuto = (Mode == 1); if(newAuto && !inAuto) { /*we just went from manual to auto*/ PID::Initialize(); } inAuto = newAuto; + OpMode = Mode; +} + +int PID::CycleMode() { + if (OpMode + 1 == 3) { + PID::Mode(0); + } else { + PID::Mode(OpMode + 1); + } + + return OpMode; } /* Initialize()**************************************************************** @@ -204,7 +318,7 @@ void PID::SetControllerDirection(int Direction) { if(inAuto && Direction !=controllerDirection) { - kp = (0 - kp); + kp = (0 - kp); ki = (0 - ki); kd = (0 - kd); } @@ -216,9 +330,68 @@ void PID::SetControllerDirection(int Direction) * functions query the internal state of the PID. they're here for display * purposes. this are the functions the PID Front-end uses for example ******************************************************************************/ -double PID::GetKp(){ return dispKp; } -double PID::GetKi(){ return dispKi;} -double PID::GetKd(){ return dispKd;} -int PID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;} +double PID::GetKp(){ return dispKp; } +double PID::GetKi(){ return dispKi;} +double PID::GetKd(){ return dispKd;} +int PID::Mode(){ return OpMode;} int PID::GetDirection(){ return controllerDirection;} +void PID::Cancel() +{ + running = false; +} +double PID::TunedKp() +{ + return 0.6 * Ku; +} + +double PID::TunedKi() +{ + return 1.2*Ku / Pu ; // Ki = Kc/Ti +} + +double PID::TunedKd() +{ + return 0.075 * Ku * Pu; //Kd = Kc * Td +} + +void PID::SetOutputStep(double Step) +{ + oStep = Step; +} + +double PID::GetOutputStep() +{ + return oStep; +} + +void PID::SetNoiseBand(double Band) +{ + noiseBand = Band; +} + +double PID::GetNoiseBand() +{ + return noiseBand; +} + +void PID::SetLookbackSec(int value) +{ + if (value<1) value = 1; + + if(value<25) + { + nLookBack = value * 4; + sampleTime = 250; + } + else + { + nLookBack = 100; + sampleTime = value*10; + } +} + +int PID::GetLookbackSec() +{ + return nLookBack * sampleTime / 1000; +} diff --git a/PID_v1.h b/PID_v1.h index 9cba046..e4e8603 100644 --- a/PID_v1.h +++ b/PID_v1.h @@ -2,15 +2,12 @@ #define PID_v1_h #define LIBRARY_VERSION 1.2.1 + class PID { - public: - //Constants used in some of the functions below - #define AUTOMATIC 1 - #define MANUAL 0 #define DIRECT 0 #define REVERSE 1 #define P_ON_M 0 @@ -24,13 +21,13 @@ class PID PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and double, double, double, int); // Setpoint. Initial tuning parameters are also set here - void SetMode(int Mode); // * sets PID to either Manual (0) or Auto (non-0) - + void Mode(int Mode); // * sets PID to either Manual or Auto + int CycleMode(); bool Compute(); // * performs the PID calculation. it should be // called every time loop() cycles. ON/OFF and // calculation frequency can be set using SetMode // SetSampleTime respectively - + int ComputeTune(); void SetOutputLimits(double, double); // * clamps the output to a specific range. 0-255 by default, but // it's likely the user will want to change this depending on // the application @@ -50,16 +47,32 @@ class PID // once it is set in the constructor. void SetSampleTime(int); // * sets the frequency, in Milliseconds, with which // the PID calculation is performed. default is 100 - + void AutoTune(); //Display functions **************************************************************** double GetKp(); // These functions query the pid for interal values. double GetKi(); // they were created mainly for the pid front-end, double GetKd(); // where it's important to know what is actually - int GetMode(); // inside the PID. + int Mode(); // inside the PID. int GetDirection(); // + // Auto Tune Public + void Cancel(); // * Stops the AutoTune + + void SetOutputStep(double); // * how far above and below the starting value will the output step? + double GetOutputStep(); // + + void SetLookbackSec(int); // * how far back are we looking to identify peaks + int GetLookbackSec(); // + + void SetNoiseBand(double); // * the autotune will ignore signal chatter smaller than this value + double GetNoiseBand(); // this should be acurately set + + double TunedKp(); // * once autotune is complete, these functions contain the + double TunedKi(); // computed tuning parameters. + double TunedKd(); + private: void Initialize(); @@ -85,6 +98,25 @@ class PID unsigned long SampleTime; double outMin, outMax; bool inAuto, pOnE; + int OpMode; + + // Autotune stuff + void FinishUp(); + bool isMax, isMin; + double noiseBand; + bool running; + unsigned long peak1, peak2; + int sampleTime; + int nLookBack; + int peakType; + double lastInputs[101]; + double peaks[10]; + int peakCount; + bool atune_peak_change; + double absMax, absMin; + double oStep; + double outputStart; + double Ku, Pu; }; #endif