Автоматическая настройка ПИД

Целью данной статьи является в открытой и доступной форме донести до инженеров-наладчиков систем АСУ ТП простой и незамысловатый способ создания функционального блока, способного находить оптимальные коэффициенты регулирования. Все началось с того, что совсем меня замучили плохо работающие ПИД-регуляторы. Имидж и репутация программиста-наладчика, который настраивает систему АСУ, подчас сильно зависит от его технических способностей по подбору коэффициентов PID (время на это обычно не отводится).

Так как процесс у меня инертный и медленный, коэффициент D меня вообще не интересовал. За основу подбора коэффициентов использовал методологию "больше-меньше" с анализом кривой переменной процесса. Иными словами, создал алгоритм, который сам все анализирует и понимает, где ему много, а где мало.

Способ создания функционального блока, способного находить оптимальные коэффициенты регулирования ПИД

Если честно, то для реализации данной идеи пришлось создать алгоритмы:

1. Имитатор работы кривой процесса с элементом сильного инерционного звена (тен греет воздух в вентиляционном канале)

2. Блок LAG – экспоненциальный сглаживатель аналогового сигнала

3. Блок, вычисляющий, растет кривая или убывает

4. Собственно сам блок автоподбора коэффициентов

5. Блок запоминающий историю переходов (помнящий 12 шагов)

Все работает, правда очень долго анализирует.

Так выглядит алгоритм автотюнинга - автомат состояний















Так выглядит график в начале работы (видны периодические колебания)

График подбора коэффициентов















График подбора коэффициентов (начало) .Так выглядел график после часа работы.

График подбора ПИД коэффициентов















График подбора ПИД коэффициентов (уже намного лучше)

Так выглядел график еще через 20 минут:

График подбора коэффициентов ПИД















График подбора коэффициентов ПИД (уже почти норма)

unnamed










Совсем норма)))

Ниже приведен код функционального блока, если кому-нибудь пригодится, буду рад:

