Autotuning PID

The purpose of this article is in an open and accessible way to bring to-setters engineers SCADA systems an easy and simple way to create a functional unit capable of finding the best regulatory factors. It all started with the fact that I was tortured very badly performing PID regulators. The image and reputation of the programmer-adjuster that adjusts automation system often depends strongly on its technical ability for the selection of the coefficients P I D (this is usually not allocated time).

Since the process I have inert and slow rate in the I do not care. The basis of selection coefficients used methodology more-less with the analysis of the process variable curve. In other words, I created an algorithm that analyzes everything himself and understands where it is plentiful, but where it is not enough.
Honestly, I had to create algorithms for the implementation of this idea:
1. The simulator work process curve with a strong element of inertia component (ten heats the air in the duct)
2. The unit LAG – exponential smoother analog signal
3. The block computes, increasing or decreasing curve
4. Actually the unit Shrink ratios
5. Block transitions story storage (remembers 12 steps)
Everything works, although very long analyzes.

Here is the autotuning algorithm - the state machine

tuning PID















Here is the schedule at the beginning of the work (periodic oscillations are visible):

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















Schedule of selection coefficients (the beginning)

It looked like a graph after hours of work:

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















Schedule selection PID coefficients (a lot better)

Thus another graph looked after 20 minutes:

Schedule selection PID coefficients (almost normal)
Below is a function block code, if someone come in handy, I will be glad:

FUNCTION_BLOCK PID_Tuning
VAR_INPUT
PV: REAL;
SP: REAL;
END_VAR
VAR_OUTPUT
Delta_old: REAL; (* Stored value the height of the curve from the bottom to the top *)
Delta_I: REAL; (* The absolute difference of 5 minutes *)
Delta_SP: REAL; (* General delta curve in real time *)
Delta_SPH: REAL; (* Height value from the setpoint curve to the top curve *)
Delta_SPL: REAL; (* The height of the setting to the bottom curve curve *)
Hi_val: REAL; (* The maximum value of the function for a period of 5 minutes *)
Lo_val: REAL; (* Minimum value of the function for a period of 5 minutes *)
Hi_val_TMP: REAL; (* Time variable for vyschityvaniya maximum value function for a period of 5 minutes *)
Lo_val_TMP: REAL; (* Time variable for vyschityvaniya minimum functions for a period of 5 minutes *)
Hi_val_old: REAL; (* Stored value *)
Lo_val_old: REAL; (* Stored value *)

END_VAR
VAR_IN_OUT
P:REAL;
I:REAL;
D:REAL;
END_VAR
VAR

BLINK: BLINK; (* Will give a pulse every 5 minutes *)
R_TRIG: R_TRIG; (* * The leading edge of the trap)
F_TRIG: F_TRIG; (Trailing edge * * Trap)
TON: TON; (**)
State, State1, State2, State3, State4, State5, State6, State7, State8, State9, State10, State11, State12: INT; (* Automatic * switch states)
Count: INT;
State_mem: State_mem;
Diff_SP: REAL; (* The difference between Delta_SPH and Delta_SPL *)
END_VAR

