С параметрами
ПИД-регулятора можно играться как с кошкой — до бесконечности: пока в колебательный процесс не впадет или не надоест.
Несмотря на тонны матана, прячущиеся за тройкой входных параметров, около 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 необходимо выставить оба диф. коэффициента в значения отличные от нуля. Причем основным диф. параметром является T
v (он расположен в числителе). T
d — это время демпфирования и находится в знаменателе диф. составляющей. Его значение можно выставить равным единице и тогда останется только основная T
v составляющая диф. компоненты ПИД-регулятора.