Если вы пишете прошивку для терморегулятора, стабилизатора скорости мотора или удержания положения сервопривода на STM32, рано или поздно потребуется PID-регулятор — и типичная первая ошибка здесь заключается в вызове расчёта регулятора из главного цикла while(1) без фиксированного шага по времени, из-за чего интегральная и дифференциальная составляющие ведут себя непредсказуемо. Проверьте, считается ли регулятор с постоянным периодом: именно это первое, что стоит проверить при «плавающем» выходе.

В этой статье разберём, как работает пропорционально-интегрально-дифференциальный регулятор, как корректно реализовать его на микроконтроллерах STM32 (F1, F4, G4 и других серий), какие подводные камни встречаются чаще всего и как подобрать коэффициенты без лабораторного стенда. Материал ориентирован на практику: минимум теории, максимум рабочего кода и проверяемых действий.

Как работает PID-регулятор: краткая теория

PID-регулятор вычисляет управляющее воздействие на основе ошибки — разницы между заданным значением (setpoint) и измеренным значением процесса. Классическая формула в непрерывном виде: u(t) = Kp·e(t) + Ki·∫e(t)dt + Kd·de(t)/dt. Каждая составляющая отвечает за свой аспект поведения системы.

Пропорциональная часть реагирует на текущую ошибку: чем больше отклонение, тем сильнее воздействие. Интегральная накапливает ошибку во времени и устраняет статическую ошибку — ситуацию, когда система стабилизировалась, но не достигла цели. Дифференциальная реагирует на скорость изменения ошибки и демпфирует колебания, предотвращая перерегулирование.

  • 🔧 Kp — задаёт скорость реакции; слишком большое значение вызывает колебания
  • 🧮 Ki — убирает остаточную ошибку; избыток приводит к медленным раскачкам и насыщению
  • 📉 Kd — гасит перерегулирование; чувствителен к шуму датчика
  • ⏱️ Период дискретизации dt должен быть стабильным и в несколько раз меньше характерного времени процесса

В микроконтроллере формула дискретизируется: интеграл заменяется суммой, производная — разностью соседних значений. Именно дискретная форма и реализуется в коде.

Организация периодического вызова на STM32

Ключевое требование — фиксированный период расчёта. Наиболее надёжный способ на STM32 — использовать аппаратный таймер (например, TIM6 или TIM7) с прерыванием по обновлению либо тик SysTick / планировщик FreeRTOS с задачей фиксированного периода через vTaskDelayUntil(). Вызов регулятора из while(1) с «примерной» задержкой недопустим: джиттер периода напрямую искажает интегральную и дифференциальную части.

// Пример: флаг выставляется в прерывании таймера

volatile uint8_t pid_tick = 0;

void TIM6_DAC_IRQHandler(void)

{

if (__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE)) {

__HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);

pid_tick = 1;

}

}

В обработчике прерывания следует выполнять минимум работы — только установку флага или чтение датчика. Сам расчёт лучше выполнять в основном цикле или задаче, чтобы не блокировать другие прерывания. Период выбирайте исходя из динамики объекта: для температурных процессов обычно достаточно периодов в сотни миллисекунд, для моторов — единицы миллисекунд. Точные значения зависят от вашего объекта, их стоит подбирать экспериментально.

⚠️ Внимание: не выполняйте длительные вычисления с плавающей точкой и операции вроде printf внутри обработчика прерывания таймера — это нарушит периодичность и может вызвать пропуски тиков.
📊 Какой объект вы регулируете PID-регулятором на STM32?