Los controladores PID están en todas partes. Regulan temperatura, velocidad de motores, fuentes de poder, sistemas de posicionamiento, equipos de proceso y probablemente una docena de cosas a un brazo de distancia de quien lee esto.

También se explican, casi siempre, con suficiente cálculo diferencial como para parecer más misteriosos de lo que realmente son. Es cierto que la I y la D de PID corresponden a términos del cálculo, pero son lo bastante simples como para construirlos en una planilla. Conviene conseguir una copia y tenerla abierta durante la lectura.

La hoja de cálculo implementa un controlador PID simulado junto con un proceso simulado, es decir, aquello que se intenta controlar. Se pueden cambiar las ganancias del controlador, alterar el proceso, introducir perturbaciones y ver qué ocurre sin compilar nada ni cablear un calefactor que podría terminar convertido en tostador.

¿Qué significa cada letra de PID?

Las tres letras corresponden a Proporcional, Integral y Derivativo. Si el cálculo quedó oxidado, basta con esto: la integral es cuánto se va acumulando en el tiempo y la derivada es cuánto cambió recién. Cada término opera sobre el error:

Código
error = setpoint - valor de proceso

El setpoint es donde se quiere que esté el sistema, el valor de proceso (PV) es donde efectivamente está, y lo deseable es que el error sea cero. El proporcional es el método de control más evidente: mientras más lejos se está, más se corrige; mientras más cerca del objetivo, menos salida proporcional se necesita.

El integral, en cambio, mira una suma acumulada de errores. El derivativo mide cuánto cambiaron las cosas desde la última vez que se observaron. El tiempo de ciclo base de la planilla lo fija dt, que por defecto vale 0,1 segundos. Es decir, se necesitan diez filas para cubrir un segundo completo.

Supongamos que se controla temperatura y se quiere llegar a 20 grados. Si la temperatura es 15, el error es +5. Si es 22, el error es -2. El trabajo del controlador es convertir ese error en una salida. Esa salida afecta algo, un calefactor o la velocidad de un motor, que a su vez cambia el valor de proceso. La planilla limita la salida entre 0 y 100 y, presumiblemente, eso sería un porcentaje de voltaje o un ciclo de trabajo PWM. Al algoritmo las unidades le dan lo mismo.

Primero, solo P

Con el desplegable Model en DEFAULT, se parte configurando:

Código
Kp = 4
Ki = 0
Kd = 0

Valor de proceso inicial en 0, setpoint en 20, ganancia del proceso en 1 y constante de tiempo en 2 segundos. Con solo el término proporcional operando, el controlador es fácil de entender:

Código
salida = Kp × error

Al comienzo el error es 20, así que el controlador pide una salida de 80, o sea 4 por 20. El proceso, sin embargo, no salta instantáneamente a 80. La planta simulada es un sistema de primer orden implementado esencialmente así:

Código
PVnuevo = PVanterior + dt/tau × (Kproceso × salida - PVanterior)

Ese es un modelo genérico útil para una cantidad sorprendente de cosas reales. Se sube un calefactor y la temperatura se acerca gradualmente a un nuevo valor. Se aplica voltaje a un motor y su velocidad no cambia de golpe. Se carga un condensador a través de una resistencia y aparece exactamente ese comportamiento exponencial.

El control proporcional no logra llegar del todo
El control proporcional no logra llegar del todo

Conviene mirar dónde se estabiliza. Con los valores sugeridos, el valor de proceso termina alrededor de 16 aunque el setpoint sea 20. ¿Por qué? Con un valor de proceso de 20 el error sería cero, y un controlador proporcional con error cero produce salida cero. Pero este proceso en particular necesita una salida de 20 para mantenerse en 20. Por lo tanto, nunca puede llegar.

Ese es el clásico error de estado estacionario del control solo proporcional. Se podría subir Kp. Con Kp=8 el proceso se acerca mucho más al setpoint. Pero aumentar la ganancia proporcional sin límite no es solución universal: los sistemas reales terminan sobrepasando el objetivo, oscilando, amplificando ruido o expresando su descontento de alguna otra forma.

¿Cómo elimina el integral el error residual?

Volviendo Kp a 4, se prueba con:

Código
Ki = 0.5

El término integral no mira solo el error actual, sino el error acumulado en el tiempo. En la planilla hay una columna de estado integral, y cada fila hace aproximadamente esto:

Código
integral = integral_previa + error × dt
I = Ki × integral

Con el controlador proporcional estancado bajo el valor deseado, mientras quede algún error positivo la integral sigue creciendo. Eso aumenta gradualmente la salida hasta que el error remanente desaparece. En vez de quedarse en 16, el proceso ahora sube hasta 20.

Respuesta del sistema ante una perturbación
Respuesta del sistema ante una perturbación

Para probarlo, se selecciona el modelo de proceso de usuario con User Model # en 1, lo que permite perturbar el valor de proceso escribiendo números en la columna User1. Dejando el comienzo en 0 y metiendo más abajo una perturbación de -5, o una secuencia como 0,5 / 0,75 / 1 / 1,5 / 2 / 1,5 / 0,75 / 0,5 / -1 / -1 / -0,5, se simula abrir la puerta de un refrigerador, aplicar carga mecánica a un motor o conectar otra carga a una fuente regulada.

