Zum Inhalt

Build und Deployment

Fuer eine schrittweise Inbetriebnahme-Anleitung siehe benutzerhandbuch.md.

Firmware (PlatformIO, zwei getrennte Projekte)

Die MCU-Firmware besteht aus zwei getrennten PlatformIO-Projekten mit identischem Dual-Core-Pattern (Core 0: micro-ROS Executor, Core 1: Echtzeit-Datenerfassung + CAN).

Drive-Knoten (Antrieb, PID, Odometrie, LED)

cd amr/mcu_firmware/drive_node
pio run -e drive_node                      # Kompilieren
pio run -e drive_node -t upload -t monitor # Upload + Monitor
pio run -e led_test -t upload -t monitor   # LED/MOSFET-Diagnose (~5s)

Sensor-Knoten (Ultraschall, Cliff, IMU, Batterie, Servo)

cd amr/mcu_firmware/sensor_node
pio run -e sensor_node                      # Kompilieren
pio run -e sensor_node -t upload -t monitor # Upload + Monitor

Erster Build pro Knoten: ~15 Min (micro-ROS aus Source). Folgebuilds gecached.

Firmware-Pruefung des Drive-Knoten

Falls unklar ist, ob die korrekte Firmware laeuft:

timeout 3 cat /dev/amr_drive | od -A x -t x1z | head -3

Kriterium:

  • Binaere XRCE-DDS-Daten mit 0x7e-Header: korrekt
  • Text wie duty= 255/1023: falsches Environment (led_test)

Falls die falsche Firmware aktiv ist:

cd amr/mcu_firmware/drive_node
pio run -e drive_node -t upload

ROS2 und Docker

ROS2 Humble laeuft auf dem Pi 5 im Docker-Container (amr_ros2). Das Docker-Image wird aus amr/docker/Dockerfile gebaut.

Einmalige Einrichtung

cd amr/docker/
sudo bash host_setup.sh     # udev-Regeln, Gruppen, Kamera-Bridge, CAN-Service
docker compose build         # Image bauen (~15-20 Min auf Pi 5)

Container-Wrapper run.sh

Der Convenience-Wrapper amr/docker/run.sh verwaltet den Container-Lebenszyklus:

cd amr/docker/
./run.sh                                          # Interaktive Shell
./run.sh bash                                     # Interaktive Shell (explizit)
./run.sh ros2 topic list                          # Einzelbefehl ausfuehren
./run.sh ros2 launch my_bot full_stack.launch.py  # Full-Stack starten
./run.sh colcon build --packages-select my_bot --symlink-install  # Build
./run.sh exec bash                                # Zweites Terminal im laufenden Container

run.sh erledigt automatisch:

  • Container via docker compose up -d starten (falls nicht laufend)
  • udev-Symlinks (/dev/amr_drive, /dev/amr_sensor) im Container anlegen
  • Kamera-Bridge-Pruefung bei use_camera:=True

Colcon Build

./run.sh colcon build --packages-select my_bot --symlink-install

Verifikation

./verify.sh    # Gesamttest (Topics, TF, Raten)

Full-Stack Launch

Launch-Argumente

Argument Default Beschreibung
use_slam True SLAM Toolbox (async Modus)
use_nav True Nav2 Navigation Stack
use_rviz False RViz2 Visualisierung
use_sensors True Sensor-Knoten ESP32-S3
use_dashboard False Dashboard-Bridge (WebSocket :9090, MJPEG :8082)
use_camera False Kamera-Knoten (v4l2_camera_node)
use_vision False Vision-Pipeline (Hailo UDP + Gemini)
use_cliff_safety True Cliff-Safety cmd_vel-Multiplexer
use_audio False Audio-Feedback (MAX98357A I2S, Docker-Volume asound.conf fuer softvol)
use_can False CAN-to-ROS2 Bridge (SocketCAN)
use_tts False TTS-Sprachausgabe (Gemini-Semantik via gTTS)
use_respeaker False ReSpeaker Mic Array DoA/VAD
use_voice False Sprachsteuerung (erfordert use_respeaker:=True, Gemini Audio-STT primaer / faster-whisper Fallback)
drive_serial_port /dev/amr_drive Serieller Port Drive-Knoten
sensor_serial_port /dev/amr_sensor Serieller Port Sensor-Knoten
camera_device /dev/video10 Video-Device (v4l2loopback-Bridge)
params_file nav2_params.yaml Nav2 Parameter-Datei
slam_params_file mapper_params_online_async.yaml SLAM Toolbox Parameter-Datei