FUNCTION_BLOCK PID_Tuning
VAR_INPUT
PV:REAL;
SP:REAL;
END_VAR
VAR_OUTPUT
Delta_old:REAL; (*запомненное значение высоты кривой от самого низа до самого верха*)
Delta_I:REAL;(*Абсолютная разница за 5 мин*)
Delta_SP:REAL; (*Общая дельта кривой в реальном времени*)
Delta_SPH:REAL; (*значение высоты кривой от уставки до верха кривой*)
Delta_SPL:REAL; (*значение высоты кривой от уставки до низа кривой*)
Hi_val:REAL; (*максимальное значение функции за период 5 минут*)
Lo_val:REAL; (*минимальное значение функции за период 5 минут*)
Hi_val_TMP:REAL; (*временная переменная для высчитывания максимального значения функции за период 5 минут*)
Lo_val_TMP:REAL; (*временная переменная для высчитывания минимального значения функции за период 5 минут*)
Hi_val_old:REAL; (*запомненное значение*)
Lo_val_old:REAL; (*запомненное значение*)
State:INT; (*Сосотояние процесса*)
END_VAR
VAR_IN_OUT
P:REAL;
I:REAL;
D:REAL;
END_VAR
VAR
BLINK: BLINK; (*будет давать импульс каждые 5 минут*)
F_TRIG,F_TRIG1:F_TRIG; (**)
TON,TON1:TON;(**)
Count:INT;
State1,State2,State3,State4,State5,State6,State7,State8,State9,State10,State11,State12:INT; (*Переключатель автомата состояний*)
State_mem:State_mem;
Diff_SP:REAL;(*Разница между Delta_SPH и Delta_SPL*)
STBL:STBL;
INC1,DEC1,STBL1:BOOL;
TIME_ET:TIME;
TIME_UP:TIME;
TIME_DOWN:TIME;
TIME_UP_DOWN:TIME:=T#6m;
END_VAR
(* @END_DECLARATION := ‘0’ *)
BLINK(ENABLE:=1 , TIMELOW:=TIME_UP_DOWN , TIMEHIGH:=T#100ms , OUT=> );
TON1(IN:=NOT TON1.Q,PT:=T#24h); TIME_ET:=TON1.ET;
STBL(
PV:=PV ,
PT:= T#100ms,
INC=>INC1 ,
DEC=>DEC1 ,
STBL=>STBL1 );

F_TRIG(CLK:=INC1);(*Когда закончится подъем *)
F_TRIG1(CLK:ЮC1 );(*Когда закончится убывание*)

IF F_TRIG.Q THEN Hi_val_TMP:=PV; Lo_val_TMP:=PV; TIME_UP:=TIME_ET; END_IF;(*Запоминаем первое значение, наверху*)
IF F_TRIG1.Q THEN Hi_val_old:=Hi_val; Lo_val_old:=Lo_val; TIME_DOWN:=TIME_ET; TIME_UP_DOWN:=TIME_DOWN – TIME_UP; END_IF;(*Запоминаем следующее значение, внизу*)

IF NOT BLINK.OUT THEN
IF PV > Hi_val_TMP THEN Hi_val:=PV; Hi_val_TMP:=PV; END_IF;(*Как только значение кривой в период времени BLINK станет больше, оно запомнится*)
IF PV < Lo_val_TMP THEN Lo_val:=PV; Lo_val_TMP:=PV; END_IF;(*Как только значение кривой в период времени BLINK станет больше, оно запомнится*)
END_IF;
Delta_I:«S(Hi_val – Lo_val);

IF SP<Hi_val_TMP THEN Delta_SPH:=Hi_val – SP; END_IF;
IF SP>Lo_val_TMP THEN Delta_SPL:=SP – Lo_val; END_IF;(*Вычисляем недорегулирование или перерегулирование мы имеем*)
IF Hi_val_TMP>SP AND SP>Lo_val_TMP THEN Delta_SP:Юlta_SPH + Delta_SPL; ELSE Delta_SP:=5.5; END_IF;
Diff_SP:«S((Delta_SPH – SP) – (SP – Delta_SPL));
CASE State OF
0:(*Инициализация. Набираем данные о процессе*)
TON(IN:ЮC1,PT:=T#10s);
IF TON.Q THEN TON(IN:ъLSE); State:=1; END_IF;
1:(*Принимаем решение что меняем P или I*)
IF Delta_I<=1.5 AND Diff_SP<0.5 THEN State:=2; END_IF; (*Принято решение, что интегральный коэффициент уже корректен*)
IF Delta_I>1.5 THEN State:=5; END_IF; (*Принято решение, что что интегральный коэффициент не корректен*)
2:(*P оценка перерегулирования или недорегулирования*)
IF Delta_SPH>Delta_SPL THEN State:=3; END_IF; (*Значит имеет место перерегулирование*)
IF Delta_SPH<Delta_SPL THEN State:=4; END_IF; (*Значит имеет место недорегулирование*)
3:(*P перерегулирование *)
IF F_TRIG.Q AND P>0.2 THEN P:=P – 0.1; Delta_old:Юlta_I; Count:=Count+1; END_IF;
IF F_TRIG1.Q THEN State:; END_IF;
4:(*P недорегулирование *)
IF F_TRIG.Q THEN P:=P + 0.1; Delta_old:Юlta_I; Count:=Count+1; END_IF;
IF F_TRIG1.Q THEN State:; END_IF;
10:(*P сравнение общей дельты кривой старой и новой перерегулирование*)
IF Delta_old > Delta_I THEN State:=3; END_IF;(*Значит улучшилось, повторяем*)
IF Delta_old < Delta_I THEN State:=4; END_IF;(*Значит ухучшилось, повторяем*)
IF Count =3 THEN Count :=0; State:=5; END_IF;(*Не нужно до бесконечности изменять P*)
IF Delta_I<1.0 THEN State:=1; END_IF;(*Значит накстройка закончилась*)
11:(*P сравнение общей дельты кривой старой и новой*)
IF Delta_old > Delta_I THEN State:=4; END_IF;(*Значит улучшилось, повторяем*)
IF Delta_old < Delta_I THEN State:=3; END_IF;(*Значит ухучшилось, повторяем*)
IF Count =3 THEN Count :=0; State:=5; END_IF;(*Не нужно до бесконечности изменять P*)
IF Delta_I<1.0 THEN State:=1; END_IF;(*Значит накстройка закончилась*)
5:(*I есть периодические колебания, запоминаем delta_I_old*)
I:=I + 1.0; Delta_old:Юlta_I; Count:=Count+1;
State:=6;
6:(*I прибавляем к коэвициенту + 0,1 *)
IF F_TRIG.Q THEN I:=I + 1.0; Delta_old:Юlta_I; Count:=Count+1; END_IF;
IF F_TRIG1.Q THEN State:=7; END_IF;(*Переходим к сравнению*)
7:(*I сравниваем старую дельту и новую*)
IF Delta_old > Delta_I THEN State:=6; END_IF;(*Значит улучшилось, повторяем*)
IF Delta_old < Delta_I THEN State:=8; END_IF;(*Значит ухучшилось, повторяем*)
IF Count =4 THEN Count :=0; State:=2; END_IF;(*Не нужно до бесконечности изменять I*)
IF Delta_I<1.0 THEN State:=1; END_IF;(*Значит накстройка закончилась*)
8:(*I убавляем коэвициент – 0,1*)
IF F_TRIG.Q THEN I:=I – 1.0; Delta_old:Юlta_I; Count:=Count+1; END_IF;
IF F_TRIG1.Q THEN State:=9; END_IF;(*Переходим к сравнению*)
9:(*I сравниваем старую дельту и новую*)
IF Delta_old > Delta_I THEN State:=8; END_IF;(*Значит улучшилось, повторяем*)
IF Delta_old < Delta_I THEN State:=6; END_IF;(*Значит ухучшилось, повторяем*)
IF Count =4 THEN Count :=0; State:=2; END_IF;(*Не нужно до бесконечности изменять I*)
IF Delta_I<1.0 THEN State:=1; END_IF;(*Значит накстройка закончилась*)
END_CASE;
State_mem(
IN:=State ,
IN1=>State1 ,
IN2=>State2 ,
IN3=>State3 ,
IN4=>State4 ,
IN5=>State5 ,
IN6=>State6 ,
IN7=>State7 ,
IN8=>State8 ,
IN9=>State9 ,
IN10=>State10 ,
IN11=>State11 ,
IN12=>State12 );
END_FUNCTION_BLOCK

#автоматическаянастройкапид, #настройкапид, #PID, #настройка, #подборкоэффициентов, #автоматсостояний, #асутп

English version
Если Вы не нашли то, что искали, сообщите об этом в комментарии

Оставьте первый комментарий

Ваш комментарий добавлен


Возврат к списку