Control PID
Apunte básico de control PID con Arduino
APUNTES
11/18/20255 min read


¿Qué es un Control PID?
El control PID es una técnica de control de retroalimentación utilizada en sistemas de control industrial. PID significa Proporcional, Integral y Derivativo. Cada una de estas componentes tiene un papel específico en la corrección del error:
Proporcional (P): Ajusta la salida en proporción al error actual. Si el error es grande, la acción de control será grande.
Integral (I): Suma los errores pasados para eliminar el error acumulado. Esta acción es útil para eliminar el error constante (offset).
Derivativo (D): Reacciona a la tasa de cambio del error, proporcionando una acción correctiva anticipada.
Ecuación del PID
La ecuación del controlador PID es:
Donde:
· u(t) es la salida del controlador.
· e(t) es el error en el tiempo.
· Kp es la ganancia proporcional.
· Ki es la ganancia integral.
· Kd es la ganancia derivativa.
En el contexto del control PID, dt representa el intervalo de tiempo (delta time) entre las lecturas sucesivas del sistema o entre las ejecuciones del algoritmo de control. Es esencial para el cálculo de las partes integral y derivativa del controlador PID.
Componentes del Controlador PID
Control Proporcional (P):
El término proporcional produce una salida que es proporcional al error actual. La constante de proporcionalidad es Kp.
· Fórmula:
Control Integral (I):
El término integral produce una salida que es proporcional al error acumulado en el tiempo. La constante de integralidad es Ki.
· Fórmula:
Control Derivativo (D):
El término derivativo produce una salida que es proporcional a la tasa de cambio del error. La constante derivativa es Kd.
· Fórmula:
¿Qué es el error?
El error en un sistema de control es la diferencia entre el valor deseado (setpoint) y el valor actual de la variable de proceso.
· Fórmula:
Donde
· e(t) es el error en el tiempo t.
· SP es el setpoint.
· PV es el valor de proceso actual.
Implementación del Algoritmo PID
El algoritmo PID puede ser implementado en una variedad de sistemas donde se necesite un control preciso. Algunos ejemplos incluyen:
· Control de temperatura en hornos y sistemas de calefacción.
· Control de velocidad en motores eléctricos.
· Sistemas de navegación automática en aviones y barcos.
· Control de nivel en tanques de almacenamiento.
Ejemplo de Implementación con dt
Si quisiéramos implementar un controlador PID sin utilizar una librería que maneje dt automáticamente, podríamos hacerlo así:
// Definición de pines
#define sensorPin A0
#define CalentadorPin 3
// Variables del PID
double Setpoint, Input, Output;
double Suma_Error, Error_Anterior;
// Ajustes del PID
double Kp = 2, Ki = 5, Kd = 1;
// Tiempo
unsigned long Tiempo_Anterior;
void setup() {
// Inicialización de pines
pinMode(CalentadorPin, OUTPUT);
// Inicialización de variables del PID
Setpoint = 100; // Temperatura deseada en grados Celsius
// Inicialización del tiempo
Tiempo_Anterior = millis();
}
void loop() {
// Leer el valor del sensor
int sensorValue = analogRead(sensorPin);
Input = map(sensorValue, 0, 1023, 0, 100); // Suponiendo que el sensor de temperatura da un valor entre 0 y 100 grados Celsius
// Calcular tiempo transcurrido
unsigned long now = millis();
double delta_tiempo = (double)(now - Tiempo_Anterior);
// Calcular el error
double error = Setpoint - Input;
// Acumulación del error (integral)
Suma_Error += (error * delta_tiempo);
// Diferenciación del error (derivada)
double dErr = (error - Error_Anterior) / delta_tiempo;
// Calcular la salida del PID
Output = Kp error + Ki Suma_Error + Kd * dErr;
// Recordar el error y el tiempo para la próxima iteración
Error_Anterior = error;
Tiempo_Anterior = now;
// Controlar el elemento calefactor
analogWrite(CalentadorPin, Output);
// Opcional: Mostrar información en el monitor serie
Serial.print("Input: ");
Serial.print(Input);
Serial.print(" Output: ");
Serial.print(Output);
Serial.print(" Setpoint: ");
Serial.println(Setpoint);
delay(100);
}
Explicación del Código con dt
Definimos las variables necesarias:
· Suma_Error para la suma de errores (integral)
· Error_Anterior para el error anterior (derivativa)
· Tiempo_Anterior para el tiempo anterior.
En el setup():
· Inicializamos los pines.
· Establecemos el valor deseado (Setpoint).
· Capturamos el tiempo inicial.
En el loop():
· Leemos el valor del sensor y lo mapeamos a una escala adecuada.
· Calculamos el tiempo transcurrido (delta_tiempo).
· Calculamos el error actual.
· Actualizamos la suma del error multiplicada por el tiempo (Suma_Error).
· Calculamos la tasa de cambio del error (derivada).
· Calculamos la salida del PID usando las fórmulas proporcionadas.
· Guardamos el error y el tiempo actuales para la próxima iteración.
· Usamos analogWrite() para ajustar el calentador según el valor calculado por el PID.
· Mostramos los valores en el monitor serie para monitoreo y ajuste.
· Añade un delay constante (100 ms) para simular un intervalo de tiempo constante entre iteraciones.
Esta implementación manual demuestra cómo se utilizan los conceptos fundamentales del PID, incluyendo el uso de dt para las componentes integral y derivativa.
Ejemplo de implementación sin dt
double setpoint, input, output;
double Kp = 2.0, Ki = 5.0, Kd = 1.0;
double anterior, integral;
// Pines
#define pwmPin 9 // Pin PWM para controlar el motor
#define sensorPin A0 // Pin para leer el sensor de velocidad
void setup() {
// Configuración de los pines
pinMode(pwmPin, OUTPUT);
pinMode(sensorPin, INPUT);
// Inicializar el setpoint
setpoint = 100; // Velocidad deseada del motor
// Inicializar las variables del PID
integral = 0;
anterior = analogRead(sensorPin);
}
void loop() {
// Leer la velocidad actual del motor desde el sensor
input = analogRead(sensorPin);
// Calcular el error
double error = setpoint - input;
// Calcular el término integral
integral += error;
// Calcular el término derivativo
double derivative = input - anterior;
// Calcular la salida del PID
output = Kp error + Ki integral - Kd * derivative;
// Limitar la salida entre 0 y 255
output = constrain(output, 0, 255);
// Ajustar la salida del motor (PWM)
analogWrite(pwmPin, output);
// Guardar los valores actuales para el siguiente loop
anterior = input;
// Añadir un pequeño delay para estabilizar el loop (asumiendo tiempo constante entre iteraciones)
delay(100);
}
Explicación del Código
Variables:
· setpoint: Valor deseado del proceso (por ejemplo, la velocidad del motor).
· input: Valor actual leído del sensor.
· output: Salida calculada del controlador PID.
· Kp, Ki, Kd: Ganancias proporcional, integral y derivativa respectivamente.
· Anterior: Almacena el valor del sensor en el ciclo anterior.
· integral: Acumulador del error integral.
Setup:
· Configura los pines y establece el valor deseado (setpoint).
· Inicializa las variables del PID y guarda el valor inicial del sensor.
Loop:
· Lee el valor actual del sensor.
· Calcula el error como la diferencia entre el setpoint y el input.
· Actualiza el término integral sumando el error.
· Calcula el término derivativo como la diferencia entre el valor actual y el anterior del sensor.
· Calcula la salida del PID combinando los términos proporcional, integral y derivativo.
· Limita la salida a un rango válido para el PWM (0 a 255).
· Ajusta la velocidad del motor utilizando analogWrite.
· Guarda los valores actuales del sensor para el próximo ciclo.
· Añade un delay constante (100 ms) para simular un intervalo de tiempo constante entre iteraciones.
Ejemplo de un programa básico para un robot seguidor de línea
#define Motor1Av 12
#define Motor1Rv 13
#define Motor1Vel 11
#define Motor2Av 9
#define Motor2Rv 8
#define Motor2Vel 10
#define NumSensores 6
int Sensores[NumSensores] = {7, 6, 5, 4, 3, 2};
/*Sensores -----
Sensores [0]| |
-----
Sensores [1]| |
-----
Sensores [2]| |
-----
*/
int i;
int Promedio = 0;
int Suma = 0;
int Error = 0;
int VelD, VelI;
int ErrorPasado = 0;
unsigned long TiempoAnterior = 0;
void setup() {
for (i = 0; i < NumSensores; i++) {
pinMode(Sensores[i], INPUT);
}
pinMode(Motor1Av, OUTPUT);
pinMode(Motor1Rv, OUTPUT);
pinMode(Motor2Av, OUTPUT);
pinMode(Motor2Rv, OUTPUT);
pinMode(Motor2Vel, OUTPUT);
pinMode(Motor1Vel, OUTPUT);
Serial.begin(9600);
}
void loop() {
double Kp = 5.0;
double Kd = 2.0;
Error = 0;
Promedio = 0;
Suma = 0;
VelD = 100;
VelI = 100;
Serial.println("Valor de los Sensores");
for (i = 0; i < NumSensores; i++) {
Serial.print("S");
Serial.print(i);
Serial.print(":");
Serial.print(digitalRead(Sensores[i]));
Serial.print(" -- ");
Promedio = Promedio + (digitalRead(Sensores[i]) ((i + 1) 10));
//Promedio= 0 + 1/0 (110) = 0/ 10
//Promedio= 0 + 1/0 (210) = 0/ 20
//...
//Promedio= 0 + 1/0 (610) = 0/60
//Promedio= 0 + 60 = 60
Suma = Suma + digitalRead(Sensores[i]);
}
// Calcular delta tiempo (dt)
unsigned long TiempoActual = millis();
double dt = (TiempoActual - TiempoAnterior);
if (Suma != 0) {
Error = (Promedio / Suma) - 35;
} else {
Error = 0;
}
double Derivativo = (Error - ErrorPasado) / dt;
/*
Derivativo = 30 - 15 = 15
derivativo = 15 - 30 = -15
corrección kd = de / dt
calculo dererivativo
Error - Error Anterior d(e)
------------------------- = ------
Tiempo - Tiempo Anterior dt
*/
VelD = VelD + (Error Kp) + (Derivativo Kd);
VelI = VelI - (Error Kp) - (Derivativo Kd);
Serial.print("Error: ");
Serial.println(Error);
ErrorPasado = Error;
TiempoAnterior = TiempoActual;
Motores(VelD, VelI);
}
void Motores(int PWMD, int PWMI) {
if (PWMD > 0) {
digitalWrite(Motor1Av, HIGH);
digitalWrite(Motor1Rv, LOW);
} else {
digitalWrite(Motor1Rv, HIGH);
digitalWrite(Motor1Av, LOW);
PWMD = PWMD * -1;
}
if (PWMI > 0) {
digitalWrite(Motor2Av, HIGH);
digitalWrite(Motor2Rv, LOW);
} else {
digitalWrite(Motor2Rv, HIGH);
digitalWrite(Motor2Av, LOW);
PWMI = PWMI * -1;
}
analogWrite(Motor2Vel, PWMD);
analogWrite(Motor1Vel, PWMI);
}
