Forum: Mikrocontroller und Digitale Elektronik Diskreter PI Regler zappelt


von Al3ko -. (al3ko)


Lesenswert?

Hi,

ich bin gerade dabei, einen PI Regler auf einem floating point DSP 
(F28335) für einen DC/DC Wandler zu implementieren. Da es mein erster 
Versuch ist, stehe ich ein wenig wackelig auf den Beinen und könnte ein 
wenig Hilfe gebrauchen (habe bisher nicht viel in hardwarenahem C 
programmiert).

Der PI Regler in kontinuierlicher Form lautet:
Meine berechneten Werte fuer KP und KI, und in Simulationen verifiziert:
KP = 11
KI = 1e5

Den PI Regler habe ich nun diskretisiert und in eine lineare 
Differenzengleichung ueberfuehrt:
> y[n]=y[n-1]+(KP+KI*Ts/2)e[n]+(KI*Ts/2-KP)e[n-1]
Daraus ergibt sich
> y[n]=y[n-1]+b0*e[n]+b1e[n-1]
Dabei entsprechen:
y[n] - Aktueller Ausgang des PI Reglers
y[n-1] - Ausgang des PI Reglers vom vorigen Sample Intervall
e[n] - Aktueller Fehler
e[n-1] - Fehler vom vorigen Sample Intervall
Ts - Sample Intervall = 1/Interruptfrequenz = 1/100kHz
b0 = (KP+KI*Ts/2) = 11.5 - Diskretisierte Werte für den PI Regler
b1 = (KI*Ts/2-KP) = -10.5 - Diskretisierte Werte für den PI Regler

Soweit, so gut. Die Implementierung des PI Reglers erfolgt innerhalb des 
ADC Timer Interrupts.

Der (unvollständige) Code mit den wichtigsten Initialisierungen des 
Reglers:
1
// Variables
2
#define b0 11.5  // Coefficients for LDE
3
#define b1 -10.5  // Coefficients for LDE
4
5
Uint16 phaseshift1;
6
Uint16 Ref;
7
int16 e0;   // Error from current sample
8
int16 e1;  // Error from previous sample
9
Uint16 y0;  // PI output from current sample
10
Uint16 y1;  // PI output from previous sample
11
Uint16 ConversionCount;
12
Uint16 Voltage1[16];
13
Uint16 Current[16];
14
15
void main(void) {
16
17
18
19
  ConversionCount = 0;        // Initialize the count
20
  e0 = 0;
21
  e1 = 0;
22
  y0 = 1200;
23
  y1 = 0;
24
  Ref = 2070;  // Approx. 5A load current
25
}
26
27
__interrupt void ADC_isr(void)
28
{
29
  Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;  // Read value from ADCINA5
30
  Current[ConversionCount] = AdcRegs.ADCRESULT1 >>4;  // Read value from ADCINB5
31
32
    /*
33
     * Calculating the phaseshift between two bridges using PI control
34
     */
35
    if(Current[ConversionCount]>2050)  // Start PI control when current is more than 3A
36
    {
37
      e1 = e0;
38
      y1 = y0;
39
      e0 = Ref-Current[ConversionCount];
40
      y0 = b0*e0+b1*e1+y1; // LDE of PI regulator
41
      if(y0>1460)
42
        {
43
        y0=1460;
44
        }
45
      if(y0<100)
46
        {
47
        y0=100;
48
        }
49
    }
50
51
    EPwm5Regs.TBPHS.half.TBPHS = y0;  // ePWM5 and ePWM6 will have phaseshift
52
    EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;  // Compensate ca. 16ns delay of ePWM6
53
54
      // If 16 conversions have been logged, start over
55
      if(ConversionCount == 15)
56
      {
57
         ConversionCount = 0;
58
      }
59
      else
60
      {
61
          ConversionCount++;
62
      }
63
64
}

