This is a small, fully self contained PID class designed to help provide simple, efficient tuning, and simple integration wherever any level of PID control might be needed.
All code is contained in a single C++ class, and doesn't depend on any specific compiler or toolchain. However, example Arduino sketches are included.
- Provide all expected features of a quality PID loop.
- Allow for stability without extensive tuning.
- Be simple to integrate into projects without code restructuring
- Be easy to "chain", sending the output of one PID to the input of another.
- Be flexible enough that any provided functions can be used in isolation.
- Be simple enough to be used in "transient" or one-off control sequences
True to the name, the main purpose of this class is PID control.
Provides an "Expected output", helpful when doing velocity control systems.
Force the PID system to cap the setpoint to a range near the current input values. This allows you to tune the PID in a smaller range, and have it perform sanely during large setpoint changes.
Allows a maximum rate of change on the output, preventing jumps in output during setpoint changes
Adjustable min and maximum range, so the output can directly drive a variety of systems.
Helps smooth the output, preventing high-frequency oscillations.
Allows you to specify a maximum output value the I term will generate. This allows active error correction, with minimal risk of I-term windup.
The I term and summed error will never increase if the system is already doing everything permitted to correct the system error.
No need for lots of convoluted calculation functions, or asyncronous calculation modes. After configuration, getOutput()
is probably the only function you need.
A bare bones PID system could look like this.
MiniPID pid=MiniPID(1,0,0);
//set any other PID configuration options here.
while(true){
//get some sort of sensor value
//set some sort of target value
double output=pid.getOutput(sensor,target);
//do something with the output
delay(50);
}
That's it. No fuss, no muss. A few lines of code and some basic tuning, and your PID system is in place.
There's several ways. In simple systems, the fastest is output=pid.getOutput(sensor,target);
. This does all the calculations with the current values, applying any configuration, and returns the output.
For more event-driven systems, you can split up the setting and getting, using output=pid.getOutput(sensor);
and pid.setSetpoint(target)
.
If your outputs are to be disabled or driven by a different system, then you may want to use the pid.reset()
method. That will set the PID controller to re-initialize the next time getOutput()
is used. Because of this, reset()
can be used when disabling PID control, when reenabling it, or at any point between.
The most complex part of PID systems is the configuration. Tuning a PID process properly typically requires either significant calculation, significant trial and error, or both.
This library is designed to produce "decent" PID results with minimal effort and time investment, by providing more extensive configuration options than most controllers.
Note, PID systems work best when the calculations are performed at constant time intervals. This PID implimentation does not handle this, and assumes the primary loop or framework handles the precise timing details.
These create the basic PID, allowing for further configuration. Generally, you initialize the PID values here, but you can re-configure on the fly using the setP(double P)
,setI(double I)
,setD(double D)
, and setF(double F)
methods.
Tuning PID systems is out of scope of this readme, but a good examples can be found all over the internet.
Feed-Forward is a 4th system variable that is very helpful on systems with a target velocity, or other systems where an on-target system results in continous motion. Feed forward is not helpful on positional control systems, or other systems where being on target results in halted (or small cyclic) motion.
Conceptually, Feed-forward defines a "best guess" as to what the system output should be for a given setpoint value. Feed forward does not consider what the system is actually doing, and a system driven solely by feed-forward is actually an open-loop system. Mathematically, a system driven solely is equivilent to output=setpoint*F
.
For this class of systems, it's helpful to consider the F term the primary variable. Using F in this way will result in a shorter time-to-target since you don't wait for error buildup (to aquire the I term). It's also simpler to tune and more stable since you don't have large P and D terms.
For tuning, F as the primary variable results in P, I, and D being used for minor corrections, with a much smaller system error. P and I will generally be sufficient, and can correct for non-linearities in the system such as such as drag, inertia, friction, and disturbances.
Optional, but highly recommended to set. The set the output limits, and ensure the controller behaves when reaching the maximum output capabilities of your physical system.
Sets the maximum output generated by the I term inside the controller. This is independent of the setOutputLimits
values. This can assist in reducing windup over large setpoint changes or stall conditions.
Reverses the output. Use this if the controller attempts to go the wrong way during operation.
Resets the PID controller. This primarily clears the I and D terms clears the previous sensor state, and sets the target setpoint the the current position.
This is useful if the output system's state may have changed since the last time the PID system output was applied. This is generally the case when the output system is in a manual control mode (such as a joystick), or was disabled and may have been physically moved.
Set the maximum rate of change in a single calculation cycle. This is particularly useful for adding "inertial" to the system, preventing jerks during setpoint changes.
The output filter prevents sharp changes in the output, adding inertia and minimizing the effect of high frequency oscillations. This adds significant stability to systems with poor tunings, but at the cost of slower setpoint changes, disturbance rejection, and increased overshoot.