Teil 5 unserer Serie war gewissermaßen die mentale Brücke zwischen der physikalischen Betrachtung unseres Problems und der nun erfolgenden immer tieferen Betrachtung seiner numerischen und algorithmischen Umsetzung.
Ich empfehle daher dringend jedem Quereinsteiger, mindestens Teil 5 zu studieren, um hier folgen zu können.
Wir können unser in Teil 5 vorgestelltes Problem offensichtlich in drei Verarbeitungsebenen teilen:
- Initialisierung
- Wechsel der Zielposition
- Laden / Berechnen der nächsten Zeit für den Timer (ISR)
Unser mathematisches Modell muss daher numerisch an die zugrundeliegende Hardware im Hinblick auf die Berechnungen (Rundungsfehler, Fehlerfortpflanzung, Fließkommazahlen oder Integers) wie auch algorithmisch (sehr kurz gehaltene ISR-Zeiten) angepasst werden.
Zunächst betrachten wir unsere Problemstellung „aus dem Stand“. Wir haben eine aktuelle Winkelposition und eine neue Zielposition . Später erweitern wir auf neue Zielpositionen mitten in einer Fahrt.
Um den Zugang zu erleichtern, projizieren wir unser mentales Modell auf eine Wegbewegung. Der Grund liegt in einem einfacheren Zugang für Leser, welche nicht dieser Fachrichtung entspringen und so gewohnte Notationen vorfinden. Statt eines Winkels betrachten wir einen Weg , und damit statt einer Winkelgeschwindigkeit die klassische Geschwindigkeit .
Wie in Teil 5 beschrieben, setzen wir ein , um vor der Zielposition wieder zu stoppen und von der Anfahrtsseite aus einzuregeln.
Damit folgt, wir müssen die Distanz fahren.
In unserem Fall fangen wir gleich zu Beginn ab, in diesem Fall wechseln wir sofort in den Einregel-Modus. Somit bleiben nur die Fälle bestehen.
Nun schauen wir uns noch einmal unsere Rampe an.
Dabei haben wir Abschnitt 4, dessen Wegstrecke zwischen und liegt, und die übrigen Abschnitte, welche jeweils zwischen und einem jeweiligen Maximalwert liegen. Dieser Unterschied ist wichtig. Wir bezeichnen diese folgend als , , , , und .
Durch Abschnitt 4 gibt es keine Begrenzung für nach oben.
Anders sieht es aus, wenn eine Fahrt gerade kürzer ausfällt, als die Summe der anderen Abschnitte.
Dazu noch einmal die Reihenfolge der Kürzungen:
- Abschnitt 4, bis dieser ganz verschwindet
- Abschnitte 2 und 6 symmetrisch
- Abschnitte 1, 3, 5 und 7 symmetrisch
Aus didaktischen Gründen starten wir mit den mathematisch komplexesten Abschnitten 1, 3, 5 und 7. Diese Phasen dienen der Begrenzung des Rucks.
In unserem Beispiel gehen wir davon aus, dass der Ruck beim Beschleunigen wie auch Abbremsen mit einem identischen Maximalwert festgelegt wird. Damit können die Funktionsverläufe der Abschnitte durch Transformationen auf einen gemeinsamen Funktionsverlauf als Basis gesetzt werden.
Wir betrachten zunächst Abschnitt 1. Hier werden Geschwindigkeit und Beschleunigung vom Stillstand bis zur maximalen Beschleunigung angehoben. Dieser Abschnitt führt zu einem und .
Genauso wie wir Geschwindigkeit und Beschleunigung erhöht haben, müssen wir diese anschließend wieder erniedrigen. Im Abschnitt 3 wird die Beschleunigung bei weiter zunehmender Geschwindigkeit bis verringert, in Abschnitt 5 bei abnehmender Geschwindigkeit negative Beschleunigung aufgebaut und im Abschnitt 7 wieder abgebaut, wie auch die Restgeschwindigkeit. Am Ende herrschen wieder .
Nun schauen wir uns an, wie sich der Verlauf ändert, wenn die Abschnitte 1, 3, 5 und 7 nicht voll durchlaufen werden, da der Weg kürzer ausfällt. Zunächst gilt durch Integration von aus und aus :
Nun legen wir die verkürzten Abschnitte alle auf einen gemeinsamen Startpunkt:
Der dunkelblaue Kurvenverlauf ist in allen Fällen gemeinsam. Unterschiedlich ist nur, wann dieser verlassen wird. Zu trennen sind dabei der Graph für Weg-Zeit zu Geschwindigkeit-Zeit und Beschleunigung-Zeit . Die Graphen von und lassen sich in allen Fällen durch einfache Spiegelungen und Translationen von Abschnitt 1, im Bedarfsfall gekürzt, modellieren.
Tatsächlich gibt es Implementationen, welche auf dieser Zeitbasis aufsetzen. Wir gehen aber einen Schritt weiter. Bei der Fahrt wird eine Funktion fortwährend gerufen, die ISR des Timers. Diese generiert den nächsten Teilschritt des Steppers und muss dann den Timer mit der nächsten Weckzeit laden.
Für unsere Ansteuerung des Steppers ist daher nicht die Zeit die Basis, sondern seine Schritte bzw. Teilschritte. Nicht die Zeit, sondern der Schritt ist sozusagen unser Takt, und daher betrachten wir nun Zeit, Geschwindigkeit und Beschleunigung aus Perspektive des Weges:
In jedem Abschnitt geht es daher darum, so schnell wie möglich bei jedem Teilschritt das Neuladen des Timers zu ermöglichen. Hier gilt es je nach Anforderung zwei grundsätzliche Ansätze zu unterscheiden:
- wird direkt vorberechnet und muss von der ISR nur noch in den Timer geladen werden.
Erfolgt die Timerprogrammierung relativ zum tatsächlichen Ausführungszeitpunkt der ISR, können sich ISR-Latenzen und Rundungsfehler über die Fahrt aufsummieren. - wird aus vorberechneten absoluten Zeitpunkten berechnet.
Dadurch bleiben die Sollzeitpunkte auf einen gemeinsamen zeitlichen Ursprung bezogen. Eine Akkumulation von Rundungsfehlern bei der Berechnung einzelner Zeitintervalle wird vermieden. Auch ISR-Latenzen führen bei geeigneter Timerprogrammierung nicht zu einer fortschreitenden zeitlichen Verschiebung.
Die Wahl hängt von den Rahmenbedingungen der Fahrt ab, etwa, ob ein Messpunkt auch während der Fahrt z.B. Sensordaten erfassen soll.
Unsere LUT muss als Schlüssel also nicht die Zeit, sondern den Weg, als Teilschritte, besitzen. Jedem Teilschritt wird die Zeit zum Nachladen des Timers zugeordnet, wie oben erläutert bereits als Differenzzeit zwischen zwei Steps oder als Differenz zum Beginn des Abschnitts.
Daher müssen wir unsere Funktionen von Zeit auf Weg umstellen.
Wird nun auch in und eingesetzt, so ergibt sich für und daraus:
Darauf aufbauend betrachten wir nun unsere symmetrisch verkürzten Abschnitte 1, 3, 5 und 7 diesmal aus der Sicht des Weges:
Der Preis ist sofort sichtbar: nun können wir in den Abschnitten 3 und 5 nicht mehr nur mit Spiegelung und Verschiebung arbeiten. Der Grund liegt in . Durch die unterschiedlichen Start- und Zielgeschwindigkeiten und im Gegensatz zu den Abschnitten 1 und 7 ergeben sich die Skalierungseffekte für die Abschnitte 3 und 5.
Da die Abschnitte 1 und 7 dagegen immer als Start- oder Endgeschwindigkeit besitzen, wie auch als Start- oder Endbeschleunigung, ist auch die Skalierung hier immer gleich und muss nicht nachträglich einberechnet werden.
Die Grafiken zeigen ebenfalls, die Abschnitte 3 und 5 besitzen viel mehr Teilschritte als die Abschnitte 1 und 7, da hier die Geschwindigkeiten wesentlich höher liegen.
Im Abschnitt 1 gilt:
Und daraus folgend:
Abschnitt 5 startet dagegen mit einer Geschwindigkeit :
Daraus folgt:
Genau hier sehen wir den Skalierungseffekt von , und durch hervorgerufen.
Leider haben wir noch einen weiteren Effekt. Für uns ist durch die ISRs wesentlich interessanter als . Für die Abschnitte 1 und 7 ist dies trivial, wie wir bereits gesehen haben: .
Bei den Abschnitten 3 und 5 mit der Umstellung von auf wird das Problem erheblich größer, denn wir erhalten eine kubische Gleichung mit einem linearen Term:
Und die Werkzeuge zu deren Auflösung sind auf analytischem Weg Winkelfunktionen oder komplexe Zahlen (Cardano ohne komplexe Zahlen scheitert bei uns an der Wurzel negativer Zahlen):
Und hier wird es interessant. Unsere Problemstellung lässt sich mit LUTs mathematisch so umstellen, dass es drei Berechnungsteile gibt. Möglich wird dies, da, wie im Vierabschnittsprofil über den Weg ersichtlich, die Verläufe immer wieder auf einen Verlauf mit Skalierung zurückführen lassen. Darauf aufbauend realisieren wir:
- die Abbildung eines Grundverlaufes in der LUT, zur compile-Zeit oder beim Programmstart
- die Berechnung der Skalierungswerte einmalig bei der Bahnberechnung
- die Berechnung der nächsten Weckzeit auf Grundlage von LUT und Skalierungswerten in der ISR
Zur Verdeutlichung noch einmal die Verläufe:
Unser „Takt“ ist der Teilschritt. Berechnen wir dabei für den Fall der vollständigen Abschnitte 3 und 5, so erhalten wir die höchste Auflösung. Bei verkürzten Abschnitten skalieren wir immer nach unten. Heißt, es fehlen nie Stützstellen, wir müssen nur zwischen zwei Stützstellen interpolieren, wenn wir keine Stützstelle exakt treffen. Und gerade für die schnellste Fahrt mit vollständigen Abschnitten werden die Stützstellen direkt in der LUT gehalten, es ist dann keine Interpolation nötig.
Ausgangspunkt ist die physikalische Weggleichung
Unsere LUT stellt die Beziehung zwischen einem Referenzweg und einer Referenzzeit her. Beide normieren wir hierzu:
Den späteren Bezug zu unserer realen Bahnberechnung stellen wir her durch den normierten Weg und die normierte Zeit :
Nun stellen wir um
Die Wahl setzt den Koeffizienten des linearen Terms auf eins. Damit folgt:
Und hier ist die Stelle, warum wir gesetzt haben. Denn dadurch gilt . Und damit reduziert sich unsere Gleichung zu:
Diese Gleichung liefert den normierten Weg aus der normierten Zeit .
Dies ist die Basis für unsere Umkehrfunktion. Wir nennen sie . Sie liefert nun aus .
Im verwendeten Bereich ist die Wegfunktion streng monoton steigend und damit eindeutig umkehrbar.
Anmerkung: Die Funktion ist sogar auf streng monoton steigend. Bei unserem symmetrischen Vierabschnittprofil gilt . Mit folgt damit unmittelbar:
Nun setzen wir und wieder ein:
Durch Multiplikation mit ergibt die Form:
Dabei rechnet den konkreten Bahnweg in einen normierten Weg um, liefert die zugehörige normierte Zeit, und skaliert diese zurück in die reale Bahnzeit .
Durch diese Umformung haben wir unser Problem in drei Teile zerlegt:
- Einmalig aufwendige Berechnung der LUT für zur Compile-Zeit oder bei der Initialisierung der Firmware
- Berechnung von und bei der Bahnberechnung
- Berechnung von bei jeder ISR
Im Kern reduzieren wir damit die Auswertung der inversen Wegfunktion auf zwei Skalierungsmultiplikationen und einen LUT-Zugriff, gegebenenfalls mit Interpolation.
Im nächsten Teil wird die numerische Umsetzung dieses Ansatzes betrachtet.