ported pid library from imu
Dependents: wheelchaircontrol wheelchaircontrolRos
PID_v1.cpp@0:539e3b3b91e1, 2018-08-09 (annotated)
- Committer:
- ryanlin97
- Date:
- Thu Aug 09 16:36:32 2018 +0000
- Revision:
- 0:539e3b3b91e1
- Child:
- 1:45383b49536c
initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ryanlin97 | 0:539e3b3b91e1 | 1 | /********************************************************************************************** |
ryanlin97 | 0:539e3b3b91e1 | 2 | * Arduino PID Library - Version 1.2.1 |
ryanlin97 | 0:539e3b3b91e1 | 3 | * by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com |
ryanlin97 | 0:539e3b3b91e1 | 4 | * |
ryanlin97 | 0:539e3b3b91e1 | 5 | * This Library is licensed under the MIT License |
ryanlin97 | 0:539e3b3b91e1 | 6 | **********************************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 7 | /* |
ryanlin97 | 0:539e3b3b91e1 | 8 | #if ARDUINO >= 100 |
ryanlin97 | 0:539e3b3b91e1 | 9 | #include "Arduino.h" |
ryanlin97 | 0:539e3b3b91e1 | 10 | #else |
ryanlin97 | 0:539e3b3b91e1 | 11 | #include "WProgram.h" |
ryanlin97 | 0:539e3b3b91e1 | 12 | #endif*/ |
ryanlin97 | 0:539e3b3b91e1 | 13 | |
ryanlin97 | 0:539e3b3b91e1 | 14 | #include "mbed.h" |
ryanlin97 | 0:539e3b3b91e1 | 15 | #include "PID_v1.h" |
ryanlin97 | 0:539e3b3b91e1 | 16 | |
ryanlin97 | 0:539e3b3b91e1 | 17 | Timer PIDtimer; |
ryanlin97 | 0:539e3b3b91e1 | 18 | /*Constructor (...)********************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 19 | * The parameters specified here are those for for which we can't set up |
ryanlin97 | 0:539e3b3b91e1 | 20 | * reliable defaults, so we need to have the user set them. |
ryanlin97 | 0:539e3b3b91e1 | 21 | ***************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 22 | PID::PID(double* Input, double* Output, double* Setpoint, |
ryanlin97 | 0:539e3b3b91e1 | 23 | double Kp, double Ki, double Kd, int POn, int ControllerDirection) |
ryanlin97 | 0:539e3b3b91e1 | 24 | { |
ryanlin97 | 0:539e3b3b91e1 | 25 | myOutput = Output; |
ryanlin97 | 0:539e3b3b91e1 | 26 | myInput = Input; |
ryanlin97 | 0:539e3b3b91e1 | 27 | mySetpoint = Setpoint; |
ryanlin97 | 0:539e3b3b91e1 | 28 | inAuto = false; |
ryanlin97 | 0:539e3b3b91e1 | 29 | PIDtimer.start(); |
ryanlin97 | 0:539e3b3b91e1 | 30 | PID::SetOutputLimits(0, 1); //default output limit corresponds to |
ryanlin97 | 0:539e3b3b91e1 | 31 | //the arduino pwm limits |
ryanlin97 | 0:539e3b3b91e1 | 32 | |
ryanlin97 | 0:539e3b3b91e1 | 33 | SampleTime = 100; //default Controller Sample Time is 0.1 seconds |
ryanlin97 | 0:539e3b3b91e1 | 34 | |
ryanlin97 | 0:539e3b3b91e1 | 35 | PID::SetControllerDirection(ControllerDirection); |
ryanlin97 | 0:539e3b3b91e1 | 36 | PID::SetTunings(Kp, Ki, Kd, POn); |
ryanlin97 | 0:539e3b3b91e1 | 37 | |
ryanlin97 | 0:539e3b3b91e1 | 38 | lastTime = PIDtimer.read()-SampleTime; |
ryanlin97 | 0:539e3b3b91e1 | 39 | printf("input %f output %f pOn %i Setpoint %f\n", *myInput, *myOutput, pOn, *mySetpoint); |
ryanlin97 | 0:539e3b3b91e1 | 40 | } |
ryanlin97 | 0:539e3b3b91e1 | 41 | |
ryanlin97 | 0:539e3b3b91e1 | 42 | /*Constructor (...)********************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 43 | * To allow backwards compatability for v1.1, or for people that just want |
ryanlin97 | 0:539e3b3b91e1 | 44 | * to use Proportional on Error without explicitly saying so |
ryanlin97 | 0:539e3b3b91e1 | 45 | ***************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 46 | |
ryanlin97 | 0:539e3b3b91e1 | 47 | PID::PID(double* Input, double* Output, double* Setpoint, |
ryanlin97 | 0:539e3b3b91e1 | 48 | double Kp, double Ki, double Kd, int ControllerDirection) |
ryanlin97 | 0:539e3b3b91e1 | 49 | { |
ryanlin97 | 0:539e3b3b91e1 | 50 | PID::PID(Input, Output, Setpoint, Kp, Ki, Kd, P_ON_E, ControllerDirection); |
ryanlin97 | 0:539e3b3b91e1 | 51 | } |
ryanlin97 | 0:539e3b3b91e1 | 52 | |
ryanlin97 | 0:539e3b3b91e1 | 53 | |
ryanlin97 | 0:539e3b3b91e1 | 54 | /* Compute() ********************************************************************** |
ryanlin97 | 0:539e3b3b91e1 | 55 | * This, as they say, is where the magic happens. this function should be called |
ryanlin97 | 0:539e3b3b91e1 | 56 | * every time "void loop()" executes. the function will decide for itself whether a new |
ryanlin97 | 0:539e3b3b91e1 | 57 | * pid Output needs to be computed. returns true when the output is computed, |
ryanlin97 | 0:539e3b3b91e1 | 58 | * false when nothing has been done. |
ryanlin97 | 0:539e3b3b91e1 | 59 | **********************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 60 | bool PID::Compute() |
ryanlin97 | 0:539e3b3b91e1 | 61 | { |
ryanlin97 | 0:539e3b3b91e1 | 62 | printf("input %f output %f pOn %i Setpoint %f outmin %f outmax %f \n", *myInput, *myOutput, pOn, *mySetpoint, outMin, outMax); |
ryanlin97 | 0:539e3b3b91e1 | 63 | if(!inAuto) return false; |
ryanlin97 | 0:539e3b3b91e1 | 64 | unsigned long now = PIDtimer.read(); |
ryanlin97 | 0:539e3b3b91e1 | 65 | unsigned long timeChange = (now - lastTime); |
ryanlin97 | 0:539e3b3b91e1 | 66 | if(timeChange>=SampleTime) |
ryanlin97 | 0:539e3b3b91e1 | 67 | { |
ryanlin97 | 0:539e3b3b91e1 | 68 | /*Compute all the working error variables*/ |
ryanlin97 | 0:539e3b3b91e1 | 69 | double input = *myInput; |
ryanlin97 | 0:539e3b3b91e1 | 70 | double error = *mySetpoint - input; |
ryanlin97 | 0:539e3b3b91e1 | 71 | double dInput = (input - lastInput); |
ryanlin97 | 0:539e3b3b91e1 | 72 | outputSum+= (ki * error); |
ryanlin97 | 0:539e3b3b91e1 | 73 | |
ryanlin97 | 0:539e3b3b91e1 | 74 | /*Add Proportional on Measurement, if P_ON_M is specified*/ |
ryanlin97 | 0:539e3b3b91e1 | 75 | if(!pOnE) outputSum-= kp * dInput; |
ryanlin97 | 0:539e3b3b91e1 | 76 | |
ryanlin97 | 0:539e3b3b91e1 | 77 | if(outputSum > outMax) outputSum= outMax; |
ryanlin97 | 0:539e3b3b91e1 | 78 | else if(outputSum < outMin) outputSum= outMin; |
ryanlin97 | 0:539e3b3b91e1 | 79 | |
ryanlin97 | 0:539e3b3b91e1 | 80 | /*Add Proportional on Error, if P_ON_E is specified*/ |
ryanlin97 | 0:539e3b3b91e1 | 81 | double output; |
ryanlin97 | 0:539e3b3b91e1 | 82 | if(pOnE) output = kp * error; |
ryanlin97 | 0:539e3b3b91e1 | 83 | else output = 0; |
ryanlin97 | 0:539e3b3b91e1 | 84 | |
ryanlin97 | 0:539e3b3b91e1 | 85 | /*Compute Rest of PID Output*/ |
ryanlin97 | 0:539e3b3b91e1 | 86 | output += outputSum - kd * dInput; |
ryanlin97 | 0:539e3b3b91e1 | 87 | |
ryanlin97 | 0:539e3b3b91e1 | 88 | if(output > outMax) output = outMax; |
ryanlin97 | 0:539e3b3b91e1 | 89 | else if(output < outMin) output = outMin; |
ryanlin97 | 0:539e3b3b91e1 | 90 | *myOutput = output; |
ryanlin97 | 0:539e3b3b91e1 | 91 | |
ryanlin97 | 0:539e3b3b91e1 | 92 | /*Remember some variables for next time*/ |
ryanlin97 | 0:539e3b3b91e1 | 93 | lastInput = input; |
ryanlin97 | 0:539e3b3b91e1 | 94 | lastTime = now; |
ryanlin97 | 0:539e3b3b91e1 | 95 | return true; |
ryanlin97 | 0:539e3b3b91e1 | 96 | } |
ryanlin97 | 0:539e3b3b91e1 | 97 | else return false; |
ryanlin97 | 0:539e3b3b91e1 | 98 | } |
ryanlin97 | 0:539e3b3b91e1 | 99 | |
ryanlin97 | 0:539e3b3b91e1 | 100 | /* SetTunings(...)************************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 101 | * This function allows the controller's dynamic performance to be adjusted. |
ryanlin97 | 0:539e3b3b91e1 | 102 | * it's called automatically from the constructor, but tunings can also |
ryanlin97 | 0:539e3b3b91e1 | 103 | * be adjusted on the fly during normal operation |
ryanlin97 | 0:539e3b3b91e1 | 104 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 105 | void PID::SetTunings(double Kp, double Ki, double Kd, int POn) |
ryanlin97 | 0:539e3b3b91e1 | 106 | { |
ryanlin97 | 0:539e3b3b91e1 | 107 | if (Kp<0 || Ki<0 || Kd<0) return; |
ryanlin97 | 0:539e3b3b91e1 | 108 | |
ryanlin97 | 0:539e3b3b91e1 | 109 | pOn = POn; |
ryanlin97 | 0:539e3b3b91e1 | 110 | pOnE = POn == P_ON_E; |
ryanlin97 | 0:539e3b3b91e1 | 111 | |
ryanlin97 | 0:539e3b3b91e1 | 112 | dispKp = Kp; dispKi = Ki; dispKd = Kd; |
ryanlin97 | 0:539e3b3b91e1 | 113 | |
ryanlin97 | 0:539e3b3b91e1 | 114 | double SampleTimeInSec = ((double)SampleTime)/1000; |
ryanlin97 | 0:539e3b3b91e1 | 115 | kp = Kp; |
ryanlin97 | 0:539e3b3b91e1 | 116 | ki = Ki * SampleTimeInSec; |
ryanlin97 | 0:539e3b3b91e1 | 117 | kd = Kd / SampleTimeInSec; |
ryanlin97 | 0:539e3b3b91e1 | 118 | |
ryanlin97 | 0:539e3b3b91e1 | 119 | if(controllerDirection ==REVERSE) |
ryanlin97 | 0:539e3b3b91e1 | 120 | { |
ryanlin97 | 0:539e3b3b91e1 | 121 | kp = (0 - kp); |
ryanlin97 | 0:539e3b3b91e1 | 122 | ki = (0 - ki); |
ryanlin97 | 0:539e3b3b91e1 | 123 | kd = (0 - kd); |
ryanlin97 | 0:539e3b3b91e1 | 124 | } |
ryanlin97 | 0:539e3b3b91e1 | 125 | } |
ryanlin97 | 0:539e3b3b91e1 | 126 | |
ryanlin97 | 0:539e3b3b91e1 | 127 | /* SetTunings(...)************************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 128 | * Set Tunings using the last-rembered POn setting |
ryanlin97 | 0:539e3b3b91e1 | 129 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 130 | void PID::SetTunings(double Kp, double Ki, double Kd){ |
ryanlin97 | 0:539e3b3b91e1 | 131 | SetTunings(Kp, Ki, Kd, pOn); |
ryanlin97 | 0:539e3b3b91e1 | 132 | } |
ryanlin97 | 0:539e3b3b91e1 | 133 | |
ryanlin97 | 0:539e3b3b91e1 | 134 | /* SetSampleTime(...) ********************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 135 | * sets the period, in Milliseconds, at which the calculation is performed |
ryanlin97 | 0:539e3b3b91e1 | 136 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 137 | void PID::SetSampleTime(int NewSampleTime) |
ryanlin97 | 0:539e3b3b91e1 | 138 | { |
ryanlin97 | 0:539e3b3b91e1 | 139 | if (NewSampleTime > 0) |
ryanlin97 | 0:539e3b3b91e1 | 140 | { |
ryanlin97 | 0:539e3b3b91e1 | 141 | double ratio = (double)NewSampleTime |
ryanlin97 | 0:539e3b3b91e1 | 142 | / (double)SampleTime; |
ryanlin97 | 0:539e3b3b91e1 | 143 | ki *= ratio; |
ryanlin97 | 0:539e3b3b91e1 | 144 | kd /= ratio; |
ryanlin97 | 0:539e3b3b91e1 | 145 | SampleTime = (unsigned long)NewSampleTime; |
ryanlin97 | 0:539e3b3b91e1 | 146 | } |
ryanlin97 | 0:539e3b3b91e1 | 147 | } |
ryanlin97 | 0:539e3b3b91e1 | 148 | |
ryanlin97 | 0:539e3b3b91e1 | 149 | /* SetOutputLimits(...)**************************************************** |
ryanlin97 | 0:539e3b3b91e1 | 150 | * This function will be used far more often than SetInputLimits. while |
ryanlin97 | 0:539e3b3b91e1 | 151 | * the input to the controller will generally be in the 0-1023 range (which is |
ryanlin97 | 0:539e3b3b91e1 | 152 | * the default already,) the output will be a little different. maybe they'll |
ryanlin97 | 0:539e3b3b91e1 | 153 | * be doing a time window and will need 0-8000 or something. or maybe they'll |
ryanlin97 | 0:539e3b3b91e1 | 154 | * want to clamp it from 0-125. who knows. at any rate, that can all be done |
ryanlin97 | 0:539e3b3b91e1 | 155 | * here. |
ryanlin97 | 0:539e3b3b91e1 | 156 | **************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 157 | void PID::SetOutputLimits(double Min, double Max) |
ryanlin97 | 0:539e3b3b91e1 | 158 | { |
ryanlin97 | 0:539e3b3b91e1 | 159 | if(Min >= Max) return; |
ryanlin97 | 0:539e3b3b91e1 | 160 | outMin = Min; |
ryanlin97 | 0:539e3b3b91e1 | 161 | outMax = Max; |
ryanlin97 | 0:539e3b3b91e1 | 162 | |
ryanlin97 | 0:539e3b3b91e1 | 163 | if(inAuto) |
ryanlin97 | 0:539e3b3b91e1 | 164 | { |
ryanlin97 | 0:539e3b3b91e1 | 165 | if(*myOutput > outMax) *myOutput = outMax; |
ryanlin97 | 0:539e3b3b91e1 | 166 | else if(*myOutput < outMin) *myOutput = outMin; |
ryanlin97 | 0:539e3b3b91e1 | 167 | |
ryanlin97 | 0:539e3b3b91e1 | 168 | if(outputSum > outMax) outputSum= outMax; |
ryanlin97 | 0:539e3b3b91e1 | 169 | else if(outputSum < outMin) outputSum= outMin; |
ryanlin97 | 0:539e3b3b91e1 | 170 | } |
ryanlin97 | 0:539e3b3b91e1 | 171 | } |
ryanlin97 | 0:539e3b3b91e1 | 172 | |
ryanlin97 | 0:539e3b3b91e1 | 173 | /* SetMode(...)**************************************************************** |
ryanlin97 | 0:539e3b3b91e1 | 174 | * Allows the controller Mode to be set to manual (0) or Automatic (non-zero) |
ryanlin97 | 0:539e3b3b91e1 | 175 | * when the transition from manual to auto occurs, the controller is |
ryanlin97 | 0:539e3b3b91e1 | 176 | * automatically initialized |
ryanlin97 | 0:539e3b3b91e1 | 177 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 178 | void PID::SetMode(int Mode) |
ryanlin97 | 0:539e3b3b91e1 | 179 | { |
ryanlin97 | 0:539e3b3b91e1 | 180 | bool newAuto = (Mode == AUTOMATIC); |
ryanlin97 | 0:539e3b3b91e1 | 181 | if(newAuto && !inAuto) |
ryanlin97 | 0:539e3b3b91e1 | 182 | { /*we just went from manual to auto*/ |
ryanlin97 | 0:539e3b3b91e1 | 183 | PID::Initialize(); |
ryanlin97 | 0:539e3b3b91e1 | 184 | } |
ryanlin97 | 0:539e3b3b91e1 | 185 | inAuto = newAuto; |
ryanlin97 | 0:539e3b3b91e1 | 186 | } |
ryanlin97 | 0:539e3b3b91e1 | 187 | |
ryanlin97 | 0:539e3b3b91e1 | 188 | /* Initialize()**************************************************************** |
ryanlin97 | 0:539e3b3b91e1 | 189 | * does all the things that need to happen to ensure a bumpless transfer |
ryanlin97 | 0:539e3b3b91e1 | 190 | * from manual to automatic mode. |
ryanlin97 | 0:539e3b3b91e1 | 191 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 192 | void PID::Initialize() |
ryanlin97 | 0:539e3b3b91e1 | 193 | { |
ryanlin97 | 0:539e3b3b91e1 | 194 | outputSum = *myOutput; |
ryanlin97 | 0:539e3b3b91e1 | 195 | lastInput = *myInput; |
ryanlin97 | 0:539e3b3b91e1 | 196 | if(outputSum > outMax) outputSum = outMax; |
ryanlin97 | 0:539e3b3b91e1 | 197 | else if(outputSum < outMin) outputSum = outMin; |
ryanlin97 | 0:539e3b3b91e1 | 198 | } |
ryanlin97 | 0:539e3b3b91e1 | 199 | |
ryanlin97 | 0:539e3b3b91e1 | 200 | /* SetControllerDirection(...)************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 201 | * The PID will either be connected to a DIRECT acting process (+Output leads |
ryanlin97 | 0:539e3b3b91e1 | 202 | * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to |
ryanlin97 | 0:539e3b3b91e1 | 203 | * know which one, because otherwise we may increase the output when we should |
ryanlin97 | 0:539e3b3b91e1 | 204 | * be decreasing. This is called from the constructor. |
ryanlin97 | 0:539e3b3b91e1 | 205 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 206 | void PID::SetControllerDirection(int Direction) |
ryanlin97 | 0:539e3b3b91e1 | 207 | { |
ryanlin97 | 0:539e3b3b91e1 | 208 | if(inAuto && Direction !=controllerDirection) |
ryanlin97 | 0:539e3b3b91e1 | 209 | { |
ryanlin97 | 0:539e3b3b91e1 | 210 | kp = (0 - kp); |
ryanlin97 | 0:539e3b3b91e1 | 211 | ki = (0 - ki); |
ryanlin97 | 0:539e3b3b91e1 | 212 | kd = (0 - kd); |
ryanlin97 | 0:539e3b3b91e1 | 213 | } |
ryanlin97 | 0:539e3b3b91e1 | 214 | controllerDirection = Direction; |
ryanlin97 | 0:539e3b3b91e1 | 215 | } |
ryanlin97 | 0:539e3b3b91e1 | 216 | |
ryanlin97 | 0:539e3b3b91e1 | 217 | /* Status Funcions************************************************************* |
ryanlin97 | 0:539e3b3b91e1 | 218 | * Just because you set the Kp=-1 doesn't mean it actually happened. these |
ryanlin97 | 0:539e3b3b91e1 | 219 | * functions query the internal state of the PID. they're here for display |
ryanlin97 | 0:539e3b3b91e1 | 220 | * purposes. this are the functions the PID Front-end uses for example |
ryanlin97 | 0:539e3b3b91e1 | 221 | ******************************************************************************/ |
ryanlin97 | 0:539e3b3b91e1 | 222 | double PID::GetKp(){ return dispKp; } |
ryanlin97 | 0:539e3b3b91e1 | 223 | double PID::GetKi(){ return dispKi;} |
ryanlin97 | 0:539e3b3b91e1 | 224 | double PID::GetKd(){ return dispKd;} |
ryanlin97 | 0:539e3b3b91e1 | 225 | int PID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;} |
ryanlin97 | 0:539e3b3b91e1 | 226 | int PID::GetDirection(){ return controllerDirection;} |
ryanlin97 | 0:539e3b3b91e1 | 227 | |
ryanlin97 | 0:539e3b3b91e1 | 228 | /*void printEverything() { |
ryanlin97 | 0:539e3b3b91e1 | 229 | printf("input %f output %f pOn %f Setpoint %f outmin %f outmax %f \n", *myInput, *myOutput, pOn, *mySetpoint, outMin, outMax); |
ryanlin97 | 0:539e3b3b91e1 | 230 | printf("KP %f KI %f Kd %f \n", PID::GetKp(), PID::GetKi(), PID::GetKd()); |
ryanlin97 | 0:539e3b3b91e1 | 231 | } |
ryanlin97 | 0:539e3b3b91e1 | 232 | */ |