Zeitlicher Determinismus durch Timer, ISRs und Hardware im SoC – Teil 4

Im Teil 1 unserer Reihe betrachteten wir, wie wir mithilfe eines Timers und der PIO ein periodisches Signal erzeugen können. In Teil 2 und Teil 3 betrachteten wir, wie wir mithilfe der PIO einen Drehgeber zeitstabil einbinden können, mit geringer Belastung der Kerne.

Mit diesen Komponenten können wir nun in ein einfaches Beispiel für Bewegungsregelung (Motion Control) einsteigen, die Anfahrt einer vorgegebenen Zielposition.

Eine solche Anfahrt lässt sich grob in zwei Phasen gliedern: move und settle, also Verfahr- und Einschwingphase.

In der Verfahrphase (move), geht es darum, möglichst schnell von Winkel-Startposition A zur Winkel-Zielposition B zu bewegen.

In der Einschwingphase (settle) wird die Zielposition stabilisiert. Das ist bei hohen Winkelauflösungen nicht trivial: Selbst vorgespannte Präzisionsgetriebe verhalten sich bei hinreichend feiner Betrachtung federnd. Hinzu kommt die elastische Wirkung des elektromagnetisch gehaltenen Stepperrotors. Mechanische Schwingungen und Nachlaufbewegungen müssen daher gezielt abgebaut werden.

In diesem Beitrag betrachten wir zunächst die zweite Phase, settle, als anschauliches Beispiel für die Notwendigkeit geeigneter Regelalgorithmen.

Wir verwenden erneut unseren einfachen Testaufbau. Ein Stepper, konkret ein GM42BYG228 (GEMS), bewegt ein Wellengetriebe 100:1, auf dessen Abtriebswelle unser ACURO AD34 mit 19 Bit Auflösung die Winkelposition misst.

Zur Erinnerung: damit erreichen wir eine Auflösung von 0,0006866455078125 Grad, das sind rund 2,4719 Bogensekunden oder rund 0,76294 Milligon. Bei solchen Auflösungen muss auch ein Wellengetriebe als „weich“ betrachtet werden. Unser Stepper besitzt 200 Vollschritte, das sind 1,8 Grad (2 gon oder 108 Bogenminuten). Mit unserem Wellengetriebe würden wir damit eine Auflösung von 0,018 Grad (20 Milligon oder 64,8 Bogensekunden) pro Vollschritt erreichen. Zwischen den Auflösungen liegt damit ein Faktor von 26,2144.

Wir haben aber auch noch die Möglichkeit der Mikroschritte. Der Treiber stellt über die Ströme der beiden Motorphasen Richtung und Stärke des resultierenden Statorfelds ein. Dadurch entsteht zwischen den Vollschrittpositionen eine weitere magnetische Gleichgewichtslage. Der Rotor wird zwischen zwei Vollschritten gehalten. Auch diese Position muss logischerweise als „weich“ betrachtet werden, wie eine Feder, welche gegen eine Last arbeitet.

Damit haben wir bereits zwei federnde Elemente in unserem System, das „vorgespannte“ Wellengetriebe und der vom kontrollierten Magnetfeld gehaltene Rotor.

Für dieses Szenario nutzen wir zunächst einen TMC2225. Diesen Treiber konfigurieren wir auf 32 Mikroschritte pro Vollschritt, 24 V Versorgungsspannung werden gewählt. Bei 32 Mikroschritten entspricht ein Mikroschritt am Abtrieb theoretisch 0,0005625 Grad, also 2,025 Bogensekunden oder 0,625 Milligon. Das sind rund 0,8192 Encoderinkremente des AD34. Mikroschritt und Encoderinkrement bilden daher kein ganzzahliges Verhältnis.

Wir starten mit einem sehr einfachen Experiment: unser SoC erzeugt per Hardware-Timer einen regelmäßigen Interrupt. Bei jedem Timerinterrupt wird eine PIO-State-Machine gestartet, die eine Positionsabfrage beim Drehgeber auslöst. In unserem Fall sammeln wir noch Zeitstempel für Telemetrie.

  void handleTimerTick(const HardwareTimer::Tick&) noexcept
  {
    uint64_t now = time_us_64();
    static_cast<void>(sensor_.startRead());
    record->timestamp_0_us = now;
  }