Haeufige Startkombinationen

cd amr/docker/

# Nur SLAM (ohne Navigation)
./run.sh ros2 launch my_bot full_stack.launch.py use_nav:=false

# SLAM + Navigation + Dashboard (ohne RViz)
./run.sh ros2 launch my_bot full_stack.launch.py use_dashboard:=True use_rviz:=False

# Vollsystem mit Kamera und Vision
./run.sh ros2 launch my_bot full_stack.launch.py \
    use_dashboard:=True use_camera:=True use_vision:=True use_rviz:=False

ESP32-Reset

Die ESP32-S3-Knoten besitzen keine eigenstaendige Reconnection-Logik fuer den micro-ROS-Agent. Ein Reset vor dem Containerstart stellt sicher, dass die Knoten beim Agent-Start in den Wartezustand eintreten.

python3 -c "
import serial, time
for name, port in [('Drive', '/dev/amr_drive'), ('Sensor', '/dev/amr_sensor')]:
    try:
        s = serial.Serial(port, 921600)
        s.dtr = False
        s.rts = True
        time.sleep(0.1)
        s.dtr = False
        s.rts = False
        time.sleep(0.1)
        s.close()
        print(f'{name} ({port}) reset OK')
    except Exception as e:
        print(f'{name} ({port}) reset FEHLER: {e}')
time.sleep(2)
print('Warte 2s auf Boot...')
"

Gemini API-Schluessel

Fuer Vision (Hailo + Gemini), TTS-Sprachausgabe und Sprachsteuerung wird ein Gemini API-Schluessel benoetigt.

# Neuen Gemini API-Schluessel erstellen unter https://aistudio.google.com/apikey
# Genutztes Modell: gemini-2.0-flash-lite (Semantic Vision und TTS; Voice nutzt Gemini Audio-STT primaer / faster-whisper Fallback)

# Schluessel anzeigen
cat ~/amr-projekt/scripts/.gemini_api.key

# Schluessel wird via docker-compose.yml als GEMINI_API_KEY in den Container durchgereicht

Vollstart (alle Subsysteme, vier Terminals)

Dieser Ablauf startet das Gesamtsystem fuer den Live-Betrieb mit SLAM, Kamera, Dashboard, Hailo-basierter Objekterkennung, CAN-Bus, Sprachsteuerung und semantischer Auswertung.

Voraussetzungen

  • Das HEF-Modell liegt unter hardware/models/yolov8s.hef.
  • GEMINI_API_KEY ist in der Host-Umgebung gesetzt (siehe oben).
  • Das Docker-Image ist aktuell (docker compose build).
  • Kein anderer Prozess blockiert die seriellen Ports.
  • Auf dem Drive-Knoten laeuft die korrekte Firmware, nicht led_test.

Startsequenz

Der Live-Betrieb benoetigt vier Terminals. T1 ist einmalig (Reset + Pruefung), T2-T4 sind langlebige Prozesse.

T1: Reset beider ESP32-S3 via DTR/RTS (einmalig)

lsusb
ls /dev/ttyACM* /dev/ttyUSB* /dev/amr_*
cd ~/amr-projekt
python3 -c "import serial,time;[exec('s=serial.Serial(p,921600);s.dtr=False;s.rts=True;time.sleep(0.1);s.dtr=True;s.rts=False;s.close()') for p in ['/dev/amr_drive','/dev/amr_sensor']]"

T2: Full-Stack Launch mit allen Subsystemen

cd ~/amr-projekt/amr/docker
./run.sh ros2 launch my_bot full_stack.launch.py \
    use_dashboard:=True use_camera:=True use_vision:=True \
    use_audio:=True use_respeaker:=True use_tts:=True \
    use_voice:=True use_can:=True

Erfolgsindikatoren:

  • micro_ros_agent_drive: session established
  • micro_ros_agent_sensor: session established
  • slam_toolbox: Registering sensor
  • dashboard_bridge: WebSocket- und MJPEG-Server gestartet
  • gemini_semantic_node: Modell konfiguriert
  • hailo_udp_receiver: wartet auf den Host-Runner
  • can_bridge_node: CAN-Bus verbunden

T3: Hailo-8L Vision auf dem Host (Python 3.13)

cd ~/amr-projekt
python3 amr/scripts/host_hailo_runner.py

Falls keine Hailo-Hardware angeschlossen ist:

cd ~/amr-projekt
PYTHONUNBUFFERED=1 python3 amr/scripts/host_hailo_runner.py --fallback

