Forum: Mikrocontroller und Digitale Elektronik Pasenanschnitt mit PI Regel Algorithmus


von Chris (Gast)


Lesenswert?

Hallo liebe Gemeinde,

ich habe folgendes Problem:

Ich habe eine B2 Brücke mit Phasenaschnitt und betreibe daran eine 
Gleichstrommaschine.
Ziel ist es über das Poti nicht den Phasenanschnittswinkel, sondern den 
maximalen Strom vorzugeben.

Mein Problem ist, dass die Maschine zwar auf das Poti reagiert und auch 
mit dem Strom hochläuft, doch nicht auf dem Wert verweilt, sondern nach 
Erreichen eines Winkels von ca. 100 Grad wieder langsam auf null 
Umdrehungen herunter läuft.

Hier ein Auszug aus meinem Quellcode:
1
while (1)
2
{
3
  Imax = 0.0048828125 * ADC_Lesen()*1000;  //5A/1024=0.0048828125 
4
        
5
  if (q==1)    //q wird im Nulldurchgang der Netzspannung hochgezählt
6
  {  
7
      Id_neu =Stromberechnung(ALPHAnutz,LAMBDA);          
8
  }
9
  if (q==2)
10
    {
11
    Id_alt = Id_neu;
12
    q=0;
13
  }
14
15
  if (aa==1)    //aa wird alle 4 ms auf 1 gesetzt
16
  { 
17
    ALPHA_ohneL = ALPHA_ohneL - (PI_ohneL(Id_neu, Id_alt, Imax));
18
     if ((ALPHA_ohneL <= ALPHAmax) && (ALPHA_ohneL >= ALPHAmin)) ALPHA = ALPHA_ohneL;  //Falls das berechnete ALPHA_ohneL im zulässigen Bereich liegt
19
      else
20
     {
21
          if (ALPHA_ohneL > ALPHAmax)
22
    {
23
      ALPHA = ALPHAmax;
24
      ALPHA_ohneL=ALPHAmax;
25
    }
26
              
27
    if (ALPHA_ohneL < ALPHAmin)
28
    {
29
      ALPHA = ALPHAmin;
30
      ALPHA_ohneL= ALPHAmin;
31
    }          
32
         } 
33
        aa = 0;  
34
         }  
35
          //letzteres halt um Bereiche einzugrenzen.
36
}

Und hier der PI-Algo:
1
float PI_ohneL(int Id_neu, int Id_alt, int Imax)
2
{
3
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
4
  xd_1 = (Imax - Id_alt)/1000;
5
6
  new_u_ist = (new_u_ist + k_Ri * (xd - xd_1) + k_Ri * T_AB / T_Ri * xd_1) ;
7
  
8
  u_ohneL =new_u_ist;
9
  
10
  return u_ohneL;    //Übergabe Stellgröße
11
}


Erkennt jemand in diesen Codeschnippseln einen Fehler?

von Karl H. (kbuchegg)


Lesenswert?

Datentypen wären cool.

Denn ehrlich gesagt glaub ich nicht, dass du hier
1
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
2
  xd_1 = (Imax - Id_alt)/1000;
auf die Kommastellen verzichten willst. Da hättest du dir den Aufwand 
von wegen alles mal 1000 auch sparen können.

von Chris (Gast)


Lesenswert?

Vielen Dank für die schnelle Antwort.
Die Datentypen der Variblen sind:
1
                
2
  int ALPHA = 17900;            //Startwert für Zündwinkel: 161°
3
  volatile int ALPHAnutz = 17900;      //Startwert für Hilfsvariable: 161°
4
  const int ALPHAmax = 17900;        //Maximaler Zündwinkel 161°
5
  const int ALPHAmin = 9000;        //Minimaler Zündwinkel 81°
6
  volatile int LAMBDA = 4000;        //Initialisierungswert für Leitdauer 36°
7
8
  float Imax = 0;
