Zum Inhalt springen

4. Erweiterung: PID-Regelkreis zur Steuerung der Geschwindigkeit

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 Beispiele könne hier heruntergeladen werden:

RegelkreisV.ods

RegelkreisS.ods

Die h-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.

MethodeBemerkung
Motor(int en,int m1,int m2,int i1,int i2,float mmps);Konstruktor der Klasse
en: Enablepin
m1: Motorpin 1
m2: Motorpin 2
i1: Inputpin1
i2: Inputpin2
mmps: Strecke, die pro Schritt zurückgelegt wird
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.