Dlloydev / QuickPID

A fast PID controller with multiple options. Various Integral anti-windup, Proportional, Derivative and timer control modes.
MIT License
195 stars 50 forks source link

Add Conditional Anti-Windup Feature? #34

Closed Dlloydev closed 2 years ago

Dlloydev commented 2 years ago

I'm getting good test results with implementing a conditional integral anti-windup feature which has the nice effect of dampening over-shoot while maintaining fast response.

The following are the plots from hardware (Uno with RC filter 100μF-10K) comparing PID_v1 with QuickPID. Note PID_v1 was modified to allow getting the iTerm value.

Setup is P_ON_E, kp=2, ki=5, kd=0, sampleTime = 40ms, loop delay = 50ms.

Without conditional anti-windup, both PID_v1 and QuickPID produce this plot...

AW_PID_v1

Using QuickPID with conditional anti-windup produces this plot...

AW_QPID25

Note the dampened overshoot and more aggressive (yet clamped) iTerm. During the overshoot portion, you can see how the iTerm and output are being reduced.

Dlloydev commented 2 years ago

This is the proposed new compute function:

bool QuickPID::Compute() {
  if (mode == MANUAL) return false;
  uint32_t now = micros();
  uint32_t timeChange = (now - lastTime);
  if (mode == TIMER || timeChange >= sampleTimeUs) {

    float input = *myInput;
    float dInput = input - lastInput;
    if (controllerDirection == REVERSE) dInput = -dInput;

    error = *mySetpoint - input;
    if (controllerDirection == REVERSE) error = -error;
    float dError = error - lastError;

    pmTerm = kpm * dInput;
    peTerm = kpe * error;
    iTerm =  ki  * error;
    dmTerm = kdm * dInput;
    deTerm = kde * dError;

    //conditional anti-windup
    bool aw = false;
    float iTermOut = (peTerm - pmTerm) + ki * (iTerm + error);
    if (iTermOut > outMax && error > 0) aw = true;
    else if (iTermOut < outMin && error < 0) aw = true;
    if (aw && ki) iTerm = constrain(iTerm + error, -outMax, outMax);

    //compute output as per PID_v1
    outputSum += iTerm;                                                           // include integral amount
    outputSum = constrain(outputSum - pmTerm, outMin, outMax);                    // include pmTerm and clamp
    *myOutput = constrain(outputSum + peTerm - dmTerm + deTerm, outMin, outMax);  // totalize, clamp, drive output

    lastError = error;
    lastInput = input;
    lastTime = now;
    return true;
  }
  else return false;
}
Dlloydev commented 2 years ago

Improved the anti-windup for iTerm with this...

    //conditional anti-windup
    bool aw = false;
    float iTermOut = (peTerm - pmTerm) + ki * (iTerm + error);
    if (iTermOut > outMax && dError > 0) aw = true;
    else if (iTermOut < outMin && dError < 0) aw = true;
    if (aw && ki) iTerm = constrain(iTermOut, -outMax, outMax);

AW_QPID25B

If the plots look good on the TCLab hardware with 2.9 minutes time constant, I'll add this to the next version (QuickPID 2.5).

Dlloydev commented 2 years ago

Plot using Arduino Leonardo with TCLab board having thermal time constant 2.9 minutes. Temp setting 31.3, sampleTime and loop delay = 5 sec, kp = 0.02, ki = 0.05, kd = 0.

Input has poor resolution and is unfiltered, no settling time was added from a previous higher temp run.

The untuned performance is OK ...

AW_QPID25C

Dlloydev commented 2 years ago

Published new version QuickPID 2.5.0