Das Problem, das ich glaube zu haben, ist folgendes:
b0 ist 11, und die Variablen meines PI Reglers (e0,e1, z0 und z1) sind 
16bit lang. Das Register für mein PWM (bzw. phaseshift zwischen zwei 
PWMs) ist lediglich 12bit groß.
Wenn wir uns die Berechnung der LDE anschauen
> y0 = b0*e0+b1*e1+y1; // LDE of PI regulator
dann könnte es vorkommen, dass wir im schlimmsten Fall bei z.B. b0*e0 
folgende Rechnung haben 11*2^16=11*65535=720896
Das darf meiner Ansicht nicht passieren, und ich befürchte, dass ich 
irgendwo/irgendwie skalieren muss, um Äpfel mit Äpfel zu vergleichen. 
Ich könnte natürlich b0 und b1 kleiner machen, allerdings stimmt meine 
(theoretische) Reglerauslegung dann nicht mehr.

Kann mir jemand auf die Sprünge helfen, wie man die Skalierung anstellt? 
Es handelt sich um einen floating point DSP, weshalb ich den Schritt auf 
fixed point Q15 nicht gemacht habe (ein Fehler?).

Ich danke euch. Gruß,

: Bearbeitet durch User
von THOR (Gast)


Lesenswert?

Also statt im C-Code auf physikalische Einheiten umzurechnen hab ich 
immer die Verstärkungswerte umgerechnet auf das, was die Hardware so 
ausspuckt (8-Bit ADC-Werte in dem Fall).

Und dann natürlich in nen Kommentar geschrieben was das für Werte sind, 
die erkennt man danach natürlich nicht mehr.

Weiterhin:
1
      if(y0>1460)
2
        {
3
        y0=1460;
4
        }
5
      if(y0<100)
6
        {
7
        y0=100;
8
        }

ist eine Regelstreckenbegrenzung. Wind Up Begrenzung einbauen.
1
Uint16 ConversionCount;
2
Uint16 Voltage1[16];
3
Uint16 Current[16];

ist ungünstig gewählt. Mach conversionCount 8bit groß, initialisiere die 
beiden Arrays mit 256 Werten.

Dann kannst du dir "      // If 16 conversions have been logged, start 
over" komplett sparen, bei 255 läuft die Variable wieder zu 0 über und 
die Pointerarithmetik [0-1] ergibt Element 255 in deinem Array. Ein 
simpler Ringpuffer.

Und dann kannst du dir mal irgendwie (serielle Schnittstelle oder so) 
die Mess- und Stellwerte anzeigen lassen.

von aSma>> (Gast)


Lesenswert?

Al3ko -. schrieb:
>> y[n]=y[n-1]+(KP+KI*Ts/2)e[n]+(KI*Ts/2-KP)e[n-1]

Hier ist ein Fehler, da fehlt ein Faktor (PI-Regler nach Tustin).
Deine Gleichung neutralisiert sich:

> #define b0 11.5  // Coefficients for LDE
> #define b1 -10.5  // Coefficients for LDE


> e1 = e0;
> y1 = y0;

Diese Zuweisung gehört ans Ende der Regelung.

> EPwm5Regs.TBPHS.half.TBPHS = y0;  // ePWM5 and ePWM6 will have
> phaseshift
>     EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;  // Compensate ca. 16ns
> delay of ePWM6

Bei mehr als zwei Punktoperatoren hat man ein Designfehler im Programm.

Al3ko -. schrieb:
> Das Problem, das ich glaube zu haben, ist folgendes:
> b0 ist 11, und die Variablen meines PI Reglers (e0,e1, z0 und z1) sind
> 16bit lang. Das Register für mein PWM (bzw. phaseshift zwischen zwei
> PWMs) ist lediglich 12bit groß.
> Wenn wir uns die Berechnung der LDE anschauen
>> y0 = b0*e0+b1*e1+y1; // LDE of PI regulator
> dann könnte es vorkommen, dass wir im schlimmsten Fall bei z.B. b0*e0
> folgende Rechnung haben 11*2^16=11*65535=720896

zo und z1 sind hier nicht bekannt.

Variablen, die im Hauptprogramm und ISR aufgerufen/verändert werden 
sollte man als volatile deklarieren.

von Al3ko -. (al3ko)


Lesenswert?

Hi THOR und aSma>>,

vielen Dank für eure Beiträge. Ich bin jetzt wieder im Labor und habe 
Möglichkeit, zu antworten.

THOR schrieb:
> Also statt im C-Code auf physikalische Einheiten umzurechnen hab
> ich
> immer die Verstärkungswerte umgerechnet auf das, was die Hardware so
> ausspuckt (8-Bit ADC-Werte in dem Fall).
Okay. Ich verwende einen 16bit DSP mit 12bit ADC. Heißt also, ich müsste 
meine Verstärkungswerte durch 2^4 teilen (>>4 shiften).
b0>>4;
b1>>4;

