Forum: Mikrocontroller und Digitale Elektronik Kurze Frage zum PI- Regler


von Jens (Gast)


Lesenswert?

Hallo Leute,

ich bin dabei mich in den PI- Regler einzuarbeiten.
Ich habe eine blöde Frage zu der Stellgröße (y) ich möchte dass der 
Regler ein
Wert zwischen 0 und 255 ausgibt.
Für y habe ich den Datentyp int genommen, wenn jetzt der Regler aber 
runter
regelt läuft der Wert in den negativen Bereich und um das zu vermeiden 
habe
ich den begrenzt.

        if(y1<0)   {y=0;  } else {y=y1;}
  if(y1>255) {y=255;} else {y=y1;}

Das funktioniert aber nicht, sobald die Variable y1 im negativem Bereich 
ist,
springt der y auf 255 anstatt auf 0.

Wo ran liegt das was mache ich falsch?

von hugo (Gast)


Lesenswert?

was hast du denn für ein Datentyp für y1 genommen? char ? dann kann y1 
nicht <0 oder >255 werden!
Ohne Quelltest kann man nicht viel zum fehler sagen

von Jens (Gast)


Lesenswert?

uint16_t ReadChannel(uint8_t mux);

        uint16_t w;
  uint16_t x;
  uint16_t e;
  uint16_t esum;
  uint16_t Kp=1;
  uint16_t Ki=1;
  uint16_t Kd;
  uint16_t y;
  uint16_t yi;
  uint16_t y1;

ISR (TIMER1_COMPA_vect)
{
  esum = esum + e;
  yi = Ki * esum;
}

int main (void)
{
  //Initialisierung des LCD Displays 4 Bit Mode
  LCD_Init();

  //Löschen des Displays
  LCD_Clear();

  //Initialisierung von TIMER1
  TCCR1B |= (1 << CS12 | 1 << WGM12 | 1 << CS10);
  TCCR1A = 0b00000000;
  TIMSK |= (1 << OCIE1A);
  OCR1A = 15624;

        //Initialisierung von TIMER2 (PWM OC2)
  DDRD |= (1<<7)|(1<<4);
  TCCR2 |= (1<<WGM20|1<<COM21|1<<COM20|1<<CS20|1<<CS21|1<<CS20);
  DDRD |= (1<<7);

        //Globale Interrupts freischalten
  sei();
__________________________________________________________________
  do
  {
  w = ReadChannel(1);
  x = ReadChannel(7);
  e = w - x;
  y1 = Kp * e + yi; //yi im Interrupt


  if(y1<0)   {y=0;  } else {y=y1;}
  if(y1>255) {y=255;} else {y=y1;}
  OCR2 = y ;
  //Ausgabe eines Textes auf dem LCD Display
  LCD_Print(0,0,"Soll  = %i          ",w);
  LCD_Print(1,0,"Ist   = %i          ",x);
  LCD_Print(2,0,"Diff  = %i          ",e);
  LCD_Print(3,0,"Stell = %i %i       ",y,y1);

    }
  while(1);

}
ende
    +
noch die ADC Funktion aber die Funktioniert ja.

So das ist mein Quelltext.

@hugo

y1 ist auch uint_16t

von Jochen S. (schiffner)


Lesenswert?

dein yl ist ein uint16_t (unsigned int), der kann doch gar nicht negativ 
werden (wie schon gesagt), probier mal int16_t

von name (Gast)


Lesenswert?

ich würde aus persönlicher erfahrung behaupten, dass du das mit der 
auflösung nur schwer stabil hinkommst. angenommen dein soll ist 128, 
deine maximal mögliche regelabweichung 50 und schon kann dein P nur noch 
2 sein ohne über deinen maximalwert 255 rauszuschießen. so ungefähr: 128 
+ (2*50) = 228

wenn du extrem grob auflöst und richtig gut aufpasst beim skalieren 
deiner werte geht es vielleicht irgendwie.

die andere frage ist wofür du den I-Anteil brauchst, bzw. ob du den 
wirklich brauchst und nicht vielleicht mir einer minimalen 
regelabweichung leben kannst. würde es dir zumindest wesentlich 
einfacher machen und dein system wäre stabiler.

schönen abend noch!

von Jens (Gast)


Lesenswert?

Es ist komisch aber der Wert läuft in den negativen Bereich über.

von Tupf (Gast)


Lesenswert?

du solltest die variablen in der interrupt funktion auch zu volatile 
definieren.

Bis dann ...

