← Índice del curso

Sensores y Actuadores · Tema 43

Control PID

El control proporcional del tema anterior tenía dos defectos: dejaba un error en régimen y no se acomodaba a las perturbaciones. El PID los corrige agregándole memoria (un término integral que acumula el error) y anticipación (uno derivativo que mira hacia dónde va la medida). Es el controlador más usado de la industria, y la parte difícil no es la fórmula, que cabe en cinco líneas, sino los detalles que la rodean: la saturación del actuador, el ruido del sensor, el tiempo de muestreo y el ajuste de tres ganancias sin romper nada.

01 · Punto de partida

Por qué hace falta más que P

Seguimos con el horno de los temas anteriores: 100 W, 2000 J/K, 0,5 K/W (constante de tiempo de 1000 s) y un sensor que tarda 20 s en enterarse. Con el proporcional puro (banda de 5 °C) la temperatura se estabiliza en 37,73 °C, a 2,27 °C de la consigna de 40 °C, y si el ambiente baja de 15 a 5 °C se queda 3,18 °C por debajo. La razón es estructural: para mantener 40 °C hace falta cierta potencia, y un controlador que solo mira el error presente solo puede dar esa potencia si el error no es cero.

El PID agrega dos cosas a la ley proporcional:

  • Memoria (integral): suma el error a lo largo del tiempo. Mientras quede un error, la suma sigue creciendo y empuja la salida hasta que el error se anula. Con ella el control encuentra solo la potencia necesaria, sea cual sea el ambiente.
  • Anticipación (derivada): mide con qué rapidez cambia la variable y frena la salida antes de pasarse, igual que se levanta el pie del acelerador al ver que se llega a la curva.

Una frase para recordar la idea: el término P actúa sobre el presente del error, el I sobre su pasado acumulado y el D sobre su futuro previsible.

02 · La ley

Los tres términos

En la forma «estándar» (con la ganancia delante de los tres términos), la salida es:

u(t) = Kp · [ e(t) + (1 / Ti) · ∫ e(t) dt + Td · de(t)/dt ]

La forma «paralela» equivalente usa tres ganancias independientes: Ki = Kp / Ti y Kd = Kp · Td. Con la forma estándar, Ti y Td son tiempos en segundos y tienen un significado físico: Ti es lo que tarda la integral en igualar a la acción proporcional si el error se mantiene constante, y Td es el tiempo con el que la derivada «adelanta» a la proporcional ante una rampa del error. En el horno, Kp se mide en 1/°C (la salida va de 0 a 1), Ti y Td en segundos.

Efecto de aumentar cada ganancia por separado (tendencias generales).
Al aumentar…Velocidad de respuestaSobreimpulsoError en régimenEstabilidadRuido en la salida
KpMás rápidaAumentaDisminuye (no se anula)EmpeoraAumenta
La integral (Ti menor)Algo más rápidaAumentaSe anulaEmpeoraCasi igual
La derivada (Td mayor)Casi igualDisminuyeSin efectoMejora (hasta un punto)Aumenta mucho

La integral es la que elimina el error en régimen: si se queda un error constante e, la integral crece sin límite a razón Kp e / Ti y empuja la salida hasta que el error desaparece; por eso, en régimen, el término I contiene la potencia que el proceso necesita. En el horno con el ambiente a 5 °C, cualquier PI bien ajustado termina en 40,00 °C con el 70 % de potencia, que es justo lo que hace falta para vencer una pérdida de 35 K / 0,5 K/W = 70 W. El proporcional puro, con la misma perturbación, queda en 36,82 °C con el 64 %.

03 · Discretizar

El PID en un microcontrolador

En un programa, el tiempo avanza a pasos de Ts (el período del lazo, tema 41) y las operaciones continuas se aproximan:

  • Integral: una suma, I ← I + Ki · e · Ts.
  • Derivada: una diferencia, (yk − yk−1) / Ts.
  • Período de muestreo: constante y exacto (proximo += periodo, tema 41), y bastante menor que los tiempos del proceso: una regla es Ts ≤ Ti / 10 y ≤ Td / 5, y del orden de un décimo del tiempo de respuesta deseado. Un muestreo irregular altera la integral y la derivada.

Cuatro detalles que separan un PID de libro de uno que funciona:

  1. Derivada sobre la medida, no sobre el error. Si la consigna da un salto, el error salta, y su derivada es un pico enorme (un «golpe» o derivative kick). Con Kp = 1, Td = 10 s, Ts = 1 s y un salto de 3 °C, el pico sería de 1 × 10 × 3 / 1 = 30 en la salida, cuando el máximo útil es 1. Derivando la medida (−de/dt con consigna constante es igual a dy/dt con signo cambiado), el salto de consigna no produce ningún golpe.
  2. Derivada filtrada. Derivar amplifica el ruido de alta frecuencia sin límite. Se agrega un filtro de primer orden con constante Td / N (con N entre 5 y 20), de modo que la ganancia a alta frecuencia queda limitada a Kp (1 + N).
  3. Salida saturada y anti-windup (sección siguiente).
  4. Transferencia sin saltos (bumpless): al pasar de manual a automático, o al volver de una falla, la integral se inicializa con el valor que hace que la salida sea la actual (I = u − Kp e); si no, la salida da un salto brusco.

Otras decisiones de implementación: las ganancias en las unidades de la variable de proceso (para no confundir °C con décimas de grado), la dirección de la acción (calefacción o refrigeración, como en el tema 42) y la reacción ante una medida inválida (tema 40): salida segura y estado congelado, para no integrar basura.

04 · Los límites del actuador

