Robotics2025-12-2812 min LesezeitBy Abhishek Nair - Fractional CTO für Deep Tech & AI

Vom Nullpunkt zum Roboter: Ein Einsteigerleitfaden für 2D-Robotersimulation und Reinforcement Learning

#Robotik#Reinforcement Learning#Stable-Baselines3#Tutorial#PPO#IR-SIM
Vom Nullpunkt zum Roboter: Ein Einsteigerleitfaden für 2D-Robotersimulation und Reinforcement Learning

Von Null zum Roboter: Ein Leitfaden für Anfänger zu 2D-Robotersimulation und bestärkendem Lernen

Ursprünglich veröffentlicht auf Medium im Dezember 2025. Dies ist eine Neuauflage mit geringfügigen redaktionellen Änderungen; die kanonische URL verweist auf das Medium-Original.

Was wir entwickeln

Am Ende dieses Leitfadens werden Sie Folgendes verstehen:

  1. Wie sich Roboter bewegen – die Mathematik hinter Differentialantrieb-Robotern (zweirädrige Roboter).
  2. Klassische Navigation – wie traditionelle Algorithmen wie „Go-to-Goal“ und Potentialfelder funktionieren.
  3. Grundlagen des verstärkenden Lernens – das Trainieren eines neuronalen Netzwerks zur Navigation durch Versuch und Irrtum.
  4. Warum einfache Dinge schwierig sind – der Weg des Debuggens, der mich von 0 % auf 100 % Erfolgsquote gebracht hat.

Ich verwende IR-SIM (Intelligent Robot Simulator), eine schlanke 2D-Simulationsbibliothek, die sich perfekt zum Lernen eignet. Keine 3D-Grafikkarte erforderlich, keine komplexe Physik-Engine – nur reine, verständliche Robotik.

Teil 1: Verstehen, wie sich Roboter bewegen

Das Differentialantriebsmodell

Die meisten Radroboter, die Sie kennen – von Roombas bis hin zu AGVs in Lagerhäusern – nutzen ein sogenanntes Differentialantriebssystem. Es ist wunderbar einfach: zwei Räder, jedes mit einem eigenen Motor. Indem man die Geschwindigkeit jedes Rades unabhängig voneinander steuert, kann man den Roboter vorwärts, rückwärts, in Kurven oder auf der Stelle drehen lassen.

Stellen Sie sich das wie einen Panzer vor, oder wie wenn Sie schon einmal einen Einkaufswagen gelenkt haben, indem Sie auf einer Seite stärker gedrückt haben.

Loading...

Mathematisch funktioniert das so:

  • Lineargeschwindigkeit (v) – wie schnell sich der Roboter vorwärts oder rückwärts bewegt.
  • Winkelgeschwindigkeit (ω) – wie schnell sich der Roboter dreht (wirbelt).

Aus diesen beiden Werten berechnest du die Geschwindigkeiten der einzelnen Räder und umgekehrt. Die Position des Roboters wird wie folgt aktualisiert:

x_new = x + v × cos(θ) × dt
y_new = y + v × sin(θ) × dt
θ_new = θ + ω × dt

Dabei ist θ die Ausrichtung des Roboters (in welche Richtung er zeigt) und dt der Zeitschritt.

Warum das wichtig ist

Das Verständnis des Differentialantriebs ist entscheidend, da es definiert, was der Roboter kann und nicht kann:

  • Kann: vorwärts / rückwärts fahren.
  • Kann: während der Fahrt wenden (Bogenbewegung).
  • Kann: auf der Stelle drehen.
  • Kann nicht: seitwärts fahren (kein Krabbenlauf).

Diese Einschränkung bestimmt alles andere – von der Art und Weise, wie wir Steuerungen entwerfen, bis hin zu den Aktionen, die unser RL-Agent ausführen kann.

Teil 2: Einrichten von IR-SIM

IR-SIM ist eine Python-Bibliothek, die 2D-Roboterumgebungen simuliert. Sie übernimmt die Physik und Visualisierung und bietet eine übersichtliche Schnittstelle für die Steuerung.

Installation

# Erstellen einer virtuellen Umgebung python3 -m venv venv source venv/bin/activate # Abhängigkeiten installieren pip install ir-sim gymnasium stable-baselines3 numpy matplotlib pyyaml

