PID autotune - la méthode du relais

Partie du forum pour tout ce qui concerne la partie régulation dans l'industrie. Forum, conseil, astuce et entraide sur les calculs de régulateurs, les différents choix de P, PI ou PID, leur utilisation et la résolution de problèmes..
Avatar de l’utilisateur
Cyril93
Maître du binaire
Maître du binaire
Messages : 484
Inscription : 29 oct. 2015, 14:22
Localisation : IDF

Re: PID autotune - la méthode du relais

Message par Cyril93 »

Salut damiette,

Le code SCL pour Step7 V5.5 si tu veux intégrer dans le TIA il y a quelques modifs à prévoir (voir les anciens post)


====================================================================================================
Le FB de mesure de fréquence
====================================================================================================

Code : Tout sélectionner

FUNCTION_BLOCK FREQ_MEASURE
TITLE = 'FREQ_MEASURE'
//
//Adaptation du FB FREQ_MEASURE de la lib Util de 3S.
//La mesure ne doit pas dépasser 24D20H31M23S647MS car le débordement de l'horloge système n'est pas testé. 
//
VERSION : '1.0'
AUTHOR  : Cyril93
NAME    : FREQ_MEA
FAMILY  : MESURE
//KNOW_HOW_PROTECT

VAR_INPUT
    IN: BOOL;                              (* input signal *)
    PERIODS: INT :=1;                  (* out is the average frequency during PERIODS (number of periods) between 1 to 10 *)
    RESET: BOOL;                        (* reset measurement *)
END_VAR
VAR_OUTPUT
    OUT: REAL;                           (* frequency [Hz]*)
    VALID: BOOL;                        (* FALSE: not yet PERIODS measurements done OR time distance between two rising edges > 3*OUT *)
END_VAR
VAR
    OLDIN: BOOL;
    INIT: BOOL;
    OLDT: TIME;
    DIFF: DINT;
    ADIFF: ARRAY[0..9] OF DINT;
    V: INT;
    B:INT;
    I: INT;            
END_VAR


BEGIN

IF RESET THEN
    B:=0;
    V:=0;
    INIT:=FALSE;
    VALID:=FALSE;
    OUT:=0;
    RETURN;
END_IF;

IF IN AND NOT OLDIN THEN    (*rising edge *)
    IF INIT THEN
        DIFF := TIME_TO_DINT(TIME_TCK()-OLDT);
        IF Diff > 0 THEN
            ADIFF[B] := DIFF;
            B := (B+1) MOD PERIODS;
            V:=MIN(IN1:=V+1, IN2:=PERIODS);
            IF V=PERIODS THEN
                OUT := 0;
                FOR I:=0 TO PERIODS-1 DO
                    OUT := OUT + ADIFF[I];
                END_FOR;
                OUT := 1000.0 * PERIODS / OUT;
                VALID:=TRUE;
            ELSE
                VALID:=FALSE;
            END_IF;
        END_IF;
    END_IF;
    INIT := TRUE;
    OLDT := TIME_TCK();
ELSIF INIT AND VALID AND TIME_TO_DINT(TIME_TCK()-OLDT) > 3000*OUT THEN
    VALID:=FALSE;
    V:=0;
    B:=0;
END_IF;

OLDIN:=IN;

END_FUNCTION_BLOCK

====================================================================================================
Le FB de la méthode du relais (c'est ce bloc qu'il faut appelé dans le programme)
====================================================================================================

Code : Tout sélectionner

FUNCTION_BLOCK CALC_PID
TITLE = 'CALC_PID'
//
//Auto-réglage des constantes PID par la méthode du relais de Astrom-Hagglund.
//
VERSION : '1.1'
AUTHOR  : Cyril93
NAME    : CALC_PID
FAMILY  : REGUL
//KNOW_HOW_PROTECT

