PDA

Просмотр полной версии : Алгоритм работы 3-х насосов на ST



K.K.
05.12.2024, 02:36
Приветствую коллеги!
Прошу подсказать или поделиться примером реализации алгоритма управления тремя насосами на ST.
Логика работы следующая:
есть 3 насоса которые включаются по сигналам нужен 1 насос - включается один насос, нужно 2 насоса включаются два насоса. Включение насосов должно быть реализовано путем чередования по последнему включенному, то к примеру есть сигнал нужен 1 насос - включили первый, останов происходит когда нет сигнала нужен насос, при следующем таком же сигнале уже запускается второй насос и т.д. Так же есть выбор с HMI резервного насоса, он подключается, когда из основных кто-то в аварии, потому что в алгоритме работа больше двух насосов запрещена. У каждого насоса есть контроль аварий. Также еще должны быть команды с ВУ на пуск, стоп каждого насоса, если система в авто, и выбор резервного насоса с ВУ. Еще необходимо реализовать охлаждение насосов по уставке времени, к примеру первый насос запустился, как только он остановиться включается охлаждение по уставке, и пока таймер не выйдет он не может включаться, даже если по условию должен включиться. Прошу подсказать как правильно такой алгоритм реализовать именно на ST.
Заранее всем спасибо!

kondor3000
05.12.2024, 08:52
Приветствую коллеги!
Прошу подсказать или поделиться примером реализации алгоритма управления тремя насосами на ST.
Логика работы следующая:
есть 3 насоса которые включаются по сигналам нужен 1 насос - включается один насос, нужно 2 насоса включаются два насоса. Включение насосов должно быть реализовано путем чередования по последнему включенному, то к примеру есть сигнал нужен 1 насос - включили первый, останов происходит когда нет сигнала нужен насос, при следующем таком же сигнале уже запускается второй насос и т.д. Так же есть выбор с HMI резервного насоса, он подключается, когда из основных кто-то в аварии, потому что в алгоритме работа больше двух насосов запрещена. У каждого насоса есть контроль аварий. Также еще должны быть команды с ВУ на пуск, стоп каждого насоса, если система в авто, и выбор резервного насоса с ВУ. Еще необходимо реализовать охлаждение насосов по уставке времени, к примеру первый насос запустился, как только он остановиться включается охлаждение по уставке, и пока таймер не выйдет он не может включаться, даже если по условию должен включиться. Прошу подсказать как правильно такой алгоритм реализовать именно на ST.
Заранее всем спасибо!

В вашем случае лучше написать ФБ работы одного насоса, со всеми хотелками и вызвать ФБ три раза.
Пример на 4 насоса есть тут (можно изменить на 3), только нет отдельного пуска и стопа для каждого насоса. Охлаждение можно запускать инверсией после выключения насоса. В примере переключение по времени 5 часов (можно изменить) и по минимальной наработке.
Уровень в последней версии включает по 1-4 насоса одновременно
https://owen.ru/forum/showthread.php?t=38920&page=2#13

