Zum Inhalt

Architektur

Zielbild

Der AMR besteht aus einem Raspberry Pi 5 als zentralem ROS2-Rechner und zwei ESP32-S3-Knoten fuer echtzeitnahe I/O- und Regelaufgaben. Die Architektur gliedert sich in drei Ebenen mit klarer Verantwortungstrennung.

Detaillierte Beschreibungen der einzelnen Architekturaspekte:

Vollstaendiges Systemdiagramm

┌─────────────────────────────────────────────────────────────────────────────────┐
│  Ebene C — Intelligente Interaktion (optional)                                  │
│                                                                                 │
│  [Host: Hailo-8L Runner]──UDP:5005──>[Docker: hailo_udp_receiver]               │
│       YOLOv8 5 Hz                       │                                       │
│                                         ▼                                       │
│                                  /vision/detections                             │
│                                         │                                       │
│                                         ▼                                       │
│                              [gemini_semantic_node]──>/vision/semantics          │
│                               Gemini 2.0 flash-lite         │                   │
│                                                             ▼                   │
│                                                      [tts_speak_node]           │
│                                                       gTTS → mpg123            │
│  [ReSpeaker DoA]──>/sound_direction                    → MAX98357A I2S          │
├─────────────────────────────────────────────────────────────────────────────────┤
│  Ebene B — Bedien- und Leitstandsebene                                          │
│                                                                                 │
│  ┌── Docker-Container (ros:humble, network_mode:host, privileged) ───────────┐  │
│  │                                                                           │  │
│  │  [micro-ROS Agent x2]   [SLAM Toolbox]   [Nav2]   [odom_to_tf]           │  │
│  │    /dev/amr_drive          /map            /cmd_vel   odom→base_link      │  │
│  │    /dev/amr_sensor         /scan                                          │  │
│  │                                                                           │  │
│  │  [cliff_safety_node]   [audio_feedback_node]   [can_bridge_node]          │  │
│  │    /nav_cmd_vel →          /audio/play →          SocketCAN →             │  │
│  │    /cmd_vel                aplay → /dev/snd       Queue 512, 100 Hz       │  │
│  │                                                                           │  │
│  │  [dashboard_bridge]                                                       │  │
│  │    WSS:9090 ◄──► ROS2 Topics                                             │  │
│  │    MJPEG:8082 ← /image_raw                                               │  │
│  └───────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│  [Browser: Benutzeroberflaeche React/Vite]  HTTPS:5173                                    │
│    Zustand Store ◄──► useWebSocket ◄──WSS:9090──► dashboard_bridge              │
│    Joystick → cmd_vel 10 Hz, Heartbeat 5 Hz                                    │
├─────────────────────────────────────────────────────────────────────────────────┤
│  Ebene A — Fahrkern                                                             │
│                                                                                 │
│  [Drive-Knoten ESP32-S3]              [Sensor-Knoten ESP32-S3]                      │
│    Core 0: micro-ROS Executor         Core 0: micro-ROS Executor                │
│      Sub: /cmd_vel, /hardware_cmd      Sub: /servo_cmd, /hardware_cmd           │
│      Pub: /odom 20 Hz                   Pub: /range, /cliff, /imu, /battery     │
│    Core 1: PID 50 Hz, Encoder,        Core 1: Sensorerfassung                   │
│      CAN TX/RX                          (Cliff 20 Hz, US 10 Hz,                │
│                                          IMU 50 Hz, Batt 2 Hz), CAN TX         │
│         │          ▲                       │          ▲                          │
│         │ UART     │ UART                  │ UART     │ UART                    │
│         │ 921600   │ 921600                │ 921600   │ 921600                  │
│         ▼          │                       ▼          │                          │
│      [Pi 5 /dev/amr_drive]          [Pi 5 /dev/amr_sensor]                      │
│                                                                                 │
│  ───── CAN-Bus 1 Mbit/s (MCP2515/TWAI) ─────                                   │
│    Drive ◄──0x120 Cliff──► Sensor                                               │
│    Drive ◄──0x141 Battery──► Sensor                                             │
│    + Diagnostik-Frames (bidirektional)                                          │
└─────────────────────────────────────────────────────────────────────────────────┘

