Tutorial · Principiante · 18 min de lectura
Cómo sintonizar un controlador PID: guía práctica paso a paso
Sintoniza un lazo PID paso a paso: primero la ganancia proporcional, luego la derivativa contra la sobreoscilación, al final la integral y evita el windup.
Un controlador PID convierte el error entre donde un sistema está y donde quieres que esté en una corrección. Tiene tres términos—proporcional, integral y derivativo— y el ajuste es el proceso de elegir con qué fuerza actúa cada uno. La forma fiable de hacerlo es un término cada vez, en un orden fijo, observando la respuesta después de cada cambio. Puedes seguirlo en el PID Controller Simulator.
Empieza por la ganancia proporcional
Pon las ganancias integral y derivativa a cero. Sube la ganancia proporcional (Kp) hasta que el sistema responda rápido a un cambio en el punto de consigna. Si es poca reacciona perezzosamente; si es demasiada se pasa de rosca y oscila. Para cuando la respuesta sea rápida pero solo ligeramente oscilatoria—ese es tu punto de partida para añadir los otros términos.
Una regla útil: P te da velocidad. Reacciona al error ahora mismo, y nada más.
Añade derivativo para domar el sobreimpulso
Ahora sube la ganancia derivativa (Kd). La acción derivativa responde a lo rápido que está cambiando el error, así que frena según el sistema se acerca al punto de consigna. Esto es lo que elimina el sobreimpulso y el rebabado que deja atrás el control proporcional puro.
Añade solo el derivativo justo para estabilizar la oscilación. Demasiado vuelve al controlador nervioso, porque el término derivativo amplifica cualquier ruido en la medida. Si tu señal es ruidosa, filtrala antes de derivar, o apóyate en la proporcional y la integral.
Añade la integral al final
Proporcional y derivativo juntos aún pueden dejar un pequeño error estacionario—el sistema aparca justo antes del punto de consigna. El término integral (Ki) arregla eso: acumula error a lo largo del tiempo y empuja hasta que el error desaparece.
Sube la ganancia integral despacio y solo si queda un error en estado estacionario repetible. La integral es el término más lento y el más fácil de pasarse, así que un poco hace mucho.
Vigila el windup integral
El windup integral es el fallo clásico del PID. Cuando el actuador se satura—un motor ya a plena velocidad, una válvula completamente abierta—el sistema no puede responder, pero el término integral sigue acumulando error. Cuando el error por fin se invierte, toda esa acción acumulada se descarga de golpe y el sistema se pasa de rosca con fuerza.
Prevento limitando el término integral (deja de crecer mientras la salida está saturada) o mediante cálculo hacia atrás (drena el integral usando la diferencia entre la salida pedida y la entregada). Un límite simple:
float candidateIntegral = integral + error * dt;
float raw = kp * error + ki * candidateIntegral + kd * derivative;
float output = constrain(raw, outputMin, outputMax);
if (raw == output) integral = candidateIntegral; // only accumulate when not saturated
Mantén honesta la temporización del bucle
Los tres términos dependen de un paso de tiempo consistente. En un microcontrolador, un delay() largo en el bucle añade tiempo muerto que deja inútil el término derivativo por alto que lo ajustes, y deforma el integral. Ejecuta el bucle rápido y a un intervalo constante, y pasa el tiempo real transcurrido al cálculo.
Acercarte rápido: el punto de partida de Ziegler–Nichols
Subir P hasta encontrar la oscilación no es solo una comprobación de cordura — te da un número del que puedes calcular todo el ajuste. Pon I y D a cero, sube P hasta que la salida oscile de forma constante sin crecer ni decrecer, y anota dos cosas:
Ku— la ganancia proporcional a la que eso ocurre (la ganancia última)Tu— el periodo de la oscilación, en segundos
Entonces:
| Controlador | Kp |
Ki |
Kd |
|---|---|---|---|
| Solo P | 0.5 Ku |
— | — |
| PI | 0.45 Ku |
0.54 Ku / Tu |
— |
| PID (clásico) | 0.6 Ku |
1.2 Ku / Tu |
0.075 Ku Tu |
| PID (sin sobreimpulso) | 0.2 Ku |
0.4 Ku / Tu |
0.066 Ku Tu |
Estos son un punto de partida, no una respuesta. La fila clásica es deliberadamente agresiva y normalmente se pasa de rosca un 20–40 %, lo cual está bien para un motor y es inaceptable para un brazo robótico cerca de una mesa. La última fila intercambia velocidad de respuesta por llegar sin sobreimpulso.
El valor del método es que sustituye tres incógnitas por dos medidas. Veinte minutos
encontrando Ku y Tu te llevan más lejos que una tarde de adivinar, y luego puedes ajustar a
ojo desde un sitio razonable.
Deriva la medida, no el error
Este es un cambio de una línea que arregla la queja más común sobre la D.
Cuando el punto de consigna da un escalón, el error da un escalón con él. La derivada de un escalón es enorme un instante, así que el controlador ordena una corrección violenta por un motivo que no tiene nada que ver con la física — no se movió nada, solo pediste algo distinto. En un robot eso es un tirón fuerte cada vez que cambias el objetivo.
// Derivative kick: the setpoint step goes straight into the output.
float derivative = (error - lastError) / dt;
// Derivative on measurement: the setpoint contributes nothing at all.
float derivative = -(measurement - lastMeasurement) / dt;
El signo se invierte porque el error es setpoint − measurement, así que derivar la medida en
su lugar implica negarlo. Cuando el punto de consigna es constante las dos formas son
matemáticamente idénticas; cuando cambia, solo la segunda se comporta bien.
Usa derivativo sobre la medida salvo que tengas una razón concreta para no hacerlo. Prácticamente no existe ningún caso en el que quieras el tirón.
Filtra el derivativo, o la D amplificará tu ruido
Derivar amplifica el contenido de alta frecuencia, y el ruido del sensor es de alta frecuencia por definición. Una señal con ±2 cuentas de jitter muestreada cada 10 ms produce una estimación de la derivativa que oscila ±200 cuentas por segundo — y la D convierte eso en órdenes de motor.
El resultado habitual es que la gente concluye que la D es inútil y la pone a cero. El problema real está antes:
// A first-order filter on the derivative term only.
// alpha = dt / (tau + dt); tau is the filter time constant in seconds.
const float tau = 0.05f; // 50 ms
const float alpha = dt / (tau + dt);
dFiltered += alpha * (dRaw - dFiltered);
Elegir tau es un intercambio: demasiado pequeño y el ruido sobrevive, demasiado grande y la D
está actuando sobre información que ya está obsoleta, que es justo lo que la D existe para evitar.
Empieza por tres a cinco veces tu periodo de bucle y súbelo solo hasta que pare el nerviosismo.
Un controlador completo
Todo lo de arriba, en un sitio:
struct Pid {
float kp, ki, kd;
float outMin, outMax;
float tau; // derivative filter time constant
float integral = 0;
float lastMeasurement = 0;
float dFiltered = 0;
bool primed = false;
};
float pidUpdate(Pid &c, float setpoint, float measurement, float dt) {
if (dt <= 0) return 0;
const float error = setpoint - measurement;
// Derivative on measurement, low-pass filtered.
if (!c.primed) { c.lastMeasurement = measurement; c.primed = true; }
const float dRaw = -(measurement - c.lastMeasurement) / dt;
const float alpha = dt / (c.tau + dt);
c.dFiltered += alpha * (dRaw - c.dFiltered);
c.lastMeasurement = measurement;
// Provisional integral, committed only if the output does not saturate.
const float candidate = c.integral + error * dt;
const float raw = c.kp * error + c.ki * candidate + c.kd * c.dFiltered;
const float out = constrain(raw, c.outMin, c.outMax);
if (raw == out) c.integral = candidate; // conditional integration
return out;
}
Tres cosas de ahí son la diferencia entre un controlador que funciona en un robot y uno que funciona en un libro de texto: derivativo sobre la medida, un derivativo filtrado, y integración condicional para que el integral no pueda hacer windup mientras el actuador está saturado.
Diagnosticar a partir de la forma de la respuesta
Puedes nombrar casi cualquier fallo de ajuste solo con la curva de respuesta:
| Lo que ves | Qué significa | Qué cambiar |
|---|---|---|
| Sube despacio, nunca llega | P demasiado baja, sin I | Sube P; añade I si queda un error |
| Se estabiliza corto del objetivo, permanentemente | Sin término integral | Añade I, despacio |
| Se pasa una vez y luego se estabiliza | P algo alta, sin D | Añade D |
| Se pasa repetidamente, decreciendo | P demasiado alta | Baja P, o añade D |
| Oscila para siempre a amplitud constante | P en la ganancia última | Has encontrado Ku — redúcela a la mitad |
| La oscilación crece | P por encima de Ku |
Reduce inmediatamente |
| Salida rápida, irregular, nerviosa | La D actuando sobre ruido | Filtra el derivativo, o filtra el sensor |
| Da tirones fuertes cada vez que cambia el consigna | Tirón derivativo | Derivativo sobre la medida |
| Se va muy lejos pasado tras un movimiento grande | Windup integral | Integración condicional o un límite |
| Ayer bien, hoy perezoso | La planta cambió — normalmente la batería | Las ganancias son propiedades de la planta, no del código |
Esa última fila merece énfasis porque no es un bug. Las ganancias describen una planta concreta, y la planta incluye la carga de la batería, la masa del chasis, la superficie y la temperatura de la reductora. Un ajuste que era perfecto con pilas nuevas será perezoso con pilasugas, porque el mismo ciclo de trabajo ahora produce menos par. Las respuestas profesionales son un lazo de velocidad por debajo (para que el controlador ordene velocidad y no ciclo de trabajo) o programación de ganancias (varios ajustes, seleccionados por condición de operación).
Temporización del bucle, cuantificada
Tanto I como D se calculan contra tiempo transcurrido, así que un bucle que corre cuando le da por terminar calcula ambos contra una mentira.
| Comportamiento del bucle | Efecto en el controlador |
|---|---|
| Intervalo fijo | Todo funciona como se diseñó |
Variable, pero dt medido y pasado |
También correcto — esta es la respuesta práctica |
Variable, dt asumido constante |
I acumula mal; D es ruido |
Contiene un delay() |
Tiempo muerto — ninguna cantidad de D lo compensa |
Mide dt con micros() y pásalo. No cuesta nada, sobrevive a un bucle cuya duración cambia
según añades funciones, y elimina una clase entera de misterio del ajuste.
Si además puedes hacer el bucle rápido, hazlo — un controlador que muestrea a 20 Hz está
actuando sobre información de hasta 50 ms de antigüedad, y ninguna ganancia arregla información
obsoleta. Quitar llamadas a delay() suele llevar a un primer robot de 20 Hz a varios cientos, y
el seguimiento mejora sin tocar ninguna ganancia.
Pruébalo tú mismo
Abre el PID Controller Simulator, pon a cero las ganancias I y D, y recorre este orden. Observa cómo el sobreimpulso, el tiempo de estabilización y el error en estado estacionario responden a cada término por separado. Una vez que la forma tenga sentido aquí, la misma intuición se traslada a un motor real, a un calentador o al lazo de dirección del Line Follower Simulator.
Explora el grafo
Forma parte de estos montajes
Proyectos y rutas de aprendizaje que incluyen este tutorial.
Lecturas adicionales