9
  int Id_neu=0 , Id_alt = 0;
10
  int ALPHA_ohneL = 17900; 
11
  
12
  int q=0;
13
  int aa = 0;


und die Variablen für den Regler:
1
float new_u_ist = 0;  //Stellgröße die der Regler ausgibt
2
float xd = 0;      //Regler abweichung aktuell
3
float xd_1 = 0;      //Reglerabweichung zum zeitpunkt -1
4
float T_AB = 0.0036;  //Abtastzeit Beobachter + delta i + Algo PI
5
float T_Ri = 0.05;    //Streckenzeitkonstante
6
float k_Ri = 0.7;    //La/Ra
7
int u_ohneL = 0;
8
float u_mitL;

Ich folge dann mal Ihrem Rat und ändere alle integer Werte der Ströme in 
float um.
"Imax" habe ich gerade gesehen, ist ja auch als float deklariert.

Sonst irgendwelche Auffälligkeiten?
Vielen Dank für die Hilfe.

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:

> Ich folge dann mal Ihrem Rat und ändere alle integer Werte der Ströme in
> float um.

Das hab ich nicht gesagt!

Was ich gesagt habe, das ist das hier
1
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
2
  xd_1 = (Imax - Id_alt)/1000;
Integer Ergebnisse entstehen.


Das xd und xd_1 als float definiert sind, ist zwar nett, interessiert 
aber niemanden, am allerwenigsten den Compiler.

FAQ: Datentypen in Operationen


D.h es interessiert nur insofern, damit ich die Absicht erkennen kann, 
die hier dahinter steckt. Dem Compiler ist das aber wurscht, was die 
Absicht war. Der orientiert sich nach dem was dort steht. Ein
1
  float result;
2
  int j = 5;
3
4
  result = j / 8;

liefert eine glatte 0 in result und keineswegs 0.625

von Mark (Gast)


Lesenswert?

Okay, also vielen Dank. Seit der Anpassung fährt die Maschine schon Mal 
nicht mehr komplett auf null herunter.
Jedoch gibts es große Schwankungen in der Drehzahl.
Liegt es rein an den Reglerparametern oder macht es auch Sinn die vom 
Poti eingelesenen Werte zu mitteln?

von Eric B. (beric)


Lesenswert?

Karl Heinz schrieb:
> Was ich gesagt habe, das ist das hier
>
>  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte ...
>  xd_1 = (Imax - Id_alt)/1000;
>
> Integer Ergebnisse entstehen.
>
> Das xd und xd_1 als float definiert sind, ist zwar nett, interessiert
> aber niemanden, am allerwenigsten den Compiler.

In diesem Fall aber schon, da Imax als float definiert ist.

von Karl H. (kbuchegg)


Lesenswert?

Eric B. schrieb:
> Karl Heinz schrieb:
>> Was ich gesagt habe, das ist das hier
>>
>>  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte ...
>>  xd_1 = (Imax - Id_alt)/1000;
>>
>> Integer Ergebnisse entstehen.
>>
>> Das xd und xd_1 als float definiert sind, ist zwar nett, interessiert
>> aber niemanden, am allerwenigsten den Compiler.
>
> In diesem Fall aber schon, da Imax als float definiert ist.

