Немного не закончил демонстрационную программу для 3-позиционного регулятора - хотел сделать FBD вариант и вместе их потестировать. Но время никак не выкрою... Может чуть позже завершу и заменю демку для этого объекта.
При разработке регулятора актуальным остаётся вопрос проверки и отладки алгоритма до начала ПНР на объекте.
Для регулятора с управлением исполнительным механизмом через аналоговый выход можно собрать макет из датчика температуры, твердотельного реле с регулируемым выходом, нагревательным элементом (лампой накаливания или керамическим резистором).
Для регулятора с трёхпозиционным исполнительным механизмом собрать простой макет уже затруднительно.
введение:
Хорошую идею предлагают разработчики ПЛК Siemens S7-300 - библиотека этого ПЛК содержит три функциональных блока (ФБ) эмулятора объекта управления FB100, FB101 и FB102, соответственно для случаев непрерывного, трёхпозизионного и ШИМ управления исполнительным механизмом.
Основой данных ФБ является FB100, на вход которого подаётся состояние выхода регулятора и производятся последующие вычисления. Для ФБ FB101 и FB102 состояния управляющих входов сначала преобразуются в положение регулирующего органа исполнительного механизма, а потом повторяются вычисления из FB100.
Алгоритм FB100 эмулирует объект управления, который описывается тремя последовательно соединёнными апериодическими звеньями 1-го порядка. Общий коэффициент усиления позволяет сделать приведение выхода эмулятора к любым единицам измерения.
С подробным описанием ФБ FB100, FB101 и FB102 можно ознакомиться в документации к ПЛК.
Предлагаю самостоятельные реализации эмуляторов для среды разработки OwenLogic, появившиеся благодаря идеям FB100, FB101. Каждый из эмуляторов реализован в двух вариантах - на языках программирования ST и FBD, что связано с неравноценной реализацией отсчёта времени и возможностей вложения полученных ФБ в другие ФБ в среде Owen Logic.
ЭМУЛЯТОР ОБЪЕКТА УПРАВЛЕНИЯ С "НЕПРЕРЫВНЫМ" УПРАВЛЕНИЕМ
Структура эмулятора объекта управления с "непрерывным" регулированием показана на рисунке Схема Emulator_Cont_.png
Для получения значения выхода эмулятора объекта нужно последовательно трижды найти численное решение дифференциального уравнения
алгоритм решения дифференциального уравнения:
Для вычисления значения выхода апериодического звена нужно интегрировать дифференциальное уравнение.
Передаточная функция апериодического звена 1-го порядка в операторной форме
Подставляя полученное значение y первого звена в качестве параметра x второго звена, и проведя аналогичные вычисления, сможем получить выход из второго звена. Аналогично получим выход из третьего звена.
Реализация эмулятора с "непрерывным" управляющим входом
на языке ST:
Код:
///<Description>Эмулятор объекта управления. Эмулируется последовательное соединение трёх апериодических звеньев 1-го порядка.</Description>
///<Author>!!FPA!!</Author>
///<GroupName>Управляющие и регулирующие модули</GroupName>
function_block Emulator_Cont_ST_
var_input
///<Description>Перезапуск работы блока (инициализация всех переменных состояния) по фронту</Description>
xReset: bool := false;
///<Description>Входная переменная. Управляющий сигнал с ПИД</Description>
rVLV_Pos: real;
///<Description>Внешнее возмущающее воздействие (суммируется с rVLV_Pos для вычислений внутри ФБ)</Description>
rDisturbance: REAL;
///<Description>Значение выхода при rVLV_Pos=0</Description>
rPV_0: REAL:=20.0;
///<Description>Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка)</Description>
rGain: REAL:=1.5;
///<Description>Постоянная времени первого апериодического звена 1-го порядка, [мс]</Description>
dwTime_T1: udint:=60000;
///<Description>Постоянная времени второго апериодического звена 1-го порядка, [мс]</Description>
dwTime_T2: udint:=10000;
///<Description>Постоянная времени третьего апериодического звена 1-го порядка, [мс]</Description>
dwTime_T3: udint:=0;
end_var
var_output
///<Description>Выходное значение переменной процесса</Description>
rPV: REAL;
end_var
var
dwCurrent, dwCycle: udint;
dwPrevious: udint := 0;
bReset_previous: bool := false;
rCycle: REAL;
rT1: REAL;
rT2: REAL;
rT3: REAL;
rPV_Prev1: real;
rPV_Prev2: real;
rPV_Prev3: real;
rPV_Inner: real;
end_var
// вычисление длительности предыдущего цикла
dwCurrent := time_to_udint(get_time());
dwCycle := dwCurrent - dwPrevious;
dwPrevious := dwCurrent;
if (xReset and (not bReset_previous)) or (dwCycle = 0) then
rPV_Prev1 := (rVLV_Pos + rDisturbance) * rGain;
rPV_Prev2 := rPV_Prev1;
rPV_Prev3 := rPV_Prev1;
rPV := rPV_Prev1 + rPV_0;
else
rCycle := udint_to_real(dwCycle);
rT1 := udint_to_real(dwTime_T1);
rT2 := udint_to_real(dwTime_T2);
rT3 := udint_to_real(dwTime_T3);
// вычисление выхода
rPV_Inner := (rVLV_Pos + rDisturbance) * rGain;
if rT1 >= rCycle then
rPV_Inner := rPV_Prev1 + rCycle * (rPV_Inner - rPV_Prev1) / rT1;
rPV_Prev1 := rPV_Inner;
end_if;
if rT2 >= rCycle then
rPV_Inner := rPV_Prev2 + rCycle * (rPV_Inner - rPV_Prev2) / rT2;
rPV_Prev2 := rPV_Inner;
end_if;
if rT3 >= rCycle then
rPV_Inner := rPV_Prev3 + rCycle * (rPV_Inner - rPV_Prev3) / rT3;
rPV_Prev3 := rPV_Inner;
end_if;
rPV := rPV_0 + rPV_Inner;
end_if;
bReset_previous := xReset;
end_function_block
ЭМУЛЯТОР ОБЪЕКТА УПРАВЛЕНИЯ С ТРЁХПОЗИЦИОННЫМ УПРАВЛЕНИЕМ
ФБ будет состоять из двух частей: эмуляция привода с трёхпозиционным управлением (на вход которого поступают сигналы "открыть" и "закрыть", а выходе формируются сигналы положения регулирующего клапана и состояния концевых выключателей "открыт" и "закрыт") и эмуляция объекта управления с непрерывным управлением.
Имеется возможность эмулировать неидеальный клапан - с отличающимися временами полного хода при открытии и закрытии, временами выборки люфта при открытии и закрытии.
на языке ST:
Код:
///<Description>Эмулятор объекта управления. Эмулируется последовательное соединение трёх апериодических звеньев 1-го порядка. Выходной параметр изменяется под воздействием трехпозиционного ПИД регулятора.</Description>
///<Author>!!FPA!!</Author>
///<GroupName>Управляющие и регулирующие модули</GroupName>
function_block Emulator_3Pos_
var_input
///<Description>Перезапуск работы блока (инициализация всех переменных состояния) по фронту</Description>
xReset: bool := false;
///<Description>Сигнал Открыть</Description>
xOpen: BOOL;
///<Description>Сигнал Закрыть</Description>
xClose: BOOL;
///<Description>Время полного хода исполнительного устройства при открытии</Description>
dwFullStroke_Open: udint:=30000;
///<Description>Время полного хода исполнительного устройства при закрытии</Description>
dwFullStroke_Close: udint:=30000;
///<Description>Время выборки люфта исполнительного устройства при открытии</Description>
dwVLV_Backlash_Open: udint := 0;
///<Description>Время выборки люфта исполнительного устройства при закрытии</Description>
dwVLV_Backlash_Close: udint := 0;
///<Description>Верхний предел обратного сигнала клапана. Для клапана с контролем положения</Description>
rVLV_Max: REAL:=100.0;
///<Description>Нижний предел обратного сигнала клапана. Для клапана с контролем положения</Description>
rVLV_Min: REAL:=0.0;
///<Description>Внешнее возмущающее воздействие (суммируется с rVLV_Pos для вычислений внутри ФБ)</Description>
rDisturbance: REAL;
///<Description>Значение выхода при rVLV_Pos=0</Description>
rPV_0: REAL:=20.0;
///<Description>Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка)</Description>
rGain: REAL:=1.5;
///<Description>Постоянная времени первого апериодического звена 1-го порядка, [мс]</Description>
dwTime_T1: udint:=60000;
///<Description>Постоянная времени второго апериодического звена 1-го порядка, [мс]</Description>
dwTime_T2: udint:=10000;
///<Description>Постоянная времени третьего апериодического звена 1-го порядка, [мс]</Description>
dwTime_T3: udint:=0;
end_var
var_output
///<Description>Выходное значение переменной процесса</Description>
rPV: REAL;
///<Description>Обратный сигнал клапана. Для клапана с контролем положения</Description>
rVLV_Pos: REAL;
///<Description>Сигнал верхнего предела клапана</Description>
bVLV_Opened: BOOL;
///<Description>Сигнал нижнего предела клапана</Description>
bVLV_Closed: BOOL;
end_var
var
dwCurrent, dwCycle: udint;
dwPrevious: udint := 0;
bReset_previous: bool := false;
rCycle: REAL;
rFullStroke_Open: REAL;
rFullStroke_Close: REAL;
rPos: real;
rT1: REAL;
rT2: REAL;
rT3: REAL;
h: real;
rPV_Prev1: real;
rPV_Prev2: real;
rPV_Prev3: real;
rPV_Inner: real;
dwVLV_Backlash_Open_: udint; // выбранная часть люфта при открытии
dwVLV_Backlash_Close_: udint; // выбранная часть люфта при закрытии
dwRunTime: udint; // часть времени dwCycle, потраченная на перемещение
end_var
// вычисление времени текущего цикла
dwCurrent := time_to_udint(get_time());
dwCycle := dwCurrent - dwPrevious;
dwPrevious := dwCurrent;
if (xReset and (not bReset_previous)) or (dwCycle = 0) then
rPV_Prev1 := (rVLV_Pos - rVLV_Min + rDisturbance) * rGain;
rPV_Prev2 := rPV_Prev1;
rPV_Prev3 := rPV_Prev1;
rPV := rPV_Prev1 + rPV_0;
dwVLV_Backlash_Open_ := 0;
dwVLV_Backlash_Close_ := 0;
else
// проверка ограничений диапазонов входных переменных
if dwCycle <= 1 then
dwCycle := 1;
end_if;
if dwFullStroke_Open < dwCycle then
dwFullStroke_Open := dwCycle;
end_if;
if dwFullStroke_Close < dwCycle then
dwFullStroke_Close := dwCycle;
end_if;
rCycle := udint_to_real(dwCycle);
rT1 := udint_to_real(dwTime_T1);
rT2 := udint_to_real(dwTime_T2);
rT3 := udint_to_real(dwTime_T3);
rFullStroke_Open := udint_to_real(dwFullStroke_Open);
rFullStroke_Close := udint_to_real(dwFullStroke_Close);
// обработка люфта - часть сигнала перемещения расходуется на компенсацию люфта
dwRunTime := dwCycle;
if xOpen then
if dwRunTime <= dwVLV_Backlash_Close_ then
dwVLV_Backlash_Close_ := dwVLV_Backlash_Close_ - dwRunTime;
else
dwVLV_Backlash_Close_ := 0;
end_if;
if dwVLV_Backlash_Open_ < dwVLV_Backlash_Open then
dwVLV_Backlash_Open_ := dwVLV_Backlash_Open_ + dwRunTime;
if dwVLV_Backlash_Open_ > dwVLV_Backlash_Open then
dwRunTime := dwVLV_Backlash_Open_ - dwVLV_Backlash_Open;
dwVLV_Backlash_Open_ := dwVLV_Backlash_Open;
else
dwRunTime := 0;
end_if;
end_if;
end_if;
if xClose then
if dwRunTime <=dwVLV_Backlash_Open_ then
dwVLV_Backlash_Open_ := dwVLV_Backlash_Open_ - dwRunTime;
else
dwVLV_Backlash_Open_ := 0;
end_if;
if dwVLV_Backlash_Close_ < dwVLV_Backlash_Close then
dwVLV_Backlash_Close_ := dwVLV_Backlash_Close_ + dwRunTime;
if dwVLV_Backlash_Close_ > dwVLV_Backlash_Close then
dwRunTime := dwVLV_Backlash_Close_ - dwVLV_Backlash_Close;
dwVLV_Backlash_Close_ := dwVLV_Backlash_Close;
else
dwRunTime := 0;
end_if;
end_if;
end_if;
// вычисление положения клапана, концевых переключателей
h := (rVLV_Max - rVLV_Min) * udint_to_real(dwRunTime);
if xOpen then
rPos := rVLV_Pos + h / rFullStroke_Open;
elsif xClose then
rPos := rVLV_Pos - h / rFullStroke_Close;
else
rPos := rVLV_Pos;
end_if;
if rPos >= rVLV_Max then
rPos := rVLV_Max;
bVLV_Opened := true;
bVLV_Closed := false;
elsif rPos <= rVLV_Min then
rPos := rVLV_Min;
bVLV_Opened := false;
bVLV_Closed := true;
else
bVLV_Opened := false;
bVLV_Closed := false;
end_if;
rVLV_Pos := rPos;
// вычисление выхода
rPV_Inner := (rVLV_Pos - rVLV_Min + rDisturbance) * rGain;
if dwTime_T1 >= dwCycle then
rPV_Inner := rPV_Prev1 + rCycle * (rPV_Inner - rPV_Prev1) / rT1;
rPV_Prev1 := rPV_Inner;
end_if;
if dwTime_T2 >= dwCycle then
rPV_Inner := rPV_Prev2 + rCycle * (rPV_Inner - rPV_Prev2) / rT2;
rPV_Prev2 := rPV_Inner;
end_if;
if dwTime_T3 >= dwCycle then
rPV_Inner := rPV_Prev3 + rCycle * (rPV_Inner - rPV_Prev3) / rT3;
rPV_Prev3 := rPV_Inner;
end_if;
rPV := rPV_0 + rPV_Inner;
end_if;
bReset_previous := xReset;
end_function_block
На FBD не реализовывал - трудоёмко... Хотя для отладки ПИД регулятора "изнутри" при нынешних ограничениях на вложенность ФБ в OwenLogic, лучше бы и на FBD иметь.
FPavel, здравствуйте!
Воспользовался Вашим регулятором с трехпозиционным управлением, огромное спасибо!
Проблема возникла, когда подал на вход уставки величину 3500. Ну и измеренную величину в том же масштабе. В итоге он регулировал без остановок.
Внес в ФБ очень небольшую поправку, предлагаю на суд автора. Screenshot_11.png
Последний раз редактировалось Юра54; 09.10.2024 в 07:52.
Тогда уж приводить не к уставке, а к диапазону измерений датчика - уставка может быть равной нулю (например, для уровня воды в котле).
Почему-то думал, что именно абсолютное значение PV и SP не будет влиять на регулирование, ведь можно пропорционально уменьшить Kp и увеличить Ti.
Если PV сильно зашумлён, то его можно дополнительно сгладить фильтром 1-го порядка (например, встроенным - свойства аналогового входа).
Обычно, при настройке ПИД регулятора подбираю Kp, Ti, период пересчёта и постоянную фильтра аналогового сигнала - они все взаимосвязаны.
Физический смысл полосы пропорциональности Xp = 1/Kp - величина невязки, при которой за счёт пропорциональной составляющей полностью откроется клапан. Брал его примерно как 3-4 допустимых диапазона отклонения при регулировании. Т.е. если при SP=3500 предполагается отклонения 50, то Xp=150-200 и Kp=1/200=0.005. Примерно, такой масштаб коэффициента Kp.
Вечером попробую проверить работу ПИД с эмулятором на уставках со значением порядка 3500.
В любом случае, спасибо - доработки означают, что Вы разобрались с логикой работы и по мере необходимости подстроите под конкретный случай.
P.S. Не могу уяснить источник проблемы (автоколебаний) - ведь сам ПИД регулятор не работает с абсолютными значениями, а только с невязкой (E=SP-PV), т.е. "смещение" в 3500 и 10000 не должно оказывать влияние...
Последний раз редактировалось FPavel; 09.10.2024 в 12:32.
...
P.S. Не могу уяснить источник проблемы (автоколебаний) - ведь сам ПИД регулятор не работает с абсолютными значениями, а только с невязкой (E=SP-PV), т.е. "смещение" в 3500 и 10000 не должно оказывать влияние...
Значения ошибки например между 35 и 37 и между 3500 и 3700 отличаются на два порядка. Для этого и предназначен коэффициент пропорциональности.
А если сделать приведение сигналов к диапазону измерения датчика, то при замене на датчик с другим диапазоном придётся менять и коэффициент пропорциональности
P.S. Не могу уяснить источник проблемы (автоколебаний) - ведь сам ПИД регулятор не работает с абсолютными значениями, а только с невязкой (E=SP-PV), т.е. "смещение" в 3500 и 10000 не должно оказывать влияние...
Проблема была в том, что не было тонкой регулировки - привод клапана работал без остановки, пока давление не возвращалось в зону нечувствительности. Поскольку я нашел способ решить проблему -делением на уставку,- дальше уже не экспериментировал.
Проверил регулятор с уставкой 3500 вместе с эмулятором объекта.
Эмулятор объекта:
- время полного хода клапана 30 с,
- люфт клапана равен 1 с в обоих направлениях
- сам объект - апериодическое звено с постоянной времени 10 с, т.е. через 30 с от начала воздействия будет установлено стабильное состояние
- выход объекта равен 0 при полностью закрытом клапане (rPV_0 = 0)
- выход объекта равен 10000 при полностью открытом клапане (rGain = 100)
- положение клапана измеряется в процентах (rVLV_Min = 0, rVLV_Max = 100)
При Кп=0,0015, Ти=20000, Тм=5000 (время пересчёта), SP=3500, зоне=20 ПИД приводит объект к уставке, хотя и "подрабатывает" время от времени.
И это даже без компенсации люфта в регуляторе.
Т.е. всё-таки не могу принять, что регулятор не работает при уставках, сопоставимых с 3500.
Но, если заработало с делением - пусть работает. В данном случае, этот коэффициент просто перенесён как общий множитель за выражение ПИД регулятора.
Единственно, захотелось добавить слагаемое - дифференциал по времени значения Кп. И вместе с опцией разрешения этого слагаемого, возможно, добавлю.
Проблема: в симуляторе и фактически не совпадают значение регистра (в примере 575-й).
Ожидаемое значение: 47024
Фактическое: -17786
Проблема в том, что разные типы переменных, по разному отображают одни и те же числа, передаёте UINT, UDINT (целое без знака), а получаете типа INT, DINT (целое со знаком).
Поменяйте тип в полученном.
Последний раз редактировалось kondor3000; 10.10.2024 в 10:25.