Zum Inhalt

Kommunikation

Dual-Path-Architektur

Die MCU-Knoten kommunizieren ueber zwei parallele Kanaele mit dem Pi 5:

  • micro-ROS/UART (primaer): ROS2-Topics via Serial Transport, 921600 Baud
  • CAN-Bus (sekundaer): 1 Mbit/s, MCP2515/SocketCAN auf Pi 5, TWAI auf ESP32-S3

CAN-Sends laufen in den Core-1-Tasks (controlTask/sensorTask), damit sie unabhaengig vom micro-ROS Agent funktionieren.

CAN-Notstopp-Redundanzpfad

Der Drive-Knoten empfaengt Cliff- (0x120) und Battery-Shutdown-Signale (0x141) vom Sensor-Knoten ueber CAN-Bus und stoppt die Motoren direkt — unabhaengig von Pi 5 und micro-ROS.

graph TD
  SENSOR["Sensor-Knoten<br>Core 1, sensorTask"] -->|"CAN 0x120<br>Cliff, 20 Hz"| DRIVE["Drive-Knoten<br>Core 1, controlTask"]
  SENSOR -->|"CAN 0x141<br>Battery Shutdown"| DRIVE
  DRIVE --> STOP["tv=0, tw=0<br>Motoren stoppen"]

  style SENSOR fill:#111D2B,stroke:#00E5FF,color:#cdd9e5
  style DRIVE fill:#111D2B,stroke:#00E5FF,color:#cdd9e5
  style STOP fill:#111D2B,stroke:#FF2A40,color:#FF2A40
Eigenschaft Wert
Latenz < 20 ms (ein controlTask-Zyklus bei 50 Hz)
Unabhaengigkeit Funktioniert ohne Pi 5, Docker oder micro-ROS
Non-blocking twai_receive() mit pdMS_TO_TICKS(0)

Dashboard-Anbindung

graph LR
  subgraph S2C ["Server to Client"]
    TOPICS["ROS2 Topics"] --> BRIDGE["dashboard_bridge<br>WSS:9090"]
    BRIDGE --> WS["useWebSocket"]
    WS --> STORE["Zustand Store"]
    STORE --> REACT["React UI"]
  end

  subgraph C2S ["Client to Server"]
    JOY["Joystick"] --> RATE["Rate-Limiting<br>10 Hz"]
    RATE --> BRIDGE2["dashboard_bridge"]
    BRIDGE2 --> ROS["ROS2 Publish"]
  end

  MJPEG["/image_raw"] --> STREAM["MJPEG :8082"]
  STREAM --> IMG["Browser img-Tag"]

  style BRIDGE fill:#111D2B,stroke:#00E5FF,color:#cdd9e5
  style BRIDGE2 fill:#111D2B,stroke:#00E5FF,color:#cdd9e5

Server → Client

Nachricht Rate Inhalt
telemetry 10 Hz Odometrie, Geschwindigkeit
scan 2 Hz LiDAR-Daten
system 1 Hz CPU, RAM, Temperatur
map 0.5 Hz SLAM-Kartendaten
sensor_status 2 Hz Cliff, Ultraschall, IMU

Client → Server

Nachricht Rate Inhalt
cmd_vel 10 Hz Joystick-Steuerung
heartbeat 5 Hz Deadman-Signal
servo_cmd 10 Hz Servo-Position
nav_goal Einmalig Navigationsziel (Kartenklick)

Datenfluss Ende-zu-Ende (Cliff-Erkennung)

graph TD
  IR["IR-Sensor<br>cliff_detected = true"] --> SHARED["SharedData<br>Mutex"]
  IR --> CAN["CAN 0x120"]

  subgraph ROS2_Pfad ["ROS2-Pfad (~50 ms)"]
    SHARED --> CORE0["Core 0<br>micro-ROS /cliff"]
    CORE0 --> AGENT["micro-ROS Agent<br>UART 921600"]
    AGENT --> SAFETY["cliff_safety_node"]
    SAFETY --> CMDVEL["/cmd_vel = Zero-Twist"]
    SAFETY --> ALARM["/audio/play = cliff_alarm"]
    SAFETY --> DASH["dashboard_bridge<br>→ WSS → Browser"]
  end

  subgraph CAN_Pfad ["CAN-Pfad (< 20 ms)"]
    CAN --> DRIVE_CAN["Drive-Knoten<br>can_cliff_stop = true"]
    DRIVE_CAN --> MOTOR_STOP["tv=0, tw=0"]
  end

  style IR fill:#111D2B,stroke:#FF2A40,color:#FF2A40
  style MOTOR_STOP fill:#111D2B,stroke:#FF2A40,color:#FF2A40
  style CMDVEL fill:#111D2B,stroke:#FF2A40,color:#FF2A40
  style ROS2_Pfad fill:#0B131E,stroke:#517C96,color:#cdd9e5
  style CAN_Pfad fill:#0B131E,stroke:#00FF66,color:#cdd9e5

Zwei parallele Pfade: ROS2 (ueber Pi 5, ~50 ms) und CAN (direkt MCU-zu-MCU, < 20 ms).