PDA

Просмотр полной версии : Дискретное управление клапаном по ПИД.



meta11ist87
27.08.2024, 11:18
Есть ПИД-регулятор. Есть клапан, который управляется дискретными выходами Открыть, Закрыть, с него приходят дискретные Открыто/Закрыто.
Время полного хода клапана - 100 секунд.
Написал впервые что-то на ST, оцените, может кто-то что-то дополнит. Может кому-то понадобится.
Коротко: запоминается предыдущее воздействие, рассчитывается новое как разница предыдущее-новое.
В другой части программы обрабатываю условия, если ПИД выдал 0% или 100%, то выдаю на соответствующие выходы время равное 1,2 полного хода, чтоб клапан точно дошёл.
Как запихнуть подобное условие в этот блок - я не смог разобраться.
А может уже есть готовые кейсы подобного управления и я просто изобрёл велосипед.


FUNCTION_BLOCK ValveControl

VAR_INPUT
PIDOutput: REAL; // 0-100% от PID-регулятора
FullTime: REAL; // Время полного хода клапана в секундах
OpenState: BOOL; // Состояние клапана "Открыт"
CloseState: BOOL; // Состояние клапана "Закрыт"
UnixTime: UDINT; // Текущее время в Unix формате (секунды)
END_VAR

VAR_OUTPUT
OpenOutput: BOOL; // Выход "Открыть"
CloseOutput: BOOL; // Выход "Закрыть"
OpenTime: REAL; // Рассчитанное время открытия (секунды)
CloseTime: REAL; // Рассчитанное время закрытия (секунды)
END_VAR

VAR
PreviousOutput: REAL; // Предыдущее значение сигнала PID
OpenTimeCounter: REAL; // Счетчик времени открытия
CloseTimeCounter: REAL; // Счетчик времени закрытия
OpenTimerStarted: BOOL; // Флаг запуска таймера открытия
CloseTimerStarted: BOOL; // Флаг запуска таймера закрытия
Initialized: BOOL; // Флаг инициализации
LastOpenTime: UDINT; // Время начала таймера открытия
LastCloseTime: UDINT; // Время начала таймера закрытия
END_VAR

// Инициализация переменных при первом вызове функции
IF NOT Initialized THEN
PreviousOutput := 0;
OpenTimeCounter := 0;
CloseTimeCounter := 0;
OpenTimerStarted := FALSE;
CloseTimerStarted := FALSE;
Initialized := TRUE;
END_IF

// Проверка состояния клапана
IF OpenState THEN
OpenTimeCounter := 0; // Сбрасываем счетчик времени открытия
OpenTimerStarted := FALSE;
OpenOutput := FALSE; // Сбрасываем выход "Открыть"
ELSIF CloseState THEN
CloseTimeCounter := 0; // Сбрасываем счетчик времени закрытия
CloseTimerStarted := FALSE;
CloseOutput := FALSE; // Сбрасываем выход "Закрыть"
END_IF

// Вычисление времени удержания выхода
IF PIDOutput > PreviousOutput THEN
OpenTimeCounter := OpenTimeCounter + (PIDOutput - PreviousOutput) / 100 * FullTime;
ELSIF PIDOutput < PreviousOutput THEN
CloseTimeCounter := CloseTimeCounter + (PreviousOutput - PIDOutput) / 100 * FullTime;
END_IF

// Установка выхода "Открыть"
IF OpenTimeCounter > 0 AND NOT OpenTimerStarted THEN
OpenTimerStarted := TRUE;
LastOpenTime := UnixTime; // Запоминаем начальное время
OpenOutput := TRUE;
END_IF

// Проверка таймера открытия
IF OpenTimerStarted THEN
// Сбрасываем выход "Открыть" только если таймер сработал
IF UnixTime - LastOpenTime >= REAL_TO_UDINT(OpenTimeCounter) THEN
OpenOutput := FALSE;
OpenTimerStarted := FALSE;
OpenTimeCounter := 0;
END_IF

// Сбрасываем выход "Открыть" если клапан уже открыт
IF OpenState THEN
OpenOutput := FALSE;
OpenTimerStarted := FALSE;
OpenTimeCounter := 0;
END_IF
END_IF

// Установка выхода "Закрыть"
IF CloseTimeCounter > 0 AND NOT CloseTimerStarted THEN
CloseTimerStarted := TRUE;
LastCloseTime := UnixTime; // Запоминаем начальное время
CloseOutput := TRUE;
END_IF