Saturación y anti-windup

El actuador tiene límites: el calefactor no puede dar más del 100 % ni menos del 0 %. Cuando el controlador pide más (por ejemplo, al arrancar el horno frío, con 25 °C de error), la salida satura; pero la integral no lo sabe y sigue acumulando error. Es el windup: la integral se carga mucho más allá de lo que el actuador puede aplicar, y cuando la temperatura por fin se acerca a la consigna, la integral acumulada sigue empujando la salida a pleno y la temperatura se pasa. Solo se «descarga» cuando el error ha sido negativo durante un buen rato.

Arranque en frío del horno con un PI (Kp = 0,5; Ti = 200 s) según la protección contra el windup.
ProtecciónSobreimpulsoEntra en ±0,5 °C a losIntegral máxima
Solo P (Kp = 0,2)0,00 °Cno llega (37,73 °C)—
Ninguna13,15 °C2106 s20,45 (2045 % de la salida)
Pinza (integración condicional)0,00 °C741 s0,50
Retrocálculo con Tt = 50 s0,00 °C880 s0,50
Retrocálculo con Tt = Ti = 200 s0,90 °C845 s0,96

Sin protección, la integral llega a 20,45: el controlador «cree» que hay que dar un 2045 % de potencia, y la temperatura se pasa 13,15 °C y tarda casi 35 minutos en volver a la banda. Con la pinza, el mismo PI no se pasa y entra en la banda a los 741 s; el límite físico (calentar a 100 % hasta 39,5 °C) es de unos 673 s, así que se llega a menos de un 10 % de ese mínimo. Las técnicas más usadas:

  • Pinza (clamping) o integración condicional: no se integra cuando la salida está saturada y el error empuja hacia la saturación. Es la más simple y robusta.
  • Retrocálculo (back-calculation): se realimenta a la integral la diferencia entre la salida limitada y la que pedía el controlador, con una constante Tt: I ← I + (Ts/Tt)·(u − usin limitar). Con Tt grande, desacumula lento (0,90 °C de sobreimpulso con 200 s); con Tt chico, rápido.
  • Limitar la propia integral a un rango razonable.
  • Congelar o reiniciar la integral cuando el sensor falla, el modo es manual o el cambio de consigna es grande (en un salto de consigna grande, algunos controladores desactivan la integral hasta que el error es pequeño).

El windup no solo aparece al arrancar: ocurre siempre que la salida satura con un error sostenido, como una perturbación grande, un actuador desconectado o un cambio de consigna brusco. La integral sin protección es el error de implementación más común de un PID casero.

05 · Sintonizar

Ajuste de las ganancias

Tres ganancias dan un espacio amplio de malas elecciones. Una sintonía incorrecta oscila: con la integral muy rápida (Kp = 0,5; Ti = 20 s) el horno oscila 1,34 °C pico a pico; con ganancias altas (Kp = 2 o 3, Ti = 100 s), 0,79 y 0,86 °C. Con una ganancia de 1 y Ti = 100 s, la oscilación es nula. Hay tres familias de métodos para encontrar buenos valores sin tanteo ciego:

1. Ziegler-Nichols de lazo cerrado

Con el control proporcional solo, se sube Kp hasta que el lazo oscila de forma sostenida: esa es la ganancia crítica Ku y el período de la oscilación, Tu. Se toman las ganancias de la tabla:

Reglas de Ziegler-Nichols (lazo cerrado) y valores para el horno (Ku = 1,58 por °C, Tu = 79 s).
TipoKpTiTdEn el horno
P0,5 Ku——Kp = 0,79
PI0,45 KuTu / 1,2—Kp = 0,71; Ti = 66 s
PID0,6 KuTu / 2Tu / 8Kp = 0,95; Ti ≈ 40 s; Td = 9,9 s

Ziegler-Nichols es una buena primera aproximación pero tiende a ser agresiva (amortigua cada oscilación a un cuarto de la anterior). Ante una perturbación de 10 °C en el ambiente, el PI de ZN deja un desvío máximo de 0,32 °C y el PID de ZN, de 0,24 °C; el PI (1; 100 s), 0,29 °C, y el P solo, 3,18 °C sin recuperarse. Su desventaja práctica: llevar un proceso real a la oscilación sostenida puede ser peligroso (un horno, una caldera) o lento.

2. El método del relé

Se obtiene la oscilación crítica de forma segura con el control todo o nada del tema anterior: un relé con histéresis casi nula hace oscilar el proceso con amplitud a y período Tu. La ganancia crítica se estima como Ku ≈ 4d / (π a), donde d es la amplitud de la salida del relé (0,5 si va de 0 a 1 alrededor del 50 %). En el horno: amplitud a = 0,50 °C y período de 80 s dan Ku ≈ 1,27 (el valor real es 1,58: un 20 % de error, aceptable) y Tu = 80 s (79 s reales). Es la base de los autotune de los controladores comerciales.

3. Respuesta al escalón y regla lambda

Se aplica un escalón de potencia con el lazo abierto y se mide la ganancia K del proceso, su constante de tiempo τ y su retardo θ (en el horno: 50 °C, 1000 s y 20 s). La regla lambda (SIMC) elige un tiempo de respuesta deseado λ y da:

Kp = τ / [ K · (λ + θ) ]   Ti = mín( τ , 4 · (λ + θ) )
Regla lambda aplicada al horno: más λ, más lento y más suave.
λKpTiSobreimpulsoEntra en la banda a los
50 s0,286280 s0,00 °C841 s
100 s0,167480 s0,00 °C1144 s
200 s0,091880 s0,00 °C2091 s

