// PIDController.cpp --- // // Filename: PIDController.cpp // Description: // Author: subhasis ray // Maintainer: // Created: Tue Dec 30 23:36:01 2008 (+0530) // Version: // Last-Updated: Tue Jun 11 17:00:51 2013 (+0530) // By: subha // Update #: 338 // URL: // Keywords: // Compatibility: // // // Commentary: // // // // // Change log: // // // // /********************************************************************** ** This program is part of 'MOOSE', the ** Messaging Object Oriented Simulation Environment, ** also known as GENESIS 3 base code. ** copyright (C) 2003-2013 Upinder S. Bhalla. and NCBS ** It is made available under the terms of the ** GNU Lesser General Public License version 2.1 ** See the file COPYING.LIB for the full notice. **********************************************************************/ // Code: #include <cfloat> #include "PIDController.h" static SrcFinfo1< double > * outputOut() { static SrcFinfo1 <double> outputOut("output", "Sends the output of the PIDController. This is known as manipulated" " variable (MV) in control theory. This should be fed into the process" " which we are trying to control."); return &outputOut; } const Cinfo* PIDController::initCinfo() { static DestFinfo process( "process", "Handle process calls.", new ProcOpFunc<PIDController>( &PIDController::process)); static DestFinfo reinit( "reinit", "Reinitialize the object.", new ProcOpFunc<PIDController>( &PIDController::reinit )); static Finfo* processShared[] = { &process, &reinit }; static ValueFinfo<PIDController, double> gain( "gain", "This is the proportional gain (Kp). This tuning parameter scales the" " proportional term. Larger gain usually results in faster response, but" " too much will lead to instability and oscillation.", &PIDController::setGain, &PIDController::getGain); static ValueFinfo<PIDController, double> saturation("saturation", "Bound on the permissible range of output. Defaults to maximum double" " value.", &PIDController::setSaturation, &PIDController::getSaturation); static ValueFinfo<PIDController, double> command("command", "The command (desired) value of the sensed parameter. In control theory" " this is commonly known as setpoint(SP).", &PIDController::setCommand, &PIDController::getCommand); static ReadOnlyValueFinfo<PIDController, double> sensed( "sensed", "Sensed (measured) value. This is commonly known as process variable" "(PV) in control theory.", &PIDController::getSensed); static ValueFinfo<PIDController, double> tauI( "tauI", "The integration time constant, typically = dt. This is actually" " proportional gain divided by integral gain (Kp/Ki)). Larger Ki" " (smaller tauI) usually leads to fast elimination of steady state" " errors at the cost of larger overshoot.", &PIDController::setTauI, &PIDController::getTauI); static ValueFinfo<PIDController, double> tauD( "tauD", "The differentiation time constant, typically = dt / 4. This is" " derivative gain (Kd) times proportional gain (Kp). Larger Kd (tauD)" " decreases overshoot at the cost of slowing down transient response" " and may lead to instability.", &PIDController::setTauD, &PIDController::getTauD); static ReadOnlyValueFinfo<PIDController, double> outputValue( "outputValue", "Output of the PIDController. This is given by:" " gain * ( error + INTEGRAL[ error dt ] / tau_i + tau_d * d(error)/dt )\n" "Where gain = proportional gain (Kp), tau_i = integral gain (Kp/Ki) and" " tau_d = derivative gain (Kd/Kp). In control theory this is also known" " as the manipulated variable (MV)", &PIDController::getOutput); static ReadOnlyValueFinfo<PIDController, double> error( "error", "The error term, which is the difference between command and sensed" " value.", &PIDController::getError); static ReadOnlyValueFinfo<PIDController, double> integral( "integral", "The integral term. It is calculated as INTEGRAL(error dt) =" " previous_integral + dt * (error + e_previous)/2.", &PIDController::getEIntegral ); static ReadOnlyValueFinfo<PIDController, double> derivative( "derivative", "The derivative term. This is (error - e_previous)/dt.", &PIDController::getEDerivative ); static ReadOnlyValueFinfo<PIDController, double> e_previous( "e_previous", "The error term for previous step.", &PIDController::getEPrevious); static DestFinfo commandIn( "commandIn", "Command (desired value) input. This is known as setpoint (SP) in" " control theory." , new OpFunc1<PIDController, double>( &PIDController::setCommand )); static DestFinfo sensedIn( "sensedIn", "Sensed parameter - this is the one to be tuned. This is known as" " process variable (PV) in control theory. This comes from the process" " we are trying to control.", new OpFunc1<PIDController, double>( &PIDController::setSensed )); static DestFinfo gainDest( "gainDest", "Destination message to control the PIDController gain dynamically.", new OpFunc1<PIDController, double>(&PIDController::setGain)); static SharedFinfo proc( "proc", "This is a shared message to receive Process messages " "from the scheduler objects." "The first entry in the shared msg is a MsgDest " "for the Process operation. It has a single argument, " "ProcInfo, which holds lots of information about current " "time, thread, dt and so on. The second entry is a MsgDest " "for the Reinit operation. It also uses ProcInfo. ", processShared, sizeof( processShared ) / sizeof( Finfo* ) ); static Finfo* pidFinfos[] = { &gain, &saturation, &command, &sensed, &tauI, &tauD, &outputValue, &error, &integral, &derivative, &e_previous, outputOut(), &commandIn, &sensedIn, &gainDest, &proc }; static string doc[] = { "Name", "PIDController", "Author", "Subhasis Ray", "Description", "PID feedback controller." "PID stands for Proportional-Integral-Derivative. It is used to" " feedback control dynamical systems. It tries to create a feedback" " output such that the sensed (measured) parameter is held at command" " value. Refer to wikipedia (http://wikipedia.org) for details on PID" " Controller." }; static Dinfo<PIDController> dinfo; static Cinfo pidCinfo( "PIDController", Neutral::initCinfo(), pidFinfos, sizeof( pidFinfos ) / sizeof( Finfo* ), &dinfo, doc, sizeof(doc)/sizeof(string)); return &pidCinfo; } static const Cinfo* pidCinfo = PIDController::initCinfo(); PIDController::PIDController(): command_(0), saturation_(DBL_MAX), gain_(1), tau_i_(0), tau_d_(0), sensed_(0), output_(0), error_(0), e_integral_(0), e_derivative_(0), e_previous_(0) { ; // do nothing else } void PIDController::setCommand( double command) { command_ = command; } double PIDController::getCommand( ) const { return command_; } void PIDController::setSensed( double sensed ) { sensed_ = sensed; } double PIDController::getSensed( ) const { return sensed_; } double PIDController::getOutput( ) const { return output_; } void PIDController::setGain( double gain ) { gain_ = gain; } double PIDController::getGain( ) const { return gain_; } void PIDController::setTauI( double tau_i ) { tau_i_ = tau_i; } double PIDController::getTauI( ) const { return tau_i_; } void PIDController::setTauD( double tau_d ) { tau_d_ = tau_d; } double PIDController::getTauD( ) const { return tau_d_; } void PIDController::setSaturation( double saturation ) { if (saturation <= 0) { cout << "Error: PIDController::setSaturation - saturation must be positive." << endl; } else { saturation_ = saturation; } } double PIDController::getSaturation( ) const { return saturation_; } double PIDController::getError( ) const { return error_; } double PIDController::getEIntegral( ) const { return e_integral_; } double PIDController::getEDerivative( ) const { return e_derivative_; } double PIDController::getEPrevious( ) const { return e_previous_; } void PIDController::process(const Eref& e, ProcPtr proc ) { double dt = proc->dt; e_previous_ = error_; error_ = command_ - sensed_; e_integral_ += 0.5 * (error_ + e_previous_) * dt; e_derivative_ = (error_ - e_previous_) / dt; output_ = gain_ * (error_ + e_integral_ / tau_i_ + e_derivative_ * tau_d_); if (output_ > saturation_){ output_ = saturation_; e_integral_ -= 0.5 * (error_ + e_previous_) * dt; } else if (output_ < -saturation_){ output_ = -saturation_; e_integral_ -= 0.5 * (error_ + e_previous_) * dt; } outputOut()->send(e, output_); } void PIDController::reinit(const Eref& e, ProcPtr proc ) { if ( tau_i_ <= 0.0 ){ tau_i_ = proc->dt; } if ( tau_d_ < 0.0 ){ tau_d_ = proc->dt / 4; } sensed_ = 0.0; output_ = 0; error_ = 0; e_previous_ = error_; e_integral_ = 0; e_derivative_ = 0; outputOut()->send(e, output_); } // // PIDController.cpp ends here