Improved PID Library

After prodding around the standard Arduino PID library, I decided to make an improved version that can be found here: ArduPID. It can also be installed via the libraries manager.

These are the improvements so far:

  • More settings to configure your controller
    • Added functions such as start(), reset(), stop(), reverse(), setBias(), and setWindUpLimits()
  • Added a debug function that can print any combination of various controller state data to the serial plotter/monitor for troubleshooting
  • Trapezoidal integration is used compared to the sample and hold integration of the standard library
  • Controllers are not constrained to a specific sample frequency - asynchronous calls of compute() will still result in valid outputs

Here is an example:

#include "ArduPID.h"


ArduPID myController;


double input;
double output;

// Arbitrary setpoint and gains - adjust these as fit for your project:
double setpoint = 512;
double p = 10;
double i = 1;
double d = 0.5;


void setup()
{
  Serial.begin(115200);
  myController.begin(&input, &output, &setpoint, p, i, d);

  // myController.reverse()               // Uncomment if controller output is "reversed"
  // myController.setSampleTime(10);      // OPTIONAL - will ensure at least 10ms have past between successful compute() calls
  myController.setOutputLimits(0, 255);
  myController.setBias(255.0 / 2.0);
  myController.setWindUpLimits(-10, 10); // Groth bounds for the integral term to prevent integral wind-up
  
  myController.start();
  // myController.reset();               // Used for resetting the I and D terms - only use this if you know what you're doing
  // myController.stop();                // Turn off the PID controller (compute() will not do anything until start() is called)
}


void loop()
{
  input = analogRead(A0); // Replace with sensor feedback

  myController.compute();
  myController.debug(&Serial, "myController", PRINT_INPUT    | // Can include or comment out any of these terms to print
                                              PRINT_OUTPUT   | // in the Serial plotter
                                              PRINT_SETPOINT |
                                              PRINT_BIAS     |
                                              PRINT_P        |
                                              PRINT_I        |
                                              PRINT_D);
  
  analogWrite(3, output); // Replace with plant control signal
}

If anyone has any improvements or finds any bugs, please let me know!

2 Likes

thanks, seems like great work.

Thinking aloud:

instead of using user space input and output variables, couldn't you just hide those in your library and pass parameter to the compute function, which would just return the result ?

User could still decide if s/he needs the input and output variables, but they would not be shared with your library through pointers.

myController.begin(&setpoint, p, i, d);
...
double input = analogRead(A0);
double output = myController.compute(input);
analogWrite(3, output);

and could have a more condensed code if they are not needed

myController.begin(&setpoint, p, i, d);
...
analogWrite(3, myController.compute(analogRead(A0)));