Tiene la ventaja de que un solo número, λ, controla el compromiso entre rapidez y robustez. Y en la práctica, el procedimiento de tanteo también sirve: con Ki = Kd = 0, subir Kp hasta que apenas oscile y quedarse con la mitad; agregar la integral con Ti del orden de Tu hasta que el error en régimen desaparezca sin oscilar; y agregar derivada solo si el proceso es rápido y el sensor limpio (sección siguiente). Siempre, de a un parámetro por vez y con el actuador protegido.

06 · El término delicado

La derivada y el ruido

La derivada es la más tentadora y la más peligrosa. Mejora la estabilidad de procesos con inercia, pero amplifica el ruido en proporción a su frecuencia: un sensor ruidoso convierte cada muestra en un salto de la salida. Con un ruido de 0,1 °C de desviación típica en el sensor, la salida (de 0 a 1) del horno varía así:

Desviación típica de la salida con 0,1 °C de ruido (Kp = 1; Ti = 100 s).
ControladorGanancia a alta frecuenciaDesviación típica de u
PI10,106
PID, Td = 10 s, N = 10110,397
PID, Td = 30 s, N = 10110,426
PID, Td = 30 s, N = 1001010,479

La derivada multiplica la variación de la salida por cuatro, y la salida varía en un 40 % de su rango solo por ruido: un relé o una válvula se gastaría rápido, y una resistencia calefactora recibiría una potencia que fluctúa sin sentido. Y la derivada del proceso del horno aporta poco: la dinámica es lenta (1000 s) y el retardo (20 s) es pequeño frente a ella. Las reglas prácticas:

  • Temperatura, nivel, humedad, caudal: procesos lentos con sensores ruidosos → PI, sin derivada.
  • Posición y velocidad de motores con encoder (tema 31): señales limpias y dinámica rápida → la derivada ayuda a frenar a tiempo.
  • Si se usa derivada: siempre filtrada (N de 5 a 10), sobre la medida, con un sensor filtrado (tema 5), y comprobando la variación de la salida con el sistema quieto.

07 · De la práctica

Mejoras prácticas

  • Prealimentación (feedforward): si se conoce la perturbación o la potencia de régimen, se suma directamente a la salida. En el horno, uff = (consigna − ambiente) / (Pmáx R) = 50 % con 15 °C de ambiente; el PI solo corrige lo que falte. Es lo que hace el «sesgo» del tema anterior, pero calculado en vez de fijo.
  • Ponderación de la consigna: aplicar la acción proporcional a una fracción de la consigna reduce el sobreimpulso ante cambios de consigna sin afectar el rechazo de perturbaciones.
  • Rampa de consigna: en vez de un salto, hacer que la consigna avance de a poco: evita el golpe y respeta los límites del actuador.
  • Limitar la velocidad de variación de la salida (slew rate), para no maltratar al actuador.
  • Banda muerta (tema 42) o histéresis de salida si el actuador es ruidoso o tiene holgura.
  • Ganancias programadas (gain scheduling): ganancias distintas según el punto de trabajo, si el proceso es muy no lineal (un horno es más lento a baja temperatura).
  • Control en cascada: un lazo exterior lento (temperatura) fija la consigna de uno interior rápido (corriente o potencia).

08 · Elegir

Qué PID para cada problema

Tipo de controlador y período de muestreo típicos.
ProcesoControladorPeríodo TsObservaciones
Temperatura de un horno o una incubadoraPI0,5 a 2 sLento, ruidoso; ventana larga si la salida es un relé
Nivel de un tanquePI (o P)0,5 a 5 sEl proceso ya integra: cuidado con la doble integral
Velocidad de un motor DC con encoderPI5 a 20 msLa medida es el conteo de pulsos filtrado (tema 26)
Posición de un servo o un ejePD o PID1 a 10 msLa derivada da amortiguamiento; sensor limpio
Caudal o presiónPI10 a 100 msRápido; ruido considerable
Rumbo de un robot (giroscopio)PD5 a 20 msSe deriva directamente la velocidad angular del giroscopio (tema 20)
Luz con realimentación óptica (tema 16)PI10 a 100 msCon corrección gamma en la salida

El criterio de muestreo es siempre el mismo: tomar un período mucho menor que el tiempo en que el proceso responde, de modo que se vean varias muestras en cada cambio. Muestrear mucho más rápido no mejora el control y solo agrega ruido; mucho más lento, lo vuelve inestable.

09 · Programación

Código del PID

Una clase PID con la interfaz Controlador de los temas 40 y 42: derivada filtrada sobre la medida, integral con pinza, salida limitada, estado seguro ante un dato inválido y transferencia sin saltos. El lazo, cada segundo y sin bloqueos, igual que antes; cambiar del proporcional al PID es cambiar una línea.

// ---------- PID de posición con derivada filtrada sobre la medida y anti-windup por pinza ----------
// Usa la interfaz Controlador de los temas 40 y 42: la aplicación pide una potencia entre 0 y 1.
class PID : public Controlador {
public:
    // ts: período del lazo en segundos (debe coincidir con el real); ti = 0 desactiva la integral; td = 0, la derivada
    PID(float kp, float ti, float td, float ts, float n = 10.0f)
        : kp_(kp), ti_(ti), td_(td), ts_(ts), n_(n) {}

    void sintonizar(float kp, float ti, float td) { kp_ = kp; ti_ = ti; td_ = td; }

