Несмотря на тонны матана, прячущиеся за тройкой входных параметров, около 90% регуляторов в мире основано на ПИД (гугл, википедия, нормальное распределение). Именно благодаря простому интерфейсу, скрывающему интегралы-дифуры, использовать ПИД-регулятор невероятно просто, главное правильно отстроить и не умереть от скуки.
TcControllerToolbox.lib — это основная профессиональная библиотека TwinCAT для работы с регуляторами, генераторами и прочими алгоритмами автоматизации. Там много разных штук, но мы посмотрим на ПИД-регуляторы, как самые распространенные и востребованные.
PLC Controller Toolbox
В прайсе эта библиотека обозначена артикулом TF4100. Это для TwinCAT 3. Если вам нужно модернизировать старую систему на TwinCAT 2, ищите TS4100. Библиотека платная, но построена профессионально — с учетом всего того, что просто необходимо учитывать.
Например: все ФБ библиотеки контролируют циклы и тайминги программных циклов. В случае повторного вызова ФБ за один и тот же цикл или если будет пропуск цикла — ФБ адекватно обработает эту ситуацию. Даже если будет пропущен не один цикл или не один раз.
Кроме непосредственно регулирования, блоки ПИД-регулятора умеют дополнительные опции.
FB_CTRL_PI
FB_CTRL_PID
- Контроль интегрального насыщения, верхний и нижний пределы.
- Ручная уставка задания в обход автоматической: можно вмешаться в работу регулятора.
- Ручное отключение интегральной составляющей.
- Жесткий контроль циклов ПЛК-задачи и таймингов.
FB_CTRL_PID_EXT
- Зона нечувствительности для входного значения. При нахождении в ней значение выхода регулятора постоянно.
- Зона нечувствительности для нулевой величины выхода.
- Окно для отключения интегральной составляющей (она постоянна или равна нулю).
- Окно для уменьшения масштаба интегральной составляющей.
FB_CTRL_PID_SPLITRANGE
- Аналогично FB_CTRL_PID, но можно задать разные наборы коэффициентов "p-i-d" для охлаждения (температура ниже нуля) и для нагрева (температура выше нуля).
- Имеет два различных выхода, но активен только один. В зависимости от величины выхода — больше нуля или меньше нуля, он запускает нагрев или охлаждение, обнуляя значение противоположного выхода. Кусок кода, для разнообразия:
fOut := fY; IF fOut > 0.0 THEN fOutPos := fOut; fOutNeg := 0.0; ELSE fOutNeg := fOut; fOutPos := 0.0; END_IF
FB_CTRL_PID_EXT_SPLITRANGE
- Всё и сразу: FB_CTRL_PID + _EXT + _SPLITRANGE.
Простой ПИД-регулятор
К тому же бесплатный. Можно было бы перевести дословно — "примитивный", но он работает и это — самое главное.
В бесплатной библиотеке TcUtilities.lib, есть встроенный функциональный блок FB_BasicPID, который умеет три параметра и — все. Больше он ничего не умеет (плюс демпфирование диф.-составляющей Td, но это у разработчика случайно получилось).
Главное отличие от профессиональной библиотеки — это:
FB_BasicPID
- нет контроля интегрального насыщения;
- нет контроля повторного или пропущенного вызова за цикл. Вызывайте строго один раз за цикл — иначе пожалеете.
С циклом более менее понятно, а вот отсутствие контроля за интегральным насыщением (integral windup, reset windup, integral saturation) может сыграть злую шутку. Мы сейчас попробуем поиграться с этим эффектом, и заодно убедимся, что функция регулятора действительно примитивная.
Да собственно уже играемся — длинная портянка графика справа показывает, как мы сначала нагрели нечто до 27 градусов, а затем пытаемся охладить, но что-то никак не получается...
Зеленый — задание-уставка.... А все потому, что не хватает возможности исполнительного механизма — он не справляется, а ПИД-регулятор все топит и топит педальку тормоза-охлаждения. И график ползет вниз...
Синий — выход ПИД-регулятора.
Красный — актуальное, текущее значение на выходе исполнительного механизма.
Чтобы хоть как-то исправить ситуацию, мы попробуем ограничить выход регулятора в соответствии с возможностями нашего исполнительного механизма. Предположим, что он может нагреть до 60 и охладить до 18 условных градусов, условного цельсия:
PID(
SetpointValue := TargetValue,
ActualValue := ActualValue,
Kp := kp,
Ti := ti,
Tv := tv,
Td := td, MaxCtrlOutput := Model.LimitHi, MinCtrlOutput := Model.LimitLow, CtrlOutput => SetpointValue); IF SetpointValue < 18 THEN SetpointValue := 18; END_IF IF SetpointValue > 60 THEN SetpointValue := 60; END_IF
Делаем мы это не из-за того, что не понимаем процессы которые происходят в регуляторы, а просто по причине недоступности внутренностей функционального блока ПИД-регулятора. Ну, не можем мы их исправить. Зато, как обычно, можно написать свой регулятор.
Пока же оставим регулятор как есть, и в итоге получим как на картинке ниже — задание изменилось и кажется, что регулятор чего-то ждет... На самом деле интегральная ошибка убежала далеко вверх (накопилась) и в данный момент стремительно падает, уменьшаясь и пытаясь достичь заданного. И это я еще ждал недолго, за сутки она бы убежала на столько далеко...
Попробуем зайти с другой стороны и заморозим накопление ошибки (интегральную часть). Для этого перепишем часть регулятора:
// расчет интегральной части IF is_Ipart THEN IF (maxLimReached AND (e >= 0.0)) OR (minLimReached AND (e <= 0.0)) THEN yi := yi1; // замораживаем интегральную часть ELSE yi := yi1 + di * (e + e1); // интегрируем END_IF ELSE yi := 0.0; END_IF; [...] y := yp + yi + yd; // суммируем П-И-Д части для расчета выхода регулятора // проверяем выход на ограничения IF y < MinCtrlOutput THEN y := MinCtrlOutput; minLimReached := TRUE; maxLimReached := FALSE; ELSIF y > MaxCtrlOutput THEN y := MaxCtrlOutput; minLimReached := FALSE; maxLimReached := TRUE; ELSE minLimReached := maxLimReached := FALSE; END_IF CtrlOutput := y; // формируем выход функционального блока
Проверяем:
Модель процесса
Это абстрактная модель "какого-то" объекта, имитирующая "какой-то" слегка нелинейный процесс. Чтобы придать модели линейность — уберите в расчете переменной delta часть под квадратным корнем (вместе с самим корнем SQRT) и оставьте только Grow и CycleTime.
PROGRAM Model VAR_INPUT Run : BOOL; Setpoint : LREAL; Grow : LREAL := 0.1; // градусы за секунду CycleTime : LREAL := 0.01; // секунды LimitHi : LREAL := 70; LimitLow : LREAL := 18; END_VAR VAR_OUTPUT Actual : LREAL; END_VAR VAR delta : LREAL; END_VAR [...] IF Setpoint > LimitHi THEN Setpoint := LimitHi; END_IF IF Setpoint < LimitLow THEN Setpoint := LimitLow; END_IF delta := Grow * CycleTime * SQRT(ABS(Actual * Actual - Setpoint * Setpoint)); IF Actual > Setpoint THEN Actual := Actual - delta; ELSE Actual := Actual + delta; END_IF
Если у вас есть модель получше — присылайте почтой, комментариями и пр.
Про диф.-составляющие
Чаще всего используется ПИ-регулятор. Дальше можете не читать.
Диф. составляющая, предсказывая будущее, тянет регулятора назад, "прижимая" выход и не давая ему вылезти сильно высоко (overshoot). Все эти штуки с предсказаниями могут выйти боком, если плохо понимать процесс, поэтому обычно Д-составляющая попросту не используется: отключают, приравнивая нулю.
Чтобы включить ПИД-регулятор в FB_BasicPID необходимо выставить оба диф. коэффициента в значения отличные от нуля. Причем основным диф. параметром является Tv (он расположен в числителе). Td — это время демпфирования и находится в знаменателе диф. составляющей. Его значение можно выставить равным единице и тогда останется только основная Tv составляющая диф. компоненты ПИД-регулятора.


