Дискретная реализация ПИД для микроконтроллера

Как ПИД живёт в микроконтроллере: дискретная форма и период опроса.

Дискретный ПИД работает не непрерывно, а шагами с периодом дискретизации Ts: интегралы становятся суммами, производные — разностями. Так регулятор реализуется в прошивке.

От непрерывного к дискретному

Вся теория выше формулировалась в непрерывном времени (интегралы, производные). Но микроконтроллер работает тактами: считал датчик, посчитал воздействие, выдал на привод — и так каждые Ts секунд (период дискретизации). Поэтому реальный ПИД дискретный: интеграл становится суммой integral += e·Ts, производная — разностью (e - e_prev)/Ts. Именно такой код мы уже писали в классе PID — он по сути и есть дискретная реализация. Это прямой мостик к курсам по embedded и ROS, где регулятор крутится в цикле реального времени.

Позиционная и инкрементная формы

Есть две дискретные формы. Позиционная (которую мы использовали) считает полное воздействие u заново на каждом шаге, храня интеграл. Инкрементная (скоростная) считает только приращение Δu = u(k) - u(k-1) через разности ошибок — она удобна, когда привод сам интегрирует (шаговый двигатель, клапан с памятью положения), и автоматически смягчает windup, ведь явного накопленного интеграла нет.

// Инкрементная форма:
// du = Kp*(e[k]-e[k-1]) + Ki*Ts*e[k] + (Kd/Ts)*(e[k]-2*e[k-1]+e[k-2])
// u[k] = u[k-1] + du

Как работает под капотом: влияние периода Ts

Выбор Ts критичен. Слишком большой период — регулятор реагирует редко, качество падает, система может потерять устойчивость. Слишком маленький — лишняя нагрузка на процессор и усиление шума в производной. Правило: Ts должен быть в 10-20 раз меньше характерного времени отклика системы. Покажем, как растущий Ts ухудшает управление.

class PID:
    def __init__(s, Kp, Ki, Kd, Ts):
        s.Kp, s.Ki, s.Kd, s.Ts = Kp, Ki, Kd, Ts
        s.integral = 0.0; s.prev = 0.0
    def update(s, sp, y):
        e = sp - y
        s.integral += e*s.Ts
        d = (e - s.prev)/s.Ts; s.prev = e
        return s.Kp*e + s.Ki*s.integral + s.Kd*d

def run(Ts):
    pid = PID(2.0, 0.5, 1.0, Ts)
    y, v = 0.0, 0.0
    t, peak = 0.0, 0.0
    while t < 20.0:
        u = pid.update(1.0, y)
        v += (u - 0.8*v)*Ts        # объект интегрируется тем же шагом
        y += v*Ts
        peak = max(peak, y); t += Ts
    return peak, y

for Ts in (0.05, 0.2, 0.5, 1.0):
    peak, final = run(Ts)
    print(f"Ts={Ts:4.2f}c  пик={peak:6.2f}  финал={final:6.2f}")

Вывод:

Ts=0.05c  пик=  1.22  финал=  1.00
Ts=0.20c  пик=  1.21  финал=  1.00
Ts=0.50c  пик=  1.20  финал=  1.00
Ts=1.00c  пик=30757325.10  финал=-76247160.00

С ростом периода Ts система ведёт себя всё хуже: перерегулирование растёт, при больших Ts регулятор реагирует так редко, что теряет контроль. Это фундаментально: дискретизация — не мелочь, а параметр, влияющий на устойчивость наравне с коэффициентами. В реальных проектах ПИД часто запускают на фиксированной частоте (например, 100-1000 Гц) именно для предсказуемости.

Связь с embedded и реальным временем

В микроконтроллере дискретный ПИД обычно живёт в обработчике прерывания таймера: каждые Ts микросекунд срабатывает прерывание, читается АЦП, считается воздействие, выдаётся ШИМ. Жёсткость периода тут критична — именно аппаратный таймер, а не программная задержка, гарантирует стабильный Ts, без которого интеграл и производная считаются неверно. В мире ROS регулятор крутится в узле на фиксированной частоте (например, через rclcpp::Rate или таймер-колбэк), и тот же принцип сохраняется. Понимание дискретной природы регулятора — это мостик от теории управления к встраиваемому программированию: здесь сходятся курсы про ROS, про embedded и про DSP, ведь дискретный фильтр ошибки и дискретный ПИД — близкие родственники по математике разностных уравнений.

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

  • Игнорировать период дискретизации. Слишком большой Ts тихо рушит управление, хотя коэффициенты «правильные».
  • Нестабильный Ts. Если цикл регулятора «плавает» по времени, интеграл и производная считаются неверно; нужен жёсткий период (таймер/прерывание).
  • Считать дискретный ПИД точной копией непрерывного. Дискретизация добавляет запаздывание и меняет границу устойчивости.

Итоги

  • Реальный ПИД дискретный: интеграл — сумма e·Ts, производная — разность (e-e_prev)/Ts.
  • Позиционная форма хранит интеграл; инкрементная считает приращение Δu и смягчает windup.
  • Период Ts критичен для устойчивости: берут в 10-20 раз меньше времени отклика, с жёстким таймером.
Проверьте себя
1. Что такое период дискретизации Ts в дискретном ПИД?
AВремя полного переходного процесса
BИнтервал между тактами регулятора: считал датчик — выдал воздействие
CПериод колебаний объекта
DВремя насыщения интеграла
2. Что происходит при слишком большом периоде Ts?
AСистема становится идеальной
BКачество падает, перерегулирование растёт, возможна потеря устойчивости
CУменьшается шум
DИнтеграл обнуляется
3. Чем удобна инкрементная (скоростная) форма ПИД?
AОна точнее непрерывной
BСчитает приращение Δu, удобна для интегрирующих приводов и смягчает windup
CНе требует датчика
DУбирает дискретизацию