Weil mich das doch interessierte ob das funktioniert, habe ich gerade mal einen Versuch gemacht und die Skizze in Programmcode umgesetzt. Vom Prinzip her findet das Programm den optimalen Punkt
Ich habe zum Test eine Regelstreckensimulation programmiert deren Form einer Gaußschen Glockenkurve entspricht. Der Punkt an dem die Stellgröße einen maximalen Istwert annimmt kann man einstellen.
Dieses sind die Kurvenverläufe bei zwei verschiedenen Werten:
Maximaler Wert bei 50% Stellgröße:

Maximaler Wert bei 70% Stellgröße:
Die Maximumerkennung habe ich in Form einer Schrittkette programmiert. Es fehlen natürlich noch ein paar Dinge wie Grenzwertüberprüfungen, Zeitumstellungen nur bei Inaktivität ändern etc., aber das Prinzip sollte daraus hervorgehen:
Code:
FUNCTION_BLOCK "Extremwertregler"
VAR_INPUT
Run : BOOL; // Funktion aktivieren
X : REAL; // Istwert
T_Wart : TIME := T#500ms; // Wartezeit zwischen Suchfunktionen
T_Takt : TIME := T#500ms; // Dauer für welche die Stellgröße nach oben/unten verschoben wird
T_Paus : TIME := T#500ms; // Wartezeit zwischen oben/unten
Gain : REAL := 0.1; // Verstellverstärkung
Y_Diff : REAL := 1.0; // Differenz um die die Stellgröße verändert wird
Y_Start : REAL := 30.0; // Startwert für Y
END_VAR
VAR_OUTPUT
Y : REAL; // Stellgröße
Y_Basis : REAL; // Bezugs-Stellgröße
X_Basis : REAL; // Bezugs-Istwert
Sum_UpDn : REAL;
State : INT; // Schrittkette
END_VAR
VAR
Timer1 : TON;
END_VAR
BEGIN
IF NOT Run THEN
State := 0;
END_IF;
(* Schrittkette Suchlauf *)
CASE State OF
0: (* Initialisieren *)
Y := Y_Start;
Y_Basis := Y;
X_Basis := X;
Sum_UpDn := 0.0;
Timer1.IN := false;
IF Run THEN
Timer1.PT := T_Takt;
State := 1;
END_IF;
1: (* Stellgröße nach oben fahren *)
Y := Y_Basis + Y_Diff;
IF Timer1.IN THEN
Sum_UpDn := Sum_UpDn + (Y - Y_Basis) * (X - X_Basis);
END_IF;
Timer1.IN := true;
IF Timer1.Q THEN
Timer1.IN := false;
Timer1.PT := T_Paus;
State := 2;
END_IF;
2: (* Pausenzeit *)
Y := Y_Basis;
Timer1.IN := true;
IF Timer1.Q THEN
Timer1.IN := false;
Timer1.PT := T_Takt;
State := 3;
END_IF;
3: (* Stellgröße nach unten fahren *)
Y := Y_Basis - Y_Diff;
IF Timer1.IN THEN
Sum_UpDn := Sum_UpDn + (Y - Y_Basis) * (X - X_Basis);
END_IF;
Timer1.IN := true;
IF Timer1.Q THEN
(* neue Basisstellgröße bestimmen *)
//Y_Basis := Y_Basis + Sum_UpDn * Gain; // << funktioniert nicht so gut
IF Sum_UpDn >= 0 THEN
Y_Basis := Y_Basis + Y_Diff * Gain;
ELSE
Y_Basis := Y_Basis - Y_Diff * Gain;
END_IF;
Y := Y_Basis;
Sum_UpDn := 0.0;
Timer1.IN := false;
Timer1.PT := T_Wart;
State := 4;
END_IF;
4: (* Wartezeit für nächsten Durchlauf *)
Timer1.IN := true;
IF Timer1.Q THEN
X_Basis := X;
Timer1.IN := false;
Timer1.PT := T_Takt;
State := 1;
END_IF;
END_CASE;
Timer1();
END_FUNCTION_BLOCK
Wenn in der Regelstrecke eine Zeitkonstante vorhanden ist, muss zwischen den Prüf-Schritten gewartet werden bis sich diese wieder eingeschwungen hat.
Was nicht so gut funktionierte ist die Basis-Stellgröße anhand der Stärke der Reaktion zu verändern (auskommentierter Teil). Es wird jetzt um einen konstanten Wert nach oben/unten verstellt. Dadurch dauert das einregeln relativ lange, da müsste man noch eine bessere Lösung finden.
Und hier mal eine Trendkurve wenn das Programm arbeitet:

Die grüne Linie zeigt die optimale Stellgröße an, das ist der Wert der an der Streckensimulation eingestellt wird.
Wie man sieht wird die Regelrichtung automatisch richtig erkannt. Das Verfahren sollte durch die Integration auch recht unempfindlich gegenüber Messwert-Störungen sein.