    float calcular(float consigna, float medida) override {
        if (isnan(medida)) return 0.0f;                              // dato inválido: salida 0 y se congela el estado (sin integrar basura)

        float e = consigna - medida;
        if (!inicializado_) { previa_ = medida; inicializado_ = true; }

        float p = kp_ * e;                                           // presente

        if (td_ > 0.0f) {                                            // derivada de la MEDIDA (no del error) y filtrada:
            float a = td_ / (td_ + n_ * ts_);                        // el cambio de consigna no produce un golpe y el ruido
            d_ = a * d_ - kp_ * td_ * n_ / (td_ + n_ * ts_) * (medida - previa_);   // queda limitado a K_p (1 + N)
        }
        previa_ = medida;

        float ki = (ti_ > 0.0f) ? kp_ / ti_ : 0.0f;                  // pasado: K_i = K_p / T_i
        float iNueva = i_ + ki * e * ts_;
        float sinLimitar = p + iNueva + d_;
        float u = constrain(sinLimitar, 0.0f, 1.0f);
        if (sinLimitar != u && e * (sinLimitar - u) > 0.0f) iNueva = i_;   // pinza: saturado y el error empuja más: no integrar
        i_ = iNueva;

        return constrain(p + i_ + d_, 0.0f, 1.0f);
    }

    // Transferencia sin saltos (de manual a automático, o tras una falla): la integral se ajusta para que la salida no cambie
    void reiniciar(float salidaActual, float consigna, float medida) {
        i_ = constrain(salidaActual - kp_ * (consigna - medida), 0.0f, 1.0f);
        d_ = 0.0f;
        previa_ = medida;
        inicializado_ = true;
    }

private:
    float kp_, ti_, td_, ts_, n_;
    float i_ = 0.0f, d_ = 0.0f, previa_ = 0.0f;
    bool inicializado_ = false;
};

// ---------- Lazo: cada segundo, sin bloqueos (temas 40 a 42) ----------
Ds18b20 sensor(dallas, 0);
SalidaRele calefactor(Config::PIN_CALEFACTOR, 60000UL);              // ventana de 60 s para la potencia proporcional
PID controlador(0.5f /* K_p en 1/°C */, 200.0f /* T_i en s */, 0.0f /* sin derivada */, 1.0f /* T_s en s */);
bool habiaFalla = true;

void loop() {
    static uint32_t proximo = 0;
    if ((int32_t)(millis() - proximo) >= 0) {
        proximo += 1000;                                             // T_s exacto, sin deriva
        Lectura l = sensor.leer();
        if (l.estado != Estado::OK) {                                // ante la duda, apagado, y se recuerda para retomar sin salto
            calefactor.apagar();
            habiaFalla = true;
        } else {
            if (habiaFalla) { controlador.reiniciar(0.0f, Config::CONSIGNA_C, l.valor); habiaFalla = false; }
            calefactor.establecer(controlador.calcular(Config::CONSIGNA_C, l.valor));
        }
    }
    calefactor.actualizar();
}

Las ganancias se pueden guardar en EEPROM (tema 40) y ajustar sin recompilar. Para depurar, conviene registrar el error y las tres contribuciones (P, I, D) por el puerto serie a una frecuencia baja: es la manera más rápida de ver cuál de los términos hace algo raro.

10 · Verificación

Errores típicos y verificación

Fallas comunes de un PID casero.
SíntomaCausaDefensa
Se pasa mucho de la consigna al arrancar y tarda en volverWindup de la integralPinza o retrocálculo; salida saturada realimentada
Salto brusco de la salida al cambiar la consignaDerivada sobre el errorDerivar la medida; rampa de consigna
La salida tiembla o el actuador vibraDerivada sin filtrar o con mucha ganancia, ruido del sensorFiltro con N entre 5 y 10; filtrar la medida; quitar la D
Oscilación sostenidaKp o integral demasiado altos para el retardoBajar Kp; aumentar Ti; acercar el sensor
Oscilación lenta con períodos largosTi demasiado cortoAumentar Ti (al menos del orden de Tu)
Nunca llega a la consigna (con PI)Integral deshabilitada o saturada, ti = 0Verificar Ti y la pinza; ¿llega el actuador a la potencia necesaria?
El PID se vuelve loco tras un cable sueltoLa integral acumuló o se usó el dato inválidoCongelar el estado y reiniciar sin saltos al volver
El comportamiento cambia si cambia el períodoTs real distinto del asumido (lazo irregular)Período exacto con proximo += periodo; medir Ts real
Empuja en sentido contrarioSigno del error mal puestoProbar el sentido con una perturbación manual
Funciona en el verano, no en el inviernoProceso no lineal con una única sintoníaGanancias programadas; sintonizar en el peor caso
  1. Empezá en manual: verificá el sentido y la potencia de régimen con la salida fija.
  2. Registrá la consigna, la medida, la salida y las contribuciones P, I y D durante una prueba con escalón y otra con perturbación.
  3. Probá la saturación a propósito: pedí una consigna inalcanzable un rato y volvé a una alcanzable: no debe haber sobreimpulso por windup.
  4. Medí el ruido de la salida con el sistema quieto y comprobá que no maltrata al actuador.
  5. Simulá la falla del sensor y verificá el estado seguro y la reanudación sin saltos.

11 · Analogía con programación

Un PID simulado con windup, ajuste y ruido

Implementamos el PID de la sección anterior en Python y lo aplicamos al mismo horno simulado del tema 42: el arranque en frío con y sin protección contra el windup, la respuesta a una perturbación, las sintonías que oscilan, el efecto de la derivada sobre el ruido, las reglas de ajuste (Ziegler-Nichols, relé y lambda) y el golpe de derivada.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

import math

