In der letzten Erweiterung soll das Objekt vom Typ Motor die Motorleistung so regeln, dass eine vorgegebene Geschwindigkeit eingehalten wird. Hierfür wird ein sogenannter PID-Regelkreis (siehe auch in Wikipedia) eingeführt:
Wenn das Auto mit einer Geschwindigkeit von \( 200 {{mm}\over{s}} \)fahren soll, ist der Soll-Wert \(= 200 {{mm}\over{s}} \).
Bei einem Ist-Wert von \( 150 {{mm}\over{s}} \) ist der Fehler \( 50 {{mm}\over{s}} \). Der Regler unseres Regelkreises wird die Motorleistung erhöhen, um den Ist-Wert zu vergrößern.
Ein PID-Regler berechnet die Motorleistung mit 3 Faktoren:
-
Proportion: Die Motorleistung ist proportional zum Fehler (ist ein Vielfaches des Fehlers).
-
Integral: Die Motorleistung ist ein Vielfaches des aufsummierten Fehlers.
-
Differentiell: Die Motoleistung ist ein Vielfaches der Änderrungsrate (Ableitung) des Fehlers.
$$ p(t)=kp \cdot err(t) + ki \cdot \int_0^t err(z) dz+kd \cdot {{d}\over{dt}} err(t) $$
\(kp,ki\) und \(kd\) sind dabei konstante Zahlenwerte. Wie sie sich auf die Regelung auswirken ist in den folgenden Videos zu sehen:
Die Header-Datei
class Motor { private: ... float fsoll=0.0; // Soll Frequenz float kp=0.5,ki=15.0,ferri=0.0; // PID-Berechnung long ftalt; // Zeitpunkt des letzten Regelprozesses public: ... void setFSoll(float a); // Setzt die Soll-Drehfrequenz void setVSoll(float a); // Setzt die Soll-Geschwindigkeit void fPID(); // Regler für die Drehfrequenz };
Für den PID-Regelkreis muss die Klasse Motor den Soll-Wert der Frequenz, die Konstanten kp und ki, sowie den integrierten (aufsummierten) Fehler und den Zeitpunkt des letzten Regeldurchgangs speichern.
Wie im Video bereits gezeigt, braucht man bei einem Regelkreis für die Geschwindigkeit kein kd.
Mit den Methoden setFSoll(float a)
und
setVSoll(float a)
wird die Soll-Geschwindigkeit bzw. die Soll-Drehfrequenz
festgelegt.
Die cpp-Datei
void Motor::setFSoll(float fs) { fsoll=fs; } void Motor::setVSoll(float vs) { fsoll=vs/mmps; }
Die Methode setFSoll(float fs)
speichert lediglich den übergebenen Wert
in der Variablen fsoll
.
Aus \( vs = fsoll \cdot mmps \) folgt \(fsoll = {{vs}\over{mmps}}\).
setVSoll(float vs)
berechnet aus der Soll-Geschwindigkeit die Soll-Drehfrequenz
und speichert sie in fsoll
.
void Motor::fPID() { float err=fsoll-f; float dt=float(micros()-ftalt)/1000000.0; ferri=ferri+err*dt; int p=err*kp+ferri*ki; power(p); ftalt=micros(); // Zeit merken }
Die Methode fPID()
berechnet den Fehler \(err=fsoll-f\),
das Zeitintervall zwischen dem und dem letzten Aufruf von fPID()
\(dt={{t - ftalt}\over{1000000}} \). Der Faktor \(1 \ over {1000000}\) kommt
von der Umrechnung von \(\mu s\) in s.
Der integrierte Fehler lässt sich sehr gut durch das Aufsummieren von \(err \cdot dt\) berechnen.
Die Motorleistung ist dann \(p=kp \cdot err(t)+ki \cdot \int_0^t err(z) dz=kp\cdot err+ki \cdot ferri\).
Anwendung der Klasse
void loop() { ... M1.setVSoll(100); // Setzt die Geschwindigkeit auf 100 mm/s M1.fPID(); // fPID-Regelung sollte bei jedem Loopdurchgang aufgerufen werden. Serial.print(micros()); Serial.print("; "); Serial.print(M1.getS()); Serial.print("; "); Serial.println(M1.getV()); }
Die Methode fPID()
sollte bei jedem loop-Durchgaang aufgerufen werden.
Die Methode setVSoll(float vs)
muss nur aufgerufen werden, wenn
vsoll
geändert werden soll.
Methode | Bemerkung |
---|---|
Motor(int en,int m1,int m2,int i1,int i2,float mmps); | Konstruktor der Klasse |
void power(int p); | Setzt die Motorleistung auf p \( (-255 \leq p \leq 255)\). Bei \(p<0\) läuft der Motor rückwärts. |
void richtung(); | Wird von einer Interruptmethode angesprungen und berechnet die Drehschritte und die Drehfrequenz. |
void fPID(); | Sollte in kurzen Zeitabständen Aufgerufen werden. |
void aus(); | Stoppt den Motor. Ein Aufruf von fPID() startet den Motor wieder. |
void reset(); | Stellt auf die Grundeinstellungen wieder her. |
long getSteps(); | Liefert die bisher gemachten Einzelschritte. |
float getF(); | Liefert die Frequenz Einzelschritte/Sekunde. |
float getS(); | Liefert die zurückgelegte Strecke. |
float getV(); | Liefert die aktuelle Geschwindigkeit. |
void setFSoll(float a); | Setzt die Soll-Drehfrequenz. |
void setVSoll(float a); | Setzt die Soll-Geschwindigkeit. |
Das Beispiel kann hier heruntergeladen werden.