Интегральная часть 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-мерами.