2019-11-02 20:16:16 +03:00
|
|
|
/**
|
|
|
|
* PID Controller
|
2020-05-29 23:23:24 +03:00
|
|
|
*
|
2019-11-02 20:16:16 +03:00
|
|
|
* The PID controller is a linear control algorithm that has three terms:
|
|
|
|
* - Proportional: A simple scaling of the error value by a gain kP
|
2020-05-29 23:23:24 +03:00
|
|
|
* - Integral: Integration of the error value over time, then multipled by gain
|
|
|
|
* kI
|
|
|
|
* - Derivative: Rate of change of the error value over time, multiplied by
|
|
|
|
* gain kD
|
|
|
|
*
|
|
|
|
* Terms of the controller can be removed by setting their gain to 0, creating a
|
|
|
|
* PI (kD = 0) or PD (kI = 0) controller. Depending on the control problem at
|
|
|
|
* hand, some terms may not increase the performance of the system, or may have
|
|
|
|
* a negative effect.
|
|
|
|
*
|
|
|
|
* For a more mathematical expanation of the PID Controller, see
|
|
|
|
* https://en.wikipedia.org/wiki/PID_controller
|
|
|
|
*
|
2019-11-02 20:16:16 +03:00
|
|
|
* Limitations of this implementation:
|
2020-05-29 23:23:24 +03:00
|
|
|
* - Since this implementation is just for demonstration, the pid_step function
|
|
|
|
* takes the dt as a parameter, and it can be provided by the user in main().
|
|
|
|
* This allows deterministic experimentation with the algorithm, rather than
|
|
|
|
* using time(NULL) which would make the function non-deterministic.
|
|
|
|
*
|
|
|
|
* Inputs: e(t) - Current error at time t. For example, how far a servo is off
|
|
|
|
* the desired angle Output: u(t) - Controller output at time t.
|
2019-11-02 20:16:16 +03:00
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2020-05-29 23:23:24 +03:00
|
|
|
struct pid
|
|
|
|
{
|
2019-11-02 20:16:16 +03:00
|
|
|
// Controller gains
|
|
|
|
float kP;
|
|
|
|
float kI;
|
|
|
|
float kD;
|
|
|
|
|
|
|
|
// State variables
|
|
|
|
float lastError;
|
|
|
|
float integral;
|
|
|
|
};
|
|
|
|
|
2020-05-29 23:23:24 +03:00
|
|
|
float pid_step(struct pid *controller, float dt, float error)
|
|
|
|
{
|
2019-11-02 20:16:16 +03:00
|
|
|
// Calculate p term
|
|
|
|
float p = error * controller->kP;
|
|
|
|
|
|
|
|
// Calculate i term
|
|
|
|
controller->integral += error * dt * controller->kI;
|
|
|
|
|
|
|
|
// Calculate d term, taking care to not divide by zero
|
2020-05-29 23:23:24 +03:00
|
|
|
float d =
|
|
|
|
dt == 0 ? 0 : ((error - controller->lastError) / dt) * controller->kD;
|
2019-11-02 20:16:16 +03:00
|
|
|
controller->lastError = error;
|
|
|
|
|
|
|
|
return p + controller->integral + d;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:23:24 +03:00
|
|
|
int main()
|
|
|
|
{
|
2019-11-02 20:16:16 +03:00
|
|
|
printf("PID Controller Example\n");
|
|
|
|
|
2020-05-29 23:23:24 +03:00
|
|
|
struct pid controller = {.lastError = 0, .integral = 0};
|
2019-11-02 20:16:16 +03:00
|
|
|
|
|
|
|
// Take the controller gains from the user
|
2020-06-28 18:25:37 +03:00
|
|
|
printf(
|
|
|
|
"Please enter controller gains in format kP, kI, KD. For example, "
|
|
|
|
"\"1.2 2.1 3.2\"\n> ");
|
2019-11-02 20:16:16 +03:00
|
|
|
scanf("%f %f %f", &controller.kP, &controller.kI, &controller.kD);
|
2020-05-29 23:23:24 +03:00
|
|
|
printf("Using kP: %f, kI: %f, kD: %f\n", controller.kP, controller.kI,
|
|
|
|
controller.kD);
|
2019-11-02 20:16:16 +03:00
|
|
|
|
2020-05-29 23:23:24 +03:00
|
|
|
// How often the pid_step algorithm expects to be called. In a real life
|
|
|
|
// scenario this would be provided by calling time(NULL) - last_time, or by
|
|
|
|
// calling the function reliably at X Hz (using a timer or RTOS etc) For
|
|
|
|
// demonstration of this algorithm though, it is defined below as 1 second,
|
|
|
|
// allowing easy testing of integral and derivative terms.
|
2019-11-02 20:16:16 +03:00
|
|
|
float time_step = 1;
|
|
|
|
|
|
|
|
float error_value;
|
2020-05-29 23:23:24 +03:00
|
|
|
while (1)
|
|
|
|
{
|
2019-11-02 20:16:16 +03:00
|
|
|
printf("Enter error value\n>");
|
|
|
|
scanf("%f", &error_value);
|
|
|
|
|
|
|
|
float output = pid_step(&controller, time_step, error_value);
|
|
|
|
printf("Output: %f\n", output);
|
|
|
|
}
|
|
|
|
}
|