Zum Inhalt springen

State Machine

Wenn ein Roboter autonom durch ein Labyrinth fahren soll, muss er ständig Entscheidungen treffen: Soll er geradeaus fahren? Abbiegen? Stoppen? Und was passiert nach einer Drehung?

Ohne eine klare Struktur landet man schnell bei einem Chaos aus if-Bedingungen, das schwer zu lesen und noch schwerer zu erweitern ist. Eine State Machine löst dieses Problem elegant.

Eine State Machine (deutsch: Zustandsautomat) beschreibt ein System, das sich immer in genau einem Zustand befindet. Auf bestimmte Ereignisse (z.B. ein Sensor schlägt an) hin wechselt das System in einen anderen Zustand.

Die drei Kernbegriffe:

BegriffBedeutungBeispiel
Zustand (State)Was der Roboter gerade tutState.DRIVE, State.TURN_LEFT, State.STOP
Ereignis (Event)Was einen Zustandswechsel auslöstWand erkannt, Drehung abgeschlossen
Übergang (Transition)Der Wechsel von einem Zustand in einen anderenState.DRIVEState.TURN_LEFT

Stellen Sie sich einen Roboter vor, der durch einen Korridor fährt:

  • Er fährt geradeaus, solange kein Hindernis vor ihm ist.
  • Erkennt er eine Wand, stoppt er.
  • Nach dem Stopp dreht er sich nach links.
  • Danach fährt er wieder geradeaus.

Dieser Ablauf lässt sich als Diagramm darstellen:

stateDiagram-v2
    [*] --> DRIVE

    DRIVE --> STOP : Wand erkannt
    STOP --> TURN_LEFT : gestoppt
    TURN_LEFT --> DRIVE : Drehung abgeschlossen

Jeder Kasten ist ein Zustand. Die Pfeile zeigen, wann und warum das System in den nächsten Zustand wechselt.

Ohne State Machine könnte der Code so aussehen:

# ❌ Schwer zu lesen und zu erweitern
while True:
distance = sensor.distance_cm()
if distance > 15:
motor_left.speed = 40000
motor_right.speed = 40000
else:
motor_left.speed = 0
motor_right.speed = 0
time.sleep(0.5)
# Drehen... aber wie lange? Und was dann?
motor_left.speed = -30000
motor_right.speed = 30000
time.sleep(1)

Das Problem: Der Code ist linear. Je mehr Situationen dazukommen, desto verschachtelter und unübersichtlicher wird er.

Eine State Machine lässt sich in Python sehr sauber mit einer Variable für den aktuellen Zustand und einer Methode pro Zustand umsetzen.

Statt einfacher Strings verwenden wir eine Klasse mit Ganzzahl-Konstanten, um die Zustände zu definieren. Das hat zwei Vorteile: Tippfehler fallen sofort auf, und alle Zustände sind an einem zentralen Ort definiert.

controller.py
from components.motor import DCMotor
from components.sensor import UltrasonicSensor
from config import LEFT_MOTOR_PINS, RIGHT_MOTOR_PINS, FRONT_SENSOR_PINS
import time
class State:
DRIVE = 0
STOP = 1
TURN_LEFT = 2
class Controller:
def __init__(self):
self.left_motor = DCMotor(*LEFT_MOTOR_PINS)
self.right_motor = DCMotor(*RIGHT_MOTOR_PINS)
self.sensor = UltrasonicSensor(*FRONT_SENSOR_PINS)
self.state = State.DRIVE # Startzustand
def run(self):
while True:
self._update()
def _update(self):
"""Wählt basierend auf dem aktuellen Zustand die passende Methode."""
if self.state == State.DRIVE:
self._drive()
elif self.state == State.STOP:
self._stop()
elif self.state == State.TURN_LEFT:
self._turn_left()
# --- Zustände ---
def _drive(self):
distance = self.sensor.distance_cm()
if distance < 15:
self.state = State.STOP # Übergang: Wand erkannt
else:
self.left_motor.speed = 40000
self.right_motor.speed = 40000
def _stop(self):
self.left_motor.stop()
self.right_motor.stop()
time.sleep(0.3)
self.state = State.TURN_LEFT # Übergang: gestoppt → drehen
def _turn_left(self):
self.left_motor.speed = -30000
self.right_motor.speed = 30000
time.sleep(0.8) # Drehen für 0.8 Sekunden
self.left_motor.stop()
self.right_motor.stop()
self.state = State.DRIVE # Übergang: Drehung abgeschlossen

Je komplexer der Roboter wird, desto mehr Zustände kommen dazu. Es hilft, das Diagramm von Anfang an mitzupflegen. Für einen vollständigen Micromouse-Algorithmus könnte das so aussehen:

stateDiagram-v2
    [*] --> DRIVE

    DRIVE --> STOP       : Wand vorne
    STOP --> TURN_LEFT   : kein Weg rechts
    STOP --> TURN_RIGHT  : Weg rechts frei
    TURN_LEFT --> DRIVE  : Drehung fertig
    TURN_RIGHT --> DRIVE : Drehung fertig
    DRIVE --> FINISH     : Ziel erreicht
    FINISH --> [*]

Die State Machine lebt vollständig in controller.py. Die anderen Module (components/, config.py) wissen nichts davon – sie stellen nur ihre Funktionen bereit.

  1. Was beschreibt ein Zustand in einer State Machine?

  2. Was löst einen Zustandswechsel aus?

  3. Wie viele Zustände kann eine State Machine gleichzeitig aktiv haben?

  4. Welcher Vorteil ergibt sich daraus, für jeden Zustand eine eigene Methode zu schreiben?

  • Ich kann erklären, was ein Zustand, ein Ereignis und ein Übergang sind.
  • Ich kann ein einfaches State-Machine-Diagramm für meinen Roboter zeichnen.