Я думал тема ПИД регуляторов закрыта.
но снова вижу оживление.
придется написать свой (тем более я обещал еще в 20 году).
вот есть тема будем называть ее 1 частью Здесь
итак начнем с маленьких шагов и сделаем большое дело.
ПИД регуляторы имею обширное применение от простейших термостатов до дронов (будь они не лады – последнее время) и 3Д принтеров.
почему ПИД -ПИД
Пропорциональный
Интегральный
Дифференциальный
регулятор
вычисляются эти три составляющие, суммируются и получаем итоговое воздействие на систему для достижения требуемого результата
Пропорциональная - Реагирует на текущую ошибку. Чем она больше, тем быстрее система, но слишком большое значение приводит к перерегулированию и колебаниям.
Интегральная - Накапливает прошлые ошибки. Позволяет устранить статическую ошибку (когда система стабилизируется не точно на уставке). Чрезмерное увеличение может вызвать "интегральное насыщение" и большие перерегулирования.
Дифференциальная - Предсказывает будущее поведение ошибки, учитывая скорость ее изменения. "Сглаживает" переходный процесс и помогает уменьшить перерегулирование. Чувствительна к шуму.
С теорией все, перейдем к практике
имеем
setpoint - уставку (то что мы хотим получить)
feedback – текущее значение (то что есть сейчас)
Коэффициенты Kp, Ki, Kd – это задаваемые нами значения которые задают пропорцию влияния каждого из расчетов на конечный результат (к ним мы вернемся позже)
вычисляем error – ошибку = setpoint – feedback
P - пропорциональная составляющая = error * Kp
I – интегральная составляющая = I_prev + error * Ki (тут I_prev это I сохраненное с предыдущего вызова макроса, при первом цикле = 0)
D – дифференциальная составляющая = Kd * (error – error_prev) (тут error_prev это error сохраненное с предыдущего вызова макроса, при первом цикле = 0)
Out - ну и наконец выход нашего блока = P+I+D
вот и готов наш первый ПИД-регулятор
вот его код на ST
Но его практически не возможно использоватьКод:function_block first_PID var_input setpoint: real; feedback: real; Kp: real; Ki: real; Kd: real; end_var var_output Out : real; end_var var error : real; I_prev : real; error_prev : real; P : real; I : real; D : real; end_var error := setpoint - feedback; P := error * Kp; I := I_prev + (error * Ki); // назовем это место в программе как (точка 1) D := Kd * (error - error_prev); Out := P+I+D; // назовем это место в программе как (точка 2) I_prev := I; error_prev := error; end_function_block
когда измеренное значение будет рядом с заданным интегральная составляющая будет накапливаться для устранения ошибки и в какой то момент может стать очень большой и для обратного хода при изменений уставки или смене измеренного значения ей потребуется время для «смотки» назад, а нам нужен регулятор с быстрым реагированием.
по тому ограничим величину I максимальным и минимальным возможным значением выхода. Вставим в точку 1 – этот код
тоже самое с выходом расчитанное значение может превышать возможные значения на выходе и их нужно ограничить. Вставим в точку 2 – этот кодКод:IF I > 1 then I := 1; elsif I < -1 then I := -1; end_IF
Или интегральную составляющую можно сохранять только если выход не перенасыщен. Вот новый кодКод:IF Out > 1 then Out := 1; elsif Out < -1 then Out := -1; end_IF
он практически единетичен первому с внесенными правками. Но есть и отличия мы ограничиваем I только когда регулятор не может повлиять на установку из за физических ограничений не смотря на максимальный выход (например отапливание 100 ватной лампочкой комнаты в 30 м2)Код:function_block first_PID var_input setpoint: real; feedback: real; Kp: real; Ki: real; Kd: real; end_var var_output Out : real; end_var var error : real; I_prev : real; error_prev : real; P : real; I : real; D : real; output_saturated: bool; end_var error := setpoint - feedback; P := error * Kp; I := I_prev + (error * Ki); D := kD * (error - error_prev); Out := P+I+D; IF Out > 1 then Out := 1; output_saturated := true; elsif Out < -1 then Out := -1; output_saturated := true; else output_saturated := false; end_IF // назовем это место в программе как (точка 3) if not output_saturated then I_prev := I; end_if; error_prev := error; end_function_block
Вот это уже вполне правильный и рабочий вариант.
Сейчас выходной сигнал у нас в диапазоне -1…1. Давайте приведем его в диапазон 0..100(%), для этого вставим в точку 3 следующий код Out := (Out+1) * 50;
Хотя это спорный вопрос
выход -1…1 (симетричный диапазон) удобен для управления двигателями (вперед/назад) и реверсивными приводами
выход 0..100 для насосов, вентиляторов, нагревателей
Я не думаю что ограничение диапазона нужно вносить в сам регулятор, но приведем НАШ регулятор в соответствие с библиотечным и добавим параметры «Минимальная мощность» и «Максимальная мощность».
Вот итоговый код:
а вот разрешение работы или «Ручной режим», что по моему одно и тоже лучше внести в регулятор.Код:function_block first_PID var_input setpoint: real; feedback: real; Kp: real; Ki: real; Kd: real; min_P: real; max_P: real; end_var var_output Out : real; end_var var error : real; I_prev : real; error_prev : real; P : real; I : real; D : real; output_saturated: bool; end_var error := setpoint - feedback; P := error * Kp; I := I_prev + (error * Ki); D := Kd * (error - error_prev); Out := P+I+D; IF Out > 1 then Out := 1; output_saturated := true; elsif Out < -1 then Out := -1; output_saturated := true; else output_saturated := false; end_IF Out := (Out+1) * 50; IF Out > max_P then Out := max_P; elsif Out < min_P then Out := min_P; end_IF if not output_saturated then I_prev := I; end_if; error_prev := error; end_function_block
Сразу приведу итоговый код (в нем я отказался от последних изменений, уже говорил что по моему им здесь не место, и далее приведем более красивое решение)
здесь в ручном режиме мы сохраняем старую ошибку для дифференциальной составляющей и интегральную составляющую делаем равной ручному заданию для плавного подхвата регулятором при выходе из ручного режима.Код:function_block first_PID var_input setpoint: real; feedback: real; Kp: real; Ki: real; Kd: real; EN_manual: bool; Set_manual: real; end_var var_output Out : real; end_var var error : real; I_prev : real; error_prev : real; P : real; I : real; D : real; output_saturated: bool; raw_output: real; end_var IF EN_manual THEN raw_output := Set_manual; I_prev := Set_manual; error_prev := setpoint - feedback; ELSE error := setpoint - feedback; P := error * Kp; I := I_prev + (error * Ki); D := Kd * (error - error_prev); raw_output := P+I+D; IF raw_output > 1 then raw_output := 1; output_saturated := true; elsif raw_output< -1 then raw_output := -1; output_saturated := true; else output_saturated := false; end_IF if not output_saturated then I_prev := I; end_if; error_prev := error; end_IF Out := (raw_output+1) * 50; end_function_block
подхват можно сделать и еще более плавным заменив строку
I_prev := Set_manual; на I_prev := Set_manual - (error * Kp);
Эти входы можно использовать и как в библиотечном регуляторе задать жесткое значение выхода при отключенном регуляторе в Set_manual и включать/выключать регулирование через инверсию EN_manual.
Все это уже идеальный регулятор, который достаточен для большинства применений.
Далее ограничение выхода минимальной и максимальной мощностью рекомендую делать отдельным блоком на выходе регулятора.
В следующий раз напишем блок «Надзиратель» для этого регулятора он будет осуществлять авто настройку коэффициентов.




Ответить с цитированием
