This is a copy of the Reference Standard PID controller ala controlguru.com
Dependents: PIDHeater Printer PIDHeater82 UltiSaverController
Fork of PID by
PID.cpp
- Committer:
- unix_guru
- Date:
- 2016-02-07
- Revision:
- 3:316f974b7f98
- Parent:
- 2:55bf0f813bb4
File content as of revision 3:316f974b7f98:
/** * Arduino PID Library - Version 1.1.1 * @author Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com * * @section LICENSE * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * @section DESCRIPTION * * A PID controller is a widely used feedback controller commonly found in * industry. * * This library is a port of Brett Beauregard's Arduino PID library: * * https://github.com/br3ttb/Arduino-PID-Library * * The wikipedia article on PID controllers is a good place to start on * understanding how they work: * * http://en.wikipedia.org/wiki/PID_controller * * For a clear and elegant explanation of how to implement and tune a * controller, the controlguru website by Douglas J. Cooper (who also happened * to be Brett's controls professor) is an excellent reference: * * http://www.controlguru.com/ */ /** * Includes */ #include "PID.h" #include "millis/millis.h" extern Serial pc; /*Constructor (...)********************************************************* * 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. ***************************************************************************/ PID::PID(float* Input, float* Output, float* Setpoint, float Kp, float Ki, float Kd, int ControllerDirection) { myOutput = Output; myInput = Input; mySetpoint = Setpoint; inAuto = false; PID::SetOutputLimits(0, 1000); // default output limit corresponds to // the arduino pwm limits SampleTime = 100; // default Controller Sample Time is 0.1 seconds PID::SetControllerDirection(ControllerDirection); PID::SetTunings(Kp, Ki, Kd); lastTime = millis()-SampleTime; } /* Compute() ********************************************************************** * This, as they say, is where the magic happens. this function should be called * every time "void loop()" executes. the function will decide for itself whether a new * pid Output needs to be computed. returns true when the output is computed, * false when nothing has been done. **********************************************************************************/ bool PID::Compute() { if(!inAuto) return false; unsigned long now = millis(); unsigned long timeChange = (now - lastTime); if(timeChange>=SampleTime) { /*Compute all the working error variables*/ float input = *myInput; float error = *mySetpoint - input; ITerm+= (ki * error); if(ITerm > outMax) ITerm= outMax; else if(ITerm < outMin) ITerm= outMin; float dInput = (input - lastInput); // pc.printf("Input = %f, Error = %f, Output = %f, SetPoint = %f\n\r",input,error,output,*mySetpoint); /*Compute PID Output*/ float output = kp * error + ITerm- kd * dInput; // pc.printf("Input = %f, Error = %f, Output = %f, SetPoint = %f\n\r",input,error,output,*mySetpoint); 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; } /* SetTunings(...)************************************************************* * This function allows the controller's dynamic performance to be adjusted. * it's called automatically from the constructor, but tunings can also * be adjusted on the fly during normal operation ******************************************************************************/ void PID::SetTunings(float Kp, float Ki, float Kd) { if (Kp<0 || Ki<0 || Kd<0) return; dispKp = Kp; dispKi = Ki; dispKd = Kd; float SampleTimeInSec = ((float)SampleTime)/1000; kp = Kp; ki = Ki * SampleTimeInSec; kd = Kd / SampleTimeInSec; if(controllerDirection ==REVERSE) { kp = (0 - kp); ki = (0 - ki); kd = (0 - kd); } } /* SetSampleTime(...) ********************************************************* * sets the period, in Milliseconds, at which the calculation is performed ******************************************************************************/ void PID::SetSampleTime(int NewSampleTime) { if (NewSampleTime > 0) { float ratio = (float)NewSampleTime / (float)SampleTime; ki *= ratio; kd /= ratio; SampleTime = (unsigned long)NewSampleTime; } } /* SetOutputLimits(...)**************************************************** * This function will be used far more often than SetInputLimits. while * the input to the controller will generally be in the 0-1023 range (which is * the default already,) the output will be a little different. maybe they'll * be doing a time window and will need 0-8000 or something. or maybe they'll * want to clamp it from 0-125. who knows. at any rate, that can all be done * here. **************************************************************************/ void PID::SetOutputLimits(float Min, float Max) { if(Min >= Max) return; outMin = Min; outMax = Max; if(inAuto) { if(*myOutput > outMax) *myOutput = outMax; else if(*myOutput < outMin) *myOutput = outMin; if(ITerm > outMax) ITerm= outMax; else if(ITerm < outMin) ITerm= outMin; } } /* SetMode(...)**************************************************************** * Allows the controller Mode to be set to manual (0) or Automatic (non-zero) * when the transition from manual to auto occurs, the controller is * automatically initialized ******************************************************************************/ void PID::SetMode(int Mode) { bool newAuto = (Mode == AUTOMATIC); if(newAuto == !inAuto) { /*we just went from manual to auto*/ PID::Initialize(); } inAuto = newAuto; } /* Initialize()**************************************************************** * does all the things that need to happen to ensure a bumpless transfer * from manual to automatic mode. ******************************************************************************/ void PID::Initialize() { ITerm = *myOutput; lastInput = *myInput; if(ITerm > outMax) ITerm = outMax; else if(ITerm < outMin) ITerm = outMin; } /* SetControllerDirection(...)************************************************* * The PID will either be connected to a DIRECT acting process (+Output leads * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to * know which one, because otherwise we may increase the output when we should * be decreasing. This is called from the constructor. ******************************************************************************/ void PID::SetControllerDirection(int Direction) { if(inAuto && Direction !=controllerDirection) { kp = (0 - kp); ki = (0 - ki); kd = (0 - kd); } controllerDirection = Direction; } /* Status Funcions************************************************************* * Just because you set the Kp=-1 doesn't mean it actually happened. these * 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 ******************************************************************************/ float PID::GetKp(){ return dispKp; } float PID::GetKi(){ return dispKi;} float PID::GetKd(){ return dispKd;} int PID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;} int PID::GetDirection(){ return controllerDirection;}