K.K.
05.12.2024, 10:52
а как сделать включение насосов именно по последнему включенному, путем чередования, не могу ни как понять (?

K.K.
05.12.2024, 10:54
В вашем случае лучше написать ФБ работы одного насоса, со всеми хотелками и вызвать ФБ три раза.
Пример на 4 насоса есть тут (можно изменить на 3), только нет отдельного пуска и стопа для каждого насоса. Охлаждение можно запускать инверсией после выключения насоса. В примере переключение по времени 5 часов (можно изменить) и по минимальной наработке.
Уровень в последней версии включает по 1-4 насоса одновременно
https://owen.ru/forum/showthread.php?t=38920&page=2#13

а как потом лучше реализовывать логику включения ? при учете что у меня будет 3 FB на насосы?

K.K.
05.12.2024, 10:57
FUNCTION_BLOCK FB_PumpControl
VAR_INPUT
SystemRunReq_1 : BOOL; // Сигнал: нужен 1 насос
SystemRunReq_2 : BOOL; // Сигнал: нужно 2 насоса
AutoMode : BOOL; // Автоматический режим: TRUE - автомат, FALSE - ручной
Pump1_Enable : BOOL; // Насос 1 включен с панели
Pump2_Enable : BOOL; // Насос 2 включен с панели
Pump3_Enable : BOOL; // Насос 3 включен с панели
Pump1_Alarm : BOOL; // Авария насоса 1
Pump2_Alarm : BOOL; // Авария насоса 2
Pump3_Alarm : BOOL; // Авария насоса 3
ReservePumpSelection : INT; // Выбор резервного насоса: 1, 2 или 3
Pump1_ManualStart : BOOL; // Ручной пуск насоса 1
Pump2_ManualStart : BOOL; // Ручной пуск насоса 2
Pump3_ManualStart : BOOL; // Ручной пуск насоса 3
Pump1_ManualStop : BOOL; // Ручной стоп насоса 1
Pump2_ManualStop : BOOL; // Ручной стоп насоса 2
Pump3_ManualStop : BOOL; // Ручной стоп насоса 3
CoolingTimeSetpoint : TIME; // Уставка времени охлаждения
END_VAR

VAR_OUTPUT
Pump1_StartCmd : BOOL; // Команда на запуск насоса 1
Pump2_StartCmd : BOOL; // Команда на запуск насоса 2
Pump3_StartCmd : BOOL; // Команда на запуск насоса 3
Pump1_Status : INT; // Статус насоса 1
Pump2_Status : INT; // Статус насоса 2
Pump3_Status : INT; // Статус насоса 3
END_VAR

VAR
LastPumpIndex : INT := 0; // Последний включенный насос
PumpsStarted : INT := 0; // Текущее количество работающих насосов
ActivePumpCount : INT := 0; // Сколько насосов требуется
CoolingTimerPump1 : TON; // Таймер охлаждения насоса 1
CoolingTimerPump2 : TON; // Таймер охлаждения насоса 2
CoolingTimerPump3 : TON; // Таймер охлаждения насоса 3
FirstRun : BOOL := TRUE; // Флаг первого запуска
END_VAR

BEGIN
// Сброс команд запуска насосов
Pump1_StartCmd := FALSE;
Pump2_StartCmd := FALSE;
Pump3_StartCmd := FALSE;

// Отключение системы, если не в автоматическом режиме
IF NOT AutoMode THEN
Pump1_Status := 0; // Отключен
Pump2_Status := 0; // Отключен
Pump3_Status := 0; // Отключен
RETURN;
END_IF;

// Определение количества необходимых насосов
IF SystemRunReq_1 THEN
ActivePumpCount := 1;
ELSIF SystemRunReq_2 THEN
ActivePumpCount := 2;
ELSE
ActivePumpCount := 0;
END_IF;

// Логика охлаждения насосов
CoolingTimerPump1(IN := NOT Pump1_StartCmd, PT := CoolingTimeSetpoint);
CoolingTimerPump2(IN := NOT Pump2_StartCmd, PT := CoolingTimeSetpoint);
CoolingTimerPump3(IN := NOT Pump3_StartCmd, PT := CoolingTimeSetpoint);

// Логика чередования и запуска насосов
PumpsStarted := 0;

IF ActivePumpCount > 0 THEN
IF NOT CoolingTimerPump1.Q AND Pump1_Enable AND NOT Pump1_Alarm AND LastPumpIndex <> 1 THEN
Pump1_StartCmd := TRUE;
Pump1_Status := 3; // Пуск
LastPumpIndex := 1;
PumpsStarted := PumpsStarted + 1;
ELSIF NOT CoolingTimerPump2.Q AND Pump2_Enable AND NOT Pump2_Alarm AND LastPumpIndex <> 2 THEN
Pump2_StartCmd := TRUE;
Pump2_Status := 3; // Пуск
LastPumpIndex := 2;
PumpsStarted := PumpsStarted + 1;
ELSIF NOT CoolingTimerPump3.Q AND Pump3_Enable AND NOT Pump3_Alarm AND LastPumpIndex <> 3 THEN
Pump3_StartCmd := TRUE;
Pump3_Status := 3; // Пуск
LastPumpIndex := 3;
PumpsStarted := PumpsStarted + 1;
END_IF;
END_IF;

// Если требуется больше насосов, запускаем второй
IF ActivePumpCount = 2 AND PumpsStarted = 1 THEN
IF NOT CoolingTimerPump1.Q AND Pump1_Enable AND NOT Pump1_Alarm AND LastPumpIndex <> 1 THEN
Pump1_StartCmd := TRUE;
Pump1_Status := 3; // Пуск
LastPumpIndex := 1;
PumpsStarted := PumpsStarted + 1;
ELSIF NOT CoolingTimerPump2.Q AND Pump2_Enable AND NOT Pump2_Alarm AND LastPumpIndex <> 2 THEN
Pump2_StartCmd := TRUE;
Pump2_Status := 3; // Пуск
LastPumpIndex := 2;
PumpsStarted := PumpsStarted + 1;
ELSIF NOT CoolingTimerPump3.Q AND Pump3_Enable AND NOT Pump3_Alarm AND LastPumpIndex <> 3 THEN
Pump3_StartCmd := TRUE;
Pump3_Status := 3; // Пуск
LastPumpIndex := 3;
PumpsStarted := PumpsStarted + 1;
END_IF;
END_IF;

// Резервный насос
IF PumpsStarted < ActivePumpCount THEN
CASE ReservePumpSelection OF
1: IF Pump1_Enable AND NOT Pump1_Alarm THEN
Pump1_StartCmd := TRUE;
Pump1_Status := 3; // Пуск
END_IF;
2: IF Pump2_Enable AND NOT Pump2_Alarm THEN
Pump2_StartCmd := TRUE;
Pump2_Status := 3; // Пуск
END_IF;
3: IF Pump3_Enable AND NOT Pump3_Alarm THEN
Pump3_StartCmd := TRUE;
Pump3_Status := 3; // Пуск
END_IF;
END_CASE;
END_IF;

// Обновление статусов насосов
IF NOT Pump1_StartCmd THEN
Pump1_Status := 0; // Отключен
END_IF;
IF NOT Pump2_StartCmd THEN
Pump2_Status := 0; // Отключен
END_IF;
IF NOT Pump3_StartCmd THEN
Pump3_Status := 0; // Отключен
END_IF;
END_FUNCTION_BLOCK

K.K.
05.12.2024, 10:58
пытаюсь делать так, только вот чередование не работает, начинает по кругу пускать насосы

EFrol
05.12.2024, 12:03
пытаюсь делать так, только вот чередование не работает, начинает по кругу пускать насосы

В принципе, Вы начали всё правильно делать, но потом запутались из-за отсутствия оптимизации. Слишком много входных параметров. Надо несколько упростить.
Как Вы определяете, что насос в аварийном состоянии?
Может выкинем понятие "резервный" - пусть включается любой свободный, если требуется?

Входные:
Mode : udint; // 0 - ручное управление, 1 - авто, требуется 1 насос, 2 - авто, требуется 2 насоса
Pump1Run, Pump2Run, Pump3Run : bool; // Обратная связь с пускателем насоса
Pump1Start, Pump2Start, Pump3Start : bool; // В режиме 0 - Пуск/Стоп, в режиме > 0 - Сброс аварии
CoolTime : udint; // Время охлаждения при остановке

Локальные:
NextStart, NextStop : udint; // Указатель на следующий для старта, и на следующий для останова
Pump1TON, Pump2TON, Pump3TON : TON; // Таймеры охлаждения
Pump1Click, Pump2Click, Pump3Click : R_TRIG; // Ловить нажатия на PumpXStart

Выходные:
Pump1Mode, Pump2Mode. Pump3Mode : udint; // 0 - насос остановлен, 1 - насос запущен, 2 - насос в аварии, 3 - насос на охлаждении

melky
05.12.2024, 12:12
делал через маску бит в байте. Просто резервную единицу в байте кручу(верчу) :) Если надо, можно и две 1 крутить, и три 1.