Deine erste Simulation

Das Robotik-Äquivalent zu „Hello, World“:

import os os.environ['MPLBACKEND'] = 'TkAgg' # Wichtig für macOS from irsim.world import World import matplotlib.pyplot as plt world_config = { 'world_name': 'my_first_robot', 'height': 10, 'width': 10, 'step_time': 0.1, 'sample_time': 0.1, 'offset': [0, 0], 'control_mode': 'keyboard' } world = World(**world_config) robot_config = { 'type': 'diff', # Differentialantrieb 'shape': 'circle', 'radius': 0.3, 'state': [2, 2, 0], # x, y, theta 'vel_min': [-1, -1], 'vel_max': [1, 1], 'goal': [8, 8] } world.add_robot(robot_config) obstacle_config = { 'type': 'obstacle', 'shape': 'circle', 'center': [5, 5], 'radius': 0.5 } world.add_obstacle(obstacle_config) plt.ion() for i in range(200): world.step() plt.pause(0.05)

Führe dies aus und du solltest einen kreisförmigen Roboter und ein Hindernis sehen. Der Roboter bewegt sich noch nicht – wir müssen ihm sagen, was er tun soll.

Teil 3: Klassische Navigation – Go-to-Goal-Regler

Vor dem Zeitalter der KI lösten Robotiker das Problem der Navigation mit cleveren Regelalgorithmen. Implementieren wir den einfachsten davon: die proportionale Regelung in Richtung eines Ziels.

Das Konzept

Stellen Sie sich vor, Sie gehen über ein Feld auf einen Freund zu. Dabei tun Sie ganz natürlich Folgendes:

  1. Sie schauen, wo er sich befindet.
  2. Sie drehen sich zu ihm hin.
  3. Sie gehen vorwärts.

Das ist im Wesentlichen das, was die „Go-to-Goal“-Steuerung tut:

  1. Berechnen Sie den Winkel zum Ziel.
  2. Vergleichen Sie ihn mit Ihrer aktuellen Ausrichtung.
  3. Drehen Sie sich, um den Fehler zu verringern.
  4. Bewegen Sie sich vorwärts.
Loading...

Der Code

import numpy as np class GoToGoalController: def __init__(self, k_linear=0.5, k_angular=2.0): self.k_linear = k_linear # Vorwärtsgeschwindigkeitsverstärkung self.k_angular = k_angular # Drehgeschwindigkeitsverstärkung def compute_velocity(self, robot_state, goal): """ robot_state: [x, y, theta] Ziel: [Ziel_x, Ziel_y] Rückgabewerte: [lineare_Geschwindigkeit, Winkelgeschwindigkeit] """ x, y, theta = Roboter_Zustand Ziel_x, Ziel_y = Ziel dx = Ziel_x - x dy = Ziel_y - y Abstand = np.sqrt(dx**2 + dy**2) Winkel_zum_Ziel = np.arctan2(dy, dx) Kursfehler = Winkel_zum_Ziel - theta Kursfehler = np.arctan2(np.sin(Kursfehler), np.cos(Kursfehler)) if distance < 0.3: return [0.0, 0.0] linear_vel = np.clip(self.k_linear * distance, 0.0, 1.0) angular_vel = np.clip(self.k_angular * heading_error, -1.0, 1.0) return [linear_vel, angular_vel]

Das funktioniert wunderbar – bis ein Hindernis im Weg steht. Der Roboter fährt direkt hinein, da er nur das Ziel kennt.

Hindernisvermeidung hinzufügen

Die einfachste Lösung: Wenn sich etwas in der Nähe und vor dir befindet, wende dich ab.