# ---- Planta: el horno del tema 42 (100 W, 2000 J/K, 0,5 K/W, retardo del sensor de 20 s) ----
P_MAX, CAP, RES = 100.0, 2000.0, 0.5
M32 = 2 ** 32


class Ruido:
    def __init__(self, sigma, semilla=12345):
        self.sigma, self.x = sigma, semilla

    def dato(self):
        if self.sigma == 0:
            return 0.0
        s = 0.0
        for _ in range(6):
            self.x = (self.x * 1664525 + 1013904223) % M32
            s += self.x / M32
        return (s - 3) / 0.7071 * self.sigma


class PID:
    """PID de posición: derivada sobre la medida con filtro, salida limitada a 0..1 y tres modos de anti-windup."""

    def __init__(self, kp, ti=math.inf, td=0.0, ts=1.0, n=10.0, modo="pinza", tt=None):
        self.kp, self.ti, self.td, self.ts, self.n, self.modo = kp, ti, td, ts, n, modo
        self.tt = tt if tt is not None else (ti if not math.isinf(ti) else math.inf)
        self.i = self.d = 0.0
        self.previa = None

    def paso(self, consigna, medida):
        e = consigna - medida
        if self.previa is None:
            self.previa = medida
        p = self.kp * e
        if self.td > 0:                                         # derivada filtrada, sobre la medida: sin golpe al cambiar la consigna
            a = self.td / (self.td + self.n * self.ts)
            self.d = a * self.d - self.kp * self.td * self.n / (self.td + self.n * self.ts) * (medida - self.previa)
        self.previa = medida
        ki = 0.0 if math.isinf(self.ti) else self.kp / self.ti
        i_nueva = self.i + ki * e * self.ts
        sin_limitar = p + i_nueva + self.d
        u = min(1.0, max(0.0, sin_limitar))
        if self.modo == "pinza" and sin_limitar != u and e * (sin_limitar - u) > 0:
            i_nueva = self.i                                    # saturado y el error empuja más: no integrar
        elif self.modo == "retro" and not math.isinf(self.tt):
            i_nueva += self.ts / self.tt * (u - sin_limitar)    # realimentar el exceso para desacumular
        self.i = i_nueva
        return min(1.0, max(0.0, p + self.i + self.d))


def simular(ctrl, consigna=40.0, t_amb=15.0, retardo=20, sigma=0.0, segundos=10800, perturbacion=None):
    temp = t_amb
    pasado = [temp] * (retardo + 1)
    ruido = Ruido(sigma)
    historia = []
    for t in range(segundos):
        if perturbacion and t == perturbacion[0]:
            t_amb = perturbacion[1]
        medida = pasado[0] + ruido.dato()
        u = ctrl.paso(consigna, medida)
        temp += (P_MAX * u - (temp - t_amb) / RES) / CAP
        pasado.append(temp)
        pasado.pop(0)
        historia.append((temp, u, ctrl.i))
    return historia


def metricas(historia, consigna=40.0):
    temps = [t for t, _, _ in historia]
    sobre = max(0.0, max(temps) - consigna)
    ultimo = max((k for k, v in enumerate(temps) if abs(v - consigna) > 0.5), default=0)
    final = sum(temps[-600:]) / 600
    return sobre, ultimo, final


# ---- 1. Los tres términos y el anti-windup (arranque en frío) ----
print("controlador                          sobreimpulso  entra en ±0,5 °C  T final   integral máx.")
for nombre, c in (("P (K_p = 0,2)", PID(0.2)),
                  ("PI (0,5; Ti = 200 s) sin protección", PID(0.5, 200, modo="ninguno")),
                  ("PI (0,5; Ti = 200 s) con pinza", PID(0.5, 200)),
                  ("PI (0,5; Ti = 200 s) retro Tt = 50 s", PID(0.5, 200, modo="retro", tt=50)),
                  ("PI (0,5; Ti = 200 s) retro Tt = Ti", PID(0.5, 200, modo="retro"))):
    h = simular(c)
    sobre, ultimo, final = metricas(h)
    print(f"{nombre:36s} {sobre:6.2f} °C     {ultimo:6d} s        {final:6.2f}    {max(i for _, _, i in h):6.2f}")

# ---- 2. Perturbación: el ambiente baja de 15 a 5 °C a los 6000 s ----
print()
print("perturbación (ambiente 15 -> 5 °C a los 6000 s):")
for nombre, c in (("P (K_p = 0,2)", PID(0.2)), ("PI (0,5; Ti = 200 s)", PID(0.5, 200)), ("PI (1; Ti = 100 s)", PID(1.0, 100)),
                  ("PI de Ziegler-Nichols (0,71; 66 s)", PID(0.71, 66)), ("PID de Ziegler-Nichols (0,95; 39,5 s; 9,9 s)", PID(0.95, 39.5, 9.9))):
    h = simular(c, segundos=12000, perturbacion=(6000, 5.0))
    tramo = [t for t, _, _ in h][6000:]
    desvio = max(abs(v - 40) for v in tramo)
    final = sum(t for t, _, _ in h[-600:]) / 600
    u = sum(u for _, u, _ in h[-600:]) / 600
    print(f"  {nombre:44s} desvío máx. {desvio:5.2f} °C   final {final:6.2f} °C   potencia final {u * 100:3.0f} %")

# ---- 3. Mala sintonía ----
print()
print("sintonías que oscilan (última hora de 2 h):")
for nombre, c in (("PI (1; Ti = 100 s): bien", PID(1.0, 100)), ("PI (0,5; Ti = 20 s): integral muy rápida", PID(0.5, 20)),
                  ("PI (2; Ti = 100 s): ganancia alta", PID(2.0, 100)), ("PI (3; Ti = 100 s): ganancia muy alta", PID(3.0, 100))):
    temps = [t for t, _, _ in simular(c, segundos=7200)][-3600:]
    print(f"  {nombre:42s} oscilación {max(temps) - min(temps):5.3f} °C")

