Basic but robust PID library

Dependents:   ESP8266_pid_mtrPos_webserver_SDcard_v2 ESP8266_pid_mtrSpeed_Webserver_SDcard ESP8266_pid_spd_and_pos_webserver_SDcard ESP8266_pid_redbot_webserver ... more

Files at this revision

API Documentation at this revision

Comitter:
electromotivated
Date:
Mon Nov 23 02:42:53 2015 +0000
Child:
1:c307cd559154
Commit message:
Fairly Robust PID controller library;

Changed in this revision

PID.cpp Show annotated file Show diff for this revision Revisions of this file
PID.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PID.cpp	Mon Nov 23 02:42:53 2015 +0000
@@ -0,0 +1,71 @@
+#include "PID.h"
+/*
+    Bryce Williams 11/19/2015
+    See PID.h for references as well as method descriptions
+*/
+
+PID::PID(float* setpoint, float* feedback, float* output,
+         float output_lower, float output_upper,
+         float  kp, float ki,  float kd, float Ts){
+    _Ts = Ts;           // Init params
+    _kp = kp; 
+    _ki = ki*Ts;        // Roll sample time into gain  
+    _kd = kd / Ts;      // Roll sample time into gain
+    
+    _setpoint = setpoint; 
+    _feedback = feedback; 
+    _output = output;
+    
+    _output_lower = output_lower;
+    _output_upper = output_upper;
+}
+
+void PID::start(){
+    // Start up such we avoid bumps... (see "Initialization" section in
+    // the reference link found in the header file).
+    last_feedback = *_feedback;   // Eliminate derivative kick at start/restart
+    i_accumulator = clip(*_output, _output_lower,
+                     _output_upper);    // P and D terms are zero, thus 
+                                        // i term is used to keep output unchanged
+    
+    sample_timer.attach(this, &PID::sample, _Ts);
+}
+
+void PID::stop(){
+    sample_timer.detach();
+}
+
+float PID::getError(){
+    return error;
+}
+
+void PID::set_parameters(float kp, float ki, float kd, float Ts){
+    stop();         // Disable Sample Interrupt... stop()
+    _Ts = Ts;       // Set New Sample Time
+    _kp = kp;       // Seet New Kp
+    _ki = ki*Ts;    // Roll sample time into gain  
+    _kd = kd / Ts;  // Roll sample time into gain
+    start();        // Enable Sample Interrupt... start()
+}
+
+void PID::sample(){
+    error = *_setpoint - *_feedback;
+
+    // Accumulate Integral Term such ki is applied to current error
+    // before adding to pool; avoids bumps if ki gain value is changed.
+    i_accumulator += _ki * error;
+    // Avoid "Windup" by clamping intergral term to output limits;
+    // essentially we stop integrating when we reach an upper or 
+    // lower bound.
+    i_accumulator = clip(i_accumulator, _output_lower, _output_upper);
+    
+    // Run it!
+    *_output = _kp*error + i_accumulator - _kd*(*_feedback - last_feedback);
+    last_feedback = *_feedback;
+    // Clamp Output
+    *_output = clip(*_output, _output_lower, _output_upper);
+}
+
+float PID::clip(float value, float lower, float upper){
+    return std::max(lower, std::min(value, upper));
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PID.h	Mon Nov 23 02:42:53 2015 +0000
@@ -0,0 +1,100 @@
+#ifndef PID_H
+#define PID_H
+#include "mbed.h"
+#include <algorithm>
+
+/*
+    Bryce Williams 11/19/2015
+    
+    PID Controller Class based on Brett Beauregard's Arduino PID Library
+    and PID blog post. 
+    
+    Brett Beauregard's blog post explains the PID code implementation very well
+    and discusses why the actual equation is a bit different than the classical 
+    equation, i.e. he explains and implements how to overcome windup, dervative
+    kick, etc.  This class uses the same implementation, but adds interrupt 
+    driven computation. 
+    
+    Reference Links:
+    1. Arduion Library:
+        (http://playground.arduino.cc/Code/PIDLibrary)
+    2. Brett Beauregard's PID Blog:
+        (http://brettbeauregard.com/blog/2011/04/improving-the-beginners-
+         pid-introduction/)
+*/
+
+class PID{
+    public:
+        /*
+            Constructor for PID objects. 
+            
+            Note: PID objects use given pointers, ie setpoint, 
+            feedback, output inside interrupts. When reading/ modifying
+            these vars make sure we don't have possible read/write 
+            conflicts if the interrupt fires. Either ensure reads/writes
+            are atomic operations, or call the stop() method perform the 
+            read/write and then call the start() method.
+        */
+        PID(float* setpoint, float* feedback, float* output,
+            float output_lower, float output_upper,
+            float  kp, float ki,  float kd, float Ts);
+        /*
+            Starts PID Controller; Attaches sample() as callback to Ticker 
+            sample_timer and starts the interrupt
+        */
+        void start();
+        /*
+            Stops PID Contoller; detaches callback from Ticker sample_timer.
+            Allows manual setting of output. 
+        */
+        void stop();
+        /*
+            Increments/ decrements Gain values and Sample time 
+            by the given value. Gives a simple method to 
+            programatically step through different values; just put in a 
+            loop and go
+            @param delta_"name" The value that will be added to its currently set value         
+        */
+//        void adjust_parameters(float delta_kp, float delta_ki, float delta_kd, float delta Ts);
+        /*
+            Overwrite Gain and Sample Time parameters with new 
+            values
+            Note: sample_timer interrupt is disabled during update
+                  to avoid synch issues.
+            
+        */
+        void set_parameters(float kp, float ki, float kd, float Ts);
+        
+        /*
+            returns current error
+        */
+        float getError();
+   
+    private:
+        float _kp, _ki, _kd;            // PID Gain values
+        float _Ts;                      // Sample time is seconds
+        float* _setpoint;               // Pointer to setpoint value
+        float* _feedback;               // Pointer to sensor feedback value (sensor input)
+        float* _output;                 // Pointer to control output value
+        float  _output_lower;            // Ouput Lower Limit
+        float  _output_upper;            // Output Upper Limit
+        
+        float i_accumulator;            // Integral Term accumulator
+        float last_feedback;            // Previous feedback value
+        float error;                    // Feedback error term
+        Ticker sample_timer;            // Generates the sample time interrupt and calls sample()
+        /* 
+            sample() performs next sample calculationand updates command value
+        */
+        void sample();
+        /*
+            Clips value to lower/ uppper
+            @param value    The value to clip
+            @param lower    The mininum allowable value
+            @param upper    The maximum allowable value
+            @return         The resulting clipped value
+        */
+        float clip(float value, float lower, float upper);
+};
+
+#endif
\ No newline at end of file