Seguimos con la serie de entradas dedicadas a teoría de controladores viendo cómo implementar un control PID en un microprocesador como Arduino.
En entradas anteriores hemos visto una introducción a la teoría de control, presentado el controlador todo/nada con histéresis, el poderoso controlador PID, y visto cómo ajustar los parámetros de un controlador PID.
Ahora toca dejar la teoría, desempolvar el teclado, ver cómo se implementa un controlador PID en un microprocesador como Arduino. Afortunadamente, es bastante sencillo hacer un PID básico y, como veremos en la segunda parte, tenemos disponible una librería muy completa para que sea incluso más sencillo.
Un PID es un elemento esencial a la hora de abordar una gran cantidad de proyectos interesantes, como regular automáticamente el nivel de luz, mantener la temperatura, conseguir que un motor gire a velocidad constante, plataformas estabilizadoras, hacer que un robot avance recto o incluso robots que se mantienen en equilibrio sobre dos ruedas.
Controlador PID a mano
Primero vamos a ver cómo implementar un controlador sencillo por nosotros mismos. En realidad, el código no es excesivamente complejo. Supongamos que nuestro controlador toma la entrada de la variable controlada en A0, y efectúa la salida mediante una señal PWM en el pin 3.
El código de un PID básico podría ser el siguiente
// Asignaciones pins
const int PIN_INPUT = A0;
const int PIN_OUTPUT = 3;
// Constantes del controlador
double Kp=2, Ki=5, Kd=1;
// variables externas del controlador
double Input, Output, Setpoint;
// variables internas del controlador
unsigned long currentTime, previousTime;
double elapsedTime;
double error, lastError, cumError, rateError;
void setup()
{
Input = analogRead(PIN_INPUT);
Setpoint = 100;
}
void loop(){
pidController.Compute();
Input = analogRead(PIN_INPUT); // leer una entrada del controlador
Output = computePID(Input); // calcular el controlador
delay(100);
analogWrite(PIN_OUTPUT, Output); // escribir la salida del controlador
}
double computePID(double inp){
currentTime = millis(); // obtener el tiempo actual
elapsedTime = (double)(currentTime - previousTime); // calcular el tiempo transcurrido
error = Setpoint - Input; // determinar el error entre la consigna y la medición
cumError += error * elapsedTime; // calcular la integral del error
rateError = (error - lastError) / elapsedTime; // calcular la derivada del error
double output = kp*error + ki*cumError + kd*rateError; // calcular la salida del PID
lastError = error; // almacenar error anterior
previousTime = currentTime; // almacenar el tiempo anterior
return output;
}
Como vemos no es demasiado difícil. Tenemos una función ‘computePID()’ que realiza todo el trabajo. En esta función calculamos el tiempo transcurrido entre llamadas, que necesitamos para calcular tanto la derivada como la integral del error.
A continuación, comparamos la entrada del controlador con la consigna para determinar el error y realizamos las ‘pseudo’ integrales y derivadas (su equivalente discreto). Finalmente, calculamos la respuesta del sistema mediante la fórmula del PID, y guardamos los valores para el siguiente ciclo.
¿No ha sido tan duro, verdad? Sin embargo, aunque es completamente funcional, nuestro controlador PID es bastante simple. Por tanto, tiene ciertas carencias ante situaciones que ocurren frecuentemente en la realidad.
Podríamos mejorar nuestro código, pero ya no iba a ser tan sencillo ni rápido hacerlo. Y lo mejor ¡no hace falta! Para nuestra alegría alguien ha hecho todo el trabajo por nosotros, como veremos en el siguiente apartado.
Controlador PID con librería
Afortunadamente tenemos disponible la librería PIDController, basada en la librería ArduinoPID desarrollada por Brett Beauregard.
ArduinoPID es una pequeña maravilla que contiene muchas mejoras respecto a un PID básico, que están detalladas en este enlace del autor. Os recomiendo que leáis la documentación de la librería porque es toda una clase sobre controladores PID.
Con esto, crear un controlador PID en Arduino es tan sencillo como el siguiente código.
#include <PIDController.hpp>
const int PIN_INPUT = 0;
const int PIN_OUTPUT = 3;
PID::PIDParameters<double> parameters(4.0, 0.2, 1);
PID::PIDController<double> pidController(parameters);
void setup()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Setpoint = 100;
pidController.TurnOn();
}
void loop()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Update();
analogWrite(PIN_OUTPUT, pidController.Output);
}
Hemos visto que resulta bastante sencillo implementar un controlador PID en un microprocesador como Arduino. Y es incluso más sencillo a la pequeña joya de librería Arduino PID. Así que no hay motivos para tener escusas o miedos a la hora de implementar un controlador PID en nuestros proyectos.
En las próximas entradas de la serie empezaremos a ver ejemplos prácticos de proyectos donde podemos implementar un control PID en Arduino. ¡Hasta pronto!