// Проверка таймера закрытия
IF CloseTimerStarted THEN
// Сбрасываем выход "Закрыть" только если таймер сработал
IF UnixTime - LastCloseTime >= REAL_TO_UDINT(CloseTimeCounter) THEN
CloseOutput := FALSE;
CloseTimerStarted := FALSE;
CloseTimeCounter := 0;
END_IF

// Сбрасываем выход "Закрыть" если клапан уже закрыт
IF CloseState THEN
CloseOutput := FALSE;
CloseTimerStarted := FALSE;
CloseTimeCounter := 0;
END_IF
END_IF

// Сохранение текущего значения сигнала PID
PreviousOutput := PIDOutput;

// Установка выходных значений OpenTime и CloseTime
OpenTime := OpenTimeCounter;
CloseTime := CloseTimeCounter;

END_FUNCTION_BLOCK

FPavel
27.08.2024, 18:39
Ваше решение напоминает RegKZR из менеджера компонентов.

На мой взгляд, эти решения неудачны тем, что при переполнении на краях диапазона выхода "аналогового" ПИД регулятора (0 или 100 %) будет неоднозначное поведение:
1 вариант: прироста по сравнению с предыдущим пересчётом нет, значит и нет управляющих сигналов (больше или меньше)
2 вариант: т.к. ПИД выдал 0 % (или 100 %), то значит требуется полное закрытие (открытие) задвижки.

На мой взгляд, оба варианта неправильны, т.к. программа лишь предполагает реальное положение регулирующего клапана. Поэтому будет или потеряно управление (1 вариант) или в систему будет вносится ненужное возмущающее воздействие (2 вариант).

Поясню по возмущению. Клапаны отрываются и закрываются неравномерно. Например, было подано 10 минимальных импульсов открытия, а чтобы вернуть клапан в исходное положение требуется не 10, а 11 импульсов закрытия. Да, после возвращения П составляющая остаётся неизменной, но на дополнительный импульс потребовалось изменить И составляющую, и, таким образом, хотя клапан вернулся в исходное состояние, выход ПИД немного уменьшился. Через время выход станет равным 0 и программа подаст огромный импульс закрытия - внесёт возмущение.
С таким явлением я сам столкнулся, причём у меня было 5 шиберов и одни смещали ПИД к 0%, а другие к 100% - никакой определённости.
Это не только со мной происходило
https://owen.ru/forum/showthread.php?t=17974&p=434043&viewfull=1#post434043


Т.к. тогда у меня был ПЛК и полный доступ ко всем потрохам ПИД, то быстрым решением стало поддержание И составляющей около 50% (с пересчётом всех переменных из преобразования в импульсы).

Правильное решение - переписать ПИД под управление 3-позиционным клапаном.
Посмотрите, тут пояснял математику и показал пример на FBD (о причинах такого выбора пояснил ниже)
https://owen.ru/forum/showthread.php?t=25068&p=430251&viewfull=1#post430251
https://owen.ru/forum/showthread.php?t=17974&p=436517&viewfull=1#post436517

И добавлю, для нормального регулирования важен подбор не только Кп, Ти, Тд, но и сглаживание зашумлённого аналогового сигнала и период пересчёта регулятора.
Время минимального импульса не может быть меньше времени переключения реле - менее 200 мс.

Если что-то непонятно по алгоритму - спрашивайте.
Может, сможете переписать на ST - я без проверки и отладки не решился.
Ещё думал добавить в ПИД ещё один фильтр 1-порядка для сглаживания измерений, тогда и Д составляющую можно было бы использовать.

kondor3000
27.08.2024, 19:10
Просто погонял блок, с 20 до 80 % вручную (концевики выключены), в некоторых случаях включаются выходы Отрыть и Закрыть одновременно, с одним временем. Такого быть не должно.

meta11ist87
27.08.2024, 20:55
Ваше решение напоминает RegKZR из менеджера компонентов.

На мой взгляд, эти решения неудачны тем, что при переполнении на краях диапазона выхода "аналогового" ПИД регулятора (0 или 100 %) будет неоднозначное поведение:
1 вариант: прироста по сравнению с предыдущим пересчётом нет, значит и нет управляющих сигналов (больше или меньше)
2 вариант: т.к. ПИД выдал 0 % (или 100 %), то значит требуется полное закрытие (открытие) задвижки.


Спасибо, попробую ваш макрос на реальной системе как будет возможность.
Вынужден согласиться со всеми вашими доводами.
Выглядит, конечно, как нечто гениальное)
На st не смогу переписать, не хватает знаний.
Да и зачем, если оно в fbd работает.