Hallo zusammen,
ich versuche eine Regelung für eine Laserdiode zu entwickeln, bei der
die optische Leistung geregelt wird (siehe Anhang).
Bei Temperaturschwankungen (Störgrösse) ändert sich die optische
Leistung. Der Sollwert (w) wird über eine Spannung vorgegeben. Der
Istwert wird über die integrierte Monitordiode gemessen und über einen
Transimpedanzwandler ebenfalls als Spannungssignal eingelesen (x).
Der Regelalgorithmus wird über einen Timer mit Interrupt alle 1024us
aufgerufen und der Stellwert über PWM ausgegeben, d.h. Ta=1024us. Mit
einem Filter wird das PWM-Signal in eine DC-Spannung gewandelt.
Mithilfe der Sprungantwort (siehe Anhang) habe ich bereits versucht nach
Ziegler/Nichols & co geeignete Parameter zu erhalten.
(Streckenparameter Ts=1.4ms, Tt=1.12ms, Kps=0.263, Kpr=2.93, Tn=5.43ms,
Kir*Ta=0.05). Die Sprungantwort wurde direkt am PIC-Eingang gemessen
(r(x),rückgeführtes Signal, offener Regelkreis, alle Zeitglieder
enthalten)
#Problem 1
Beim Einschalten schwingt der Kreis zu stark oder die Anregelzeit ist zu
lang (sollte ca. 5ms betragen).
#Problem 2
Beim Auftreten der Störgrösse (Temperaturzu- bzw. abnahme) funktioniert
zwar die Regelung, aber nur bis zu einer gewissen Grenze (die PWM-Grenze
von 964int ist dabei noch nicht erreicht. Das bedeutet, dass die
Leistung bei Temperaturänderung ab einem gewissen Punkt nicht nicht mehr
nachgeregelt wird). Das kann ich mir nicht erklären, da die Leistung
durch den PI-Regler über das PWM-Signal komplett ausgeregelt werden
sollte.
#Problem 3
Ich befürchte das die Regelbarkeit des Kreises schlecht ist, da die
Abtastzeit im Verhältnis zu den Streckenparametern zu gross ist. Leider
weiss ich nicht, wo ich den Code noch optimieren kann. Die meiste Zeit
wird für die beiden Multiplikationen beansprucht (gemessene Zeit für
einen Durchlauf 896us, wobei die Routine alle 1024us aufgerufen wird).
Weder mit den Parametern nach Ziegler/Nichols noch nach Prinzip der
Stabilitätsgrenze oder Herumprobieren haben geholfen. Auch im Forum habe
ich noch keine Lösung gefunden.
Vielleicht kann mir jemand ein paar Tipps geben?
Besten Dank.
Michael schrieb:> Ich befürchte das die Regelbarkeit des Kreises schlecht ist, da die> Abtastzeit im Verhältnis zu den Streckenparametern zu gross ist. Leider> weiss ich nicht, wo ich den Code noch optimieren kann. Die meiste Zeit> wird für die beiden Multiplikationen beansprucht (gemessene Zeit für> einen Durchlauf 896us, wobei die Routine alle 1024us aufgerufen wird).
Wenn ich jetzt mein über 20 Jahre altes RT Wisen versuche auszupacken,
dann meine ich mich zu erinnern, daß deine Abtastzeit zumindest so 4-5
mal kleiner sein sollte als deine relevanten Zeitkonstanten. Da die im
Bereich einer Millisekunde liegen wäre eine Abtastzeit von 200µs
angebracht.
Du schreibst von viel zeit für Multiplikation, was multiplizierst Du
denn?
Fliesskomma?
In C?
Du solltest auf jeden Fall zu Festkomma oder integerberechnung
übergehen, das müsste ohne zu große Probleme machbar sein, deine
Regelgröße und dein Istwert kommen ja auch mit 8/10/16 Bit Genauigkeit
als Integer rein.
Viel Erfolg
Hier am Anfang deiner ISR hast du zb schon mal 2 Stellen, an denen du
locker Zeit sparen kannst
1
voidpi_sample_isr(void)
2
{
3
output_toggle(PIN_A5);//Test um Ta zu messen
4
set_adc_channel(1);//ADC-Kanal 1 auswählen
5
delay_us(10);//Delay für Setup
6
w=read_adc();//Einlesen der Spannung am Pin7 (AN0)
7
set_adc_channel(0);//ADC-Kanal 0 auswählen
8
delay_us(10);//Delay für Setup
9
x=read_adc();//Einlesen der Spannung am Pin6 (AN1)
10
e=w-x;
du musst zb nicht die Soll-Spannung bei jedem ISR Aufruf erneut
bestimmen. Im Vergleich zum Rest ändert sich die ja so gut wie nie. Es
reicht völlig, wenn das die Hauptschleife macht.
Mach die Division durch 8192 erst nachdem du die Einzelterme
zusammengezählt hast
a/8192 + b/8192 -> ( a + b ) / 8192
mathematisch ist das äquivalent. Aber in C mit Integer Arithmetik macht
es unter Umständen einen großen Unterschied.
Vielen Dank für die Infos.
Die Sollwertabfrage habe ich jetzt in der Hauptschleife und die Division
nur am Ende durchgeführt. Jetzt benötigt ein Durchlauf der ISR noch
768us. Dieser Wert variiert noch, da die zu verarbeitenden Grössen sich
auch ändern. Deshalb darf der nächste Interrupt erst erfolgen wenn der
vorherige abgeschlossen wurde, d.h. das Interrupt-Flag gelöscht wurde.
Nur so bleibt die Abtastzeit konstant. Somit bleibt in meinem Fall die
Abtastzeit (Zählerüberlauf) weiterhin auf 1024us, da der nächste
kleinere Teiler 1:4 (=512us@8MHz) ist. Also benötige ich eine
Durchlaufzeit <512us.
@U.R. Schmitt
Um Zeit zu sparen, habe ich bereits die Multiplikation der
Reglergleichung als Integer programmiert, sonst dauert das noch länger.
Überleg welche deiner Variablen unbedingt long bzw. int32 sein müssen.
Welche Operationen werden in welcher Bitbreite durchgeführt?
Wozu den Prozessor durch eine 32 Bit Multiplikation durchjagen, wenn es
16 Bit auch tun, weil das Ergebnis sowieso nicht größer als 16 Bit
werden kann.
(Ich habs jetzt nicht analysiert, aber das erscheint mir augenfällig)
zb
signed long w=0;
signed long x=0;
signed int32 e=0;
w kommt vom ADC, x ebenfalls.
Müssen das wirklich long sein?
e=w-x;
if(e>964)
e=964;
if(e<-964)
e=-964;
e ist die Differenz von w und x (bei dir jetzt gerechnet als long
Subtraktion). Braucht es für e wirklich 32 Bit? Kann w - x überhaupt
groß genug werden um einen int zu sprengen?
Und so gehst du deine komplette Berechnung durch. Nimm Worst Case
Zahlenwerte an und sieh nach wie gross die Ergebnisse werden.
Ich denke du kannst auf die Art mindestens 30%, wenn nicht mehr, der
Laufzeit deiner ISR einsparen. Achte vor allen Dingen darauf, dass du
Multiplikationen in der Bitbreite runterbringst, das bringt am meisten.
Du bist dir hoffentlich bewusst, dass
a >> 13
und
a / 8192
nicht dasselbe machen, wenn a eine negative Zahl ist?
Guten Morgen,
die Geschichte mit den Datentypen ist schon ziemlich heikel wenn man
nicht aufpasst. Ich bin da auch schon öfter in die Falle getappt.
Also ich hab mir das folgendermassen gedacht:
w=x=signed long, da ADC mit 10bit arbeitet
(bei CCS-Compiler: long=int16=16 bit)
e kann auch negativ werden, deshalb signed
e=w-x deshalb w,x auch signed
Pwert=Kpr*e: da ich eine float Berechnung vermeiden möchte, aber
Kpr=2.93 ist, muss ich 2.93 umwandeln:
293 2^13 2400256 24002 (die letzten beiden Stellen streichen)
---* ---- = ------- --> -----
100 2^13 819200 8192
also muss ich e mit 24002 multiplizieren und wenn e=964max dann brauche
ich int32 (32bit). Darum müssen doch auch alle anderen Variablen vom
gleichen Datentyp sein? Sonst erhält man ein falsches Ergebnis (hab ich
mit dem Simulator getestet).
Alternativ könnte ich auch Pwert=Kpr*(int32)e rechnen (cast), aber das
bringt wohl auch keinen Vorteil?
##############################################################
Schiebeoperation bei negativen Zahlen:
Da bin ich gestern auch schon darüber gestolpert. Ich glaube ich kann
das so abfangen:
y=Pwert+Isum;
if(y&0x80000000)
{
y>>=13;
y|=0xFFF80000;
}
else
{
y>>=13;
}
U.R. Schmitt schrieb:> Dann caste es doch zum shiften in unsigned!
Sorry das war wohl Blödsinn.
@ Karl Heinz:
Warum ist
a >> 13
und
a / 8192
nicht das selbe? Meine C Kenntnisse sind in den letzten Jahren etwas
eingerostet, aber macht >> nicht bei signed Variablen ein arithmetischen
Shift, also zieht bei negativen Zahlen Einsen nach?
U.R. Schmitt schrieb:> nicht das selbe? Meine C Kenntnisse sind in den letzten Jahren etwas> eingerostet, aber macht >> nicht bei signed Variablen ein arithmetischen> Shift, also zieht bei negativen Zahlen Einsen nach?
Was >> bei signed Werten macht, ist überhaupt nicht definiert. Damit
fängt es schon mal an :-)
Und selbst wenn es das von dir vorgeschlagene tut (was sehr
wahrscheinlich ist), kommt immer noch nicht dasselbe raus.
Probiers einfach aus:
-9 / 4 ergibt -2 das sollte soweit klar sein
-9 ist binär
00001001 +9
11110110 invertiert
11110111 2-er Komplement
Binär ist also -9 gleich 0b11110111
das jetzt um 2 Stellen nach rechts geschoben 0b11111101
Das ist welche Zahl
11111101 Muster
00000010 invertiert
00000011 2-er Komplement
0b11 ist 3, d.h. 0b11111101 war -3
Huch -9 / 4 ergibt -3? Das .... stimmt wohl nicht ganz
Division schneiden konzeptionell Kommastellen ab, runden also zur 0 hin.
Shift Operationen 'runden' aber immer zur nächst kleineren Zahl. Daher
kommt bei negativen Zahlen was anderes raus
-2.3 in Richtung 0 gerundet ist nun mal -2
-2.3 zur nächst kleineren Zahl gerundet ergibt aber -3
Michael schrieb:> 293 2^13 2400256 24002 (die letzten beiden Stellen streichen)> ---* ---- = ------- --> -----> 100 2^13 819200 8192
Hast du mal in der Simulation ausprobiert, was passiert wenn du anstelle
von *24002 / 8192 einfach *24 / 8 rechnest?
Du hast es hier ja mit einem PI Regler zu tun. Seine Aufgabe ist es ein
e auf 0 zu bringen. Und ob er da jetzt mit einem Stellwert um 1 oder 2
größer oder kleiner gegensteuert, spielt doch bei großen Fehlern nicht
so sehr die große Rolle.
Ich könnte mir auch einen Hybrid-Ansatz vorstellen. Bei großen
Fehlerwerten e rechnet der PI Regler zwar schnell aber etwas ungenauer
(*24/8) und wenn e kleiner wird (vorzugsweise dann, wenn die Berechnung
dann wieder komplett in 16 Bit passt) wird sukzessive wieder in Richtung
*24002/8192 zurückgeschaltet.
> ich int32 (32bit). Darum müssen doch auch alle anderen Variablen vom> gleichen Datentyp sein?
Dazu muss e immer noch nicht 32 Bit sein.
Du kannst e ja als 16 Bit berechnen und nur zur Multiplikation auf 32
Bit hochcasten.
> Sonst erhält man ein falsches Ergebnis (hab ich> mit dem Simulator getestet).
Logisch. Wenn du mathematisch ein 32 Bit Ergebnis erhältst aber nur mit
16 Bit multiplizerst, hast du einen Überlauf und damit ein falsches
Ergebnis
> Alternativ könnte ich auch Pwert=Kpr*(int32)e rechnen (cast), aber das> bringt wohl auch keinen Vorteil?
Ein bischen was würde es schon bringen. Die vorhergehende Subtraktion
ist dann nicht mehr 32 Bit sondern nur noch 16 Bit
Aber über so einen Hybrid Ansatz: ungenau aber schnell bei großen
Fehlern und bei kleinem Fehler sukzessive auf genauere Berechnung
umschalten. Ich denke das könnte in der Laufzeit schon einiges bringen.
Karl heinz Buchegger schrieb:> Division schneiden konzeptionell Kommastellen ab, runden also zur 0 hin.> Shift Operationen 'runden' aber immer zur nächst kleineren Zahl.
Mist das hab ich Blödmann übersehen.
Dürfte aber bei dem Algorithmus nicht ins Gewicht fallen.
Aber das das Verhalten von '>>' bei negativen Zahlen nicht spezifiziert
ist wusste ich nicht (mehr?)
Programmiere in den letzten 5 Jahren praktisch ausschliesslich in Java,
dort ist es spezifiziert und es gibt sogar ein arithmetisches und ein
binäres SHR, wobei ich mir nie merken kann welches jetzt welches ist.
Aber in den Anwendungen die ich machen muss ist Bitschieberei eher die
Aussahme :-)
Karl heinz Buchegger schrieb:> Hast du mal in der Simulation ausprobiert, was passiert wenn du anstelle> von *24002 / 8192 einfach *24 / 8 rechnest?
Es ist eh ziemlich unsinnig die Parameter mit einer Genauigkeit von 13
Bit rechnen zu wollen. Er hat sie wenn ich das recht sehe grafisch
bestimmt, also nicht genauer als sagen wir 2%, das wären 6 Bit. Seine
Messwerte sind mit einem 10 Bit Wandler, wobei die Genauigkeit auch
nicht mehr als 8 Bit sein dürften.
Und am wichtigsten. Seine Abtastzeit ist so groß wie die wesentlichen
Zeitkonstanten der Regelstrecke, das bedeutet, wenn er einen Stellwert
anlegt, ist der im Extramfall bis zum nächsten Abtasten schon komplett
falsch, weil das System inzwischen seinen Ausgangswert schon um relativ
die Hälfte geändert hat.
Also sollte es vollkommen reichen mit 8Bit zu Rechnen.
Egal wie aber er sollte mit der Abtastfrequenz auf zumindest 0,25 ms
runterkommen.
Gruß
Hallo,
ich habe versucht, das Ganze mit 8bit zu realisieren, aber da ergibt
sich für mich ein Problem:
w-Wert einlesen (w=unsigned int=8bit,ADC=8bit), Wertebereich: 0..0xFF
o.k.
x-Wert einlesen (x=unsigned int=8bit,ADC=8bit), Wertebereich: 0..0xFF
o.k.
e=w-x?
Wenn ich auch negative Werte zulassen möchte(muss), welchen Datentyp
nehme ich dann für e?
e=signed int --> Wertebereich -127..+128, dann kann |w-x| nur <128 sein
e=signed long(16bit) -->Wertebereich -32768<=e<=32767 <-- würde das mit
einem cast funktionieren? Wenn ja wie?
e=(???)w-(???)x;
Die Berechnung der P-,I- und D-Anteile habe ich vereinfacht und komplett
auf "rationale Beiwerte" verzichtet, d.h.:
...
Pwert=e<<2; //Pwert=4*e, 2 bit schieben nach links
if(e<0)
Pwert|=0x0003; //2 bit rechts jetzt 0 --> mit 1 maskieren
Iwert=e>>1; //Iwert=0.5*e, 1 bit schieben nach rechts
if(e<0)
Iwert|=0x8000; //MSB mit 1 maskieren
...
usw.
Das geht auf jeden Fall schneller und ich kann jetzt die ISR konstant
nach 256us aufrufen. Nachteilig ist, dass ich so nur Werte von 2^x
verwenden kann.
Die Berechnung wie vorgeschlagen hat irgendwie nicht richtig
funktioniert, ich weiss nicht warum aber der uP hat nicht das
ausgegeben, was ich erwartet habe (entweder maximale Leistung oder 0).
Noch eine regelungstechnische Frage:
Die aufgenommene Sprungantwort der Laserdiode entspricht ja der
optischen Antwort auf den Eingangssprung--> deshalb die kurzen
Streckenzeiten ~1ms.
Aber: Die Diode ist in einem Gehäuse eingebaut und dieses erwärmt sich
durch den Betrieb der Laserdiode - nur eben viel langsamer. Und durch
die Erwärmung nimmt die optische Leistung (=Istgrösse) bei konstantem
Strom ab.
Muss man da nicht quasi eine Störsprungantwort aufnehmen? Diese hätte
nämlich deutlich grössere Zeitkonstanten.
Dann wäre e=w-x (w=konstant, x=f(z), Stichwort Festwertregler)
Irgendwie hab ich da gerade das Problem mit dem Ei und der Henne... :-)
Vielen Dank.
Gruss Michael
Michael schrieb:> Hallo,>> ich habe versucht, das Ganze mit 8bit zu realisieren, aber da ergibt> sich für mich ein Problem:>> w-Wert einlesen (w=unsigned int=8bit,ADC=8bit), Wertebereich: 0..0xFF> o.k.> x-Wert einlesen (x=unsigned int=8bit,ADC=8bit), Wertebereich: 0..0xFF> o.k.> e=w-x?> Wenn ich auch negative Werte zulassen möchte(muss), welchen Datentyp> nehme ich dann für e?
Falsche Fragestellung
Ist mir auch früher schon mal aufgefallen, dass du anscheinend der
Ansicht bist, dass der Datentyp des Ergebnisses sich irgendwie auf die
Berechnung auswirkt.
Dem ist nicht so!
zuerst musst du dich fragen:
wenn ich
w - x
rechne, wie mach ich das, dass ich das richtige Ergebnis bekomme, selbst
wenn w und x uint8_t sind.
Dazu musst du beide Werte zunächst nach int16_t bringen.
(int16_t)w - int16_t(x)
welchen Wertebereich kann das haben?
Das Ergebnis kann von -255 bis +255 gehen (w gleich 255, x gleich 0;
bzw. umgekehrt). Und damit ist als Datentyp für e ein int8_t schon aus
dem Rennen.
e muss ein int16_t sein.
....ZA
Ist mir auch früher schon mal aufgefallen, dass du anscheinend der
Ansicht bist, dass der Datentyp des Ergebnisses sich irgendwie auf die
Berechnung auswirkt.
Dem ist nicht so!
....ZE
Vielleicht konnte ich mich nicht richtig verständlich machen. Meiner
Meinung nach wird eine Berechnung in int als Ergebnis ebenfals int
haben, also 8 bit. Dieses Ergebnis möchte ich einer Variablen vom typ
long (16bit) zuweisen, wegen der negativen Werte. Und das geht so nicht,
da das High-Byte des Ergebnisses mit 0 gefüllt ist. Also muss ich wie Du
gerade schriebst einen cast anwenden, der das 8bit Ergebnis auf 16bit
wandelt.
Ist das soweit richtig?
Ich habe es mittlerweile so gemacht, das ich w<x abgefragt habe und das
Ergebnis mit 0xFF00 maskiert habe. Funktioniert auch. Aber Deine
Variante ist sicher eleganter.
(int16_t)w - int16_t(x) <-- ist die Schreibweise wirklich so?
Michael schrieb:> Vielleicht konnte ich mich nicht richtig verständlich machen. Meiner> Meinung nach wird eine Berechnung in int als Ergebnis ebenfals int> haben, also 8 bit.
Du scheinst einen seltsamen Compiler zu benutzen.
int hat 8 Bit
long hat 16 Bit
mit long in 16 Bit kann ich noch leben, aber ein int mit 8 Bit ist vom
C-Standard her überhaupt nicht gedeckt. int hat mindestens 16 Bit.
Ausser natürlich dein Compiler kocht sein eigenes Nicht-Standard
Süppchen.
> da das High-Byte des Ergebnisses mit 0 gefüllt ist. Also muss ich wie Du> gerade schriebst einen cast anwenden, der das 8bit Ergebnis auf 16bit> wandelt.> Ist das soweit richtig?
No.
Du darfst schon gar kein 8-Bit Ergebnis haben. Denn 8 Bit reichen nicht
um jedes mögliche Ergebnis aufnehmen zu können. Es ist völlig wurscht ob
du dann im Nachhinein das Ergebnis von 8 auf 16 Bit aufbläst. Da ist das
Kind schon in den Brunnen gefallen. Die Berechnung selber darf nicht in
8 Bit gemacht werden.
>> Ich habe es mittlerweile so gemacht, das ich w<x abgefragt habe und das> Ergebnis mit 0xFF00 maskiert habe. Funktioniert auch.
Glaub ich nicht.
nagative Zahlen funktionieren anders. Stichwort 2-er Komplement.
> (int16_t)w - int16_t(x) <-- ist die Schreibweise wirklich so?
Schreibfehler
(int16_t)w - (int16_t)x
Ein Cast reicht auch. Der andere Teilnehmer an der Operation wird dann
vom Compiler implizit auf int16_t gecastet.
Hier nochmal der C-code. Es läuft zwar aber irgendwie passt da was noch
nicht. Ich bin noch am Testen und melde mich morgen nochmal.
Besten Dank
Gruss Michael
Der momentane Stand ist so, dass sich ein stabiler Wert der
Laserleistung einstellt. Die Schwankungen (springen) des Istwertes sind
durch Verwendung des 8bit ADC auch geringer geworden.
Aber das Regelverhalten bei Auftreten der Störgrösse (Temperatur) ist
immer noch unbefriedigend. D.h. Wird die Laserdiode von aussen erwärmt,
so nimmt die Laserleistung erst ab und dann steigt sie über den Sollwert
hinaus (bis zu 20% je nach Temperatur). Beim Kühlen entsprechend
umgekehrt. Also der Regler arbeitet schon, aber erstens langsam und
zweitens viel zu starke Gegenkopplung.
Als Einstellmöglichkeit habe ich die Verstärkung des rückgeführten
Istwertes(Monitorstrom), und die Parameter des Reglers Kpr und Kir (und
den Sollwert).
e=(w-x) x: Verstärkung des Monitorsignals (stromproportionale Spannung,
x=Umon=Rv*Imon; w=Usoll)
Ist Rv zu klein, so ist auch das ausgegebene Stellsignal zu klein und
die Leistung nimmt bei Erwärmung ab, obwohl das PWM-Signal grösser wird.
(PWM-Signal nimmt zu wenig zu)
Ist Rv zu gross so ist so ist das ausgegebene Stellsignal zu gross und
die Leistung steigt bei Erwärmung an, das PWM Signal wird ebenfalls
grösser.
(PWM-Signal nimmt weiterhin zu, obwohl es kleiner werden müsste). Bei zu
grossem Rv schwingt die Regelung.
Bei 100% Laserdiode (30mW) habe ich eine Sollspannung von 1.99V und eine
Monitorspannung von 2.97V. Müssten nicht beide Signale so skaliert
werden, dass sie gleich gross sind?
Wenn aber beide Signale gleich skaliert sind, sind ja beide auch gleich
gross, da Laserstrom und Monitorstrom proportional sind. Das würde
bedeuten, das bei einem P-Regler die Regelabweichung 0 wäre und das
Stellsignal auch (y=Kpr*e). Aber das kann nicht sein, da der P-Regler
eine bleibende Regelabweichung benötigt. Das System würde sonst nur
schwingen.
Ein häufig diskutiertes Thema hier im Forum...