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
Uint16phaseshift1;
6
Uint16Ref;
7
int16e0;// Error from current sample
8
int16e1;// Error from previous sample
9
Uint16y0;// PI output from current sample
10
Uint16y1;// PI output from previous sample
11
Uint16ConversionCount;
12
Uint16Voltage1[16];
13
Uint16Current[16];
14
15
voidmain(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
__interruptvoidADC_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ß,
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
Uint16ConversionCount;
2
Uint16Voltage1[16];
3
Uint16Current[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.
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.
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ß,
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.
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.
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?
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.
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...