Verstehe ich dich richtig?

> Weiterhin:      if(y0>1460)
>         {
>         y0=1460;
>         }
>       if(y0<100)
>         {
>         y0=100;
>         }
>
> ist eine Regelstreckenbegrenzung. Wind Up Begrenzung einbauen.
Jap, daran habe ich auch gedacht. Blöde Frage, da ich mit linearen 
Differenzengleichungen noch nicht so viel gearbeitet habe. Bei der 
konventionellen Darstellung/Implementierung des PI Reglers könnte man 
z.B. den Integralanteil bzw. die Aufsummierung des Fehlers einfrieren. 
Bei der Differenzengleichung habe ich ein wenig den Überblick verloren, 
wo und wie der Integralanteil bzw. die Fehlersumme ist. Wie würde man 
den Anti-Windup in diesem Fall einbauen? Ich verlange keinen Code (würde 
ihn aber dennoch dankend annehmen :D), eine kleine Erklärung wäre aber 
schon hilfreich.

> Uint16 ConversionCount;
> Uint16 Voltage1[16];
> Uint16 Current[16];
>
> ist ungünstig gewählt. Mach conversionCount 8bit groß, initialisiere die
> beiden Arrays mit 256 Werten.
Ist geändert, danke für den Hinweis.

> Und dann kannst du dir mal irgendwie (serielle Schnittstelle oder so)
> die Mess- und Stellwerte anzeigen lassen.

Ich verwende einen JTAG, und zeige mir die Parameter y0, y1, e0 und e1 
an. Die gehen sofort in die Sättigung, sobald der Regler aktiviert ist.

aSma>> schrieb:
> Al3ko -. schrieb:
>>> y[n]=y[n-1]+(KP+KI*Ts/2)e[n]+(KI*Ts/2-KP)e[n-1]
>
> Hier ist ein Fehler, da fehlt ein Faktor (PI-Regler nach Tustin).
> Deine Gleichung neutralisiert sich:
Danke für den Hinweis. Habe die Rechnung noch mal neu angestellt und die 
LDE neu berechnet. Somit ergeben sich auch neue Koeffizienten für
>> #define b0 11.5  // Coefficients for LDE
>> #define b1 -10.5  // Coefficients for LDE

>> e1 = e0;
>> y1 = y0;
>
> Diese Zuweisung gehört ans Ende der Regelung.
Danke, ist geändert.
>> EPwm5Regs.TBPHS.half.TBPHS = y0;  // ePWM5 and ePWM6 will have
>> phaseshift
>>     EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;  // Compensate ca. 16ns
>> delay of ePWM6
>
> Bei mehr als zwei Punktoperatoren hat man ein Designfehler im Programm.
Diese Schreibweise / Punktoperatoren kommt von TI selbst.
> Al3ko -. schrieb:
>> Das Problem, das ich glaube zu haben, ist folgendes:
>> b0 ist 11, und die Variablen meines PI Reglers (e0,e1, z0 und z1) sind
>> 16bit lang. Das Register für mein PWM (bzw. phaseshift zwischen zwei
>> PWMs) ist lediglich 12bit groß.
>> Wenn wir uns die Berechnung der LDE anschauen
>>> y0 = b0*e0+b1*e1+y1; // LDE of PI regulator
>> dann könnte es vorkommen, dass wir im schlimmsten Fall bei z.B. b0*e0
>> folgende Rechnung haben 11*2^16=11*65535=720896
>
> zo und z1 sind hier nicht bekannt.
zo und z1? Alle Koeffizienten der LDE werden in der main() Funktion 
initialisiert.
> Variablen, die im Hauptprogramm und ISR aufgerufen/verändert werden
> sollte man als volatile deklarieren.
Ist geändert.

Stellt sich nach wie vor noch die Frage:
Wie sieht der Anti Windup aus, wenn man eine LDE hat und nicht mehr 
direkten Zugriff auf die Fehlersumme bzw. den Integralanteil hat?

Ich danke euch.

Gruß,

von aSma>> (Gast)


Lesenswert?