Kriterium:

  • Der Runner verbindet sich mit dem MJPEG-Stream der dashboard_bridge.
  • Erste Detektionen erscheinen nach erfolgreichem Stream-Zugriff.

T4: Vite-Dev-Server Dashboard

cd ~/amr-projekt/dashboard
npm run dev -- --host 0.0.0.0

Die Benutzeroberflaeche ist danach erreichbar unter https://amr.local:5173/.

Verifikation

Im laufenden System ueber ein zweites Terminal pruefen:

cd ~/amr-projekt/amr/docker
./run.sh exec bash

Dann im Container:

ros2 topic list --no-daemon
timeout 5 ros2 topic hz /odom
timeout 5 ros2 topic hz /scan
ros2 topic echo /odom --once --no-daemon
ros2 topic echo /vision/detections --once --no-daemon

Erwartete Kerndaten:

  • /odom aktiv (~18 Hz)
  • /scan aktiv (~7-8 Hz)
  • /map aktiv
  • /camera/image_raw aktiv
  • /vision/detections aktiv nach Start des Hailo-Runners

System herunterfahren

  1. Host-Runner und Benutzeroberflaeche mit Ctrl+C beenden.
  2. ROS2-Launch in Terminal 1 mit Ctrl+C beenden.
  3. Container und Ports bereinigen:
docker stop $(docker ps -q) && docker rm $(docker ps -aq)
fuser -k 8082/tcp 9090/tcp 5173/tcp

CAN-Bus (SocketCAN)

host_setup.sh Sektion 6 installiert can-utils und den can0.service (systemd, 1 Mbit/s, txqueuelen=1000). Nach Aenderungen an /boot/firmware/config.txt ist ein Reboot noetig.

# CAN-Status pruefen
ip -details link show can0

# CAN-Bridge Diagnostik-Knoten (im Docker)
ros2 launch my_bot full_stack.launch.py use_can:=True

# ReSpeaker DoA/VAD-Knoten (im Docker)
ros2 launch my_bot full_stack.launch.py use_respeaker:=True

# Standalone CAN-Validierung (ohne Docker)
python3 amr/scripts/can_validation_test.py --duration 30

Dashboard (React + Vite + TypeScript + Tailwind)

cd dashboard/
npm install && npm run dev -- --host 0.0.0.0   # Entwicklung (https://amr.local:5173)
npm run build                                   # Produktion (tsc + vite build)
npm run lint                                    # ESLint
npx tsc --noEmit                                # TypeScript Type-Check

Die Benutzeroberflaeche verbindet sich automatisch per WebSocket (wss://amr.local:9090) mit der dashboard_bridge im Container. SLAM-Kartenklick sendet ein Nav2 NavigateToPose Goal.

Wartungsskripte

# Projekt-Abhaengigkeiten aktualisieren (npm, pip, PlatformIO, Docker, ROS2-Image)
./scripts/update_dependencies.sh

# Systemwartung mit AMR-Diagnose (Temperatur, Speicher, Services, USB, EEPROM)
sudo ./scripts/rover_wartung.sh            # Vollstaendig mit apt-Updates
sudo ./scripts/rover_wartung.sh --check    # Nur Diagnose, keine Aenderungen

Typische Fehlerbilder

micro-ROS-Agent laeuft, aber keine Session

Ursachen:

  • Falsche Drive-Knoten-Firmware (led_test statt drive_node)
  • ESP32 nach Neustart nicht resettet

Massnahmen:

  • drive_node explizit flashen: cd amr/mcu_firmware/drive_node && pio run -e drive_node -t upload
  • DTR/RTS-Reset vor dem Containerstart erneut ausfuehren (siehe Abschnitt ESP32-Reset)

SLAM meldet Message Filter dropping message

Ursache:

  • odom -> base_link TF fehlt, meist wegen fehlender Verbindung zum Drive-Knoten

Massnahme:

ros2 topic echo /odom --once --no-daemon

Falls keine Daten: Drive-Knoten Reset und micro-ROS-Agent-Verbindung pruefen.

Hailo meldet HAILO_OUT_OF_PHYSICAL_DEVICES

Ursache:

  • Alter Runner-Prozess blockiert das Device

Massnahme:

pkill -f host_hailo_runner

Port-Konflikte bei Neustart

Massnahme:

fuser -k 8082/tcp 9090/tcp 5173/tcp 5174/tcp

Regel

Lange Kommandoablaeufe bleiben hier und nicht in CLAUDE.md.