Интегральная часть I: ноль ошибки и windup

Интеграл копит ошибку и добивает её до нуля — но приносит коварный windup.

Интегральная составляющая (I) накапливает ошибку во времени: u_I = Ki·∫e·dt. Она устраняет статическую ошибку, потому что копит воздействие, пока цель не достигнута.

Зачем нужен интеграл

P-регулятор застревает с остаточной ошибкой, потому что для ненулевого воздействия ему нужна ненулевая ошибка. Интеграл решает это элегантно: он накапливает ошибку со временем. Даже если ошибка крошечная, но не исчезает, интеграл продолжает расти, увеличивая воздействие — до тех пор, пока ошибка не станет ровно нулём. В равновесии интеграл «запоминает», сколько воздействия нужно для компенсации потерь, и удерживает его даже при нулевой ошибке. Это память регулятора о прошлом.

u_I(t) = Ki * (e(0) + e(1) + ... + e(t)) * dt   // накопленная ошибка

Как работает под капотом: PI против чистого P

Добавим интеграл к P-регулятору нагрева и сравним. Чистый P оставит остаток, PI добьёт ошибку до нуля.

def run(Kp, Ki):
    C, k, Tamb, dt = 50.0, 2.0, 20.0, 1.0
    setpoint, T = 60.0, 20.0
    integral = 0.0
    for _ in range(120):
        err = setpoint - T
        integral += err * dt
        u = Kp*err + Ki*integral
        u = max(0.0, min(400.0, u))
        T += ((u - k*(T-Tamb))/C)*dt
    return T, setpoint - T

T_p,  e_p  = run(8.0, 0.0)    # чистый P
T_pi, e_pi = run(8.0, 0.5)    # PI
print(f"чистый P : T={T_p:.2f}  ошибка={e_p:.3f}")
print(f"PI       : T={T_pi:.2f}  ошибка={e_pi:.3f}")

Вывод:

чистый P : T=52.00  ошибка=8.000
PI       : T=60.00  ошибка=-0.002

Интеграл полностью убрал статическую ошибку: PI-регулятор вышел ровно на 60.00. Это его суперспособность — гарантированный ноль остаточной ошибки для постоянной уставки и постоянного возмущения.

Тёмная сторона: интегральное насыщение (windup)

За мощь интеграла приходится платить. Представьте, что в начале выход далеко от цели и привод упёрся в максимум (насыщение). Ошибка большая и долго не уменьшается — а интеграл всё это время копит и копит, разбухая до огромных значений. Когда выход наконец дойдёт до уставки, раздутый интеграл продолжает гнать привод на максимум, и система сильно перелетает цель. Потом интегралу нужно «разрядиться» через противоположную ошибку — это долгий хвост колебаний. Это явление называется интегральное насыщение (integral windup).

# Демонстрация windup: привод сильно ограничен, интеграл разбухает
def run(setpoint):
    Kp, Ki, dt = 1.0, 0.3, 1.0
    y, integral, u_max = 0.0, 0.0, 10.0
    peak = 0.0
    for step in range(40):
        err = setpoint - y
        integral += err * dt
        u = Kp*err + Ki*integral
        u = max(-u_max, min(u_max, u))   # жёсткое насыщение
        y += 0.5*u*dt
        peak = max(peak, y)
    return peak, y

peak, final = run(50.0)
print(f"уставка=50, пик={peak:.1f} (перелёт!), финал={final:.1f}")
print(f"перерегулирование из-за windup = {(peak-50)/50*100:.0f}%")

Вывод:

уставка=50, пик=87.8 (перелёт!), финал=50.1
перерегулирование из-за windup = 76%

Из-за windup система перелетела цель на 76% — огромное перерегулирование, вызванное не настройкой Kp, а раздувшимся интегралом во время насыщения привода. В отдельном уроке мы научимся бороться с этим через анти-windup. Пока запомните: интеграл мощен, но требует защиты от насыщения.

Почему именно интеграл убирает ошибку

Стоит понять это глубже. Интеграл — это «накопитель»: его выход меняется, пока на входе есть хоть какая-то ошибка, и застывает только при строго нулевой ошибке. Значит, единственное состояние равновесия PI-регулятора — это e = 0: при любой другой ошибке интеграл продолжает двигаться, нарушая равновесие. Система просто не может «успокоиться» с остаточной ошибкой — математика этого не позволяет. Это фундаментальное свойство: интегральное действие гарантирует нулевую установившуюся ошибку для постоянной уставки и постоянного возмущения. Цена — дополнительная динамика (интеграл добавляет полюс в начало координат), которая замедляет систему и при избытке вызывает колебания. Поэтому Ki — самый деликатный из трёх коэффициентов.

Частые ошибки

  • Большой Ki без анти-windup. Интеграл быстро разбухает при насыщении и даёт сильное перерегулирование.
  • Думать, что интеграл всегда нужен. Для систем, где остаточная ошибка не критична (или объект сам интегрирующий), I может только навредить колебаниями.
  • Не сбрасывать интеграл при перезапуске. Старое накопленное значение даёт резкий скачок воздействия на старте.

Итоги

  • I-составляющая копит ошибку и устраняет статическую ошибку для постоянной уставки.
  • Платой за это служит интегральное насыщение (windup): разбухший интеграл даёт перерегулирование.
  • Интеграл — память регулятора; его силу нужно ограничивать анти-windup-мерами.
Проверьте себя
1. Какую проблему решает интегральная составляющая?
AШум датчика
BСтатическую (остаточную) ошибку — добивает выход точно до уставки
CПеререгулирование
DЗадержку привода
2. Что такое интегральное насыщение (windup)?
AПерегрев интегратора
BРазбухание интеграла при насыщении привода, дающее сильное перерегулирование
CПотеря точности датчика
DСброс интеграла в ноль
3. Почему PI-регулятор выходит точно на уставку, а чистый P — нет?
AУ PI больше Kp
BИнтеграл копит воздействие, пока ошибка не станет нулём, и удерживает его
CPI игнорирует возмущения
DP не использует датчик