New PID library with digital anti-windup and process control

Fork of PID_modified by Chun Feng Huang

Files at this revision

API Documentation at this revision

Comitter:
benson516
Date:
Thu Mar 30 07:52:35 2017 +0000
Parent:
6:0d1e877c7f60
Commit message:
New PID 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
--- a/PID.cpp	Thu Dec 15 20:17:49 2016 +0000
+++ b/PID.cpp	Thu Mar 30 07:52:35 2017 +0000
@@ -1,175 +1,274 @@
 #include "PID.h"
 
-PID::PID(float Kp_in, float Ki_in, float Kd_in,  float Sampletime_in){
-    
+PID::PID(float Kp_in, float Ki_in, float Kd_in,  float Sampletime_in):
+        derivative_error(Sampletime_in),
+        SAT_command(1,-1),
+        SAT_output(1,-1)
+{
+    // To enble, run this->start() function
+    enable = false;
+
+    // Sampling time
+    Ts = Sampletime_in;
+
+
+    // Parameters
+    is_limiting_command = false;
+    is_limiting_output = false; // true
+    //
+    is_using_integral = false; // Determine if the integral control is going to be used.
+    is_using_derivative = false; // Determine if the derivative control is going to be used.
+    //
+    is_using_outSource_d_error = false; // Determine whether using the signal for d_error or using numerical derivative to derive d_error from error.
+    //
+    is_antiWindUp = false; // true
+
     // Parameters
-    Outputlimit_bool = false; // true
-    Inputlimit_bool = false;
-    AntiWindUp_bool = false; // true
-    outputLimits_H = 7.4;
-    outputLimits_L = -7.4;
-    inputLimits_H = 0.0;
-    inputLimits_L = 0.0; 
-    feedbackvalue = 0.0; 
-    
-    // Feedback gain 
+    // Feedback gain
+    Kp = Kp_in;
+    Ki = Ki_in;
+    Kd = Kd_in;
+    //
+    if (Ki_in == 0.0){
+        is_using_integral = false;
+    }else{
+        is_using_integral = true;
+    }
+    //
+    if (Kd_in == 0.0){
+        is_using_derivative = false;
+    }else{
+        is_using_derivative = true;
+    }
+    //
+    // Ka = 0.0017;// Volt.-sec./rad
+    //
+    // Small over-bound value for numerical stability
+    overBound_value = 0.01; // Small positive value, adjustable
+
+
+    // States
+    error = 0.0;
+    d_error = 0.0;
+    error_int = 0.0;
+    //
+    error_int_increment = 0.0;
+
+    // Input signal
+    command = 0.0;
+    feedbackValue = 0.0;
+    // Output signal
+    output = 0.0;
+    // Error by saturation
+    delta_output = 0.0; // Error by saturation
+
+}
+// Process controller
+void PID::start(){ // Run
+    if (enable){
+        return;
+    }
+    //
+    enable = true;
+}
+void PID::pause(){ // Stop updating but no reset
+    //
+    enable = false;
+}
+void PID::stop(){ // Stop and reset
+    if (!enable){
+        return;
+    }
+    //
+    enable = false;
+    reset();
+}
+void PID::reset(void){
+    // States
+    error = 0.0;
+    d_error = 0.0;
+    error_int = 0.0;
+    //
+    error_int_increment = 0.0;
+
+    // Input signal
+    command = 0.0;
+    feedbackValue = 0.0;
+    // Output signal
+    output = 0.0;
+    // Error by saturation
+    delta_output = 0.0; // Error by saturation
+
+    // Reset the derivative
+    derivative_error.reset(0.0);
+}
+//
+void PID::EnableAntiWindUp(float Ka_in)
+{
+    is_antiWindUp = true;
+}
+//
+void PID::set_PID_gains(float Kp_in, float Ki_in, float Kd_in){ // Setting Kp, Ki, and Kd
     Kp = Kp_in;
     Ki = Ki_in;
     Kd = Kd_in;
-    // Ka = 0.1; // Volt.-sec./deg.
-    Ka = 0.0017;// Volt.-sec./rad
-    
-    // Sampling time
-    Ts = Sampletime_in; 
-    
-    // Variables
-    error[0] = 0.0;
-    error[1] = 0.0;
-    error_I = 0.0;
     //
-    reference = 0.0;
-    output = 0.0;
-     
-
-     
+    if (Ki_in == 0.0){
+        is_using_integral = false;
+    }else{
+        is_using_integral = true;
+    }
+    //
+    if (Kd_in == 0.0){
+        is_using_derivative = false;
+    }else{
+        is_using_derivative = true;
+    }
 }
-
-void PID::SetOutputLimits(float setoutputLimits_H, float setoutputLimits_L){
-    Outputlimit_bool = true;
-    outputLimits_H = setoutputLimits_H;
-    outputLimits_L = setoutputLimits_L;    
+void PID::SetInputLimits(float inputLimits_H_in, float inputLimits_L_in){
+    is_limiting_command = true;
+    //
+    SAT_command.set_bound(inputLimits_H_in, inputLimits_L_in);
 }
+void PID::SetOutputLimits(float outputLimits_H_in, float outputLimits_L_in){
+    is_limiting_output = true;
+    // is_antiWindUp = true;
+    //
+    SAT_output.set_bound(outputLimits_H_in, outputLimits_L_in);
 
-void PID::SetInputLimits(float setinputLimits_H, float setinputLimits_L){
-    Inputlimit_bool = true;
-    inputLimits_H = setinputLimits_H;
-    inputLimits_L = setinputLimits_L;     
-}
-
-void PID::EnableAntiWindUp(float Ka_in)
-{
-    AntiWindUp_bool = true;
-    Ka = Ka_in;
+    //
+    // Set the over-bound value to be 1% of peak-peak range
+    overBound_value = 0.001*(outputLimits_H_in - outputLimits_L_in);
 }
 
-void PID::Compute(float reference_in, float feedbackvalue_in){
+// Main function for computing the PID
+//--------------------------------------------------//
+void PID::set_d_error(float d_error_in){ // Insert d_error before iteration.
+    d_error = d_error_in;
+    is_using_outSource_d_error = true;
+}
+//
+void PID::iterateOnce(float command_in, float feedbackValue_in){
+    // Main process
+    iterateOnce_noAntiWindUP(command_in, feedbackValue_in);
 
-    if(Inputlimit_bool == true){
-        if( reference_in > inputLimits_H){
-            reference = inputLimits_H;
-        }else if( reference_in < inputLimits_L){
-            reference = inputLimits_L;
+    // Output satuation
+    if(is_limiting_output){
+        if (is_antiWindUp){
+            // Output saturation + anti-windup
+            this->Saturation_AntiWindUp();
         }else{
-            reference = reference_in;
+            // Output saturation only
+            this->Saturation_output();
         }
-        
-    }else{
-        reference = reference_in;
-    }
-    
-    // bypass
-    feedbackvalue = feedbackvalue_in;
-    
-    error[0] = reference - feedbackvalue;
-    // output = output + ( Kp + Ki + Kd )*error[0] + ( -Kp - 2.0*Kd )*error[1] + Kd*error[2];
-    output = Kp*error[0] + Ki*error_I;
-    
-    // Delay 1
-    error[1] = error[0];  
-    
-    // Integration
-    error_I += Ts*error[0];
-    
-    // Output satuation
-    if(Outputlimit_bool && AntiWindUp_bool){
-        if( output >= outputLimits_H){
-            // output = output - (output - outputLimits_H)*Ka;
-            error_I -= Ka*(output - outputLimits_H); // Anti-windup
-            output = outputLimits_H;
-        }else if( output <= outputLimits_L){
-            // output = output - (output - outputLimits_L)*Ka;
-            error_I -= Ka*(output - outputLimits_L); // Anti-windup
-            output = outputLimits_L;
-        }
+        //
     }else{
         // output = output;
     }
-            
+
 }
-void PID::Compute_noWindUP(float reference_in, float feedbackvalue_in){
+//
+void PID::iterateOnce_noAntiWindUP(float command_in, float feedbackValue_in){
 
-    if(Inputlimit_bool == true){
-        if( reference_in > inputLimits_H){
-            reference = inputLimits_H;
-        }else if( reference_in < inputLimits_L){
-            reference = inputLimits_L;
-        }else{
-            reference = reference_in;
-        }
-        
+    //
+    // -- Important! --
+    // Post-integral action:
+    // This integral action generates the error_int of time (k-1) (previous time), which means the back-step integral.
+    // The actual integral action move to here is for implementing the (digital-version) anti-windup.
+    if (is_using_integral){
+        error_int += error_int_increment;
     }else{
-        reference = reference_in;
+        error_int = 0.0;
+    }
+    //
+
+    // Processing input signals
+    //----------------------------------------//
+    // Comand saturation
+    if(is_limiting_command){
+        // Saturation
+        command = SAT_command.filter(command_in);
+        //
+    }else{
+        command = command_in;
     }
-    
-    // bypass
-    feedbackvalue = feedbackvalue_in;
-    
-    error[0] = reference - feedbackvalue;
-    // output = output + ( Kp + Ki + Kd )*error[0] + ( -Kp - 2.0*Kd )*error[1] + Kd*error[2];
-    output = Kp*error[0] + Ki*error_I;
-    
-    // Delay 1
-    error[1] = error[0];  
-    
-    // Integration
-    error_I += Ts*error[0];
-    
-    // Output satuation
-    /*
-    if(Outputlimit_bool && AntiWindUp_bool){
-        if( output >= outputLimits_H){
-            // output = output - (output - outputLimits_H)*Ka;
-            error_I -= Ka*(output - outputLimits_H); // Anti-windup
-            output = outputLimits_H;
-        }else if( output <= outputLimits_L){
-            // output = output - (output - outputLimits_L)*Ka;
-            error_I -= Ka*(output - outputLimits_L); // Anti-windup
-            output = outputLimits_L;
+
+    // bypass the feedback value
+    feedbackValue = feedbackValue_in;
+    //----------------------------------------//
+
+    // PID control
+    //----------------------------------------//
+    // Calculating the error
+    error = command - feedbackValue;
+    //
+    if (is_using_derivative){
+        if (is_using_outSource_d_error){
+            // Please use the insert function for d_error.
+        }else{
+            // Calculating the derivative of error
+            d_error = derivative_error.filter(error);
         }
+        // Control output
+        output = Kp*error + Ki*error_int + Kd*d_error;
     }else{
-        // output = output;
+        // Control output
+        output = Kp*error + Ki*error_int;
     }
-    */
-            
+    //----------------------------------------//
+
+    // Pre-integral action
+    error_int_increment = Ts*error;
+
 }
+//--------------------------------------------------//
 
-// Use separately
-// Compute_noWindUP() -- [ Saturation_output(): optional, can be replaced by customized saturation function ] -- Anti_windup(delta) -- output
+
+// Used separately
+// Compute_noWindUP() -- [ Saturation_output(): optional, can be replaced by customized saturation function ] -- AntiWindUp(delta) -- output
 //////////////////////////////////////////////////
 void PID::Saturation_output(){
-        if( output >= outputLimits_H){
-            delta_output = outputLimits_H - output;
-            output = outputLimits_H;
-        }else if( output <= outputLimits_L){
-            delta_output = outputLimits_L - output;
-            output = outputLimits_L;
-        }else{
-            delta_output = 0.0;
+    //
+    output = SAT_output.filter(output);
+    delta_output = SAT_output.delta_out; // (original_out - limited_out)
+}
+void PID::AntiWindUp(float delta){ // delta_V = V - V_sat
+
+    /*
+    // (Analog) Anti-windup compensation
+    // error_int -= Ka*delta; // Anti-windup
+    error_int_increment -= Ka*delta; // Anti-windup
+    */
+
+    // (Digital) Anti-windup
+    // If the output is going to be over bound, stop the integral action in that direction
+    if (Ki > 0.0){
+        //
+        if (delta > overBound_value && error_int_increment > 0.0){ // Positive saturation
+            error_int_increment = 0.0;
+        }else if (delta < -overBound_value && error_int_increment < 0.0){ // Negative saturation
+            error_int_increment = 0.0;
         }
-}
-void PID::Anti_windup(float delta){ // delta_V = Vs - V
-    // Anti-windup compensation
-    error_I += Ka*delta; // Anti-windup
+    }else if (Ki < 0.0){
+        //
+        if (delta > overBound_value && error_int_increment < 0.0){ // Positive saturation
+            error_int_increment = 0.0;
+        }else if (delta < -overBound_value && error_int_increment > 0.0){ // Negative saturation
+            error_int_increment = 0.0;
+        }
+    }
+
 }
 ////////////////////////////////////////////////// end Use separately
 
-// Use alone
-// Compute_noWindUP() -- Anti_windup() -- output
+// Used alone
+// Compute_noWindUP() -- Saturation_AntiWindUp() -- output
 //////////////////////////////////////////////////
-void PID::Anti_windup(){ // delta_V = Vs - V
-
-    PID::Saturation_output();
+void PID::Saturation_AntiWindUp(){ // delta_V = V - V_sat
+    //
+    // Output saturation
+    this->Saturation_output();
     // Anti-windup compensation
-    error_I += Ka*PID::delta_output; // Anti-windup
+    this->AntiWindUp(delta_output);
 }
-////////////////////////////////////////////////// end Use alone
\ No newline at end of file
+////////////////////////////////////////////////// end Use alone
--- a/PID.h	Thu Dec 15 20:17:49 2016 +0000
+++ b/PID.h	Thu Mar 30 07:52:35 2017 +0000
@@ -1,55 +1,89 @@
 #ifndef PID_H
 #define PID_H
-
-#include "mbed.h"
+//
+#include "FILTER_LIB.h"
 
 class PID{
-    public:
+public:
+
+    // Sampling time
+    float Ts;
+    //
+    bool enable;
+
+    // Flags
+    bool is_limiting_command;
+    bool is_limiting_output;
+    //
+    bool is_using_integral; // Determine if the integral control is going to be used.
+    bool is_using_derivative; // Determine if the derivative control is going to be used.
+    //
+    bool is_using_outSource_d_error; // Determine whether using the signal for d_error or using numerical derivative to derive d_error from error.
+    //
+    bool is_antiWindUp;
+
+    // Parameters
+    // Feedback gain
+    float Kp;
+    float Ki;
+    float Kd;
+    //
+    // float Ka;
+
+    // States
+    float error;
+    float d_error;
+    float error_int;
+    //
+    float error_int_increment; // error_int += error_int_increment;
 
-        PID(float Kp_in, float Ki_in, float Kd_in,  float Sampletime_in);   
-        void Compute(float reference_in, float feedbackvalue_in);
-        
-        // 
-        void Compute_noWindUP(float reference_in, float feedbackvalue_in);
-        // Method 1: Separated operation for anti-windup
-        void Saturation_output();
-        void Anti_windup(float delta); // delta_V = Vs - V
-        // Method 2: Single anti-windup operation
-        void Anti_windup();
-        //
-           
-        void SetOutputLimits(float setoutputLimits_H, float setoutputLimits_L);
-        void SetInputLimits(float setinputLimits_H, float setinputLimits_L); 
-        void EnableAntiWindUp(float Ka_in); 
-        
-        float Kp;
-        float Ki;
-        float Kd;
-        float Ka;
- 
-        float error[2];
-        double error_I;
-        
-        float output;
-        float reference;
-        float delta_output; // Error by saturating
-        
-        float Ts;
+    // Input signal
+    float command;
+    float feedbackValue;
+    // Output signal
+    float output;
+    // Error by saturation
+    float delta_output; // Error by saturation
+
+
+    PID(float Kp_in, float Ki_in, float Kd_in,  float Sampletime_in);
+    // Process controller
+    void start(); // Run
+    void pause(); // Stop updating but no reset
+    void stop(); // Stop and reset
+    void reset(); // Reset all the states to initial values.
+    //
+    void EnableAntiWindUp(float Ka_in);
+    //
+    void set_PID_gains(float Kp_in, float Ki_in, float Kd_in); // Setting Kp, Ki, and Kd
+    void SetInputLimits(float inputLimits_H_in, float inputLimits_L_in);
+    void SetOutputLimits(float outputLimits_H_in, float outputLimits_L_in);
 
-    private:
-    
-        bool Outputlimit_bool;
-        bool Inputlimit_bool;
-        bool AntiWindUp_bool;
-        
-        float outputLimits_H;
-        float outputLimits_L;
-        float inputLimits_H;
-        float inputLimits_L;
-        
-        float feedbackvalue;
- //       Ticker PID_timer;
+    // Main function for computing the PID
+    void set_d_error(float d_error_in); // Insert d_error before iteration.
+    void iterateOnce(float command_in, float feedbackValue_in);
+    void iterateOnce_noAntiWindUP(float command_in, float feedbackValue_in);
+
+    // Anti-windup method
+    // Method 1: Separated operation for anti-windup
+    void Saturation_output();
+    void AntiWindUp(float delta); // delta_V = V - V_sat
+    // Method 2: Single anti-windup operation
+    void Saturation_AntiWindUp();
+    //
+
+private:
+
+    // Derivative
+    Derivative_appr derivative_error;
+
+    // Saturation
+    Saturation SAT_command;
+    Saturation SAT_output;
+
+    // Small over-bound value for numerical stability
+    float overBound_value; // Small positive value
 
 };
 
-#endif /* PID_H*/
\ No newline at end of file
+#endif /* PID_H*/