з.ы. ROR, ROL тут нету, а то было бы проще.

Валенок
05.12.2024, 12:36
з.ы. ROR, ROL тут нету, а то было бы проще.
в КДС3 нету?

melky
05.12.2024, 12:38
Валенок блин, есть. Я не глядя почему-то про Овен Лоджик подумал.
На CDS 2.3 есть программа ротации для кондиционеров, но на CFC, переводить на ST не пробовал. И вообще, переведется ли?
в общем смысл у меня был простой. N общее количество, Р - резервное количество. Р поднимаем биты в 1 и крутим эти биты раз в Х часов. Если авария, уменьшаем Р на единицу.
Соответственно если Р всего одна единица, то она в 0 и запускаем ее. Если Р было 2, станет 1, а та, что 0 в запуск.

Ну тут без всяких массивов, структур и прочего. Кондеи тогда были тупые, просто питанием игрался.

Валенок
05.12.2024, 12:47
..
Ну тут без всяких массивов, структур и прочего.
Что могу сказать? Плохо))

melky
05.12.2024, 12:57
Валенок да там сам принцип на смещении бит :). Еще вот AI! делал классный макрос на 8-мь насосов (ну или оборудования любого), там несложной обвязкой можно было задавать количество, добавить аварии, сделать выбор какие должны быть в работе и т.д. Но блин макрос я перенести в CodeSys не смог. Из-за обратной связи в одном месте, на CDS она не хочет работать так, как предполагает макрос. Вот его бы адаптировать, он кучу подобных задач решает.

1exan
06.12.2024, 04:13
Валенок да там сам принцип на смещении бит :). Еще вот AI! делал классный макрос на 8-мь насосов (ну или оборудования любого), там несложной обвязкой можно было задавать количество, добавить аварии, сделать выбор какие должны быть в работе и т.д. Но блин макрос я перенести в CodeSys не смог. Из-за обратной связи в одном месте, на CDS она не хочет работать так, как предполагает макрос. Вот его бы адаптировать, он кучу подобных задач решает.

