Electronic – PID Control Loops with large and unpredictable anomalies

control systempid controller

Short Question
Is there a common way to handle very large anomalies (order of magnitude) within an otherwise uniform control region?

Background
I am working on a control algorithm that drives a motor across a generally uniform control region. With no / minimal loading the PID control works great (fast response, little to no overshoot). The issue I'm running into is there will usually be at least one high load location. The position is determined by the user during installation, so there is no reasonable way for me to know when / where to expect it.

When I tune the PID to handle the high load location, it causes large over shoots on the non-loaded areas (which I fully expected). While it is OK to overshoot mid travel, there are no mechanical hard stops on the enclosure. The lack of hardstops means that any significant overshoot can / does cause the control arm to be disconnected from the motor (yielding a dead unit).

Things I'm Prototyping

  • Nested PIDs (very agressive when far away from target, conservative when close by)
  • Fixed gain when far away, PID when close
  • Conservative PID (works with no load) + an external control that looks for the PID to stall and apply additional energy until either: the target is achieved or rapid rate of change is detected (ie leaving the high load area)

Limitations

  • Full travel defined
  • Hardstops cannot be added (at this point in time)
  • Error will likely never zero out
  • The high load could have be obtained from a less than 10% travel (meaning no "running start")

Best Answer

Your error calculation does not seem to accumulate error when working with the derivative term, and you may want to modify this since only derivative term is able to react to fast changes in the process.

If I got it right your code

// Determine the error delta
dE = abs(last_error - new_error);
last_error = new_error;

will always calculate control term based on the current error, which is the traditional way how the PID was implemented. It is fine since the I term is supposed to take care of accumulated error anyway.

However, I had a customer who came up with the following idea you might want to try out. Since you have a part of the process curve where more aggressive changes are needed, you can let even the the D part error to accumulate:

if(TD)                                                 // Calculate D term
{  
   Last_C += (Error - Last_C) / TD;                    // D term simulates
   Dterm = (Error - Last_C) * KD;                      // capacitor discharging
}
else    
   Dterm = 0;                                          // D term is OFF (TD is 0)

There are two interesting things to note here:

  • The TD value is not the derivative gain (which is KD) but the derivative time, a user constant which controls the time for error to accumulate. If it was set to zero, the D part of the PID is disabled disregarding the KD gain value set.

  • Note how the current error was used to 'charge' the Last_C value before taking it over to the D part calculation. The Last_C variable is acting like a capacitor, it would build up while the error was large, so that your derivative part would act based also on a recent 'history' of the error, and after that (when error was smaller) this 'history' will discharge like a capacitor.

Of course, you should limit the total output the way you probably already do (anti windup reset, bumpless auto to manual transfer, and other usual stuff).

I can post more details about other terms of my PID algorithm if you find it useful, but you might want to try this and see what happens. It served my customer well for years.