BLINK (ENABLE: = 1, TIMELOW: = T # 5m, TIMEHIGH: = T # 100ms, OUT =>);
F_TRIG (CLK: = BLINK.OUT, Q =>); (* When the meander drops, the unit will make the pulse start of the cycle *)
R_TRIG (CLK: = BLINK.OUT, Q =>); (* When a square wave rises, the unit will make the pulse end of the cycle *)

IF F_TRIG.Q THEN Hi_val_TMP: = PV; Lo_val_TMP: = PV; END_IF; (* Remember the values ​​of the variables at the beginning of the cycle *)
IF R_TRIG.Q THEN Hi_val_old: = Hi_val; Lo_val_old: = Lo_val; END_IF; (* Remember the values ​​from the previous cycle *)

IF NOT BLINK.OUT THEN
IF PV> Hi val TMP THEN Hi Val: = PV; Hi_val_TMP: = PV; END_IF; (* As soon as the value of the curve in the time period BLINK will be more, it will be remembered *)
IF PV <Lo_val_TMP THEN Lo_val: = PV; Lo_val_TMP: = PV; END_IF; (* As soon as the value of the curve in the time period BLINK will be more, it will be remembered *)
END_IF;
Delta_I: = ABS (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; (* Compute undershoot or overshoot, we have *)
IF Hi_val_TMP> SP AND SP> Lo_val_TMP THEN Delta_SP: = Delta_SPH + Delta_SPL; ELSE Delta_SP: = 5.5; END_IF;
Diff_SP: = ABS ((Delta_SPH – SP) – (SP – Delta_SPL));
CASE State OF
0: (. * Initialize We collect data about the process *)
TON (IN: = TRUE, PT: = T # 2m);
IF TON.Q THEN TON (IN: = FALSE); State: = 1; END_IF;
1: (* Decide what to change P or I *)
IF Delta_I <= 1.5 AND Diff_SP <0.5 THEN State: = 2; END_IF; (* It was decided that the integral gain is already correct *)
IF Delta_I> 1.5 THEN State: = 5; END_IF; (* It was decided that that the integral gain is not correct *)

2: (* P rating overshoot or failure to settle *)
IF Delta SPH> Delta SPL THEN State: = 3; END_IF; (* Means there is overshoot *)
IF Delta_SPH <Delta_SPL THEN State: = 4; END_IF; (* Means there is undershoot *)
3: (* P * overshoot)
IF R_TRIG.Q AND P> 0.2 THEN P: = P – 0.1; Delta_old: = Delta_I; Count: = Count + 1; END_IF;
IF F_TRIG.Q THEN State: = 10; END_IF;
4: (* P * undershoot)
IF R_TRIG.Q THEN P: = P + 0.1; Delta_old: = Delta_I; Count: = Count + 1; END_IF;
IF F_TRIG.Q THEN State: = 11; END_IF;
10: (* P Compare the total delta curve of the old and new overshoot *)
IF Delta_old> Delta_I THEN State: = 3; END_IF; (* means improved, repeat *)
IF Delta_old <Delta_I THEN State: = 4; END_IF; (* uhuchshilos So, repeat *)
IF Count = 3 THEN Count: = 0; State: = 5; END_IF; (* No need to endlessly change the P *)
IF Delta_I <1.0 THEN State: = 1; END_IF; (* So ended nakstroyka *)
11: (* P Compare the total delta of the old and the new curve *)
IF Delta_old> Delta_I THEN State: = 4; END_IF; (* means improved, repeat *)
IF Delta_old <Delta_I THEN State: = 3; END_IF; (* uhuchshilos So, repeat *)
IF Count = 3 THEN Count: = 0; State: = 5; END_IF; (* No need to endlessly change the P *)
IF Delta_I <1.0 THEN State: = 1; END_IF; (* So ended nakstroyka *)
5: (* I have periodic oscillations, remember delta_I_old *)
State: = 6;
6: (* I add to the koevitsientu + 0.1 *)
IF R_TRIG.Q THEN I: = I + 1.0; Delta_old: = Delta_I; Count: = Count + 1; END_IF;
IF F_TRIG.Q THEN State: = 7; END_IF; (* * Go to comparison)
7: (* I compare the old and the new delta *)
IF Delta_old> Delta_I THEN State: = 6; END_IF; (* means improved, repeat *)

IF Delta_old <Delta_I THE State: = 8; ENDIF; (* means deteriorated, repeat *)
IF Count = 4 THEN Count: = 0; State: = 2; END_IF; (* No need to endlessly change the I *)
IF Delta_I <1.0 THEN State: = 1; END_IF; (* So ended nakstroyka *)
8: (* I diminish koevitsient – 0.1 *)
IF R_TRIG.Q THEN I: = I – 1.0; Delta_old: = Delta_I; Count: = Count + 1; END_IF;
IF F_TRIG.Q THEN State: = 9; END_IF; (* * Go to comparison)
9: (* I compare the old and the new delta *)
IF Delta_old> Delta_I THEN State: = 8; END_IF; (* means improved, repeat *)
IF Delta_old <Delta_I THEN State: = 6; END_IF; (* uhuchshilos So, repeat *)
IF Count = 4 THEN Count: = 0; State: = 2; END_IF; (* No need to endlessly change the I *)
IF Delta_I <1.0 THEN State: = 1; END_IF; (* So ended nakstroyka *)

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 );

Russian version


Feedback

Be the first to comment

You comment add


Back to the list