Tutorial · Einsteiger · 17 Min. Lesezeit
Endliche Zustandsmaschinen für Roboterverhalten
Endliche Zustandsmaschine für Roboterverhalten: explizite Übergänge, Timeouts, Erholungszustände, nicht blockierende Updates und testbare Sicherheitsregeln.
Roboterprogramme beginnen oft als Schleife voller if-Anweisungen: Der Linie folgen, es sei denn, ein Hindernis ist nah, drehen, bis der Weg frei ist, stoppen, wenn der Akku niedrig ist – und irgendwie erholen, wenn ein Sensor per Timeout ausfällt. Mit wachsendem Verhalten kann dieselbe Bedingung widersprüchliche Befehle auslösen.
Eine endliche Zustandsmaschine (FSM) macht das aktive Verhalten explizit. Zu jedem Zeitpunkt ist der Roboter in einem Zustand; Ereignisse und Wächter bestimmen, wann er in einen anderen wechselt. Das Muster ist klein genug für einen Arduino und strukturiert genug zum Testen.
Zustand, Ereignis und Aktion trennen
Ein Zustand beschreibt, was der Roboter über Zeit tut: FOLLOW_LINE, AVOID, SEARCH oder FAULT. Ein Ereignis ist etwas Beobachtetes: Ein Hindernis betritt die Stoppzone, die Linie geht verloren, ein Timeout läuft ab oder der Bediener drückt Stopp. Eine Aktion ist die Ausgabe: Raddrehzahlen setzen, einen Integrator löschen oder einen Alarm auslösen.
Keine Zustände für momentane Fakten erzeugen. OBSTACLE_DETECTED ist meist ein Ereignis oder Wächter; AVOIDING_OBSTACLE ist ein Zustand, weil er andauert, während der Roboter ein Verhalten ausführt.
Mit einer Übergangstabelle vor Code anfangen:
| Aktueller Zustand | Wächter oder Ereignis | Nächster Zustand | Eintrittsaktion |
|---|---|---|---|
| Idle | Start angefordert und Sensoren gültig | Follow | Regler zurücksetzen |
| Follow | Hindernis innerhalb Stoppschwelle | Avoid | Vorwärtsfahrt stoppen |
| Follow | Linie 200 ms vermisst | Search | Letzte Seite merken |
| Avoid | Weg 500 ms frei | Follow | Regler zurücksetzen |
| Jeder aktive Zustand | Sensor-Timeout | Fault | Motoren stoppen |
| Fault | Bediener-Reset und Zustand gesund | Idle | Fehler löschen |
Die Tabelle legt fehlende Fälle offen und macht Sicherheitspriorität sichtbar.
Die Updateschleife nicht blockierend halten
Ein Zustand soll ein kleines Update tun und zurückkehren. Ein zwei Sekunden delay() in AVOID hindert das Programm zwei Sekunden lang daran, eine Stoppanforderung oder einen Sensorausfall zu sehen.
enum class State { Idle, Follow, Avoid, Search, Fault };
State state = State::Idle;
unsigned long enteredAt = 0;
void transitionTo(State next) {
state = next;
enteredAt = millis();
}
void loop() {
Sensors s = readSensors();
if (!s.healthy && state != State::Fault) transitionTo(State::Fault);
switch (state) {
case State::Idle: updateIdle(s); break;
case State::Follow: updateFollow(s); break;
case State::Avoid: updateAvoid(s); break;
case State::Search: updateSearch(s); break;
case State::Fault: updateFault(s); break;
}
}
Jedes Update liest den bereits erfassten Sensorschnappschuss und schreibt einen kohärenten Aktorbefehl. So lesen verschiedene Zustände nicht zu verschiedenen Zeiten eines Zyklus sich ändernde Werte.
Übergänge an klare Grenzen legen
Eine Zuständigkeitsregel wählen. Ein einfacher eingebetteter Entwurf lässt jeden Zustand seine abgehenden Übergänge anfordern, während eine übergeordnete Sicherheitsprüfung jeden aktiven Zustand überstimmen kann.
void updateFollow(const Sensors& s) {
if (s.obstacleMm < stopThresholdMm) {
setWheels(0, 0);
transitionTo(State::Avoid);
return;
}
if (s.lineMissingForMs > 200) {
transitionTo(State::Search);
return;
}
followLine(s.lineError);
}
Nach einem Übergang sofort zurückkehren, damit der alte Zustand den Aktoren nicht auch befiehlt. Beim Eintritt in einen Regelzustand Historie zurücksetzen, die nicht über Verhalten hinweg sickern darf – etwa ein PID-Integral oder D-Abtastwert.
Sparsam mit Austrittsaktionen. Der meiste Aufräumaufwand passt in die zentrale transitionTo()-Funktion oder den Eintrittspfad des nächsten Zustands. Separate onEntry-, onUpdate- und onExit-Funktionen lohnen erst, wenn mehrere Zustände sie wirklich brauchen.
Timeouts und Hysterese als Wächter nutzen
Physische Ereignisse sind verrauscht. Verlässt AVOID in dem Moment, wo die Entfernung dieselbe Schwelle überschreitet, die hineinführte, kann Messrauschen schnelles Zustandsflattern verursachen. Zwei Schwellen oder eine Beharrungszeit verwenden: unter 250 mm hinein, aber erst heraus, wenn die Entfernung 500 ms über 350 mm bleibt.
Jeder Zustand, der Fortschritt erwartet, braucht ein Timeout. Betritt der Roboter SEARCH, findet aber nie die Linie, soll er nicht ewig kreiseln. Nach getestetem Intervall zu FAULT oder begrenzter Erholung wechseln.
Timer auf Zeit seit Eintritt oder Zeit seit letztem Fortschritt stützen. Subtraktion wie now - enteredAt >= timeout verwenden – über vorzeichenlosen Timerüberlauf hinweg korrekt, wenn Typen konsistent sind.
Sicherheitsübergängen Priorität geben
Definieren, was alles unterbrechen darf: Not-Halt, ungültige kritische Sensordaten, Motortreiberfehler oder Watchdog-Timeout. Diese Bedingungen vor verhaltensspezifischen Übergängen prüfen und die sichere Ausgabe explizit machen.
Ein FAULT-Zustand sollte einrasten, wenn automatische Erholung gefährliche Bewegung neu starten könnte. Einen Bediener-Reset zu verlangen, nachdem die Ursache behoben ist, ist oft sicherer, als direkt in FOLLOW zurückzuspringen.
Nicht zwei unabhängige Zustandsmaschinen ohne Schiedsregel dieselben Motorbefehle schreiben lassen. Beeinflussen Navigation wie Batteriemanagement beide Bewegung, soll ein Verantwortlicher den finalen Befehl wählen und Sicherheitsgrenzen anwenden.
Übergänge ohne laufende Motoren testen
Entscheidungen in Funktionen verlegen, die einen Sensorschnappschuss und Zeit nehmen und dann einen nächsten Zustand oder Befehl zurückgeben. Eine Folge synthetischer Schnappschüsse füttern:
- Gültige Linie und freier Weg bleibt in
FOLLOW. - Eine einzelne verrauschte Nahtmessung löst nicht aus, wenn Beharrung verlangt wird.
- Ein anhaltendes nahes Hindernis betritt
AVOIDund befiehlt zuerst Stopp. - Ein gehaltener freier Weg über das Austrittsintervall kehrt zu
FOLLOWzurück. - Ein Sensortimeout erreicht
FAULTaus jedem aktiven Zustand.
Zustandswechsel als alt → neu, auslösendes Ereignis und Zeitstempel loggen. Fortlaufende Pro-Schleife-Logs verstecken Übergänge im Rauschen; Übergangslogs erklären, warum sich Verhalten änderte.
Wissen, wann eine FSM aufhört, das einfachste Modell zu sein
Eine FSM arbeitet gut, wenn der Roboter eine überschaubare Zahl sich ausschließender Modi hat. Sie wird schwierig, wenn sich Zustände multiplizieren, um Kombinationen darzustellen wie „navigieren während Batterie überwachen während Lokalisierung wiederholen“. Die Übergangszahl kann schneller wachsen als die Verhalten selbst.
Behaviour Trees komponieren wiederverwendbare Aktionen mit Sequenz-, Fallback- und Erholungsstruktur – weshalb Navigationssysteme wie Nav2 sie für komplexe Aufgaben nutzen. Kein Behaviour-Tree-Framework für vier Zustände einführen. Wechseln, wenn die Übergangstabelle wirklich schwer prüfbar wird oder Verhalten hierarchische Wiederverwendung braucht.
Gängige Entwurfsfehler diagnostizieren
- Der Roboter ignoriert Stopp beim Drehen: Ein Zustand enthält eine blockierende Schleife oder Delay.
- Motoren erhalten zwei Befehle pro Zyklus: Übergangscode läuft nach Zustandswechsel weiter.
- Die Maschine flattert: Verrauschte Wächter ohne Hysterese oder Beharrung.
- Erholung endet nie: Der Zustand hat keine Fortschrittsbedingung oder Timeout.
- Ein Fehler startet Bewegung instant neu: Sicherheitsfehler rasten nicht ein oder Resetbedingungen sind zu schwach.
- Testen braucht den ganzen Roboter: Entscheidungen und Hardwarezugriff sind vermischt statt Sensorschnappschüsse und Befehlsausgaben zu nutzen.
Der Hindernisvermeidungssimulator liefert eine nützliche Verhaltensfolge zum Modellieren: Marschfahrt, um eine Bedrohung herumlenken, ankommen oder stoppen, wenn keine sichere Bewegung bleibt. Der Labyrinthlösersimulator zeigt eine weitere Trennung zwischen Wahrnehmung, Kartenupdate, Planung und Einzellzellenbewegung. Der Mini-Sumo-Roboter ist der schärfste Test der Einrast-Sicherheitsregel oben: Sein Kantenreflex muss jeden anderen Zustand verdrängen, aus jedem Zustand – sonst fährt sich der Roboter selbst aus dem Ring.
Den Graphen erkunden
Teil dieser Bauprojekte
Projekte und Lernpfade, die dieses Tutorial enthalten.
- ProjektEin handygesteuertes Roboterauto über Bluetooth oder WLAN bauen
- ProjektEinen ROS-2-Roboter bauen, der sieht und zu einem Marker fährt
- ProjektMini-Sumo-Roboter bauen: Grip, Keil, Kantensensorik
- ProjektPick-and-Place-Roboter: Der Fehlgriffe erkennt
- LernpfadGreifen und Endeffektoren: Wirklich aufnehmen
- LernpfadReaktive Navigation: Einem Roboter Hindernisse meiden lehren
Weiterführende Literatur