Wurde das entsprechende Paket vom Drehgeber empfangen, so löst die Zustandsmaschine einen zweiten Interrupt aus. In der dadurch ausgelösten ISR wird das Paket ausgewertet und der angegebene Drehwinkel mit einer vorgegebenen Zielposition verglichen. Unterscheiden sich Ist- und Sollposition, wird über eine zweite State Machine genau ein Mikroschritt in Richtung des Sollwerts ausgelöst.

  void handleSensorResult(const AD34::ReadResult& result) noexcept
  {
    uint64_t now = time_us_64();
    
    if (!result)
      return;

    counter_++;
    
    const std::int32_t diff = shortestSingleturnDifference(targetPosition_, result.sample.singleturn);
    if (diff != 0)
      static_cast<void>(stepper_.startStep(diff > 0));
    
    record->timestamp_1_us = now;
    record->diff = diff;   
    ...
  }

ACHTUNG: Dies ist KEIN Mustercode! Dieser Code dient der Veranschaulichung, wie es nicht sauber geht. Der Regler besitzt weder Totzone noch Geschwindigkeitsbegrenzung oder Dämpfung. Er dient ausschließlich dazu, das entstehende Fehlverhalten sichtbar zu machen.


Der zeitliche Ablauf der Interrupt-Behandlungen zeigt sich als äußerst stabil:

Nun starten wir unser Experiment mit 10.000 Regelzyklen pro Sekunde, entsprechend einer Timerfrequenz von 10 kHz. Für den Versuch setzen wir die Zielposition um 100 AD34-Encoderinkremente gegenüber der gemessenen Startposition nach vorn. Untersucht wird damit ausdrücklich nur das Endanfahren und Stabilisieren einer nahe gelegenen Zielposition, nicht die schnelle Verfahrphase.

Das Ergebnis ist eine dauerhaft angeregte Grenzschwingung mit etwa 227,2 Hz. Ob diese Frequenz zugleich einer mechanischen Eigenfrequenz des Systems entspricht, müsste durch einen gesonderten Ausschwing- oder Anregungsversuch untersucht werden. Das System oszilliert „brutal“, wie das folgende Hörbeispiel zeigt:

Das System kommt so nie zur Ruhe. Während der Grenzschwingung wurden an der 24-V-Versorgung dauerhaft mehr als 900 mA aufgenommen. Der optische Drehgeber liefert infolge der starken Vibrationen zeitweise keine gültigen Pakete mehr. Bei längerem Betrieb drohen durch die dauerhafte Schwingungsanregung eine thermische Überlastung des Motors sowie mechanische Belastungen von Getriebe, Kupplung und Drehgeber.

Nun reduzieren wir die Frequenz des Regelzyklus von 10 kHz auf 1 kHz. Alle übrigen Hard- und Softwareparameter bleiben zunächst unverändert: Da unser vereinfachter Regler pro Regelzyklus höchstens einen Mikroschritt auslöst, bestimmt die Zyklusfrequenz gleichzeitig die maximale Stellgeschwindigkeit. Die Reduzierung von 10 kHz auf 1 kHz verringert daher nicht nur die Abtastfrequenz, sondern auch die maximal mögliche Schrittfrequenz von 10.000 auf 1.000 Mikroschritte pro Sekunde.

Zu sehen ist: Das System pendelt sich tatsächlich ein und kommt schließlich zur Ruhe. In allen wiederholten Testläufen blieb die gemessene Positionsabweichung anschließend bis zum Ende der Aufzeichnung bei exakt null Encoderinkrementen. Auch hier wieder das Hörbeispiel bei ansonsten unveränderten Bedingungen, einschließlich Aufnahmepegel:

Das Beispiel zeigt die Notwendigkeit eines Regelalgorithmus, der auf das konkrete elektromechanische System zugeschnitten ist. Die Reduzierung der Zyklusfrequenz wirkt hier lediglich wie eine grobe Verringerung der Stellaggressivität; sie ist noch kein systematisch entworfener Regler. Damit beschäftigt sich der nächste Teil unserer Serie. Spoiler: Unser Ergebnis wird noch wesentlich besser.