# ---- 4. La derivada y el ruido de 0,1 °C ----
print()
print("ruido de 0,1 °C en el sensor: variación de la salida (desviación típica, 0 a 1):")
for nombre, c in (("PI (1; 100 s)", PID(1.0, 100)), ("PID Td = 10 s, N = 10", PID(1.0, 100, 10)),
                  ("PID Td = 30 s, N = 10", PID(1.0, 100, 30)), ("PID Td = 30 s, N = 100", PID(1.0, 100, 30, n=100))):
    u = [x for _, x, _ in simular(c, sigma=0.1, segundos=7200)][-3600:]
    media = sum(u) / len(u)
    sd = math.sqrt(sum((x - media) ** 2 for x in u) / len(u))
    print(f"  {nombre:24s} u media {media:5.3f}   desviación típica {sd:5.3f}   ganancia de alta frecuencia {c.kp * (1 + c.n) if c.td > 0 else c.kp:5.1f}")

# ---- 5. Ajuste por Ziegler-Nichols de lazo cerrado y por el método del relé ----
print()
tau, retardo, k_planta = RES * CAP, 20.0, P_MAX * RES
w = 0.05
for _ in range(60):
    w = (math.pi - math.atan(w * tau)) / retardo
ku, tu = math.sqrt(1 + (w * tau) ** 2) / k_planta, 2 * math.pi / w
print(f"ganancia crítica K_u = {ku:.2f} 1/°C (banda crítica {1 / ku:.2f} °C) y período T_u = {tu:.0f} s")
print(f"  P:   K_p = {0.5 * ku:.2f}")
print(f"  PI:  K_p = {0.45 * ku:.2f}, Ti = {tu / 1.2:.0f} s")
print(f"  PID: K_p = {0.6 * ku:.2f}, Ti = {tu / 2:.1f} s, Td = {tu / 8:.1f} s")

temp, pasado, estado = 15.0, [15.0] * 21, []
for t in range(10800):                                  # relé ideal (histéresis cero): produce la oscilación crítica
    u = 1.0 if pasado[0] < 40 else 0.0
    temp += (P_MAX * u - (temp - 15.0) / RES) / CAP
    pasado.append(temp)
    pasado.pop(0)
    estado.append((temp, u))
ultima = [t for t, _ in estado][-3600:]
amplitud = (max(ultima) - min(ultima)) / 2
cambios = [k for k in range(3600 - 1) if estado[-3600 + k][1] != estado[-3600 + k + 1][1]]
periodo = 2 * (cambios[-1] - cambios[0]) / (len(cambios) - 1)
ku_rele = 4 * 0.5 / (math.pi * amplitud)
print(f"método del relé: amplitud {amplitud:.2f} °C y período {periodo:.0f} s -> K_u estimada {ku_rele:.2f} 1/°C (real {ku:.2f}, error {abs(ku_rele - ku) / ku * 100:.0f} %)")

# ---- 6. Regla de la lambda (SIMC) a partir de una prueba de escalón ----
print()
for lam in (50, 100, 200):
    kp = tau / (k_planta * (lam + retardo))
    ti = min(tau, 4 * (lam + retardo))
    h = simular(PID(kp, ti))
    sobre, ultimo, final = metricas(h)
    print(f"  lambda = {lam:3d} s: K_p = {kp:.3f}, Ti = {ti:4.0f} s -> sobreimpulso {sobre:4.2f} °C, entra en la banda a los {ultimo} s")

# ---- 7. Golpe de la derivada al cambiar la consigna ----
print()
kp, td, ts, salto = 1.0, 10.0, 1.0, 3.0
print(f"salto de consigna de {salto:.0f} °C con derivada sobre el error: pico de {kp * td * salto / ts:.0f} en la salida (el máximo útil es 1); sobre la medida: 0")

Resultado: el PI (0,5; Ti = 200 s) sin protección acumula una integral de 20,45 en el arranque y se pasa 13,15 °C (2106 s para volver a la banda); con pinza, 0,00 °C y 741 s (la integral máxima es 0,50); con retrocálculo Tt = 50 s, 0,00 °C y 880 s, y con Tt = Ti, 0,90 °C. Ante la baja de ambiente de 15 a 5 °C, el P solo se desvía 3,18 °C y se queda en 36,82 °C con el 64 %; los PI y PID vuelven a 40,00 °C con el 70 % (desvíos máximos de 0,39; 0,29; 0,32 y 0,24 °C). Las sintonías (0,5; 20 s), (2; 100 s) y (3; 100 s) oscilan 1,338; 0,789 y 0,860 °C. Con 0,1 °C de ruido, la desviación típica de la salida pasa de 0,106 (PI) a 0,397 con una derivada de 10 s y a 0,479 con N = 100. Ziegler-Nichols da, para Ku = 1,58 y Tu = 79 s, PI = (0,71; 66 s) y PID = (0,95; 40 s; 9,9 s); el método del relé estima Ku = 1,27 (error del 20 %) con un período de 80 s; la regla lambda con 100 s da (0,167; 480 s) sin sobreimpulso. La derivada sobre el error con un salto de 3 °C daría un pico de 30 en la salida.

12 · Exploración interactiva

Términos, windup, ajuste y ruido