?
Wo hättest du das gesehen?
1
float PI_ohneL(int Id_neu, int Id_alt, int Imax)
2
{
3
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
4
  xd_1 = (Imax - Id_alt)/1000;
Imax, Id_neu, Id_alt sind alles int.

von npn (Gast)


Lesenswert?

Karl Heinz schrieb:
> Imax, Id_neu, Id_alt sind alles int.

Id_neu und Id_alt ja, aber Imax nicht:

Chris schrieb:
> float Imax = 0;
> int Id_neu=0 , Id_alt = 0;

von npn (Gast)


Lesenswert?

Sorry, ich hab nur die vermutlich globale Imax gesehen, aber da ist ja 
noch diese Funktion, wo Imax nochmal vorkommt (lokal).
Chris schrieb:
> float PI_ohneL(int Id_neu, int Id_alt, int Imax)

von Matze (Gast)


Lesenswert?

1
float PI_ohneL(int Id_neu, int Id_alt, int Imax)
2
{
3
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
4
  xd_1 = (Imax - Id_alt)/1000;

Müsstest du dann nicht auch in der Parameterliste "float Imax" benutzen?

So:
1
float PI_ohneL(int Id_neu, int Id_alt, float Imax)
2
{
3
  xd = (Imax - Id_neu)/1000;   // teilen durch 1000 wegen integer Werte der Ströme
4
  xd_1 = (Imax - Id_alt)/1000;

von Chris (Gast)


Lesenswert?

Benutze jetzt überall float und habe die Teilung durch 1000 gestrichen.

Das Problem sind nun diese Schwingungen der Drehzahl. Drehzahl wird 
nicht konstant gehalten.
VZ1 Filterung der Poti Werte hat auch nichts gebracht.

von Karl H. (kbuchegg)


Lesenswert?

Mark schrieb:

> Jedoch gibts es große Schwankungen in der Drehzahl.

Wenn ein Regler schwingt, ist das normalerweise ein Hinweis darauf, dass 
die Regelparameter nicht stimmen.

> Liegt es rein an den Reglerparametern oder macht es auch Sinn die vom
> Poti eingelesenen Werte zu mitteln?

Ganz ehrlich?
Was meiner Meinung nach extrem Sinn machen würde, das ist eine 
Möglichkeit zu finden, wie du als Entwickler dir im laufenden Betrieb 
dir die Zahlenwerte für new_u_ist bzw. ALPHA_ohneL sichtbar machen 
kannst.

Das beobachten der zeitlichen Entwicklung verrät einem schon einiges. 
4ms ist natürlich nicht viel, aber wenn man zb jeden 10-ten Wert 
ausgibt, sollte auch das schon einiges verraten.

von Karl H. (kbuchegg)


Lesenswert?

Die Reglergleichung
1
  new_u_ist = (new_u_ist + k_Ri * (xd - xd_1) + k_Ri * T_AB / T_Ri * xd_1) ;
sieht komisch aus.

Dir ist bewusst, dass new_u_ist seinen Wert erst dann nicht mehr ändert, 
wenn Id_alt den Wert von IMax erreicht hat?
'Seinen Wert nicht mehr ändert' bedeutet aber nicht, dass new_u_ist dann 
den Wert 0 hätte. WEnn new_u_ist aber nicht 0 ist, dann verringert sich 
hier
1
   ALPHA_ohneL = ALPHA_ohneL - (PI_ohneL(Id_neu, Id_alt, Imax));
das Alpha laufend.

von Pandur S. (jetztnicht)


Lesenswert?

Die Regelung in Integern laufen zu lassen waere besser. Offensichtlich 
gibt es noch ein regelungstechnisches Problem. Also muessen Variablem in 
Echtzeit zugaenglich gemacht werden. Was ist die Zeitkonstante des 
Vorgangs ?

Falls langsam, die Variable(n) ueber das UART rauslassen.
Falls schnell, einen Tracebuffer definieren, die Variable(n) alle DeltaT 
reinschreiben und nachher per UART auslesen.

von Udo S. (urschmitt)


Lesenswert?

Irgendwas stimmt hier noch nicht:

Chris schrieb:
> Ziel ist es über das Poti nicht den Phasenanschnittswinkel, sondern den
> maximalen Strom vorzugeben.

Chris schrieb:
> Das Problem sind nun diese Schwingungen der Drehzahl. Drehzahl wird
> nicht konstant gehalten.

Wenn du auf den Strom regelst kannst du nicht erwarten dass die Drehzahl 
konstant bleibt.

Redest du von einer Reihenschluss oder einer Nebenschlussmaschine.
Denn du kannst erst mal nicht generell von einem festen Zusammenhang 
zwischen Strom und Drehzahl ausgehen.

von Chris (Gast)


Lesenswert?

Jetzt Nicht schrieb:
> Was ist die Zeitkonstante des
> Vorgangs ?

Zeitkonstante beträgt 20ms.
Problem ist, dass es sich um eine fertige, mit Stopplack versehene, 
Platine
handelt, mit winzigem uC. Da irgendwelche Kabel für die USART 
Schnittstelle ranzubekommen wird schwierig.


Udo Schmitt schrieb:
> Redest du von einer Reihenschluss oder einer Nebenschlussmaschine.
> Denn du kannst erst mal nicht generell von einem festen Zusammenhang
> zwischen Strom und Drehzahl ausgehen.

Fremderregt.


Udo Schmitt schrieb:
> Chris schrieb:
>> Ziel ist es über das Poti nicht den Phasenanschnittswinkel, sondern den
>> maximalen Strom vorzugeben.
>
> Chris schrieb:
>> Das Problem sind nun diese Schwingungen der Drehzahl. Drehzahl wird
>> nicht konstant gehalten.
>
> Wenn du auf den Strom regelst kannst du nicht erwarten dass die Drehzahl
> konstant bleibt.

Das stimmt natürlich. Da ist lediglich die Ankerspannung proportinal zur 
Drehzahl. Da habe ich mich falsch ausgedrückt.
Was ich meine, wenn ich über das Poti 3 Ampere vorgebe, die GM im 
Leerlauf 1 A zieht, und ich konstant belaste, dann sollte sich über die 
Regelung ein bestimmter ALPHA einstellen. Und zwar so, dass im Anker der 
GM 3 Ampere fließen. Doch der ALPHA schwankt so sehr, dass der Strom 
auch zwischen 2 und 4 A schwankt und dem zur Folge auch die 
Ankerspannung/Drehzahl.

Oder auch wenn ich im Leerlauf etwa 1 A vorgebe, jagt die Maschine mit 
2,5 A hoch und in der nächsten Sekunde ist der Strom wieder fast null 
und die Maschine bleibt stehen und das wiederholt sich immer.

von Chris (Gast)


Lesenswert?

Karl Heinz schrieb:
> Die Reglergleichung  new_u_ist = (new_u_ist + k_Ri * (xd - xd_1) + k_Ri
> * T_AB / T_Ri * xd_1) ;
> sieht komisch aus.
>
> Dir ist bewusst, dass new_u_ist seinen Wert erst dann nicht mehr ändert,
> wenn Id_alt den Wert von IMax erreicht hat?
> 'Seinen Wert nicht mehr ändert' bedeutet aber nicht, dass new_u_ist dann
> den Wert 0 hätte. WEnn new_u_ist aber nicht 0 ist, dann verringert sich
> hier   ALPHA_ohneL = ALPHA_ohneL - (PI_ohneL(Id_neu, Id_alt, Imax));
> das Alpha laufend.

Wenn der Strom im Motor Id_neu (oder kann ja auch ruhig Id_alt sein?) 
den Wert von Imax (über Poti vorgegeben) erreicht, soll sich die 
Stellgröße new_u_ist auch nicht mehr ändern.
Der feste Wert soll dann einem bestimmten Winkel ALPHA zur folge haben.

von Chris (Gast)


Lesenswert?

Chris schrieb:
> Karl Heinz schrieb:
>> Die Reglergleichung  new_u_ist = (new_u_ist + k_Ri * (xd - xd_1) + k_Ri
>> * T_AB / T_Ri * xd_1) ;
>> sieht komisch aus.
>>
>> Dir ist bewusst, dass new_u_ist seinen Wert erst dann nicht mehr ändert,
>> wenn Id_alt den Wert von IMax erreicht hat?
>> 'Seinen Wert nicht mehr ändert' bedeutet aber nicht, dass new_u_ist dann
>> den Wert 0 hätte. WEnn new_u_ist aber nicht 0 ist, dann verringert sich
>> hier   ALPHA_ohneL = ALPHA_ohneL - (PI_ohneL(Id_neu, Id_alt, Imax));
>> das Alpha laufend.
>
> Wenn der Strom im Motor Id_neu (oder kann ja auch ruhig Id_alt sein?)
> den Wert von Imax (über Poti vorgegeben) erreicht, soll sich die
> Stellgröße new_u_ist auch nicht mehr ändern.
> Der feste Wert soll dann einem bestimmten Winkel ALPHA zur folge haben.

Hmm..
So berechne ich meinen Zündwinkel:
1
 
2
ALPHA_ohneL = ALPHA_ohneL - (PI_ohneL(Id_neu, Id_alt, Imax));

Dann würde ich ja schon wollen, dass new_u_ist (was PI_ohneL zurückgibt) 
null wird wenn Id==Imax ist

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:

> Wenn der Strom im Motor Id_neu (oder kann ja auch ruhig Id_alt sein?)
> den Wert von Imax (über Poti vorgegeben) erreicht, soll sich die
> Stellgröße new_u_ist auch nicht mehr ändern.
> Der feste Wert soll dann einem bestimmten Winkel ALPHA zur folge haben.

Das ist aber nicht das was du programmiert hast.
Wenn new_u_ist einen Wert ungleich 0 hat (fest ist), dann verändert sich 
das Alpha.

Dein Alpha verändert sich nur dann nicht mehr, wenn new_u_ist irgendwann 
mal 0 wird.

von Chris (Gast)


Lesenswert?

Danke Karl Heinz.
Aber jetzt noch mal eine doofe Frage, wie schaffe ich es denn jetzt, 
dass "u" null wird wenn Id==Imax ist?

von Karl H. (kbuchegg)


Lesenswert?

Mir kommt hier
1
  new_u_ist = (new_u_ist + k_Ri * (xd - xd_1) + k_Ri * T_AB / T_Ri * xd_1) ;
2
3
               ***********
die Aufintegration des Wertes seltsam vor.
Aber ich kenn natürlich nicht das System im Detail

von Chris (Gast)


Lesenswert?

Danke Karl Heinz. Auch an alle anderen wohlwollenden Helfer.
Die beiden Sachen - float/int Gemisch und die Aufintegration - haben 
schon ein Mal das Schwingen beseitigt.
Jetzt fährt der Alpha nur nicht bis zum minimalem Wert runter, wenn das 
Poti auf Null steht..

Ich strenge meine grauen Zellen mal selber noch etwas an.. :D

Für Anregungen bin ich trotzdem immer gerne zu haben :))

von Chris (Gast)


Lesenswert?

Also den Grund kenne ich bislang immer noch nicht.
Dachte eigentlich brauche ich den "Quatsch" ja (jetzt noch) nicht. Sage 
einfach:
1
Alpha = Alpha - (Imax*1000-Id_neu*1000)/1000 //evtl *10 wegen kleinen Ändeungen

Aber ist genau das selbe Problem. Alpha geht einfach nicht auf den 
minimalen Wert herunter.
Alpha berechnet 1,5A, vom Poti vorgegeben null, Ergebnis aber nicht das 
gewünschte.
Weiß jemand Rat?

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Also den Grund kenne ich bislang immer noch nicht.
> Dachte eigentlich brauche ich den "Quatsch" ja (jetzt noch) nicht. Sage
> einfach:
>
>
1
> Alpha = Alpha - (Imax*1000-Id_neu*1000)/1000 //evtl *10 wegen kleinen 
2
> Ändeungen
3
>
>
> Aber ist genau das selbe Problem. Alpha geht einfach nicht auf den
> minimalen Wert herunter.

Dann würde ich mir halt mal den Wert von Id_neu genauer ansehen.
Offenbar ist das Ergebnis von
1
   Imax*1000-Id_neu*1000
dann schon 0 bzw. so klein, dass es praktisch 0 wird und keine relevante 
Änderung mehr in Alpha hervorruft.
Hast du jetzt eigentlich auf Floating Point umgestellt?

> Weiß jemand Rat?

Ich hab mich hier im Forum schon tausendemale immer gleich geäussert: 
Wer ein komplexes dynamisches System zu implementieren versucht, ohne 
sich eine Möglichkeit zu verschaffen, sich Werte ansehen zu können (und 
sei es nur, indem er provisorisch eine UART oder ein SPI LCD an 
irgendwelche Pins ankabelt), der ist in meinen Augen ein Dummk..f, der 
es nicht besser verdient hat. Die Attitüde "das wird schon auf Anhieb 
funktionieren" ist zwar die Idealvorstellung, aber der Regelfall ist nun 
mal "Es funktioniert nicht out of the box". Genau das ist einer der 
wesentlichen Unterschiede, die einen Entwickler mit Erfahrung von einem 
Neuling abhebt. Der E.m.E plant von vorneherein Debug-Möglichkeiten mit 
ein.

von Chris (Gast)


Lesenswert?

Karl Heinz schrieb:
> Dann würde ich mir halt mal den Wert von Id_neu genauer ansehen.
> Offenbar ist das Ergebnis von   Imax*1000-Id_neu*1000
> dann schon 0 bzw. so klein, dass es praktisch 0 wird und keine relevante
> Änderung mehr in Alpha hervorruft.
> Hast du jetzt eigentlich auf Floating Point umgestellt?

Ja der berechnete Strom ist zwar klein, aber eher 0,5 A statt wahren 1 
A.
Und weil alles auf float umdeklariert ist, müsste ja trotzdem eine 
Änderung registriert werden.

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Karl Heinz schrieb:
>> Dann würde ich mir halt mal den Wert von Id_neu genauer ansehen.
>> Offenbar ist das Ergebnis von   Imax*1000-Id_neu*1000
>> dann schon 0 bzw. so klein, dass es praktisch 0 wird und keine relevante
>> Änderung mehr in Alpha hervorruft.
>> Hast du jetzt eigentlich auf Floating Point umgestellt?
>
> Ja der berechnete Strom ist zwar klein, aber eher 0,5 A statt wahren 1
> A.
> Und weil alles auf float umdeklariert ist, müsste ja trotzdem eine
> Änderung registriert werden.

müsste?

hätti - wari
(Hätt ich mehr Rennen gewonnen - wäre ich Weltmeister geworden. Gerhard 
Berger, österr. F1-Rennfahrer)

Mit 'müsste' kann man nicht debuggen. Entweder es tut das oder es tut 
das nicht.

von Chris (Gast)


Lesenswert?

Ja führt wohl kein Weg dran vorbei.
Wird nur echt schwierig beim ATmega2560 an die Pins zwei Kabel 
ranzulöten..

von Jochen (Gast)


Lesenswert?

Na IRGENDEINEN Pin wirst du doch wohl erreichen können, der nichts mit 
dem Poti und den beiden Ausgängen zu tun hat. LED, Taster, was auch 
immer. Software-UART und fertig. Stell dich mal nicht so an...

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Ja führt wohl kein Weg dran vorbei.
> Wird nur echt schwierig beim ATmega2560 an die Pins zwei Kabel
> ranzulöten..

Wenn man bedenkt, dass du mittlerweile schon 2 Tage am Problem 
knabberst, ohne zu einer abschliessenden Lösung gekommen zu sein, sind 
selbst 2 Stunden unter der Lupe mit dem feinen Lötkolben und Fädeldraht 
fummeln quasi ein Schnäppchen.

von Falk B. (falk)


Lesenswert?

Tja, Design for Test.

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.