Анти-windup, насыщение и фильтрация D

Делаем ПИД пригодным для реального мира: анти-windup, насыщение, фильтр D.

Анти-windup — приёмы, не дающие интегралу разбухать, пока привод в насыщении. Без них ПИД на реальном железе работает заметно хуже, чем в теории.

Проблема: реальный привод ограничен

В теории привод может выдать любое воздействие. В реальности нагреватель имеет максимум мощности, мотор — максимальный момент, клапан открыт максимум на 100%. Это насыщение привода. Когда регулятор требует больше, чем привод может дать, выход «упирается в потолок», и именно тогда интеграл начинает опасно разбухать (windup из урока про I-составляющую). Грамотный ПИД обязан это учитывать.

Анти-windup: ограничение интеграла

Самый простой и популярный приём — не накапливать интеграл, пока привод в насыщении (метод «clamping»). Логика: если воздействие уже упёрлось в предел, добавлять ещё бессмысленно — значит, и интеграл копить незачем. Как только система выйдет из насыщения, накопление возобновляется. Сравним ПИД с анти-windup и без.

def run(anti_windup):
    Kp, Ki, dt = 1.0, 0.3, 1.0
    sp, y = 50.0, 0.0
    integral, u_max = 0.0, 10.0
    peak, ys = 0.0, []
    for _ in range(60):
        e = sp - y
        integral += e*dt
        u = Kp*e + Ki*integral
        u_sat = max(-u_max, min(u_max, u))
        if anti_windup and u != u_sat:
            integral -= e*dt          # откатываем накопление в насыщении
        y += 0.5*u_sat*dt
        ys.append(y); peak = max(peak, y)
    return peak, ys[-1]

p0, f0 = run(False)
p1, f1 = run(True)
print(f"БЕЗ анти-windup: пик={p0:.1f}  (перелёт {(p0-50)/50*100:.0f}%)")
print(f"С  анти-windup: пик={p1:.1f}  (перелёт {(p1-50)/50*100:.0f}%)")

Вывод:

БЕЗ анти-windup: пик=87.8  (перелёт 76%)
С  анти-windup: пик=51.4  (перелёт 3%)

Разница драматична: без анти-windup интеграл разбухает за время насыщения и даёт огромное перерегулирование; с анти-windup система выходит на уставку аккуратно. Это одна из самых важных практических доработок — без неё ПИД на реальном железе ведёт себя непредсказуемо.

Насыщение привода как часть модели

Второй принцип: всегда моделируйте насыщение привода и учитывайте его при проектировании. Регулятор должен «знать», что привод ограничен, и не строить планы, которые тот не сможет исполнить. Ограничение воздействия (clamp) ставится всегда, даже если кажется, что до предела далеко — это страховка от выбросов.

Фильтрация дифференциальной составляющей

Третья доработка касается шумной D. Чтобы производная не дёргала привод от шума, её фильтруют. Простейший приём — экспоненциальное сглаживание: новое значение D — это смесь свежей производной и предыдущего сглаженного значения. Это убирает высокочастотный шум, почти не портя полезную динамику. Часто также применяют D от выхода, а не от ошибки, чтобы избежать «derivative kick» — гигантского всплеска при ступенчатом изменении уставки.

# Экспоненциальный фильтр гасит шум в производной
import random
random.seed(3)
alpha = 0.3            # коэффициент сглаживания (0..1): меньше -> сильнее фильтр
d_filt = 0.0
prev = 1.0
print("сырая_D   фильтр_D")
for _ in range(6):
    measured = 1.0 + random.uniform(-0.05, 0.05)   # шумная "ошибка" ~1
    raw_d = (measured - prev)/0.1
    d_filt = alpha*raw_d + (1-alpha)*d_filt        # сглаживание
    prev = measured
    print(f"{raw_d:7.2f}   {d_filt:7.2f}")
print("фильтр сглаживает скачки, не давая D дёргать привод")

Вывод:

сырая_D   фильтр_D
  -0.26     -0.08
   0.31      0.04
  -0.17     -0.03
   0.23      0.05
   0.02      0.04
  -0.56     -0.14
фильтр сглаживает скачки, не давая D дёргать привод

Другие схемы анти-windup

Мы показали простейший «clamping» — остановку интеграла в насыщении. Есть и более изящная схема — back-calculation (обратный пересчёт): когда привод насыщается, в интеграл подаётся корректирующий сигнал, пропорциональный разнице между требуемым и реально выданным воздействием, плавно «сдувающий» накопленное. Эта схема мягче и настраивается отдельным коэффициентом скорости разрядки. Ещё один практический приём — условное интегрирование: копить интеграл только когда ошибка и воздействие одного знака. Какую схему выбрать — зависит от задачи, но любой анти-windup почти всегда лучше его отсутствия. Это тот случай, когда простое решение (clamping) закрывает 90% проблем, а изысканные схемы добирают последние проценты для ответственных применений.

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

  • ПИД без анти-windup на ограниченном приводе. Самая распространённая практическая ошибка; даёт большое перерегулирование на старте.
  • Нефильтрованная D на шумном датчике. Привод дёргается, механика изнашивается; всегда фильтруйте производную.
  • Забыть про derivative kick. Дифференцирование ошибки при скачке уставки даёт всплеск; берите D от измерения выхода.

Итоги

  • Реальный привод насыщается; без анти-windup интеграл разбухает и даёт перерегулирование.
  • Анти-windup (clamping) останавливает накопление интеграла в насыщении.
  • D фильтруют от шума и берут от выхода, чтобы избежать derivative kick.
Проверьте себя
1. Зачем нужен анти-windup?
AЧтобы ускорить систему
BЧтобы интеграл не разбухал, пока привод в насыщении
CЧтобы убрать статическую ошибку
DЧтобы отфильтровать шум
2. Что такое насыщение привода?
AПерегрев регулятора
BФизический предел воздействия: больше максимума привод выдать не может
CНакопление интеграла
DШум измерения
3. Зачем фильтруют дифференциальную составляющую?
AЧтобы усилить шум
BЧтобы шум не превращался производной в дёрганье привода
CЧтобы убрать интеграл
DЧтобы ускорить нарастание