Hi, ich möchte auf einem Mikrocontroller einen PID-Regler implementieren: abhängig von einem analogen Eingangswert (0..1023) soll ein Digitalausgang an- oder ausgeschaltet werden. Der Hardwareteil ist kein Problem, ich würde jetzt nur eine fertige PID-Implementierung suchen, bei der man entsprechende Parameter dynamisch übergeben kann (um das Rad nicht noch mal komplett neu erfinden zu müssen). Deswegen meine Frage: kennt jemand eine passend Implementierung in C? Danke!
:
Verschoben durch User
An sich ist das eine ganze einfache Gleichung, die man in C oder den meisten höheren Programmiersprachen nahezu wortwörtlich hinschreiben kann. Der folgende Halbsatz: "... bei der man entsprechende Parameter dynamisch übergeben kann (um das Rad nicht noch mal komplett neu erfinden zu müssen)." deutet an, dass für Dich dennoch ein Problem zu bestehen scheint. Der einzige Anhaltspunkt scheint mir das Wort "dynamisch" zu sein. Kann es sein, dass Du auch die Reglerparameter programmatisch bestimmen willst? Vielleicht schreibst Du mal ausführlicher, worum es Dir geht.
Stokmarknes schrieb: (um das Rad nicht noch mal komplett neu > erfinden zu müssen). Nenn das Kind doch beim Namen: Weil du dich nicht mit programmieren oder der Mathematik dahinter beschäftigen willst. Das sollte wie oben beschrieben kein Problem sein, denn das gibts bestimmt schon tausend mal im Netz Stokmarknes schrieb: > bei der man entsprechende Parameter > dynamisch übergeben kann Was meinst du damit. Es sollte ja kein Problem sein, daß die Konstanten als 8 oder 16 Bit Werte in einer Speicherzelle stehen. Willst du sie jetzt im Programm verändern können und neu flashen oder sollen sie während des Programmablaufs geändert werden? Und wenn, dann wie? Mit Potis? Mit einer RS232 Terminalanbindung? ... Und hier wirds schwieriger mit einem fertigen Programm
Theor schrieb: > Der einzige Anhaltspunkt scheint mir das Wort "dynamisch" zu sein. Kann > es sein, dass Du auch die Reglerparameter programmatisch bestimmen > willst? Genau das, es geht um eine generische Implementierung, bei der der Anwender die (PID-)Parameter entsprechend seiner Umgebung selber verändern kann.
Stokmarknes schrieb: > Theor schrieb: >> Der einzige Anhaltspunkt scheint mir das Wort "dynamisch" zu sein. Kann >> es sein, dass Du auch die Reglerparameter programmatisch bestimmen >> willst? > > Genau das, es geht um eine generische Implementierung, bei der der > Anwender die (PID-)Parameter entsprechend seiner Umgebung selber > verändern kann. Das sind nach meinem Verständnis zwei verschiedene Vorgänge. Einmal ist der Anwender der Akteur, im anderen Fall das Programm. 1. Der Anwender verändert die Parameter. Das würde an meiner Grundaussage nichts ändern. Die PID-Gleichung ist einfach hin zu schreiben. Hinzufügen ist nur eine Eingabemöglichkeit. Das ist sehr einfach und völlig unproblematisch - was das Programm betrifft. 2. Das Programm ermittelt (bestimmt) die Parameter. Das ist schon wesentlich komplizierter.
Nun ja, dann hast du eben zwei Teilprobleme. Einmal den PID-Regler an sich und zum anderen das dynamische übertragen von Werten. Für letzteres gibt es viele Möglichkeiten, da musst du schon eher sagen wo es hingehen soll. Drehencoder mit Display, UART-Terminal, rein analoge Drehpoties...
Die Aufgabenstellung ist derart megatrivial, dass es eine einfache Uebungsaufgabe darstellt. Mach mal und komm dann wieder. Suchen, anpassen und verstehen sind schwieriger als selbst neu schreiben.
Stokmarknes schrieb: > Deswegen meine Frage: kennt jemand eine passend Implementierung in C? AVR221 von Atmel. Sehr empfehlenswert ;)
Christopher J. schrieb: > Für letzteres > gibt es viele Möglichkeiten, da musst du schon eher sagen wo es hingehen > soll. Drehencoder mit Display, UART-Terminal, rein analoge Drehpoties... Jemand, der mit der Materie erst anfängt, weiß zunächst noch gar nicht, womit er(sie) anfangen soll und fragt deshalb hier im Forum, was für einen Anfänger am einfachsten umzusetzen ist. (Stichwort: Fahrrad neu erfinden)
Meine Fresse, seitenweise großkotzige Selbstdarstellung aber nicht eine einzige Antwort dabei, die irgendwie weiter helfen würde. Was habt ihr alle für ein Problem mit eurem Ego? Seid und dürft ihr im realen Leben sonst nix? Könnt ihr euch nicht lieber einen SUV kaufen statt hier rumzuprollen? An den Thread-Opener: schau dir mal https://homepages.uni-regensburg.de/~erc24492/PID-Regler/AVR221/IAR/doxygen/pid_8c.html an, ich habe zwar keine Idee, was die Reset-Funktion machen soll, aber das dürfte das sein, was du suchst.
Zorg3000 schrieb: > aber nicht eine > einzige Antwort dabei, die irgendwie weiter helfen würde. Doch, natürlich sind da brauchbare Antworten bei. Hier ist z.B. eine: M. K. schrieb: > AVR221 von Atmel. Sehr empfehlenswert ;) Man sollte vllt. noch erwähnen, das damit eine Application Note von Atmel/Microchip und eine Impementation in C gemeint ist. Und noch eine: Bastler schrieb: > Schon mal bei Arduino nachgeschaut? > > https://playground.arduino.cc/Code/PIDLibrary Du Spassvogel verlinkst ja selber auf die AVR221: Zorg3000 schrieb: > An den Thread-Opener: schau dir mal > https://homepages.uni-regensburg.de/~erc24492/PID-Regler/AVR221/IAR/doxygen/pid_8c.html Zorg3000 schrieb: > ich habe zwar keine Idee, was die Reset-Funktion machen soll Es ist sinnvoll, nach einer Änderung von P,I oder D den Regler zurückzusetzen. Schadet auch nicht bei der Initialisierung. Einfach mal AVR221 lesen.
:
Bearbeitet durch User
Hallo, hab den Rest der Beiträge nicht gelesen, aber hab mir mal die Formeln aus Wikipedia besorgt und nach programmiert ggf hilft es dir ja. float PIDRegler(float T1,float T2) // PID-Regler { // Empfindlichkeit des PID-Reglers float Tn = 1.0; // dient zur Einstellung des PID Reglers float Tv = 1.0; // dient zur Einstellung des PID Reglers float Kp = 1.0; // dient zur Einstellung des PID Reglers // Variablen für P,I,D float P,I,D; // Zeitkostente float delta_t=0.5; En = T1 - T2; // T1 = Temperatur_1 = Sollwert hier // T2 = Temperatur_2 = Istwert // En = Regelabweichung // Berechnugn P I D P = En - en1; D = (delta_t / Tn) * En; I = Tv * (En - 2 * en1 + en2); // Berechnung neuer Stellgröße delta_y = Kp * ( P + D + I); Yn = yn1 + delta_y; // Speichert alte Werte en2 = en1; en1 = En; yn1 = Yn; // Begrenzt Yn auf 100 if (Yn > 100.0) Yn=99.99; if (Yn < 0.0) Yn= 0.0; return Yn; // Gibt Stellgröße zurück und springt ins Hautprogramm zurück } DIE FUNKTION MUSS ALLE 0.5 SEKUNDEN AUFGERUFEN WERDEN DAS ES FUNKTIONIERT. falls du dich nicht so auskennst. TV, TN , KP kann man nachgoogln .. zu not auch der Funktion übergeben. Hoffe konnte dir helfen. Gruß
P.s Bei meinem alten Programm sind en1,en2, etc als Globale Varirablen deklaiert. Endweder machst du das auch oder deklaierst die in der Funktion mit -> static da die Werte beim wiederaufruf vorhanden sein müssen. static float en1,en2,etc.; oder vorm Hauptprogramm float en1,en2,etc.; Gruß
Vergiss float. Longint ist besser geeignet und schneller zu rechnen. Denn Float hat 5-6 signifikante Stellen, waehrend longint 9 signifikante Stellen hat.
Michael V. schrieb: > aber hab mir mal die Formeln aus Wikipedia besorgt und nach programmiert > ggf hilft es dir ja. Wobei erwähnt werden sollte, dass das lediglich für einfache/träge Strecken erfolgreich ist, auf diese gezeigte Weise von analog auf digital umzusteigen. Wenn phasenreserve und Dynamik kritisch sind, wird meist Tustin Transformation (bilinear Transformation) verwendet. Gruß,
Al3ko -. schrieb: > Wobei erwähnt werden sollte, dass das lediglich für einfache/träge > Strecken erfolgreich ist, auf diese gezeigte Weise von analog auf > digital umzusteigen. > > Wenn phasenreserve und Dynamik kritisch sind, wird meist Tustin > Transformation (bilinear Transformation) verwendet. Gute Ergänzung :) Stokmarknes schrieb: > abhängig von einem analogen Eingangswert (0..1023) soll ein > Digitalausgang an- oder ausgeschaltet werden. Würde da nicht ein Zweipunkt-Regler ausreichen ? Gruß
:
Bearbeitet durch User
> aber long int haben doch keine Kommastellen ?
Was willst du denn mit Kommastellen ?
Nehmen wir einen Temperaturregler :
ADC : -32768 .. 32767 , entsprechend zB -30..400 Grad, wie auch immer
linearisiert : mit 20 Schritt pro Grad.
Solltemp = 2452, entsprechend (irgendwas), in der ADC skala
Isttemp = 1127
Fehler = 1325
Proportional gain= 150 -> P Anteil = 198750
Integral gain = 10
Integrator = Integrator + 10* Fehler
sei der Integrator bei 2134436 -> I Anteil = 2134436
Ausgang Regler = P Anteil + I Anteil = 2333186
Das Stellglied, dh die Heizung ist PWM mit 0-4095 (12bit)
Wir bilden den Reglerausgang auf das Stellglied ab, mit
2^24 = 16777216 -> 2^12 = 4096
als div 4096, heisst als shift rechts 12.
Ausgang Regler (2333186) -> Stellglied = 569
Fertig. Du benoetigst keine Kommastellen.
:
Bearbeitet durch User
Zitronen F. schrieb: > Fertig. Du benoetigst keine Kommastellen. Klar so gehts auch, aber benutzt man die Umgewandelten Temperaturen (z.B 23.10°C) und nicht den ADC-Wert... halt nicht. Naja ich wollte hier nur den Fragesteller helfen. Viele Wege führen nach Rom :) Gruß
:
Bearbeitet durch User
Michael V. schrieb: > Klar so gehts auch, aber benutzt man die Umgewandelten Temperaturen (z.B > 23.10°C) und nicht den ADC-Wert... halt nicht. Falls man für den Benutzer da was anzeigen muss, kann man immer noch eine Rechenroutine für eine Benutzerschnittsstelle zu machen, die dann die gewohnte Temperatur errechnet. Aber intern reicht es, ganzzahlig zu arbeiten, auch weil der PID Regler ja am besten eine gleichmässige Durchlaufzeit haben sollte. Wie o.a. enthält AVR221 auch schöne Grafiken und ist nicht nur für AVR Anwender nützlich. Bei mir läuft dieser PID Regler auch auf XMega und STM32. Michael V. schrieb: > Würde da nicht ein Zweipunkt-Regler ausreichen ? Sollte man als erstes mal checken.
Die float-Zahl fuer den Benutzer wird ja eher selten benoetigt. Einmal zur Eingabe des Vorgabewertes, und repetitiv zur Anzeige. Wobei je nach Zeitkonstante die Anzeige limitiert schnell sein soll. Mehr als 10 Anzeigewerte pro Sekunde werden eh nichts.
Zitronen F. schrieb: > Vergiss float. Longint ist besser geeignet und schneller zu rechnen. Nö, float ist schneller, da es nur 24Bit berechnen muß. Zitronen F. schrieb: > Denn Float hat 5-6 signifikante Stellen, waehrend longint 9 signifikante > Stellen hat. Von den 9 Stellen bleibt aber kaum was übrig, wenn die Koeffizienten sehr klein werden. Float rechnet dagegen über einen großen Bereich genau. Ich hab deshalb den Ärger mit Ganzzahlen hinter mir gelassen.
Peter D. schrieb: > Zitronen F. schrieb: >> Vergiss float. Longint ist besser geeignet und schneller zu rechnen. > > Nö, float ist schneller, da es nur 24Bit berechnen muß. Ähhhm, NÖ! Bevor ein float berechnet werden kann muss es ersteinmal ausgepackt und normalisiert werden. Guck dir im objdump mal ein float MUL und ein 32Bit int MUL an. Da staunste Bauklötze bei den Berechnungen. Statt longint und Konsorten sollten stdint.h genutzt werden, also uint32_t. Da hat der 8Bit Prozessor (z.B. nen AVR) die 32Bit mul/div/add/sub schon alle durch bevor der float überhaupt erstmal ausgepackt wurde. Ein 32Bit Prozessor hat den 32Bit Wert noch schneller durch. Float wird erst schnell mit einer FPU.
Danke für die brauchbaren Hinweise. Was mir noch unklar ist: im Beispiel oben gibt es diese Werte hier: Michael V. schrieb: > float Tn = 1.0; // dient zur Einstellung des PID Reglers > float Tv = 1.0; // dient zur Einstellung des PID Reglers > float Kp = 1.0; // dient zur Einstellung des PID Reglers und in pid.c einen SCALING_FACTOR. Die scheinen die Reaktionsgeschwindigkeit des Reglers zu beeinflussen. Ich hätte das aber jetzt so verstanden, dass man das eigentlich mit den PID-Werten macht - wieso gibt es die noch mal zusätzlich?
Die AVR221 ist ein PID der mit Integer Werten arbeitet. Du müsstest also die entsprechenden Werte für TN, TV und KP als Integer Werte umrechnen (im einfachsten Fall einfach die Nachkommastellen abschneiden). Der Scaling Factor ist dazu zu, die Genauigkeit zu erhöhen. Dazu ein Beispiel: Du hast KP ermittelt und festgestellt dass z.B. 1.25 ein guter KP-Wert wäre. Der PID aus der AVR221 rechnet/arbeitet aber nur mit Interger Werten. Willst du also die Nachkommastellen mit benutzen im PID dann musst du das Komma um mindestens zwei Stellen nach rechts schieben, also mindestens mit 100 multiplizieren. Genau das macht der Scaling Factor in der AVR221. Man rechnet dann in der PID-Routine/Funktion mit den um den Scaling Factor multiplizierten Werten und dividiert den Rückgabewert später wieder durch den Scaling Factor. In der AVR221 wurde nur nicht 100 als Scaling Factor gewählt sondern 128. 128 ist eine Zweierpotenz, jeder C-Compiler sollte das heute erkennen und so wird aus einer Multiplikation/Division eine Shift-Operation die erheblich schneller ausgeführt werden kann als eine Multiplikation/Division.
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.