diff --git a/speeduino/src/PID_v1/PID_v1.cpp b/speeduino/src/PID_v1/PID_v1.cpp index b03e7d0d..3dd81abf 100755 --- a/speeduino/src/PID_v1/PID_v1.cpp +++ b/speeduino/src/PID_v1/PID_v1.cpp @@ -54,7 +54,7 @@ bool PID::Compute() /*Compute all the working error variables*/ long input = *myInput; long error = *mySetpoint - input; - ITerm+= (ki * error)/100; + ITerm += (ki * error)/100; if(ITerm > outMax) ITerm= outMax; else if(ITerm < outMin) ITerm= outMin; long dInput = (input - lastInput); @@ -201,19 +201,27 @@ int PID::GetDirection(){ return controllerDirection;} * The parameters specified here are those for for which we can't set up * reliable defaults, so we need to have the user set them. ***************************************************************************/ +/** + * @brief A standard integer PID controller. + * + * @param Input Pointer to the variable holding the current value that is to be controlled. Eg In an idle control this would point to RPM + * @param Output The address in the page that should be returned. This is as per the page definition in the ini + * + * @return byte The current target advance value in degrees + */ integerPID::integerPID(long* Input, long* Output, long* Setpoint, byte Kp, byte Ki, byte Kd, byte ControllerDirection) { myOutput = Output; - myInput = (long*)Input; + myInput = Input; mySetpoint = Setpoint; inAuto = false; integerPID::SetOutputLimits(0, 255); //default output limit corresponds to //the arduino pwm limits - SampleTime = 100; //default Controller Sample Time is 0.1 seconds + SampleTime = 250; //default Controller Sample Time is 0.25 seconds. This is the 4Hz control time for Idle and VVT integerPID::SetControllerDirection(ControllerDirection); integerPID::SetTunings(Kp, Ki, Kd); @@ -228,7 +236,7 @@ integerPID::integerPID(long* Input, long* Output, long* Setpoint, * pid Output needs to be computed. returns true when the output is computed, * false when nothing has been done. **********************************************************************************/ -bool integerPID::Compute() +bool integerPID::Compute(bool pOnE) { if(!inAuto) return false; unsigned long now = millis(); @@ -239,13 +247,42 @@ bool integerPID::Compute() /*Compute all the working error variables*/ long input = *myInput; long error = *mySetpoint - input; - ITerm += (ki * error)/1000; //Note that ki is multiplied by 1000 rather than 100, so it must be divided by 1000 here - if(ITerm > outMax) { ITerm = outMax; } - else if(ITerm < outMin) { ITerm = outMin; } long dInput = (input - lastInput); + outputSum += (ki * error)/1024; //Note that ki is multiplied by 1024 so it must be divided by 1024 here + if(outputSum > outMax) { outputSum = outMax; } + else if(outputSum < outMin) { outputSum = outMin; } + /*Compute PID Output*/ - long output = (kp * error)/100 + ITerm - (kd * dInput)/100; + long output; + + if(pOnE) + { + output = (kp * error) + outputSum - ((kd * dInput)/128); + } + else + { + outputSum -= kp * dInput; + if(outputSum > outMax) { outputSum = outMax; } + else if(outputSum < outMin) { outputSum = outMin; } + + output = outputSum - ((kd * dInput)/128); + } + + + if(output > outMax) output = outMax; + else if(output < outMin) output = outMin; + *myOutput = output; + + /*Remember some variables for next time*/ + lastInput = input; + lastTime = now; + + return true; + } + else return false; +} + if(output > outMax) output = outMax; else if(output < outMin) output = outMin; @@ -277,10 +314,10 @@ void integerPID::SetTunings(byte Kp, byte Ki, byte Kd) ki = Ki * SampleTimeInSec; kd = Kd / SampleTimeInSec; */ - long InverseSampleTimeInSec = 100000 / SampleTime; + long InverseSampleTimeInSec = 1000 / SampleTime; kp = Kp; - ki = (long)((long)Ki * 1000) / InverseSampleTimeInSec; - kd = ((long)Kd * InverseSampleTimeInSec) / 100; + ki = (long)((long)Ki * 1024) / InverseSampleTimeInSec; + kd = ((long)Kd * InverseSampleTimeInSec); if(controllerDirection == REVERSE) { @@ -293,17 +330,15 @@ void integerPID::SetTunings(byte Kp, byte Ki, byte Kd) /* SetSampleTime(...) ********************************************************* * sets the period, in Milliseconds, at which the calculation is performed ******************************************************************************/ -void integerPID::SetSampleTime(int NewSampleTime) +void integerPID::SetSampleTime(uint16_t NewSampleTime) { if (SampleTime == (unsigned long)NewSampleTime) return; //If new value = old value, no action required. - if (NewSampleTime > 0) - { - unsigned long ratioX1000 = (unsigned long)(NewSampleTime * 1000) / (unsigned long)SampleTime; - ki = ((unsigned long)ki * ratioX1000) / 1000; - //kd /= ratio; - kd = ((unsigned long)kd * 1000) / ratioX1000; - SampleTime = (unsigned long)NewSampleTime; - } + SampleTime = NewSampleTime; + + //This resets the tuning values with the appropriate new scaling + //The +1/-1 is there just so that this doesn't trip the check at the beginning of the SetTunings() function + SetTunings(dispKp+1, dispKi+1, dispKd+1); + SetTunings(dispKp-1, dispKi-1, dispKd-1); } /* SetOutputLimits(...)**************************************************** @@ -325,8 +360,8 @@ void integerPID::SetOutputLimits(long Min, long Max) if(*myOutput > outMax) *myOutput = outMax; else if(*myOutput < outMin) *myOutput = outMin; - if(ITerm > outMax) ITerm= outMax; - else if(ITerm < outMin) ITerm= outMin; + if(outputSum > outMax) { outputSum = outMax; } + else if(outputSum < outMin) { outputSum = outMin; } } } @@ -351,10 +386,11 @@ void integerPID::SetMode(int Mode) ******************************************************************************/ void integerPID::Initialize() { - ITerm = *myOutput; + outputSum = *myOutput; lastInput = *myInput; - if(ITerm > outMax) ITerm = outMax; - else if(ITerm < outMin) ITerm = outMin; + lastMinusOneInput = *myInput; + if(outputSum > outMax) { outputSum = outMax; } + else if(outputSum < outMin) { outputSum = outMin; } } /* SetControllerDirection(...)************************************************* @@ -379,9 +415,6 @@ void integerPID::SetControllerDirection(byte 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 ******************************************************************************/ -byte integerPID::GetKp(){ return dispKp; } -byte integerPID::GetKi(){ return dispKi;} -byte integerPID::GetKd(){ return dispKd;} int integerPID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;} int integerPID::GetDirection(){ return controllerDirection;} @@ -541,7 +574,4 @@ void integerPID_ideal::SetControllerDirection(byte 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 ******************************************************************************/ -byte integerPID_ideal::GetKp(){ return dispKp; } -byte integerPID_ideal::GetKi(){ return dispKi;} -byte integerPID_ideal::GetKd(){ return dispKd;} int integerPID_ideal::GetDirection(){ return controllerDirection;} diff --git a/speeduino/src/PID_v1/PID_v1.h b/speeduino/src/PID_v1/PID_v1.h index 4eff6502..3d2e8d2a 100755 --- a/speeduino/src/PID_v1/PID_v1.h +++ b/speeduino/src/PID_v1/PID_v1.h @@ -95,7 +95,7 @@ class integerPID void SetMode(int Mode); // * sets PID to either Manual (0) or Auto (non-0) - bool Compute(); // * performs the PID calculation. it should be + bool Compute(bool pOnE); // * performs the PID calculation. it should be // called every time loop() cycles. ON/OFF and // calculation frequency can be set using SetMode // SetSampleTime respectively @@ -114,25 +114,22 @@ class integerPID // means the output will increase when error is positive. REVERSE // means the opposite. it's very unlikely that this will be needed // once it is set in the constructor. - void SetSampleTime(int); // * sets the frequency, in Milliseconds, with which + void SetSampleTime(uint16_t); // * sets the frequency, in Milliseconds, with which // the PID calculation is performed. default is 100 //Display functions **************************************************************** - byte GetKp(); // These functions query the pid for interal values. - byte GetKi(); // they were created mainly for the pid front-end, - byte GetKd(); // where it's important to know what is actually int GetMode(); // inside the PID. int GetDirection(); // + void Initialize(); private: - void Initialize(); - byte dispKp; // * we'll hold on to the tuning parameters in user-entered - byte dispKi; // format for display purposes - byte dispKd; // + byte dispKp; + byte dispKi; + byte dispKd; uint16_t kp; // * (P)roportional Tuning Parameter uint16_t ki; // * (I)ntegral Tuning Parameter uint16_t kd; // * (D)erivative Tuning Parameter @@ -145,9 +142,10 @@ class integerPID // what these values are. with pointers we'll just know. unsigned long lastTime; - long ITerm, lastInput; + long outputSum, lastInput, lastMinusOneInput; + int16_t lastError; - unsigned long SampleTime; + uint16_t SampleTime; long outMin, outMax; bool inAuto; }; @@ -191,9 +189,6 @@ class integerPID_ideal //Display functions **************************************************************** - byte GetKp(); // These functions query the pid for interal values. - byte GetKi(); // they were created mainly for the pid front-end, - byte GetKd(); // where it's important to know what is actually int GetMode(); // inside the PID. int GetDirection(); // void Initialize();