Если вы пишете прошивку для терморегулятора, стабилизатора скорости мотора или удержания положения сервопривода на 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?