AntiWindup brauchst du sicherlich nicht bei einer sehr schnellen 
Strecke. Eine Simulation hilft dir bei der Entscheidung. Schaltbilder 
gibts bei google. Das sind dann ein paar Zeilen mehr Code. Mehr auch 
nicht. Die nächste Stuffe wäre eher ein PID Regler.

Al3ko -. schrieb:
> #define b0 11.5  // Coefficients for LDE
> #define b1 -10.5  // Coefficients for LDE

Schlechter Ansatz bei Festkommaarichmetik. Deine Zahlen werden 
abgerundet.

Oftmals zittert der ADC, deswegen mittelt man ein paar Werte. K.A. was 
du da machst. Interessant auch zu wissen, ob du die aktuellen Sensor 
Daten auswertest oder schon welche, die 16*TA alt sind. Dann hast du 
nämlich eine Totzeit eingebaut.

von THOR (Gast)


Lesenswert?

Al3ko -. schrieb:
>> Weiterhin:      if(y0>1460)
>>         {
>>         y0=1460;
>>         }
>>       if(y0<100)
>>         {
>>         y0=100;
>>         }
>>
> Wie würde man
> den Anti-Windup in diesem Fall einbauen? Ich verlange keinen Code (würde
> ihn aber dennoch dankend annehmen :D), eine kleine Erklärung wäre aber
> schon hilfreich.

Einfach, immer wenn Code in einem der beiden if-statements ausgeführt 
wird, darf sich die I-Anteil Fehlersumme (esum?) nicht ändern.

von Al3ko -. (al3ko)


Lesenswert?

THOR schrieb:
> Einfach, immer wenn Code in einem der beiden if-statements ausgeführt
> wird, darf sich die I-Anteil Fehlersumme (esum?) nicht ändern.

Verrate mir bitte, wo sich in der folgenden LDE nach Tustin der I-Anteil 
Fehlersumme (esum?) befindet:
Genau das ist das, was ich mit folgendem Satz versuchte auszudrücken:

Al3ko -. schrieb:
> Differenzengleichungen noch nicht so viel gearbeitet habe. Bei der
> konventionellen Darstellung/Implementierung des PI Reglers könnte man
> z.B. den Integralanteil bzw. die Aufsummierung des Fehlers einfrieren.
> Bei der Differenzengleichung habe ich ein wenig den Überblick verloren,
> wo und wie der Integralanteil bzw. die Fehlersumme ist. Wie würde man
> den Anti-Windup in diesem Fall einbauen?

: Bearbeitet durch User
von THOR (Gast)


Lesenswert?

Ich hab grad mal geguckt. Du hast für die Differenzengleichung eine 
alternative Darstellung gewählt bei der esum einzeln nicht vorkommt.

http://rn-wissen.de/wiki/index.php?title=Regelungstechnik#Digitaler_Regler

(Abschnitt  "Umsetzung in Code nach der Differenzengleichung:")

Das ist natürlich ungünstig in Bezug auf den Wind-Up Effekt.

Regelungstechnik ist bei mir nen Tick zu lang her als dass ich jetzt auf 
Anhieb sagen könnte was man da tut. Ich würde die esum-Variante des 
C-Codes verwenden. Ich finde, der liest sich auch besser.

Alle Variablen können überlaufen und müssen davor geschützt werden, bei 
esum ist das aber besonders wahrscheinlich. Mir hat mal ein PID wild 
rumgezappelt weil der Variablenwert für esum hinter 255 wieder 0 wurde.

von aSma>> (Gast)


Lesenswert?

Al3ko -. schrieb:
> Verrate mir bitte, wo sich in der folgenden LDE nach Tustin der I-Anteil
> Fehlersumme (esum?)
> befindet:y[n]=(Kp+KpKiT/2)e[n]+(KpKiT/2−Kp)e[n−1]+y[n−1]

Das ist echt traurig, dass du das nicht siehst. Trenne diese Gleichung 
in ein P- und I-Anteil...

von THOR (Gast)


Lesenswert?

Ich sehe es auch nicht.

von aSma>> (Gast)


Lesenswert?

THOR schrieb:
> Ich sehe es auch nicht.

Schade. Man muss hier auch ein wenig nachdenken.

Beim PID Regler gilt das Superpositionsprinzip:
PID = P+I+D.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.