PID controller theory v2.1

Transfer function of the PID controller this library implements is:

Continuos PID is transformed to the discrete domain and can be described as a sum of three components:




Where the u(k) is the control signal (voltage Uq in our case) in moment k, e(k),e(k-1) is the tracking error in current moment k and previous step k-1. Tracking error presents the difference in between the target velocity value vd and measured velocity v.

Implementation details

The PID algorithm is implemented in the SimpleFOClibrary in the PIDController class. The class is instantiated by specifying the parameters:

PIDController(float P, float I, float D, float ramp, float limit);

And the class has only one function:

// PID controller function
float PIDController::operator() (float error){
    // calculate the time from the last call
    unsigned long timestamp_now = _micros();
    float Ts = (timestamp_now - timestamp_prev) * 1e-6;
    // quick fix for strange cases (micros overflow)
    if(Ts <= 0 || Ts > 0.5) Ts = 1e-3; 

    // u(s) = (P + I/s + Ds)e(s)
    // Discrete implementations
    // proportional part 
    // u_p  = P *e(k)
    float proportional = P * error;
    // Tustin transform of the integral part
    // u_ik = u_ik_1  + I*Ts/2*(ek + ek_1)
    float integral = integral_prev + I*Ts*0.5*(error + error_prev);
    // antiwindup - limit the output voltage_q
    integral = _constrain(integral, -limit, limit);
    // Discrete derivation
    // u_dk = D(ek - ek_1)/Ts
    float derivative = D*(error - error_prev)/Ts;

    // sum all the components
    float output = proportional + integral + derivative;
    // antiwindup - limit the output variable
    output = _constrain(output, -limit, limit);

    // limit the acceleration by ramping the output
    float output_rate = (output - output_prev)/Ts;
    if (output_rate > output_ramp)
        output = output_prev + output_ramp*Ts;
    else if (output_rate < -output_ramp)
        output = output_prev - output_ramp*Ts;

    // saving for the next pass
    integral_prev = integral;
    output_prev = output;
    error_prev = error;
    timestamp_prev = timestamp_now;
    return output;

Therefore you can integrate the PID into your code very easily by just calling:

void setup(){
  PIDController some_pid = PIDController{.....};
void loop(){
  float control = some_pid(target-measurement);

This PID class is implemented in the BLDCMotor and StepperMotor class for handling the motion control velocity (motor.PID_velocity) and position (motor.P_angle). You can change the values parameters of these PID controllers by changing their public variables

// PID controller configuration structure
class PIDController
  float P; //!< Proportional gain 
  float I; //!< Integral gain 
  float D; //!< Derivative gain 

For example:

motor.PID_velocity.P = 1;
motor.P_angle.P = 10;