von Jens (Gast)


Lesenswert?

Der Regler ist eine reine Übungsaufgabe, der leider noch nicht 
funktioniert.

von Jens (Gast)


Lesenswert?

@Tupf

wie meinst du das? kannst du es näher erklären? Ich bin noch ein 
ziemlicher Anfänger.

von Tupf (Gast)


Lesenswert?

gehe bitte in das GCC Forum. Das Forum kannst du links in der Spalte 
anklicken. Dann suche im Betreff nach volatile. Da gibt es hunderte von 
Erklaerungen.

Viel Spass ...

von Waldo (Gast)


Lesenswert?

Hallo,
ich würde erstmal grundsätzlich den PI-Regler formulieren:

y(t) = k*( e(t)+1/Ti*int(e(t)) )

oder

y(t) = k*e(t) + int(k/Ti*e(t))

Als Abtastregler und in erster Näherung:

y = k*e + e_int

mit

e_int = e_int + T * k/Ti*e

Damit das ganze nicht überläuft, muss man jede Einzelrechnung auf 
Überlauf prüfen:

e_int < min: e_int = min
e_int > max: e_int = max

y = k*e + e_int

y < min: y = min
y > max: y = max

So läuft der Regler nicht über und kommt auch sofort aus der Begrenzung 
heraus, wenn e(t) das Vorzeichen wechselt.

e(t) muss vorzeichenbehaftet laufen, wie alle Werte, die im Regler 
verarbeitet werden. Ich würde anfangen und alles mit int16 rechnen.
Dann kann man den Ausgang auf 0 bis 255 begrenzen und als unit8 
ausgeben.

Gruss

von Jens (Gast)


Lesenswert?

Ich danke euch allen, ich habe mein Regler jetzt am laufen.

Als Datentyp habe ich jetzt wirklich den int genommen und damit 
funktioniert es.

Und das e und esum habe ich begrenzt.
Außerdem habe ich in die Formel das Ta rein genommen, was vorher 
eigentlich
auch nicht kritisch war, weil die Abtastzeit 1 sek betrug.

Jetzt frage ich mich, was ist wenn die Abtastzeit kürzer werden soll?
Den Interrupt kann ich ja verkürzen wie ich möchte, die Zeit muss ich 
aber auch in meine Rechnung einbeziehen, versteht der Kontroller auch 
Kommazahlen?
z.B. 0.2 sek welchen Datentyp muss ich dafür nehmen float?

von Walter S. (waldo)


Lesenswert?

Hallo,
es ändert sich die Berechnung des Integrals:

e_sum = e_sum + Ta * k/Ti*e

wenn also die Abtastzeit kleiner wird, muss das Integral pro Abtastpunkt 
weniger stark anwachsen. Der Faktor für das Integral wird also kleiner:

e_sum = e_sum + kI* e

mit

kI = Ta * k/Ti

und der Reglerausgang (Stellgrösse)

y = k*e + e_sum

Gruss Waldo

von Jens (Gast)


Lesenswert?

Das verstehe ich jetzt aber nicht.

Was ist den jetzt Ti?

von Walter S. (waldo)


Lesenswert?

Hallo,
ein PI-Regler wird in der Regel folgendermaßen formuliert:

y(t) = k*( e(t)+1/Ti*int(e(t)) )

Es gibt die Reglerverstärkung k und die Integrationszeitkonstante Ti oft 
auch als Nachstellzeit bezeichnet. Die Reglerverstärkung bewertet die 
Summe aus P-Anteil und I-Anteil, ist also die Gesamtverstärkung.

Das Integral sollte also wie folgt berechnet werden:

yI = yI + Ta * k/Ti * e

mit

Ta ... Abtastzeit
Ti ... Integrationszeit bzw. Nachstellzeit
k  ... Reglerverstärkung

Der Reglerausgang:

y = k * e + yI

Also muss in der Interruptroutine nicht einfach die Regelabweichung 
aufsummiert werden. Das hat den Nachteil, dass die Begrenzung schwer zu 
machen ist. Es ist besser yI in der Interruptroutine zu rechnen und die 
Begrenzung zu machen:

yI = yI + Ta *k/Ti * e

yI > max: yI = max
yI < min: yI = min

Die Konstante Ta * k/Ti kann im Voraus berechnet werden, um Zeit zu 
sparen.

Gruss

von Thilo M. (Gast)


Lesenswert?

Hast du hier schon mal geschaut?
Beitrag "PID-Regler mit anti-Windup"

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.