Simple PID

pid_controller.cpp

Committer:
williampeers
Date:
2017-07-31
Revision:
0:3ce55a50a6b3

File content as of revision 0:3ce55a50a6b3:

//*********************************************************************************
// Arduino PID Library Version 1.0.1 Modified Version for C++
// Platform Independent
// 
// Revision: 1.1
// 
// Description: The PID Controller module originally meant for Arduino made
// platform independent. Some small bugs present in the original Arduino source
// have been rectified as well.
// 
// For a detailed explanation of the theory behind this library, go to:
// http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
// 
// Revisions can be found here:
// https://github.com/tcleg
// 
// Modified by: Trent Cleghorn , <trentoncleghorn@gmail.com>
// 
// Copyright (C) Brett Beauregard , <br3ttb@gmail.com>
// 
//                                 GPLv3 License
// 
// This program is free software: you can redistribute it and/or modify it under 
// the terms of the GNU General Public License as published by the Free Software 
// Foundation, either version 3 of the License, or (at your option) any later 
// version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT ANY 
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE.  See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License along with 
// this program.  If not, see <http://www.gnu.org/licenses/>.
//*********************************************************************************

//*********************************************************************************
// Headers
//*********************************************************************************
#include "pid_controller.h"

//*********************************************************************************
// Macros and Globals
//*********************************************************************************
#define CONSTRAIN(x,lower,upper)    ((x)<(lower)?(lower):((x)>(upper)?(upper):(x)))

//*********************************************************************************
// Public Class Functions
//*********************************************************************************

PIDControl::
PIDControl (float kp, float ki, float kd, float sampleTimeSeconds, float minOutput, 
            float maxOutput, PIDMode mode, PIDDirection controllerDirection)     	
{
    controllerDirection = controllerDirection;
    mode = mode;
    iTerm = 0.0f;
    input = 0.0f;
    lastInput = 0.0f;
    output = 0.0f;
    setpoint = 0.0f;
    
    if(sampleTimeSeconds > 0.0f)
    {
        sampleTime = sampleTimeSeconds;
    }
    else
    {
        // If the passed parameter was incorrect, set to 1 second
        sampleTime = 1.0f;
    }
    
    PIDOutputLimitsSet(minOutput, maxOutput);
    PIDTuningsSet(kp, ki, kd);
}
        
bool PIDControl::
PIDCompute() 
{
    float error, dInput;

    if(mode == MANUAL)
    {
        return false;
    }
    
    // The classic PID error term
    error = setpoint - input;
    
    // Compute the integral term separately ahead of time
    iTerm += alteredKi * error;
    
    // Constrain the integrator to make sure it does not exceed output bounds
    iTerm = CONSTRAIN(iTerm, outMin, outMax);
    
    // Take the "derivative on measurement" instead of "derivative on error"
    dInput = input - lastInput;
    
    // Run all the terms together to get the overall output
    output = alteredKp * error + iTerm - alteredKd * dInput;
    
    // Bound the output
    output = CONSTRAIN(output, outMin, outMax);
    
    // Make the current input the former input
    lastInput = input;
    
    return true;
}
     
void PIDControl::
PIDModeSet(PIDMode mode)                                                                                                                                       
{
    // If the mode changed from MANUAL to AUTOMATIC
    if(mode != mode && mode == AUTOMATIC)
    {
        // Initialize a few PID parameters to new values
        iTerm = output;
        lastInput = input;
        
        // Constrain the integrator to make sure it does not exceed output bounds
        iTerm = CONSTRAIN(iTerm, outMin, outMax);
    }
    
    mode = mode;
}

void PIDControl::
PIDOutputLimitsSet(float min, float max) 							  							  
{
    // Check if the parameters are valid
    if(min >= max)
    {
        return;
    }
    
    // Save the parameters
    outMin = min;
    outMax = max;
    
    // If in automatic, apply the new constraints
    if(mode == AUTOMATIC)
    {
        output = CONSTRAIN(output, min, max);
        iTerm  = CONSTRAIN(iTerm,  min, max);
    }
}

void PIDControl::
PIDTuningsSet(float kp, float ki, float kd)         	                                         
{
    // Check if the parameters are valid
    if(kp < 0.0f || ki < 0.0f || kd < 0.0f)
    {
        return;
    }
    
    // Save the parameters for displaying purposes
    dispKp = kp;
    dispKi = ki;
    dispKd = kd;
    
    // Alter the parameters for PID
    alteredKp = kp;
    alteredKi = ki * sampleTime;
    alteredKd = kd / sampleTime;
    
    // Apply reverse direction to the altered values if necessary
    if(controllerDirection == REVERSE)
    {
        alteredKp = -(alteredKp);
        alteredKi = -(alteredKi);
        alteredKd = -(alteredKd);
    }
}

void PIDControl::
PIDTuningKpSet(float kp)
{
    PIDTuningsSet(kp, dispKi, dispKd);
}

void PIDControl::
PIDTuningKiSet(float ki)
{
    PIDTuningsSet(dispKp, ki, dispKd);
}

void PIDControl::
PIDTuningKdSet(float kd)
{
    PIDTuningsSet(dispKp, dispKi, kd);
}

void PIDControl::
PIDControllerDirectionSet(PIDDirection controllerDirection)	  									  									  									  
{
    // If in automatic mode and the controller's sense of direction is reversed
    if(mode == AUTOMATIC && controllerDirection == REVERSE)
    {
        // Reverse sense of direction of PID gain constants
        alteredKp = -(alteredKp);
        alteredKi = -(alteredKi);
        alteredKd = -(alteredKd);
    }
    
    controllerDirection = controllerDirection;
}

void PIDControl::
PIDSampleTimeSet(float sampleTimeSeconds)                                                       									  									  									   
{
    float ratio;

    if(sampleTimeSeconds > 0.0f)
    {
        // Find the ratio of change and apply to the altered values
        ratio = sampleTimeSeconds / sampleTime;
        alteredKi *= ratio;
        alteredKd /= ratio;
        
        // Save the new sampling time
        sampleTime = sampleTimeSeconds;
    }
}