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 | void pi_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ß
U.R. Schmitt schrieb: > reichen mit 8Bit zu Rechnen. Sorry meine natürlich mit 8 Bit Werten. Daraus folgt die Ergebnisse in 16 Bit.
Ja, vielleicht reichen 8 bit für den ADC auch aus. Ich werde das mal testen. Die Berechnung muss auf jedenfall kürzer werden.
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.
@Michael: Vieleicht postest Du deinen jetzigen Code nochmal als Anhang mit rein.
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
Da hat sich noch ein Fehler eingeschlichen.. y=(unsigned int)Pwert+Isum; //falsch y=Pwert+Isum; //richtig
Funktioniert die Regelung jetzt besser? Gib mal ein bischen Feedback. Gruß
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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.