Simulation und Reinforcement Learning
Ein Simulator lässt den Roboter tausende Male laufen, ohne dass ein Teil bricht; Reinforcement Learning (RL) lässt den Computer in diesem Simulator durch Versuch und Irrtum einen Lauf-Controller finden. Strido verwendet MuJoCo für schnelle Physik und Training, Gazebo zum Testen des kompletten ROS-2-Stacks und PPO, um eine Lauf-Policy zu trainieren, die auf dem Pi mit reinem NumPy läuft.
In Strido
- MuJoCo-Modell:
models/mujoco/strido.xml+scene.xml, vonmake modelsaus dem CAD erzeugt. - Gazebo-Modell:
ros2/strido_description/urdf/strido.urdf.xacro(mitsim:=true), Weltros2/strido_bringup/worlds/flat.sdf, gestartet übergazebo.launch.py. - RL-Code:
packages/strido-sim/src/strido_sim/rl/(env.py,train.py,evaluate.py); Policy-Laufzeitpackages/strido-core/src/strido/policy.py. - Veröffentlichte Policy:
models/policies/ppo-v1/policy.npz(MLP 23 → 128 → 128 → 12). - Befehle:
make sim-walk,make sim-gazebo,make rl-smoke,make rl-train,make rl-eval(Simulation).
Warum zwei Simulatoren
| MuJoCo 3 | Gazebo Harmonic | |
|---|---|---|
| Läuft als | Python-Prozess (mujoco==3.17.0) | Docker-Container mit ROS 2 Jazzy |
| Stärke | Schnelle, deterministische Kontaktphysik; lässt sich aus Python leicht tausende Male pro Sekunde schrittweise ausführen | Simuliert Sensoren (IMU, GPU-LiDAR, Kamera) und führt die echten ROS-2-Nodes und Controller aus |
| Verwendet für | Gait-Demo, PPO-Training und -Auswertung | End-to-End-Test von gait_node / policy_node mit denselben Topics wie am Roboter |
| Befehl | make sim-walk, make rl-* | make sim-gazebo |
Eine in MuJoCo trainierte Policy in Gazebo laufen zu lassen, ist eine günstige Sim-to-Sim-Prüfung: Funktioniert sie nur im Simulator, in dem sie trainiert wurde, hat sie dessen Eigenheiten gelernt und wird auch den echten Roboter nicht überstehen.
Die Modelle, aus dem CAD erzeugt
strido_cad.models erstellt alle Simulationsmodelle aus der verifizierten CAD-Baugruppe, sodass der simulierte Roboter die echten Gliedlängen, Massen und Gelenkgrenzen hat:
- 13 Glied-Meshes (
ros2/strido_description/meshes/), jeweils auf 25 k Flächen reduziert; - Massen aus Teilevolumen und Dichten, Trägheiten aus den Meshes, Kabel nach verlegter Länge: insgesamt 3,07 kg (
ros2/strido_description/config/mass_report.yaml); - MJCF mit 12 Positionsaktoren (
kp 25,kv 0.4, Momentgrenze ±3,432 N·m = Blockiermoment des SPT5435LV bei 6 V), ein IMU-Site mit Quaternion-, Gyro- und Beschleunigungssensoren sowie Fuß-Sites; - Physikschritt 0,002 s (500 Hz) mit dem Integrator
implicitfast.
Eine Vereinfachung: Das Knie ist als direktes Drehgelenk am Unterschenkel modelliert. Das Viergelenk-Koppelgetriebe wird in Software gelöst (Gait, Firmware), und seine Grenzen werden angewendet, bevor Befehle das Gelenk erreichen (Kinematik).
In Gazebo ergänzt das Xacro mit sim:=true gz_ros2_control mit einer position-Befehlsschnittstelle je Gelenk, eine IMU (200 Hz), ein GPU-LiDAR (360 Messpunkte, 10 Hz, 0,05–12 m, Gaußsches Rauschen σ 0,01 m), eine Kamera (640 × 360, 15 Hz) und einen Ground-Truth-Odometrie-Publisher (/ground_truth/odom, 50 Hz).
MuJoCo: der Gait-Lauf
make sim-walk # 10 s trot at 0.15 m/s → build/sim/walk.mp4
uv run --locked python -m strido_sim.walk --seconds 6 --wz 0.6 # turn
uv run --locked mjpython -m strido_sim.walk --viewer # interactive (macOS needs mjpython)Dies führt strido.gait aus, denselben Trot, den der Roboter über ROS 2 ausführt, mit 100 Hz wie der echte Befehlsstrom. Zusätzlich hält es die Richtung mithilfe des Gierwinkels aus dem Simulator, den der Roboter nicht hat. Aufgezeichnetes Ergebnis: 1,10 m in 10 s bei 0,15 m/s, Körperhöhe 0,174 m (Verifikation).
Gazebo: der vollständige ROS-2-Stack
make sim-gazebo # build image, headless walk test, exit code = pass/fail
make sim-gazebo POLICY=models/policies/ppo-v1/policy.npz # same test with policy_nodewalk_smoke wartet 6 s, bis der Roboter steht, sendet 8 s lang 0,15 m/s auf /cmd_vel und besteht, wenn sich die Basis ≥ 0,4 m vorwärts bewegt hat (Ground Truth), aufrecht geblieben ist (|Roll|, |Pitch| < 0,5 rad, Höhe > 0,10 m) und /joint_states, /imu/data, /scan, /camera/image_raw sowie /ground_truth/odom alle veröffentlicht wurden. Aufgezeichnet: 0,98 m in 8 s mit dem Gait-Node.
Reinforcement Learning in einem Absatz
Ein Agent (hier: ein neuronales Netz, die Policy
wobei der Diskontfaktor
wobei
Die Umgebung, wie implementiert
StridoEnv (strido_sim/rl/env.py) ist eine Gymnasium-Umgebung: „mit der befohlenen Geschwindigkeit laufen“.
Observations (23 Zahlen)
| Teil | Größe | Quelle am Roboter |
|---|---|---|
| Winkelgeschwindigkeit (Gyro) | 3 | IMU (/imu/data) |
| Schwerkraftrichtung im Körperkoordinatensystem | 3 | Orientierungsquaternion der IMU |
| Befehl vx, vy, ωz | 3 | /cmd_vel |
| Vorherige Action | 12 | eigene Ausgabe der Policy |
| Gangtakt sin, cos (Periode 0,4 s) | 2 | ein Timer |
Es gibt keine Gelenkwinkel oder -geschwindigkeiten. Die SPT5435LV-Servos melden nichts zurück, eine Policy, die sich auf Gelenkrückmeldung stützt, könnte also nicht auf dem Roboter laufen (ADR-006 in Entscheidungen). Die Observation enthält nur, was der Roboter messen kann oder weiß.
Actions (12 Zahlen)
Jede Action liegt in
- die Anstiegsbegrenzung der Firmware von 6 rad/s (
pro Policy-Schritt), - die Gelenk- und Kniekurbel-Begrenzung, genau wie in der Firmware.
Werden diese im Training modelliert, lernt die Policy mit denselben Grenzen, die der Roboter vorgibt, statt sie erst auf der Hardware zu entdecken.
Timing
Policy mit 50 Hz (CONTROL_DT = 0.02 s), Physik mit 500 Hz:
Befehle
Jede Episode zieht einen Befehl: 20 % der Episoden stehen still (Nullbefehl); die übrigen ziehen gleichverteilt aus vx −0,15…0,30 m/s, vy −0,10…0,10 m/s, ωz −0,6…0,6 rad/s. policy_node begrenzt /cmd_vel am Roboter auf diese Bereiche, weil die Policy nie etwas außerhalb davon gesehen hat.
Reward
Pro Schritt, so skaliert, dass perfektes Folgen höchstens
Dabei sind
Eine Episode endet mit einer Strafe von −2, wenn die Basis unter 0,09 m sinkt oder sich über etwa 53° neigt.
Die exponentiellen „Tracking-Kerne“ liefern einen glatten Reward, der bei perfektem Folgen 1 ist und mit dem Fehler abklingt: Bei der Breite 0,01 senkt ein Geschwindigkeitsfehler von
Domain Randomisation
Der echte Roboter wird nicht genau dem Modell entsprechen. Statt darauf zu hoffen, variiert das Training das Modell in jeder Episode, damit die Policy lernt, mit einer Bandbreite zurechtzukommen:
| Randomisiert | Bereich |
|---|---|
| Servosteifigkeit (kp und der passende Bias) | × 0,85 … 1,15 |
| Masse der Basis | × 0,90 … 1,15 |
| Kontaktreibung, Boden und Füße gemeinsam | × 0,6 … 1,2 |
| IMU-Rauschen | Gyro σ 0,05 rad/s, Schwerkraft σ 0,02 |
| Anfangsgelenkwinkel | Stehpose + σ 0,02 rad |
Boden und Füße werden gemeinsam skaliert, weil MuJoCo die größere Reibung der beiden sich berührenden Geoms verwendet; nur eines zu skalieren, würde oft nichts ändern.
Training, Export und Auswertung
uv sync --locked --group rl # once: PyTorch, Stable-Baselines3, Gymnasium (opt-in, large)
make rl-smoke # 20 k steps + evaluation: checks the pipeline in seconds
make rl-train # 20 M steps, 8 parallel envs, CPU → build/rl/ppo-v1/
make rl-eval # evaluate models/policies/ppo-v1/policy.npz with a video
uv run --locked --group rl python -m strido_sim.rl.evaluate build/rl/<run>/policy.npz --vx 0.2 --video build/rl/eval.mp4Wichtige PPO-Einstellungen in train.py: 1024 Schritte je Umgebung je Update, Batch 2048, 5 Epochen, Lernrate [128, 128], Critic [256, 256]; Observations und Rewards normalisiert (VecNormalize, Clip 10). Der aufgezeichnete Lauf ppo-v1 dauerte 1734 s auf einer CPU.
Das Training schreibt policy.zip (Stable-Baselines3), vecnormalize.pkl, progress.csv und policy.npz: die Gewichte und Biases des Actors sowie Mittelwert/Varianz der Observations als einfache Arrays. strido.policy.MlpPolicy implementiert den Vorwärtsdurchlauf in einem Dutzend Zeilen NumPy neu: normalisieren evaluate.py und in policy_node, das Ausgewertete ist also Bit für Bit die Funktion, die der Roboter ausführt, und der Pi benötigt kein PyTorch.
Was ppo-v1 erreicht
| Test | Ergebnis |
|---|---|
| MuJoCo, Befehl vx 0,2 m/s (aktuelle Grenzen) | 0,215 m/s im Mittel, 2,15 m in 10 s, aufrecht |
| MuJoCo, v0.1-Durchlauf | vx 0,15 → 0,152 m/s; 0 → steht; −0,1 → −0,068 m/s; vy 0,1 → 0,023 m/s (schwach); ωz 0,5 → 232° in 10 s |
Gazebo über policy_node (Sim-to-Sim) | 0,82 m in 8 s bei 0,15 m/s, aufrecht |
Seitliches und rückwärtiges Folgen ist schwach: zu erwarten bei einer Policy ohne Gelenkrückmeldung (ADR-006).
Die Sim-to-Real-Lücke
Alles oben ist Simulation. Auf einem physischen Strido ist noch nichts gelaufen. Die bekannten Lücken:
- Servoverhalten. kp 25 / kv 0,4 und null Latenz sind Schätzungen; echte Hobby-Servos haben Totzone, Spiel, Getriebenachgiebigkeit und eine spannungsabhängige Geschwindigkeit.
- Spannungseinbruch des Akkus. Servogeschwindigkeit und -moment sinken mit der Akkuspannung; das Modell nimmt 6 V an.
- IMU. Das Training liest ein perfektes Quaternion plus Gaußsches Rauschen; das des Roboters stammt aus einem Mahony-Filter mit eigener Verzögerung und eigenem Bias.
- Kontakte. Gedruckte Füße auf einem echten Boden, keine gleichmäßige Ebene mit randomisierter Reibung.
- Nicht modellierte Mechanik. Das Knie-Koppelgetriebe ist in der Simulation ein direktes Gelenk; Drucktoleranzen und Biegung sind nicht modelliert.
Das Vorgehen des Repos für einen ersten Hardwarelauf: in MuJoCo und Gazebo auswerten, mit dem Roboter auf einem Ständer beginnen, /strido/estop griffbereit halten und damit rechnen, mit gemessenem Servoverhalten (kp/kv, Latenz) neu zu trainieren, sobald der Roboter existiert.
ros2 launch strido_bringup robot.launch.py use_policy:=true policy:=/path/policy.npzWARNING
Eine gelernte Policy kann schnelle, große Bewegungen befehlen, die der programmierte Gait nie ausführen würde. Anstiegsbegrenzung, Gelenkbegrenzungen und Watchdog der Firmware gelten weiterhin, können aber nicht verhindern, dass ein Bein Ihre Hand trifft. Testen Sie zuerst auf einem Ständer (Sicherheit).
Selbsttest
- Warum beobachtet die Policy keine Gelenkwinkel?
- Welche zwei Firmware-Verhalten werden in
env.step()nachgebildet, und warum? - Warum werden Boden- und Fußreibung gemeinsam randomisiert?
- Warum wird
policy.npzexportiert, statt das Stable-Baselines3-Modell auf dem Pi auszuführen? - Was sagt Ihnen das Ausführen der in MuJoCo trainierten Policy in Gazebo?
Antworten
- Die SPT5435LV-Servos haben keine Positionsrückmeldung; eine Policy darf nur verwenden, was der echte Roboter misst (IMU, Befehl, ihre eigene vorherige Action, einen Takt).
- Die Anstiegsbegrenzung von 6 rad/s und die Gelenk-/Kniekurbel-Begrenzung, damit die Policy unter denselben Einschränkungen trainiert, die der Roboter vorgibt.
- MuJoCo verwendet die größere Reibung der beiden sich berührenden Geoms; nur eine zu skalieren, hätte oft keine Wirkung.
- Der Roboter bräuchte PyTorch; die NumPy-Laufzeit ist klein, und derselbe Code wird in der Auswertung verwendet, sodass die eingesetzte Funktion genau die ausgewertete ist.
- Ob die Policy von MuJoCo-spezifischen Eigenheiten abhängt: Eine Sim-to-Sim-Lücke ist ein Warnzeichen für die Sim-to-Real-Lücke.
Weiterführende Literatur
- MuJoCo-Dokumentation
- Gazebo-Harmonic-Dokumentation
- Stable-Baselines3 PPO
- Schulman et al., Proximal Policy Optimization Algorithms (2017)
- Tobin et al., Domain Randomization for Transferring Deep Neural Networks from Simulation to the Real World (2017)
- Theorie: Reinforcement Learning, Fortbewegung mit Beinen