VAR_INPUT
    bEN : BOOL;                        (* Démarre la séquence par le front montant *)
    rSP : REAL;                         (* Point de consigne *)
    rPV: REAL;                          (* Mesure process *)
    iCY: INT:=2;                        (* Nombre de cycle du réglage automatique (minimum 2)*)
    bRST: BOOL;                       (* Remise à zéro *)
    rL_VAL: REAL;                     (* Valeur basse pour la sortie *)
    rH_VAL: REAL;                    (* Valeur haute pour la sortie *)
    iPERF: INT;                         (* Performances des constantes PID : 0=rapides, 1=normales, 2=lentes *)
END_VAR
VAR_OUTPUT
    bEND: BOOL;                        (* Fin séquence *)
    rSQ_VAL: REAL;                    (* Sortie onde carrée pour l'exitation du process *)
    rKP: REAL;                           (* Gain proportionnel *)
    rTN: REAL;                           (* Temps d'intégrale en s *)
    rTV: REAL;                           (* Temps de dérivée en s *)
END_VAR
VAR
    PU: REAL;                          (* Gain ultime *)
    TU: REAL;                          (* Période ultime *)
    b: REAL;                           (* Amplitude de PV *)
    a: REAL;                           (* Amplitude de SQ_VAL *)
  
    Tu_Freq: FREQ_MEASURE;          
    b_Min: REAL;
    b_Max: REAL;
    EdgeEnd: BOOL;
    FlagEnd: BOOL;
    End: BOOL;
    
    status: INT;
    cpt: INT;
    old_bEN: BOOL;
    RstFreq: BOOL;
    PerFreq: INT;
            
END_VAR
CONST
    PI := REAL#3.14159265358979323846264338327950288;
END_CONST


BEGIN
(*** Séquence autotune ***)

IF bRST THEN
    status:=0;
END_IF;

CASE status OF
0: (* Attente Démarrage autotune *)
    rSQ_VAL:=rL_VAL;
    IF bEN AND NOT old_bEN THEN
        RstFreq:=TRUE;
        b_Min:=3.402823E+38 ;
        b_Max:=-3.402823E+38 ;
        rKP:=0;
        rTN:=0;
        rTV:=0;
        cpt:=0;
        bEND:=FALSE;
        status:=1;
        End:=FALSE;
    END_IF;

1: (* Onde carrée haute *)
    rSQ_VAL:=rH_VAL;
    IF rPV > rSP THEN
        rSQ_VAL:=rL_VAL;
        cpt:=cpt+1;
        status:=2;
    END_IF;

2: (* Onde carrée basse et fin de séquence *)
    IF rPV < rSP THEN
        cpt:=cpt+1;
        IF cpt >= iCY THEN
            status:=1;
            End:=TRUE;
        ELSE
            status:=1;
        END_IF;
    END_IF;

END_CASE;

old_bEN:=bEN;

(*** Calcul des paramètres ***)

(* min/max et freq de PV  *)

IF rPV<b_Min THEN
    b_Min:=rPV;
END_IF;
IF rPV>b_Max THEN
    b_Max:=rPV;
END_IF;

(* Nombre de période pour le bloc FREQ_MEASURE *)
PerFreq:=(iCY/2)-1;
PerFreq:=LIMIT(MN:=1, IN:=PerFreq, MX:=10);

Tu_Freq(
    IN := rPV > rSP ,
    PERIODS :=PerFreq  ,
    RESET := RstFreq); 
    
RstFreq:=FALSE;  

(* Calcul sur front montant de End et valeur de sortie de FREQ_MEASURE ok *)
EdgeEnd:= End AND Tu_Freq.OUT <> 0 AND NOT FlagEnd; 
FlagEnd:= End AND Tu_Freq.OUT <> 0; 

IF EdgeEnd THEN
    
    (* amp de SQ_VAL *)
    a:=rH_VAL-rL_VAL;

    (* amp de PV *)
    b:=b_Max-b_Min;

    (* Gain et période ultime *)
    PU:=(4 * b)/(PI * a);
    TU:=1/Tu_Freq.OUT;

    (* Valeur PID *)
    CASE iPERF OF
    0:(* Performances rapides *)
        rKP:= 0.6 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;

    1:(* Performances normales *)
        rKP:= 0.25 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;

    2:(* Performances lentes *)
        rKP:= 0.15 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;
    END_CASE;
    
    bEND:=TRUE;
    status:=0;

END_IF;

END_FUNCTION_BLOCK

Avatar de l’utilisateur
Cyril93
Maître du binaire
Maître du binaire
Messages : 484
Inscription : 29 oct. 2015, 14:22
Localisation : IDF

Re: PID autotune - la méthode du relais

Message par Cyril93 »

et le DFB pour unity pro, je l'ai fait avec la V5 mais ça doit être compatible avec les autres versions

nota : copier coller dans un éditeur de texte et sauver en .XDB puis importer les DFB dans Unity Pro

Code : Tout sélectionner

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FBExchangeFile>
	<fileHeader company="Schneider Automation" product="Unity Pro XL V5.0 - 100504A" dateTime="date_and_time#2016-5-4-10:34:10" content="Fichier source bloc fonctions" DTDVersion="41"></fileHeader>
	<contentHeader name="Projet" version="0.0.95" dateTime="date_and_time#2016-5-4-11:33:51"></contentHeader>
	<FBSource nameOfFBType="CALC_PID" version="0.02" dateTime="dt#2016-05-04-10:33:47">
		<comment>Auto-réglage des constantes PID par la méthode du relais de Astrom-Hagglund.</comment>
		<inputParameters>
			<variables name="bEN" typeName="BOOL">
				<comment>Démarre la séquence par le front montant</comment>
				<attribute name="PositionPin" value="1"></attribute>
			</variables>
			<variables name="rSP" typeName="REAL">
				<comment>Point de consigne</comment>
				<attribute name="PositionPin" value="2"></attribute>
			</variables>
			<variables name="rPV" typeName="REAL">
				<comment>Mesure process</comment>
				<attribute name="PositionPin" value="3"></attribute>
			</variables>
			<variables name="iCY" typeName="INT">
				<comment>Nombre de cycle du réglage automatique</comment>
				<attribute name="PositionPin" value="4"></attribute>
			</variables>
			<variables name="bRST" typeName="BOOL">
				<comment>Remise à zéro</comment>
				<attribute name="PositionPin" value="5"></attribute>
			</variables>
			<variables name="rL_VAL" typeName="REAL">
				<comment>Valeur basse pour la sortie</comment>
				<attribute name="PositionPin" value="6"></attribute>
			</variables>
			<variables name="rH_VAL" typeName="REAL">
				<comment>Valeur haute pour la sortie</comment>
				<attribute name="PositionPin" value="7"></attribute>
			</variables>
			<variables name="iPERF" typeName="INT">
				<comment>Performances des constantes PID : 0=rapides, 1=normales, 2=lentes</comment>
				<attribute name="PositionPin" value="8"></attribute>
			</variables>
		</inputParameters>
		<outputParameters>
			<variables name="bEND" typeName="BOOL">
				<comment>Fin séquence</comment>
				<attribute name="PositionPin" value="1"></attribute>
			</variables>
			<variables name="rSQ_VAL" typeName="REAL">
				<comment>Sortie onde carrée pour l'exitation du process</comment>
				<attribute name="PositionPin" value="2"></attribute>
			</variables>
			<variables name="rKP" typeName="REAL">
				<comment>Gain proportionnel</comment>
				<attribute name="PositionPin" value="3"></attribute>
			</variables>
			<variables name="rTN" typeName="REAL">
				<comment>Temps d'intégrale en s</comment>
				<attribute name="PositionPin" value="4"></attribute>
			</variables>
			<variables name="rTV" typeName="REAL">
				<comment>Temps de dérivée en s </comment>
				<attribute name="PositionPin" value="5"></attribute>
			</variables>
		</outputParameters>
		<publicLocalVariables>
			<variables name="PU" typeName="REAL">
				<comment>Gain ultime</comment>
			</variables>
			<variables name="TU" typeName="REAL">
				<comment>Période ultime</comment>
			</variables>
		</publicLocalVariables>
		<privateLocalVariables>
			<variables name="b" typeName="REAL"></variables>
			<variables name="a" typeName="REAL"></variables>
			<variables name="Tu_Freq" typeName="FREQ_MEASURE"></variables>
			<variables name="b_Min" typeName="REAL"></variables>
			<variables name="b_Max" typeName="REAL"></variables>
			<variables name="EdgeEnd" typeName="BOOL"></variables>
			<variables name="FlagEnd" typeName="BOOL"></variables>
			<variables name="End" typeName="BOOL"></variables>
			<variables name="status" typeName="INT"></variables>
			<variables name="cpt" typeName="INT"></variables>
			<variables name="old_bEN" typeName="BOOL"></variables>
			<variables name="RstFreq" typeName="BOOL"></variables>
			<variables name="PerFreq" typeName="INT"></variables>
			<variables name="PI" typeName="REAL">
				<comment>Constante PI</comment>
				<variableInit value="3.14159265358979323846264338327950288"></variableInit>
			</variables>
		</privateLocalVariables>
		<FBProgram name="CALC_PID">
			<STSource>(*** Séquence autotune ***)

IF bRST THEN
    status:=0;
END_IF;

CASE status OF
0: (* Attente Démarrage autotune *)
    rSQ_VAL:=rL_VAL;
    IF bEN AND NOT old_bEN THEN
        RstFreq:=TRUE;
        b_Min:=3.402823E+38 ;
        b_Max:=-3.402823E+38 ;
        rKP:=0.0;
        rTN:=0.0;
        rTV:=0.0;
        cpt:=0;
        bEND:=FALSE;
        status:=1;
	End:=FALSE;
    END_IF;

1: (* Onde carrée haute *)
    rSQ_VAL:=rH_VAL;
    IF rPV > rSP THEN
        rSQ_VAL:=rL_VAL;
        cpt:=cpt+1;
        status:=2;
    END_IF;

2: (* Onde carrée basse et fin de séquence *)
    IF rPV < rSP THEN
        cpt:=cpt+1;
        IF cpt >= iCY THEN
            status:=1;
            End:=TRUE;
        ELSE
            status:=1;
        END_IF;
    END_IF;

END_CASE;

old_bEN:=bEN;

(*** Calcul des paramètres ***)

(* min/max et freq de PV  *)

IF rPV<b_Min THEN
    b_Min:=rPV;
END_IF;
IF rPV>b_Max THEN
    b_Max:=rPV;
END_IF;

(* Nombre de période pour le bloc FREQ_MEASURE *)
PerFreq:=(iCY/2)-1;
PerFreq:=LIMIT(1, PerFreq, 10);

Tu_Freq(
    IN := rPV > rSP ,
    PERIODS :=PerFreq  ,
    RESET := RstFreq); 
    
RstFreq:=FALSE;                                           

(* Calcul sur front montant de End et valeur de sortie de FREQ_MEASURE ok *)
EdgeEnd:= End AND Tu_Freq.OUT <> 0.0 AND NOT FlagEnd;
FlagEnd:= End AND Tu_Freq.OUT <> 0.0 ;


IF EdgeEnd THEN
    (* amp de SQ_VAL *)
    a:=rH_VAL-rL_VAL;

    (* amp de PV *)
    b:=b_Max-b_Min;

    (* Gain et période ultime *)
    PU:=(4.0 * b)/(PI * a);
    TU:=1.0/Tu_Freq.OUT;

    (* Valeur PID *)
    CASE iPERF OF
    0:(* Performances rapides *)
        rKP:= 0.6 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;

    1:(* Performances normales *)
        rKP:= 0.25 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;

    2:(* Performances lentes *)
        rKP:= 0.15 * PU;
        rTN:= 0.5 * TU;
        rTV:= 0.125 * TU;
    END_CASE;

    bEND:=TRUE;
    status:=0;

END_IF;
</STSource>
		</FBProgram>
	</FBSource>
	<FBSource nameOfFBType="FREQ_MEASURE" version="0.02" dateTime="dt#2016-04-27-14:11:07">
		<comment>Adaptation du FB FREQ_MEASURE de la lib Util de 3S.</comment>
		<inputParameters>
			<variables name="IN" typeName="BOOL">
				<comment>input signal</comment>
				<attribute name="PositionPin" value="1"></attribute>
			</variables>
			<variables name="PERIODS" typeName="INT">
				<comment>out is the average frequency during PERIODS (number of periods) between 1 to 10</comment>
				<attribute name="PositionPin" value="2"></attribute>
				<variableInit value="1"></variableInit>
			</variables>
			<variables name="RESET" typeName="BOOL">
				<comment>reset measurement</comment>
				<attribute name="PositionPin" value="3"></attribute>
			</variables>
		</inputParameters>
		<outputParameters>
			<variables name="OUT" typeName="REAL">
				<comment>frequency [Hz]</comment>
				<attribute name="PositionPin" value="1"></attribute>
			</variables>
			<variables name="VALID" typeName="BOOL">
				<comment>FALSE: not yet PERIODS measurements done OR time distance between two rising edges > 3*OUT</comment>
				<attribute name="PositionPin" value="2"></attribute>
			</variables>
		</outputParameters>
		<privateLocalVariables>
			<variables name="OLDIN" typeName="BOOL"></variables>
			<variables name="INIT" typeName="BOOL"></variables>
			<variables name="OLDT" typeName="DINT"></variables>
			<variables name="DIFF" typeName="DINT"></variables>
			<variables name="ADIFF" typeName="ARRAY[0..9] OF DINT"></variables>
			<variables name="V" typeName="INT"></variables>
			<variables name="B" typeName="INT"></variables>
			<variables name="I" typeName="INT"></variables>
		</privateLocalVariables>
		<FBProgram name="FREQ_MEASURE">
			<STSource>(* Adaptation du FB FREQ_MEASURE de la lib Util de 3S. *)

IF RESET THEN
    B:=0;
    V:=0;
    INIT:=FALSE;
    VALID:=FALSE;
    OUT:=0.0;
    RETURN;
END_IF;

IF IN AND NOT OLDIN THEN    (*rising edge *)
    IF INIT THEN
        DIFF := (FREERUN ()-OLDT)/1000;
        IF Diff > 0 THEN
            ADIFF[B] := DIFF;
            B := (B+1) MOD PERIODS;
            V:=MIN(IN1:=V+1, IN2:=PERIODS);
            IF V=PERIODS THEN
                OUT := 0.0;
                FOR I:=0 TO PERIODS-1 DO
                    OUT := OUT + DINT_TO_REAL(ADIFF[I]);
                END_FOR;
                OUT := 1000.0 * INT_TO_REAL(PERIODS) / OUT;
                VALID:=TRUE;
            ELSE
                VALID:=FALSE;
            END_IF;
        END_IF;
    END_IF;
    INIT := TRUE;
    OLDT := FREERUN ();
ELSIF INIT AND VALID AND ((FREERUN ()-OLDT)/1000) > REAL_TO_DINT(3000.0*OUT) THEN
    VALID:=FALSE;
    V:=0;
    B:=0;
END_IF;

OLDIN:=IN;
</STSource>
		</FBProgram>
	</FBSource>
</FBExchangeFile>

Dernière modification par Cyril93 le 21 déc. 2016, 12:06, modifié 1 fois.
damiette
Apprend le binaire
Apprend le binaire
Messages : 4
Inscription : 03 nov. 2016, 12:20

Re: PID autotune - la méthode du relais

Message par damiette »

OK, Merci beaucoup
Répondre