Получилось быстро набирать давление и выставляться до тысячных бара, что важно для моего запорно-регулирующего гидропневматического клапана. В ПИД регуляторе выставил Kp в 100, остальные коэффициенты в ноль и на выходе регулятора поставил макрос усиливающий сигнал, т.е. получился П-регулятор с нелинейным усилением на финише. Макрос:
FUNCTION_BLOCK FB_SplitSignal2
// ================================================== ==========================
// Разделение биполярного сигнала ПИД на два однополярных
// С УМНЫМ УСИЛЕНИЕМ МАЛОГО СИГНАЛА (ДЛЯ НАПОЛНЕНИЯ И СБРОСА)
// ================================================== ==========================
VAR_INPUT
PID_Signal : REAL; // Вход от ПИД (-100...+100%)
DeadZone : REAL := 0.3; // Зона нечувствительности (чтобы не висело)
BoostThreshold : REAL := 10.0; // Сигнал меньше этого (%) — усиливаем
BoostFactor : REAL := 2.5; // Во сколько раз усиливаем
MaxBoost : REAL := 15.0; // Потолок усиленного сигнала
Hysteresis : REAL := 0.5; // Гистерезис для плавности
END_VAR
VAR_OUTPUT
Out_Napoln : REAL; // Сигнал для наполнения (0-100%)
Out_Sbros : REAL; // Сигнал для сброса (0-100%)
State : UDINT; // 0-нет, 1-наполнение, 2-сброс
END_VAR
VAR
RawSignal : REAL; // Сырой сигнал с ПИД
ErrorAbs : REAL; // Модуль сигнала
WorkSignal : REAL; // Обработанный сигнал (уже с усилением)
BoostActive : BOOL := FALSE; // Флаг усиления
CurrentDeadZone : REAL; // Текущая мертвая зона с гистерезисом
END_VAR
// ----------------------------------------------------------------------------
// 1. БЕРЕМ СИГНАЛ И МОДУЛЬ
// ----------------------------------------------------------------------------
RawSignal := PID_Signal;
ErrorAbs := ABS(RawSignal);
// ----------------------------------------------------------------------------
// 2. ЛОГИКА УСИЛЕНИЯ (работает одинаково для + и -)
// ----------------------------------------------------------------------------
// Определяем мертвую зону с гистерезисом
IF BoostActive THEN
CurrentDeadZone := DeadZone; // Если усиление активно — выключаем при DeadZone
ELSE
CurrentDeadZone := DeadZone + Hysteresis; // Если не активно — включаем позже
END_IF;
// --- Расчет усиления ---
IF ErrorAbs > CurrentDeadZone AND ErrorAbs < BoostThreshold THEN
// Сигнал маленький -> УСИЛИВАЕМ
WorkSignal := RawSignal * BoostFactor;
BoostActive := TRUE;
// Ограничиваем усиленный сигнал (чтобы не перелететь)
IF WorkSignal > MaxBoost THEN
WorkSignal := MaxBoost;
ELSIF WorkSignal < -MaxBoost THEN
WorkSignal := -MaxBoost;
END_IF;
ELSIF ErrorAbs >= BoostThreshold THEN
// Сигнал большой -> РАБОТАЕМ КАК ОБЫЧНО
WorkSignal := RawSignal;
BoostActive := FALSE;
ELSE
// Сигнал меньше DeadZone -> ВЫКЛЮЧАЕМ ВСЁ
WorkSignal := 0.0;
BoostActive := FALSE;
END_IF;
// ----------------------------------------------------------------------------
// 3. РАЗДЕЛЕНИЕ НА ДВА КАНАЛА (СИММЕТРИЧНО)
// ----------------------------------------------------------------------------
IF WorkSignal > 0 THEN
// Положительный сигнал -> НАПОЛНЕНИЕ
Out_Napoln := WorkSignal;
Out_Sbros := 0.0;
State := 1;
ELSIF WorkSignal < 0 THEN
// Отрицательный сигнал -> СБРОС (инвертируем, чтобы было положительное число)
Out_Napoln := 0.0;
Out_Sbros := -WorkSignal; // <--- ВОТ ЗДЕСЬ БЫЛА ОШИБКА, ТЕПЕРЬ ИСПРАВЛЕНО!
State := 2;
ELSE
// Сигнал равен 0 -> ВСЁ ВЫКЛЮЧЕНО
Out_Napoln := 0.0;
Out_Sbros := 0.0;
State := 0;
END_IF;
// ----------------------------------------------------------------------------
// 4. ЗАЩИТА ОТ ВЫХОДА ЗА 100% (НА ВСЯКИЙ СЛУЧАЙ)
// ----------------------------------------------------------------------------
IF Out_Napoln > 100.0 THEN Out_Napoln := 100.0; END_IF;
IF Out_Sbros > 100.0 THEN Out_Sbros := 100.0; END_IF;
IF Out_Napoln < 0.0 THEN Out_Napoln := 0.0; END_IF;
IF Out_Sbros < 0.0 THEN Out_Sbros := 0.0; END_IF;
END_FUNCTION_BLOCK
Коэффициенты макроса конкретно у меня такие:
DeadZone = 0.3
BoostThreshold = 30
BoostFactor = 8
MaxBoost = 50
Hysteresis = 0.5Снимок экрана 2026-06-18 102331.png


Ответить с цитированием