Un controlador solo proporcional reacciona de inmediato, pero una vez que todo se estabiliza vuelve a tolerar un error permanente. El integral no. Si el proceso queda bajo el setpoint, la acción integral sigue creciendo hasta compensar la perturbación.

Demasiado de algo bueno

El término derivativo responde a qué tan rápido cambia el error:

Código
D = Kd × (error - error_previo) / dt

Si el control proporcional pregunta "¿cuán lejos estamos?", el derivativo pregunta "¿qué tan rápido nos estamos acercando?", o más precisamente, qué tan rápido cambia el error.

Haciendo el proceso más veloz, con la constante de tiempo bajando de 2 a cerca de 0,8 segundos, y probando algo deliberadamente agresivo:

Código
Kp = 8
Ki = 1
Kd = 0
Sobrepaso en el modelo de usuario 1
Sobrepaso en el modelo de usuario 1

La respuesta llega rápido al setpoint, pero lo sobrepasa. El motivo se ve en la planilla: mientras el proceso sube a toda velocidad sigue estando bajo el setpoint, así que la integral continúa acumulando error positivo. Cuando por fin se llega al destino, el término integral todavía empuja. Según el proceso y las ganancias, el resultado puede ser un sobrepaso leve, uno grande o una oscilación sostenida.

Sobrepaso observado en el modelo 2
Sobrepaso observado en el modelo 2

Como la planta por defecto es de primer orden, el derivativo tiene poco material con el cual trabajar. El User Model 2 agrega otro retardo usando la columna USER2 como estado intermedio, lo que produce más desfase y hace que un ajuste PI agresivo sea más propenso al sobrepaso. Partiendo con Kd=0 y anotando el valor máximo alcanzado, se prueba luego Kd = 0,2, luego 0,5 y quizás 1,0, e incluso valores negativos. Existe un rango donde el sobrepaso se reduce levemente, pero más allá la respuesta empieza a oscilar. Empujando Kd lo suficiente, el término derivativo pasa a ser parte del problema en vez de la cura. Los gráficos se autoescalan, así que a veces un pico que parece igual o mayor en realidad es menor que el anterior: hay que leer los números.

Ese es el equilibrio básico del PID. P reacciona al error que existe ahora. I reacciona al error que lleva rato existiendo. D reacciona a hacia dónde parece dirigirse el error.

TérminoA qué respondeQué aportaQué rompe si sobra
ProporcionalError actualCorrección inmediataOscilación y amplificación de ruido
IntegralError acumuladoElimina el error permanenteSobrepaso y windup
DerivativoVelocidad de cambio del errorAnticipa y frena el sobrepasoRepique y sensibilidad al ruido

A veces conviene quedarse con PI, o incluso con control puramente proporcional.

Es solo una planilla

Se pueden agregar modelos propios modificando USR_PROCESS para que llame a otra función. DEF_PROCESS es un modelo de retardo simple, USR_PROCESS0 agrega ruido aleatorio, USR_PROCESS1 permite inyectar una perturbación en la columna USER1 y USR_PROCESS2 es igual pero con un proceso de segundo orden en la columna USER2. Por supuesto, los sistemas de control reales son más desordenados que estos modelos.

La salida puede tener límites duros. De hecho, la planilla incluye topes mínimo y máximo, lo que expone otro problema clásico: el windup integral. Si el controlador pide desesperadamente una salida de 150 pero el actuador solo entrega 100, la integral puede seguir acumulando error aunque el actuador no pueda responder. Cuando el error finalmente se invierte, todo ese integral almacenado tiene que desenrollarse. En términos prácticos, basta imaginar que se desconecta un termistor y el sistema cree que hay un error de temperatura gigantesco. Intentará corregirlo sin poder. Cuando alguien vuelve a enchufar el sensor, todo el error acumulado debe revertirse. Por eso los controladores prácticos suelen incluir esquemas anti-windup.

La acción derivativa trae sus propios problemas. La planilla calcula la derivada a partir del error, lo que significa que cambiar el setpoint de golpe produce un pulso derivativo enorme, el famoso derivative kick. Los controladores reales suelen calcular la derivada a partir del valor de proceso. Y como las mediciones reales traen ruido, y derivar es excelente para amplificar ruido de alta frecuencia, el término D habitualmente se filtra.

Nada de eso está en la hoja, y es deliberado: el propósito es que cada número quede a la vista. Cambiar un setpoint a mitad de la columna genera una entrada escalón. Cambiar una columna de usuario inyecta una perturbación. Ajustar Kp, Ki o Kd deja ver de inmediato qué porción de la salida del controlador cambió y por qué.

Para quien programa firmware, esa transparencia es exactamente el paso previo a escribir el lazo en un microcontrolador. La estructura de la planilla, con dt fijo, error, estado integral y error previo, es la misma que termina en un ESP32 o un Arduino controlando un extrusor, un horno de reflow o un motor con encoder. Equivocarse en una celda cuesta una tecla; equivocarse en la constante de una resistencia de potencia cuesta el componente.