Forum: Mikrocontroller und Digitale Elektronik PID-Regler-Implementierung


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Stokmarknes (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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 Moderator
von Bastler (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Schon mal bei Arduino nachgeschaut?

https://playground.arduino.cc/Code/PIDLibrary

von Theor (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Der Andere (Gast)


Bewertung
1 lesenswert
nicht lesenswert
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

von Stokmarknes (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Theor (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Christopher J. (christopher_j23)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Pandur S. (jetztnicht)


Bewertung
0 lesenswert
nicht lesenswert
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.

von M. K. (sylaina)


Bewertung
2 lesenswert
nicht lesenswert
Stokmarknes schrieb:
> Deswegen meine Frage: kennt jemand eine passend Implementierung in C?

AVR221 von Atmel. Sehr empfehlenswert ;)

von Wolle G. (wolleg)


Bewertung
0 lesenswert
nicht lesenswert
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)

von Zorg3000 (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
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.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Bewertung
3 lesenswert
nicht lesenswert
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
von Michael V. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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ß

von Michael V. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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ß

von Pandur S. (jetztnicht)


Bewertung
0 lesenswert
nicht lesenswert
Vergiss float. Longint ist besser geeignet und schneller zu rechnen.

Denn Float hat 5-6 signifikante Stellen, waehrend longint 9 signifikante 
Stellen hat.

von Michael V. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Zitronen F.

aber long int haben doch keine Kommastellen ?

Gruß

von Al3ko -. (al3ko)


Bewertung
0 lesenswert
nicht lesenswert
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ß,

von Michael M. (michael89)


Bewertung
0 lesenswert
nicht lesenswert
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
von Pandur S. (jetztnicht)


Bewertung
2 lesenswert
nicht lesenswert
> 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
von Michael M. (michael89)


Bewertung
0 lesenswert
nicht lesenswert
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
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Bewertung
3 lesenswert
nicht lesenswert
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.

von Pandur S. (jetztnicht)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Bewertung
1 lesenswert
nicht lesenswert
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.

von Stokmarknes (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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?

von M. K. (sylaina)


Bewertung
1 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.