Los cuatro instrumentos usan el mismo horno simulado del programa de Python (consigna de 40 °C, ambiente de 15 °C, 20 s de retardo, un paso por segundo). El primero permite ajustar Kp, Ti y Td; el segundo compara la integral con y sin protección; el tercero calcula las ganancias de Ziegler-Nichols y las prueba; el cuarto muestra qué hace la derivada con el ruido.

Instrumento 1: los tres términos

Sobreimpulso · entra en ±0,5 °C a los—
Desvío máx. tras la perturbación—
T final · oscilación final—
Salida final · desv. típica—
Estado
—

Instrumento 2: windup de la integral

Sobreimpulso: sin protección · con pinza—
Integral máxima: sin protección · con pinza—
Entra en la banda: sin · con—
Estado
—

Instrumento 3: ajuste por Ziegler-Nichols

Kp · Ti · Td—
Ki = Kp/Ti · Kd = Kp·Td—
En el horno: sobreimpulso · en banda a los—
Oscilación final—
Estado
—

Instrumento 4: la derivada y el ruido

Ganancia a alta frecuencia Kp(1 + N)—
Desviación típica de la salida—
Cambio medio de la salida por segundo—
Estado
—

Recorrido guiado

  1. Con Kp = 1, Ti = 100 s, pinza y sin ruido: sin sobreimpulso, entra en la banda hacia los 670 s y, tras la perturbación de los 100 min, el desvío máximo es de unos 0,3 °C: el PI vuelve a 40,00 °C con el 70 % de potencia. Elegí «sin integral»: queda en el error del proporcional.
  2. Elegí Kp = 0,5 con Ti = 20 s: el lazo oscila (≈ 1,3 °C pico a pico). Con Kp = 3 y Ti = 100 s oscila por la ganancia alta (≈ 0,86 °C).
  3. Con Kp = 0,5, Ti = 200 s y anti-windup «ninguno»: sobreimpulso de unos 13 °C y 35 minutos para volver; con «pinza», sin sobreimpulso.
  4. Subí el ruido a 0,1 °C con Td = 10 s: la desviación típica de la salida pasa de 0,11 a unos 0,40 y el estado avisa de que la salida es ruidosa. Con Td = 0 vuelve a ser aceptable.
  5. En el windup, con Kp = 0,5 y Ti = 200 s: sin protección, 13,15 °C de sobreimpulso y una integral máxima de 20,45; con pinza, 0,00 °C y 0,50. Con Ti = 1000 s el windup es menor pero sigue presente.
  6. En Ziegler-Nichols, con Ku = 1,58 y Tu = 79 s y el PID: Kp = 0,95, Ti = 39,5 s y Td = 9,9 s; casi sin sobreimpulso. Si se sobreestima Ku (3, por ejemplo) con PI, las ganancias resultan demasiado altas para el horno y el lazo oscila.
  7. Pasá a P en Ziegler-Nichols: Kp = 0,5 Ku = 0,79 y un error en régimen apreciable: ZN no elimina el error con P solo.
  8. En el ruido, con Td = 30 s y N = 100, la ganancia a alta frecuencia es de 101 y la salida varía todavía más: un N más alto filtra menos el ruido de la derivada.
Desafío: el PID de un motor con encoder

Un motor DC (tema 31) se controla en velocidad con un encoder que da 20 pulsos cada 10 ms (un período de muestreo de 10 ms), y el proceso responde en unos 100 ms. ¿Qué controlador y qué período? PI con Ts = 10 ms (diez veces menor que la respuesta): la medida es la velocidad calculada por conteo de pulsos (con la cuantización de 1 pulso por muestra, un 5 % a 20 pulsos), y la derivada solo amplificaría esa cuantización: se evita. La integral elimina el error de velocidad cuando cambia la carga. Windup: el motor satura el PWM al 100 % al arrancar; hace falta pinza o retrocálculo. Salida: el PWM del puente H (tema 30), con un tiempo mínimo para no invertir sentido bruscamente. Seguridad: si el encoder deja de dar pulsos con la salida alta, el motor está trabado o el cable cortado: tras un tiempo límite, apagar (tema 44). Para la posición, en cambio, el motor es un integrador y se usa PD o PID con derivada filtrada, porque la medida es más limpia.

13 · Método de trabajo

Cómo poner en marcha un PID, para cualquier proceso

  1. Verificá en manual el sentido, la potencia de régimen y el límite del actuador antes de cerrar el lazo.
  2. Elegí un período de muestreo constante, mucho menor que el tiempo de respuesta, y programalo sin deriva.
  3. Empezá con PI; agregá derivada solo si el proceso es rápido, el sensor limpio y hace falta amortiguar.
  4. Obtené Ku y Tu (relé o tanteo) o la respuesta al escalón (K, τ, θ), y calculá las ganancias iniciales.
  5. Implementá bien los detalles: derivada sobre la medida y filtrada, anti-windup, salida limitada, estado seguro ante datos inválidos y transferencia sin saltos.
  6. Ajustá de a un parámetro con pruebas de escalón y de perturbación; busque rapidez sin oscilación.
  7. Probá los casos malos: saturación sostenida, sensor desconectado, consigna imposible, arranque en frío.
  8. Registrá y documentá las ganancias, la fecha y las condiciones de la sintonía; guardalas en la EEPROM.

Si el arranque se pasa, es el windup; si salta al cambiar la consigna, la derivada sobre el error; si la salida tiembla, la derivada o el ruido; si oscila, la ganancia o la integral; si no llega, la integral.

14 · Comprensión

Confusiones frecuentes

«Un PID siempre es mejor que un PI»