def compute_velocity_with_avoidance(self, robot_state, goal, obstacles): x, y, theta = robot_state for obs in obstacles: obs_x, obs_y = obs[&#x27;center&#x27;] obs_radius = obs[&#x27;radius&#x27;] dist = np.sqrt((obs_x - x)**2 + (obs_y - y)**2) - obs_radius - 0.3 angle_to_obs = np.arctan2(obs_y - y, obs_x - x) relative_angle = abs(angle_to_obs - theta) if dist < 0.5 and relative_angle < np.pi / 3: if angle_to_obs - theta > 0: return [0.1, -0.8] # nach rechts abbiegen return [0.1, 0.8] # nach links abbiegen return self.compute_velocity(robot_state, goal)

Das ist reaktive Navigation – einfach, schnell, aber begrenzt. Der Roboter kann in Ecken stecken bleiben und plant nicht voraus.

Teil 4: Potentialfelder – ein sanfterer Ansatz

Was wäre, wenn wir statt abrupter „Wenn-Dann“-Regeln ein sanftes Kraftfeld schaffen würden, das den Roboter auf natürliche Weise lenkt?

Das Konzept

Stell dir vor, das Ziel sei ein Magnet, der den Roboter anzieht, und Hindernisse seien Magnete, die ihn abstoßen. Der Roboter folgt der resultierenden Kraft.

Loading...

Anziehungskraft (Ziel):

F_att = k_att × (goal_position − robot_position)

Abstoßendes Potential (Hindernisse):

F_rep = k_rep × (1/distance − 1/threshold) × direction_away

Der Roboter bewegt sich in Richtung der Gesamtkraft.

Die Einschränkung

Potentialfelder sehen auf dem Papier elegant aus, haben aber ein bekanntes Problem: lokale Minima. Stellen Sie sich zwei Hindernisse vor, die in gleichem Abstand auf beiden Seiten des Ziels liegen. Die abstoßenden Kräfte heben sich gegenseitig auf und der Roboter bleibt stecken.

Hier kommen lernbasierte Ansätze ins Spiel – sie können potenziell kreative Lösungen finden, die handgefertigte Algorithmen übersehen.

Teil 5: Verstärkendes Lernen

Jetzt wird es interessant. Was wäre, wenn wir den Roboter durch Versuch und Irrtum lernen lassen würden, statt Regeln zu schreiben?

RL in 60 Sekunden

Verstärkendes Lernen besteht aus vier Komponenten:

  1. Agent – das Gehirn unseres Roboters (ein neuronales Netzwerk).
  2. Umgebung – die Simulationswelt.
  3. Zustand – was der Roboter wahrnimmt (Position, Hindernisse, Ziel).
  4. Belohnung – eine Zahl, die dem Roboter mitteilt, ob er gut oder schlecht abschneidet.

Der Agent führt Aktionen aus, beobachtet die Ergebnisse und passt seine Strategie an, um die Gesamtbelohnung zu maximieren. Es ist wie das Trainieren eines Hundes mit Leckerlis – nur dass der „Hund“ ein neuronales Netzwerk ist und die „Leckerlis“ positive Zahlen sind.

Loading...

Die Gymnasium-Schnittstelle

Um moderne RL-Bibliotheken wie Stable-Baselines3 zu nutzen, bündeln wir IR-SIM in einer Gymnasium-Umgebung:

import gymnasium as gym from gymnasium import spaces import numpy as np class DiffDriveNavEnv(gym.Env): def __init__(self): super().__init__() self.observation_space = spaces.Box( low=-np.inf, high=np.inf, shape=(7,), dtype=np.float32 ) self.action_space = spaces.Box( low=np.array([0.0, -1.0]), # nur vorwärts! high=np.array([1.0, 1.0]), dtype=np.float32 ) def step(self, action): self.world.set_robot_velocity(action) self.world.step() obs = self._get_observation() reward = self._compute_reward(obs) terminated = self._check_goal_reached() truncated = self._check_collision() or self.steps > 1000 return obs, reward, terminated, truncated, {}

Die Beobachtung: Was sieht der Roboter?

Ich habe 7 Werte ausgewählt, die dem Roboter Situationsbewusstsein verleihen:

def _get_observation(self): dx = self.goal[0] - self.robot.x dy = self.goal[1] - self.robot.y dist = np.sqrt(dx**2 + dy**2) dx_norm = dx / max(dist, 0.01) dy_norm = dy / max(dist, 0.01) angle_to_goal = np.arctan2(dy, dx) dtheta = angle_to_goal - self.robot.theta dtheta = np.arctan2(np.sin(dtheta), np.cos(dtheta)) v = self.robot.linear_velocity w = self.robot.angular_velocity min_obstacle_dist = self._get_min_obstacle_distance() return np.array([ dx_norm, dy_norm, dtheta, v, w, min_obstacle_dist, dist ], dtype=np.float32)

Teil 6: Die Belohnungsfunktion – wo alles schiefgeht

Hier ist das schmutzige Geheimnis des RL: Die Belohnungsfunktion ist alles. Eine schlechte Belohnung führt zu bizarrem, unerwartetem Verhalten. Eine gute Belohnung führt zu eleganten Lösungen.

Mein erster Versuch (Katastrophe)

Ich dachte, ich wäre clever:

def _compute_reward_v1(self, obs): reward = 0 reward += 100 / (obs[6] + 0.1) # umgekehrte Entfernung reward -= 0.01 # Zeitstrafe if obs[5] < 0.5: reward -= 10 * (0.5 - obs[5]) # Strafe für Nähe zum Hindernis reward += 0.5 * abs(obs[3]) # Bonus für jede Bewegung reward += 1.0 * (1 - abs(obs[2]) / np.pi) # Bonus für Ausrichtung auf das Ziel if self._check_collision(): Belohnung -= 200 # Strafe für Kollision if obs[6] < 0.3: Belohnung += 50 # Bonus für Zielerreichung return Belohnung

Ergebnis: Der Roboter drehte sich im Kreis. Warum?

Das Problem: konkurrierende Signale

Bei so vielen Belohnungstermen war der Agent verwirrt:

  • Die inverse Entfernung erzeugte seltsame Gradienten.
  • Der Bewegungsbonus förderte jede Bewegung, einschließlich des Drehens.
  • Keine Strafe für übermäßiges Drehen.
Loading...

Die Lösung: forschungsbasierte Einfachheit

Nachdem ich Artikel über RL-Navigation gelesen hatte, vereinfachte ich das System drastisch:

def _compute_reward_v2(self, obs): progress = self.prev_distance - obs[6] self.prev_distance = obs[6] reward = 0 reward += 10.0 * max(0.0, progress) # primär: Belohnung für Vorwärtsbewegung angular_vel = abs(obs[4]) if angular_vel > 0.8: reward -= 5.0 * (angular_vel - 0.8) # Abzug: übermäßiges Drehen if obs[5] < 0.3: reward -= 5.0 # Abzug: zu nah an Hindernissen elif obs[5] < 0.5: reward -= 1.0 if obs[6] < 0.3: reward += 100.0 # Zielbonus return reward

Wichtige Erkenntnisse:

  1. Fortschrittsbasierte Belohnung — Belohne das Schließen der Lücke, nicht die absolute Entfernung.
  2. Rotationsstrafe — das Drehen muss explizit unterbunden werden.
  3. Wenige, klare Signale — den Agenten nicht mit komplexen Zielen verwirren.

Die Korrektur des Aktionsraums

Es gab noch eine weitere entscheidende Änderung: Bewegung nur nach vorne.

Mein ursprünglicher Aktionsraum erlaubte [-1, 1] für die lineare Geschwindigkeit. Der Roboter konnte rückwärts fahren. Das Problem: Er lernte, sich unendlich weit von Hindernissen zurückzuziehen, anstatt um sie herum zu navigieren.

Durch die Beschränkung der linearen Geschwindigkeit auf [0, 1] musste der Roboter vorwärts fahren und einen Weg finden. Er konnte nicht durch Rückzug entkommen.

Teil 7: Training des Agenten

Wenn die Umgebung korrekt eingerichtet ist, ist das Training überraschend unkompliziert:

from stable_baselines3 import PPO from stable_baselines3.common.callbacks import CheckpointCallback env = DiffDriveNavEnv(randomize=True) model = PPO( "MlpPolicy", env, learning_rate=3e-4, n_steps=2048, batch_size=64, n_epochs=10, gamma=0.99, verbose=1, tensorboard_log="./logs/tensorboard/" ) model.learn( total_timesteps=500_000, callback=CheckpointCallback( save_freq=20_000, save_path="./models/ppo/checkpoints/" ) ) model.save("./models/ppo/best_model")

PPO: Warum dieser Algorithmus?

Proximal Policy Optimization (PPO) ist das Arbeitspferd des modernen RL. Es ist:

  • Stabil – driftet nicht leicht ab.
  • Sample-effizient – lernt relativ schnell.
  • Universell einsetzbar – funktioniert bei vielen Problemen.

Es funktioniert, indem es kleine, vorsichtige Schritte im Policy-Raum unternimmt – es ändert nie zu viel auf einmal.

Trainingszeit

Auf meinem M1-Mac dauern 500.000 Zeitschritte etwa 45 Minuten. Die Verwendung von MPS (Apples GPU-Beschleunigung) beschleunigt den Vorgang erheblich:

import torch def get_device(): if torch.backends.mps.is_available(): return "mps" # Apple Silicon GPU if torch.cuda.is_available(): return "cuda" # NVIDIA GPU return "cpu"

Teil 8: Die Ergebnisse

Nachdem die Belohnungsfunktion und der Aktionsraum festgelegt wurden, sieht eine erfolgreiche Navigation wie folgt aus:

Loading...

Der Weg ist bemerkenswert direkt – der Agent hat gelernt, effiziente Routen durch Hindernisfelder zu finden, ohne explizite Wegplanungsalgorithmen.

Vergleich aller Ansätze

Eines der interessantesten Experimente war der Einsatz aller fünf Steuerungsansätze in denselben Szenarien:

Loading...

In diesem Szenario übertreffen die klassischen Steuerungen PPO tatsächlich bei der Schrittzahl. Potential Field ist mit 100 Schritten besonders effizient. Aber sehen Sie sich an, was in einem anspruchsvolleren Szenario passiert:

Loading...

Das ist die eigentliche Erkenntnis: PPO ist robuster. Wenn die Hinderniskonfiguration knifflig wird, versagen klassische Steuerungsalgorithmen – Go-to-Goal kollidiert, Potential Field bleibt in einem lokalen Minimum stecken. PPO findet einen Weg hindurch.

Beachte, wie SAC (Soft Actor-Critic) das klassische Drehverhalten zeigt, das ich zuvor erwähnt habe – dieses kleine spiralförmige Muster ist genau das, was passiert, wenn die Belohnungsfunktion übermäßige Drehung nicht bestraft. (Die SAC- und TD3-Läufe wurden noch mit dem alten Belohnungssystem trainiert.)

Der Algorithmusvergleich

Go-to-Goal: schnell, einfach, interpretierbar. Scheitert bei komplexen Hindernislayouts.

Potential Field: glatte Pfade, keine Feinabstimmung. Lokales Minimum-Falle, kann zeitlich auslaufen.

PPO: robust, bewältigt komplexe Szenarien. Benötigt Training, weniger interpretierbar.

SAC (musste mit der neuen Belohnung neu trainiert werden): theoretisch sample-effizient; hatte Schwierigkeiten mit dieser Aufgabe.

TD3 (musste mit der neuen Belohnung neu trainiert werden): gut für kontinuierliche Steuerung; gab zu schnell auf.

Teil 9: Bonus – Odometrie-Drift verstehen

Ein weiteres Konzept, das es wert ist, visualisiert zu werden: Odometrie-Drift.

In der realen Welt schätzen Roboter ihre Position durch Zählen der Radumdrehungen. Kleine Fehler summieren sich – Räder rutschen, Messungen sind nicht perfekt, und schon bald glaubt der Roboter, er befinde sich an einem Ort, an dem er nicht ist.

Loading...

In dieser Simulation habe ich den Messwerten der Radausleser kleine zufällige Störsignale hinzugefügt. Selbst bei winzigen Fehlern pro Schritt weicht die geschätzte Position merklich vom tatsächlichen Weg ab. Deshalb benötigen echte Roboter zusätzliche Sensoren (Kameras, LIDAR, GPS), um ihre Positionsschätzungen zu korrigieren – ein Thema für einen anderen Beitrag.

Teil 10: Gewonnene Erkenntnisse

Nach wochenlangem Debuggen, Neu-Trainieren und Kopfzerbrechen:

1. Beginne mit klassischen Reglern

Implementiere vor dem Trainieren von RL klassische Lösungen. Sie validieren deine Simulation, liefern Basisdaten und ihr Verhalten beeinflusst das RL-Design (zum Beispiel die „Forward-Only“-Einschränkung).

2. Systematisch debuggen

Wenn RL versagt, widerstehen Sie dem Drang, Hyperparameter willkürlich anzupassen. Stattdessen:

  • Überprüfen Sie die Physik der Umgebung.
  • Überprüfen Sie die Beobachtungswerte während der Episoden.
  • Stellen Sie sicher, dass die Kollisionserkennung funktioniert.
  • Vergewissern Sie sich, dass die Belohnungssignale sinnvoll sind.

3. Einfachere Belohnungen sind besser

Komplexe Belohnungsfunktionen mit vielen Termen verwirren den Agenten. Fortschrittsbasierte Belohnungen mit klaren, konsistenten Signalen schneiden besser ab als aufwendige Formulierungen mit mehreren Zielen.

4. Die Gestaltung des Aktionsraums ist entscheidend

Die Einschränkung des Aktionsraums entsprechend dem gewünschten Verhalten verhindert, dass der Agent kontraproduktive Strategien lernt. Wenn Ihr Roboter nicht rückwärts fahren soll, lassen Sie keine negative lineare Geschwindigkeit zu.

5. Dokumentieren Sie alles

Das wertvollste Ergebnis ist nicht das endgültige Modell – es ist der Weg dorthin. Diagnoseskripte und Änderungsprotokolle helfen Ihnen zu verstehen, was schiefgelaufen ist und warum die Korrekturen funktioniert haben.

Erste Schritte

Möchten Sie es ausprobieren?

# Klonen und einrichten git clone https://github.com/padawanabhi/robot_sim.git cd robot_sim python3 -m venv venv && source venv/bin/activate pip install -r requirements.txt # Simulation testen python scripts/01_test_simulation.py # RL-Agent trainieren python scripts/06_train_rl.py --algorithm ppo --timesteps 500000 # Trainiertes Modell auswerten python scripts/07_evaluate_rl.py --algorithm ppo --episodes 5 # Alle Steuerungen vergleichen python scripts/09_compare_controllers.py

Training überwachen

tensorboard --logdir logs/tensorboard # http://localhost:6006 öffnen

Was kommt als Nächstes?

Dieses Projekt hat Türen zu mehreren spannenden Richtungen geöffnet:

  • Curriculum-Lernen — mit einfachen Umgebungen (ohne Hindernisse) beginnen und den Schwierigkeitsgrad schrittweise erhöhen.
  • Koordination mehrerer Roboter — mehrere Agenten trainieren, ohne miteinander zu kollidieren zu navigieren.
  • Sim-to-Real-Transfer — erlernte Strategien auf physische Roboter wie TurtleBot oder benutzerdefinierte Arduino-Konstruktionen übertragen.
  • 3D-Umgebungen — Steigen Sie für die 3D-Navigation auf Gazebo oder Isaac Sim um.

Fazit

Robotik muss nicht einschüchternd sein. Mit einem einfachen 2D-Simulator und etwas Ausdauer können Sie:

  • Verstehen, wie Roboter sich bewegen und denken.
  • Klassische Steuerungsalgorithmen implementieren.
  • Neuronale Netze für die Navigation trainieren.
  • die Kunst des Debuggens von ML-Systemen erlernen.

Die wertvollste Lektion betraf nicht Roboter oder RL – es ging um systematische Problemlösung. Wenn sich Ihr Agent im Kreis dreht, geben Sie nicht dem Algorithmus die Schuld. Sie vertiefen sich in die Beobachtungen, überprüfen die Physik und vereinfachen, bis Sie den Fehler finden.

Das ist die wahre Fähigkeit. Und sie lässt sich weit über die Robotik hinaus anwenden.

Ressourcen

Wenn Sie die bisherigen Beiträge verfolgt haben, greift der 3D-Folgebeitrag – Von der Flachwelt zur Physik dieselben Ideen in PyBullet auf, mit Sensoren, Navigationsalgorithmen und PPO-Training an einem 3D-Roboter mit Differentialantrieb.

Abhishek Nair - Fractional CTO für Deep Tech & AI
Abhishek Nair - Fractional CTO für Deep Tech & AI
Robotics & AI Engineer
About & contact
Why trust this guide?

Follow Me