PIDs Demystified

    • PIDs Demystified

      Im Folgenden will ich versuchen zu erklären, was PIDs sind und wie sie funktionieren. Dabei will ich neben dem "Was" auch auf das "Wie" eingehen. Die Ermittlung optimaler Werte lasse ich jedoch bewusst außen vor, da es hierfür schon sehr viele sehr gute Tutorials gibt und meiner Meinung nach auch nicht der eine, richtige Weg existiert.
      In allen Beispielen will ich mich lediglich auf die Pitch-Achse beziehen. Diese ist bei einer gewöhnlichen Fernsteuerungsbelegung (Mode 2) vertikal auf dem rechten Stick gelegt. Dieses Beispiel soll es einfach machen, positive Werte als Stick Oben - also Vorwärtsrolle - und negative Werte als Stick Unten - also Rückwärtsrolle zu identifizieren.

      PIDs sind Variablen in einem sogennanten PID-Controler. Dieser wird in vielen Regelungssystemen verwendet in denen die Reaktion auf eine Regulierung träge geschieht. Ein alternatives Beispiel ist hier eine Heizanlage die etwas schnellstmöglich und möglichst exakt auf eine bestimmte Temperatur aufheizen und dort halten soll.
      PID steht für Proportional, Integral und Derivative. Dies sind die englischen Bezeichungen für etwas, an das ihr euch eventuell noch aus dem Schulmathematikunterricht erinnert: Graphen und Kurvendiskussion. Dabei erübrigt sich die Übersetzung von Proportional und Integral, "Derivative" bedeutet aber "Ableitung".

      Bevor ich nun aber auf die einzelnen Komponenten eingehen will, gilt es noch einmal die Grundsätze klar zu erklären. In einem Flight Controller treffen für die Pitch-Achse zu jedem Zeitpunkt zwei Werte aufeinander: Der Input durch die Fernsteuerung (Set-Point genannt) und der Messwert des Gyroskops auf derselben Achse. Ohne PID Controller sind beide Werte komplett unabhängig und während es unterschiedlichste Input Werte gäbe würden die Messwerte davon komplett unbeeinflusst sein. Auch wichtig ist zu verstehen, dass es hier nicht um absolute Winkel des Copters sondern um Winkelgeschwindigkeiten geht. Typischerweise (abhängig von Rates und Expo) haben Piloten hier einen Maximalwert von über 1000 Grad pro Sekunde konfiguriert (also drei komplette Flips pro Sekunde).
      Für die einzelnen PID Komponenten wird allerdings immer nur die Differenz zwischen dem Input (Set-Point) und dem Messwert ausgewertet, dies ist der Set-Point-Error. Die Aufgabe des PID Controllers ist also, den Set-Point-Error zu jedem Zeitpunkt möglichst gering zu halten. Es wird also der Error (die Differenz) errechnet, diese durch den PID-Controller in Korrekturen auf derselben Achse übersetzt und erwartet, dass so der Error über die Zeit möglichst gering ist.

      Der PID Controller nimmt nun für die P-Komponente also für den proportionalen Wert, den Set-Point-Error kehrt diesen um und multipliziert diesen mit dem konifgurierten P-Wert. Dies ist die einfachste und grundlegenste Verbindung des Inputs mit dem Output. Es ist durchaus möglich allein mit dem P-Teil des PID kontrollers einen Copter zu manövirieren und zu fliegen. Dies könnt ihr leicht testen indem ihr eure I und D Werte auf 0 setzt.

      Hier lässt sich schon ganz gut verstehen, weshalb ein Copter nicht direkt den Input in einen Output übersetzt sondern stattdessen über den Set-Point-Error geht: Wenn zum Beispiel ein gemeiner Springbaum euren Copter mit nem fiesen Ast aus der Bahn werfen will, misst das Gyroskop plötzlich einen Front-Flip, der nicht durch den Input vorgegeben war. Dies lässt natürlich den Set-Point-Error in die Höhe schnellen, da der Copter plötzlich 1234 Grad pro Sekunde Flips macht und Input eigentlich 0 Grad pro Sekunde ist. Ohne eine Änderung des Inputs wird also schon allein die P-Komponente des PID-Controllers hier versuchen, die Rotation des Copters zu bremsen und die Pitch-Winkelgeschwindigkeit auf die vorgegebenen 0 Grad pro Sekunde zu reduzieren.
      Wofür gibt es dann noch die I und die D Komponente? Wenn ein Copter zum Beispiel schlecht balanciert ist oder einen schwächeren Motor hat, dann gibt es konstant leichte Set-Point-Errors, die durch die P-Komponente nicht ausreichend stark korrigiert werden. Entscheidend ist hier, dass dieser Fehler sich über die Zeit aufsummiert und bei mangelnder Korrektur der Copter irgendwann trotzdem kopfsteht.
      Auch lässt Proportional die Trägheit des Systems komplett außen vor. So wie ein Pendel im Zenit nicht einfach anhält, wird allein durch die P-Komponente nachdem ein Copter von 1234 Grad pro Sekunde auf 0 Grad pro Sekunde gebremst wurde, die Winkelgeschwindigkeit über dieses Optimum hinausschießen und der Copter kurze Zeit später bei -123 Grad pro Sekunde angelangt sein. Bei Coptern und PID-Tuning Guides wird hier meist von einem Bounce-Back gesprochen.


      Für die beiden Defizite der P-Komponente gibt es nun das Integral und Derivative (die Ableitung). Vielleicht erinnert ihr euch noch, was das Integral in der Kurvendiskussion ausgesagt hat. Dort wurde es zum Beispiel verwendet um eine bestimmte Fläche zu errechnen, die an einer Seite durch eine Kurve begrenzt wird. Dies ist mit dem Integral möglich, da es prinzipiell die Werte je Einheit aufsummiert. Im PID Kontroller ist hier die Einheit die Zeit eines PID-Loop-Durchlaufes und der Wert der Set-Point-Error. Um im Tuning unabhängig von der Loopgeschwindigkeit zu sein wird hier außerdem ein ausgleichender Faktor eingesetzt. Je Loop-Durchlauf wird also der Set-Point-Error aufsummiert und somit auch ein kleiner Set-Point-Error über die Zeit immer stärker Korrigiert.
      Als Hilfe kann man sich die Korrektur durch die I-Komponente auch vorstellen, als würde hier der absolute Winkel des Copters betrachtet. Gerade in Zero-Throttle-zero-Input-Phasen kann die I-Komponente so sicherstellen, dass der Copter nicht durch einfaches Trudeln plötzlich Kopfsteht.


      Zuletzt gibt es die D-Komponente. Derivative ist die Ableitung der Kurve und stellt quasi die Steilheit dieser dar. Stellt man sich dies in Winkelgeschwindigkeiten vor wird es ziemlich abstrakt, denn hier geht es um die die Änderung der Geschwindigkeit mit der der Winkel des Copters geändert wird.
      Wenn ich also den Pitch-Stick nach vorn drücke um einen Front-Flip zu machen, schnellt der Set-Point-Error plötzlich nach oben. Während dieser Error größer wird hat Derivative einen Positiven Wert und beschleunigt somit die Einleitung der Korrektur. Hier wird zusätzliche Energie aufgebracht um die Trägheit des Copters zu überwinden und möglichst schnell den Error zu reduzuieren. Wenn der Error jetzt aber wieder geringer wird und sinkt ist Derivative negativ. So bremst die D-Komponente also die Korrektur schon aus, bevor diese komplett beendet ist.

      Die Errechnung der D-Komponente ist ziemlich simpel. Hier wird einfach die Differenz des Set-Point-Errors aus dem letzten Loop-Durchlauf und des aktuellen Set-Point-Errors errechnet. Wie auch schon bei der I-Komponente muss hier die Loopgeschwindigkeit durch einen Faktor ausgeglichen werden damit das Tuning nicht durch die Loopgeschwindigkeit beeinflusst wird.

      Am Ende des PID Controllers werden die errechneten Werte von P, I und D einfach aufsummiert und in eine Korrektur übersetzt. Dank Airmode wird diese Korrektur mit maximalem Motoreinsatz umgesetzt und kann dabei auch den Throttle-Input überschreiben.
      Ausschließlich in der Mitte von Nix zu schweben macht mich depressiv!
      ...meine Rechtschreibprüfung funktioniert zum Glück nur bei kurzen Wörtern. Korrekte Wörter werden da immer durch unsinnige andere ersetzt.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von simon.bethke ()

    • Schön Erklärt!

      Ergänzend evtl. noch: Beim Setpoint Error von dem Simon spricht handelt es sich einfach nur um die Differenz zwischen der gewünschten Drehrate also der Sollwertvorgabe welche wir eingeben und der tatsächlichen. Wenn man also über die Sticks eine Drehrate von 1000 vorgibt und zum Zeitpunkt t=0 die tatsächliche Drehrate 0 ist, dann wird 1000 die Sollwertvorgabe für den PTerm. Die Motoren Beschleunigen und im nächsten Schritt ist die tatsächliche Drehrate 500, was dazu führt, dass bei der Vorgabe 1000 der Regler einen Wert von 500 bekommt. Das geht dann immer so weiter bis der Fehler 0 ist, also die Vorgabe erreicht wurde. Da dem System eine gewisse Trägheit (die Motoren können leider nicht unendlich schnell beschleunigt oder gebremst werden) anhaftet und P alleine dadurch nicht in der Lage ist das perfekt zu aus zu regeln benötigt man die beiden anderen Werte I und D um das zu korrigieren.

      Bei Simons Beispiel mit dem bösen Baum ist es umgekehrt wie von mir beschrieben, der Sollwert von den Sticks ist 0 und die tatsächliche Drehrate 1234 weil der Baum eine Kraft auf dem Copter wirkt, der P Term wird also mit -1234 gefüttert um dies auszugleichen.
    • Meint Ihr es macht Sinn, wenn ich dem Artikel etwas mehr Zeit widme, Grafiken nachliefere und die Typos entferne? Welche passagen sollten umformuliert werden oder sind verwirrend (also verwirrender als der rest)?
      Ausschließlich in der Mitte von Nix zu schweben macht mich depressiv!
      ...meine Rechtschreibprüfung funktioniert zum Glück nur bei kurzen Wörtern. Korrekte Wörter werden da immer durch unsinnige andere ersetzt.
    • Ich würde dir auch gerne noch mehr Daumen nach oben verpassen.
      Das ist endlich mal eine Erklärung was ein PID-Regler wirklich ist. Dafür gibt es die Regelungstechnik and den FHs und Unis ja schon des Längeren um diese Regler zu entwerfen und zu bestimmen. Viele denken ja, dass die PIDs etwas Kopter-spezifisches sind.

      Aber Regelkreise sind überall gebräuchlich nur eben in diesem Fall zur Stabilisierung des Kopters da :)
      Ironie ist wie dieser Smiley:
      °.-/)
    • Wollte gerade schauen ob er angepinnt ist .... 8) .

      Einzigster Verbesserungsvorschlag :
      Die Überschrift vielleicht etwas abändern ... so dass quasi keiner ,der was über PIDs wissen möchte ,daran vorbei kommt !?

      @simon.bethke :
      Ich behaupte jetzt einfach mal dass du dich mit dem Thread hier verewigt hast :thumbup: :thumbup: :thumbup: !
      Ihr seid neu hier? Hier könnt ihr euch vorstellen :
      Klick : Vorstellungsrunde
    • Vielleicht fragt ihr euch ja woher der Typ das alles wissen will. Ganz einfach: Betaflight ist Open Source und ich verstehe den Source Code. Eigentlich ist das auch relativ einfach, im folgenden will ich mal den PID Controller anhand des Codes von Betaflight 3.1.7 erklären.

      Los geht es mit der Datei PID.c die ihr hier finden könnt: github.com/betaflight/betaflig…1.7/src/main/flight/pid.c

      Los geht's

      C-Quellcode

      1. // -----calculate error rate
      2. const float errorRate = currentPidSetpoint - gyroRate; // r - y
      Hier wird der Setpoint-Error in die Variable errorRate geschrieben. Dieser ist einfach die Differenz aus SetPoint und Gyro-Wert.

      Nun geht es mit P-Term los:

      C-Quellcode

      1. // -----calculate P component and add Dynamic Part based on stick input
      2. axisPID_P[axis] = Kp[axis] * errorRate * tpaFactor;
      P-Term soll in das Array axisPID_P[] geschrieben werden, dieses Array hat drei elemente für die Achsen Roll, Pitch und Yaw. Hierzu wird aus dem Array Kp[] der Wert den Ihr im Betaflight konfigurator gesetzt habt mit dem Setpoint Error in errorRate multipliziert. Außerdem kommt noch der tpaFactor hinzu, der je nach konfiguration bei höheren Throttle eingaben P-Term reduzieren soll.

      Jetzt geht es mit I-Term weiter:

      C-Quellcode

      1. // -----calculate I component
      2. if (motorMixRange < 1.0f) {
      3. // Only increase ITerm if motor output is not saturated
      4. axisPID_I[axis] += Ki[axis] * errorRate * dT * dynKi * itermAccelerator;
      5. }
      I-Term wird nur dann neu berechnet, wenn die motor mix-range nicht gesättigt ist. Dies wäre der Fall, wenn für eine weitere Korrektur ein Motor langsamer als die minimale oder schneller als die maximale Drehzahl drehen müsste.
      I-Term soll auf den aktuellen Wert in dem Array axis_PID_I[] (aus der letzten PID-Loop) addiert werden. Auch hier wird der konfigurierte Wert aus dem Array Ki[] mit dem SetPoint error in errorRate multipliziert. Weitere Faktoren sind dT als ausgleichender Faktor für die PID-Loop-Frequenz, dynKi der I-Term erst ab einem bestimmten Throttlewert hinzu mischt, damit die Motoren am Boden vor dem losfliegen nicht amok laufen und itermAccelerator, der in Anti-Gravity situationen I-Term boostet.

      Zuletzt kommt jetzt noch D-Term:

      C-Quellcode

      1. // -----calculate D component
      2. if (axis != FD_YAW) {
      3. float dynC = dtermSetpointWeight;
      4. if (pidProfile->setpointRelaxRatio < 100) {
      5. dynC *= MIN(getRcDeflectionAbs(axis) * relaxFactor, 1.0f);
      6. }
      7. const float rD = dynC * currentPidSetpoint - gyroRate; // cr - y
      8. // Divide rate change by dT to get differential (ie dr/dt)
      9. const float delta = (rD - previousRateError[axis]) / dT;
      10. previousRateError[axis] = rD;
      11. axisPID_D[axis] = Kd[axis] * delta * tpaFactor;
      12. ...
      13. // apply filters
      14. ...
      15. ...
      16. }
      Alles anzeigen
      Grundsätzlich wird D-Term nicht für die Yaw Achse berechnet.

      Nun wird die Variable dynC vorbereitet um mit dieser später D-Term Setpoint weight und -transition umsetzen zu können. Jetzt wird die variable rD fast wie der Set-Point Error gefüllt. Einziger unterschied ist, dass die D-Term Setpoint features umsetzen zu können. (Es wird nicht einfach errorRate multipliziert, da Punkt-vor-Strich-Rechnung gilt und das Ergebnis deswegen ein anderes wäre).
      In Zeile 263 wird nun die Differenz zu dem rD-Wert aus dem vorherigen PID-Loop-Durchgang errechnet. Genau hierfür wird dieser auch in der nächsten Zeile wieder im previousRateError[] Array für den nächsten Loop-Durchlauf abgelegt.
      D-Term soll nun in das Array axisPID_D[] geschrieben werden. Dazu wird der konfigurierte D-Term Wert aus dem Array Kd[] mit dem error-change aus der delta Variable sowie dem tpaFactor multipliziert.

      Zu guter Letzt geht es in die Datei mixer.c (github.com/betaflight/betaflig…src/main/flight/mixer.c):

      C-Quellcode

      1. // Calculate and Limit the PIDsum
      2. scaledAxisPIDf[FD_ROLL] =
      3. constrainf((axisPID_P[FD_ROLL] + axisPID_I[FD_ROLL] + axisPID_D[FD_ROLL]) / PID_MIXER_SCALING,
      4. -pidProfile->pidSumLimit, pidProfile->pidSumLimit);
      5. scaledAxisPIDf[FD_PITCH] =
      6. constrainf((axisPID_P[FD_PITCH] + axisPID_I[FD_PITCH] + axisPID_D[FD_PITCH]) / PID_MIXER_SCALING,
      7. -pidProfile->pidSumLimit, pidProfile->pidSumLimit);
      8. scaledAxisPIDf[FD_YAW] =
      9. constrainf((axisPID_P[FD_YAW] + axisPID_I[FD_YAW]) / PID_MIXER_SCALING,
      10. -pidProfile->pidSumLimit, pidProfile->pidSumLimitYaw);
      Hier werden nun nacheinander und je Achse die errechneten Werte aus axisPID_P[], axisPID_I[] und axisPID_D[] summiert und weiterverarbeitet.
      Ausschließlich in der Mitte von Nix zu schweben macht mich depressiv!
      ...meine Rechtschreibprüfung funktioniert zum Glück nur bei kurzen Wörtern. Korrekte Wörter werden da immer durch unsinnige andere ersetzt.