La derivada amplifica el ruido; en procesos lentos y con sensores ruidosos empeora la salida sin mejorar el control. Muchos lazos industriales (temperatura, nivel) usan PI.

«La integral solo sirve para eliminar el error en régimen»

También rechaza perturbaciones y encuentra la potencia de régimen sola. A cambio, introduce un desfasaje que empeora la estabilidad y el windup.

«Si la salida está saturada, la integral no importa»

Justamente entonces es cuando acumula: sin protección sigue sumando error y produce el sobreimpulso al salir de la saturación.

«Para ajustar un PID basta con subir la ganancia hasta que responda rápido»

Subirla acerca el lazo a la oscilación y amplifica el ruido. El procedimiento seguro parte de Ku o de un modelo y se afina de a un parámetro.

«Ziegler-Nichols da el ajuste final»

Da un punto de partida, algo agresivo. Casi siempre hay que reducir un poco la ganancia o alargar Ti según el sobreimpulso que se tolere.

«El período de muestreo no importa mientras sea rápido»

Un período irregular altera la integral y la derivada; uno demasiado largo agrega retardo; uno demasiado corto, ruido. Debe ser constante y adecuado al proceso.

15 · Práctica guiada

Predecí antes de comprobar

1. Formas del PID

Un PID estándar tiene Kp = 2 (1/°C), Ti = 50 s y Td = 5 s. ¿Cuánto valen Ki y Kd en la forma paralela?

Ver solución

Ki = Kp/Ti = 2/50 = 0,04 por °C y por segundo; Kd = Kp·Td = 2 × 5 = 10 s/°C.

2. Ziegler-Nichols

Un proceso oscila con Ku = 3 y Tu = 40 s. ¿Qué ganancias da Ziegler-Nichols para un PID?

Ver solución

Kp = 0,6 × 3 = 1,8; Ti = 40/2 = 20 s; Td = 40/8 = 5 s.

3. Golpe de la derivada

Con Kp = 0,5, Td = 20 s y Ts = 2 s, la consigna salta 4 °C. ¿Qué pico produce la derivada sobre el error en el primer paso? ¿Y sobre la medida?

Ver solución

0,5 × 20 × 4 / 2 = 20, veinte veces el máximo útil (la salida satura). Sobre la medida: 0, porque la medida no salta.

4. Windup

Un PI con Kp = 0,5 y Ti = 200 s mantiene un error de 10 °C durante 300 s con la salida saturada. ¿Cuánto vale la integral al final, sin protección?

Ver solución

Ki = 0,5/200 = 0,0025 por °C·s; I = 0,0025 × 10 × 300 = 7,5, es decir 750 % de la salida; el actuador solo puede dar 100 %. Descargarla exige un error negativo acumulado de 3000 °C·s.

5. Ruido

Un PID con Kp = 1 y N = 10 tiene derivada filtrada. ¿Cuánto vale su ganancia a alta frecuencia y qué hace con un ruido de 0,1 °C?

Ver solución

Kp(1 + N) = 1 × 11 = 11. Un ruido de 0,1 °C a alta frecuencia podría aparecer como hasta 1,1 en la salida, más que todo su rango (0 a 1); en la simulación, la desviación típica sube de 0,106 a 0,397.

6. Diagnóstico

Un PID de temperatura, al arrancar con la cámara fría, se pasa 10 °C de la consigna y tarda media hora en volver; luego funciona bien en régimen. ¿Qué pasa y qué cambiás?

Ver solución

Windup de la integral durante el arranque (la salida estuvo saturada mucho tiempo). Se agrega anti-windup (pinza o retrocálculo) y, opcionalmente, una rampa de consigna. Solo afecta al arranque porque en régimen la salida no satura.

16 · Síntesis

Ideas para recordar

  • El PID suma tres acciones sobre el error: proporcional (presente), integral (pasado acumulado) y derivativa (tendencia): u = Kp[e + (1/Ti)∫e dt + Td de/dt].
  • La integral elimina el error en régimen y encuentra sola la potencia necesaria (70 % con 5 °C de ambiente en el horno), a costa de empeorar la estabilidad.
  • En un microcontrolador: período Ts constante, derivada sobre la medida y filtrada (ganancia Kp(1 + N)), salida limitada y transferencia sin saltos.
  • Sin anti-windup, la integral se carga mucho más allá del actuador (20,45 en el horno) y la temperatura se pasa 13 °C; la pinza o el retrocálculo lo evitan.
  • Ziegler-Nichols (Kp = 0,6 Ku, Ti = Tu/2, Td = Tu/8) da un punto de partida agresivo; el método del relé estima Ku y Tu con seguridad; la regla lambda ajusta rapidez y robustez con un solo número.
  • La derivada amplifica el ruido (0,106 → 0,397 de desviación típica de la salida con 0,1 °C de ruido): en procesos lentos y ruidosos se usa PI.
  • Derivar la medida y no el error evita el golpe al cambiar la consigna (un pico de 30 en el ejemplo).
  • Antes de cerrar el lazo: probar en manual, proteger la saturación, definir el estado seguro ante datos inválidos y registrar las contribuciones P, I y D.

¿Listo para el tema 44?

Antes de avanzar, explicá qué es el windup y cómo se evita, calculá las ganancias de Ziegler-Nichols para un PID con Ku = 2 y Tu = 30 s y decí por qué la derivada se aplica a la medida y con un filtro. Si podés justificar las tres respuestas, ya sabés poner en marcha un PID.

En el próximo tema nos ocupamos de lo que pasa cuando algo sale mal: seguridad, fallos y robustez, con sensores defectuosos, límites de seguridad, perros guardianes y modos seguros.