Docker-Container-Grenzen

┌── Docker-Container (ros:humble-ros-base, arm64) ─────────────────────┐
│                                                                       │
│  ROS2 Nodes: micro-ROS Agents, SLAM, Nav2, cliff_safety,             │
│              dashboard_bridge, audio_feedback, can_bridge,            │
│              hailo_udp_receiver, gemini_semantic, tts_speak,          │
│              odom_to_tf, aruco_docking                                │
│                                                                       │
│  Devices:  /dev/amr_drive, /dev/amr_sensor, /dev/ttyUSB0, /dev/snd   │
│  Volumes:  my_bot (rw), scripts (ro), dashboard (ro, TLS-Certs),     │
│            hardware (ro), asound.conf, X11, Named (build/install/log) │
│  Network:  host (kein Bridge-Netzwerk)                                │
│  Privileged: true                                                     │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘

┌── Host (Raspberry Pi 5, Debian Trixie) ────────────────────────────┐
│  host_hailo_runner.py (Python 3.13, Hailo-8L SDK)                     │
│  Dashboard Dev-Server (npm run dev, :5173)                            │
│  mkcert-Zertifikate                                                   │
└───────────────────────────────────────────────────────────────────────┘

Der Container laeuft mit network_mode: host und privileged: true, um Zugriff auf serielle Geraete, Audio-Hardware und SocketCAN zu ermoeglichen. Host-only-Prozesse (Hailo-Runner, Dashboard-Dev-Server) kommunizieren ueber localhost-Ports.

Datenfluss Ende-zu-Ende

Beispiel: Cliff-Erkennung bis Dashboard-Anzeige und Motorstopp:

1. Sensor-Knoten Core 1 (sensorTask, 20 Hz)
   IR-Sensor → cliff_detected = true
        ├──► SharedData.cliff (Mutex) → Core 0 micro-ROS Pub /cliff
        │                                      │
        │                                      ▼
        │                               micro-ROS Agent (UART 921600)
        │                                      │
        │                                      ▼
        │                               cliff_safety_node
        │                                 /cmd_vel = Zero-Twist (20 Hz)
        │                                 /audio/play = "cliff_alarm"
        │                                      │
        │                                      ├──► dashboard_bridge → WSS → Browser
        │                                      │     sensor_status (event)
        │                                      │
        │                                      └──► audio_feedback_node
        │                                            aplay → MAX98357A
        └──► CAN 0x120 (1 Byte: 0x01) ──► Drive-Knoten Core 1
                                            can_cliff_stop = true
                                            tv=0, tw=0 (< 20 ms)

Zwei parallele Pfade: ROS2 (ueber Pi 5, ~50 ms) und CAN (direkt MCU-zu-MCU, < 20 ms). Der CAN-Pfad ist die Rueckfallebene bei Ausfall von Pi 5 oder micro-ROS.

Architekturregel

Zeitkritische Low-Level-Funktionen bleiben auf den MCU-Knoten. Koordination, Kartierung und Navigation bleiben auf dem Pi 5.

TF-Baum

odom → base_link              (dynamisch, odom_to_tf, 20 Hz)
  ├── laser                   (statisch, x=0.10, z=0.235, yaw=pi)
  ├── camera_link             (statisch, x=0.10, z=0.08, use_camera)
  └── ultrasonic_link         (statisch, x=0.15, z=0.05, use_sensors)

Der LiDAR ist 180° gedreht montiert — die TF-Transformation (yaw=pi) kompensiert dies.

Weitergehende Details

Bereich Dokument
Firmware (Drive-Knoten, Sensor-Knoten) amr/mcu_firmware/CLAUDE.md
ROS2-System (Topics, Launch, Nodes) Topics und TF-Baum
Dashboard (Komponenten, API) Dashboard
Vision-Pipeline Vision-Pipeline
Hardware und Schaltplan hardware/docs/hardware-setup.md
CAN-Bus Spezifikation hardware/can-bus/CAN-Bus.md
Serielle Ports und udev Serielle Schnittstellen
Roboter-Parameter Roboter-Parameter
Build und Deployment Build und Deployment
Systemdokumentation Systemdokumentation
Benutzerhandbuch Benutzerhandbuch