Если адаптировать практически "в лоб" - то вот:



FUNCTION_BLOCK Rotation
VAR_INPUT
dwAvailable : DWORD;
byNumDev: BYTE := 8;
byNeedWork : BYTE;
bRotate : BOOL;


END_VAR
VAR_OUTPUT
dwOut : DWORD;
byInWork : BYTE;
byNextStop : BYTE;
byNextStart : BYTE;
END_VAR
VAR
tx: TIME;
tn: TIME;


dwSelected : DWORD;
dwTempSelected : DWORD;
dwNextSel : DWORD;


ardwMoto : ARRAY [0..31] OF DWORD;


dwMaxMoto : DWORD;
dwMinMoto : DWORD;
dwTmpMoto : DWORD;


i : BYTE;


Q : BOOL;
init: BOOL;
bMemRotate : BOOL;
bSelected : BOOL;
END_VAR
VAR CONSTANT
uiMIN: DWORD := 2147483648;
uiMAX: DWORD := 2147483647;
END_VAR




tx := TIME();
IF NOT init THEN init := TRUE; tn := tx; END_IF;
IF tx - tn >= SEL(Q, t#1s, t#0s) THEN
tn := tn + SEL(Q, t#1s, t#0s);
Q := NOT Q;
END_IF;


dwTempSelected := dwSelected := dwNextSel AND dwAvailable;


byInWork := 0;
dwMaxMoto := 0;
dwMinMoto := uiMIN;
byNextStop := 0;
byNextStart := 0;
dwOut := 0;


IF byNumDev <= 0 THEN byNumDev := 1; END_IF;
IF byNumDev > 32 THEN byNumDev := 32; END_IF;


FOR i := 0 TO byNumDev - 1 DO
bSelected := dwTempSelected.0;
ardwMoto[i] := ardwMoto[i] + BOOL_TO_BYTE(Q AND bSelected);
byInWork := byInWork + BOOL_TO_BYTE(bSelected);


IF dwAvailable.0 THEN
IF bSelected THEN dwTmpMoto := uiMIN;
ELSE dwTmpMoto := 0;
END_IF;
ELSE
dwTmpMoto := uiMAX;
END_IF;
dwTmpMoto := dwTmpMoto OR ardwMoto[i];


IF dwTmpMoto > dwMaxMoto THEN
dwMaxMoto := dwTmpMoto;
byNextStop := i;
END_IF;


IF dwTmpMoto < dwMinMoto THEN
dwMinMoto := dwTmpMoto;
byNextStart := i;
END_IF;


dwTempSelected := SHR(dwTempSelected,1);
dwAvailable := SHR(dwAvailable,1);
END_FOR;




IF byNeedWork > byInWork OR (bRotate AND NOT bMemRotate) THEN dwNextSel := SHL(DWORD#1, byNextStart); END_IF;
bMemRotate := bRotate;


IF byInWork > byNeedWork THEN dwSelected := dwSelected AND (NOT SHL(DWORD#1, byNextStop)); END_IF;


dwOut := dwSelected;
dwNextSel := dwNextSel OR dwOut;

EFrol
06.12.2024, 08:16
Этих примеров море и даже без битовой маски https://owen.ru/forum/showthread.php?t=40336&p=448127&viewfull=1#post448127

melky
06.12.2024, 08:58
1exan это вы макрос AI! адаптировали?

1exan
06.12.2024, 09:13
1exan это вы макрос AI! адаптировали?

Если я правильно понял - про этот макрос (https://owen.ru/forum/showthread.php?t=10555&p=449492&viewfull=1#post449492) разговор

melky
06.12.2024, 09:19
1exan да, про этот. Он там вычислял время работы каждого насоса и запуск идет в очередь того, кто меньше всего наработал. В рамках например ротации кондиционеров не так существенно, типа не надо поддерживать давление или еще что-то из быстро меняющихся параметров. Но сам по себе макрос уникален. Вопрос только в обвязке для работы макроса. Уже ей надо делать то или иное.
Как-то делал пример обвязки, может где скрин сохранился, но сам пример похоже потерял со смертью винта.

In_Da_Cher_A
06.12.2024, 09:46
всё это типа ТЗ можно спокойно упростить
чисто по мне - вся эта заумь с усложнённой ротацией - это тупой откровенный бред, никакой технологической необходимости в таком изврате нет ни грамма,
ротация по времени - такая же шняга, актуально более менее только на насосах с баббитовым подшипником скольжения, современные шарики спокойно ходят годами, и параноидальное выравнивание с точностью до секунды наработки - никому ненужное, кроме программиста хехе ;), усложнение программы