Dashboard¶
Zweck¶
Referenz fuer die AMR-Benutzeroberflaeche (Bedien- und Leitstandsebene, Ebene B). Beschreibt Tech-Stack, Architektur, Komponenten, State Management, Kommunikation und Entwicklungsprozess.
Regel¶
Dashboard-spezifische Details (Komponenten, Hooks, Store, Theme) gehoeren in diese Datei. WebSocket-Protokoll und ROS2-Anbindung werden hier beschrieben, Topic-Definitionen stehen in docs/ros2_system.md.
1. Tech-Stack¶
| Bereich | Technologie | Version |
|---|---|---|
| Framework | React | 19.2.0 |
| Sprache | TypeScript | ~5.9.3 |
| Build-Tool | Vite | 7.3.1 |
| CSS-Framework | Tailwind CSS | 4.2.0 |
| State Management | Zustand | 5.0.11 |
| Joystick | nipplejs | 0.10.2 |
| Linting | ESLint (Flat Config) | 9.39.1 |
| Type-Check | TypeScript strict mode | — |
2. Verzeichnisstruktur¶
dashboard/
├── src/
│ ├── components/ # 22 React-Komponenten
│ │ ├── Dashboard.tsx # Hauptseite Steuerung (4-Spalten-Grid)
│ │ ├── DetailPage.tsx # Detailseite (4er-Grid)
│ │ ├── ValidationPage.tsx # Validierung (TestPanel-Wrapper)
│ │ ├── VoicePage.tsx # Sprache (ReSpeaker, Transkription, Mute)
│ │ ├── TestPanel.tsx # 15 Validierungstests (Sensorik, Antrieb, Nav, System)
│ │ ├── StatusPanel.tsx # Verbindung, Odometrie, IMU, Latenz
│ │ ├── SystemMetrics.tsx # CPU, RAM, Disk, Batterie, Netzwerk
│ │ ├── CameraView.tsx # MJPEG-Stream + Vision-BBox-Overlay
│ │ ├── MapView.tsx # SLAM-Karte (Canvas, Pfad-Trail, Ziel-Marker)
│ │ ├── LidarView.tsx # Polar-LiDAR-Visualisierung (360 Grad)
│ │ ├── Joystick.tsx # nipplejs-basiert (2D-Steuerung)
│ │ ├── ServoControl.tsx # Pan/Tilt-Regler (PCA9685)
│ │ ├── HardwareControl.tsx # Motor-Limit, Servo-Speed, LED-PWM
│ │ ├── CommandInput.tsx # Freitext-Kommando + Verlauf (15 Eintraege)
│ │ ├── ActiveDevicesPanel.tsx # 6 Geraete-Status mit Hz-Raten
│ │ ├── SensorDetailPanel.tsx # IMU, Ultraschall, Cliff
│ │ ├── AudioPanel.tsx # ReSpeaker DoA, Voice Activity, Sounds
│ │ ├── RobotInfoPanel.tsx # Roboter-Metadaten
│ │ └── EmergencyStop.tsx # E-Stop-Button (5x Zero-Velocity)
│ ├── hooks/
│ │ ├── useWebSocket.ts # WebSocket-Verbindung (auto-reconnect)
│ │ ├── useJoystick.ts # Joystick-Logik (rate-limited, Heartbeat)
│ │ └── useImageFit.ts # Bildausstattungs-Berechnung
│ ├── store/
│ │ └── telemetryStore.ts # Zustand Store (60+ Properties)
│ ├── types/
│ │ └── ros.ts # Type-Definitionen fuer WebSocket-Messages
│ ├── App.tsx # Root (Tab-Navigation: Steuerung/Details/Validierung/Sprache)
│ ├── main.tsx # React-Einstiegspunkt
│ └── index.css # Tailwind + HUD-Theme
├── vite.config.ts # HTTPS via mkcert
├── eslint.config.js # ESLint Flat Config
├── tsconfig.json # TypeScript-Konfiguration
└── package.json # Abhaengigkeiten und Scripts
3. Seiten und Layout¶
Steuerung (Standardansicht, Dashboard.tsx)¶
4er-Grid-Layout (Desktop, responsive auf Mobile):
[Sidebar 320px] [Kamera] [SLAM-Karte] [LiDAR]
[alle 6 Zeilen] [Joystick] [Servo + Hardware-Steuerung]
- Sidebar: StatusPanel + SystemMetrics + CommandInput
- Kamera: MJPEG-Stream (
https://amr.local:8082/stream) + Detection-BBox-Overlay (farbig nach Confidence) - SLAM-Karte: Canvas-basiert, Roboter-Position (Dreieck + Glow), Pfad-Trail (max 500 Punkte, dedupliziert < 2 cm), Klick-Navigation (sendet
nav_goal) - LiDAR: Polar-Scan-Visualisierung (360 Grad)
- Joystick: nipplejs (statisch, 140px kompakt), Linear + Angular Velocity
- Servo: Pan/Tilt-Slider (Pan: 45–135 Grad, Tilt: 80–135 Grad), Center-Button, 10 Hz throttled
- Hardware: Motor-Limit (0–100%), Servo-Speed (1–10), LED-PWM (0–255)
Details (Detailansicht, DetailPage.tsx)¶
2-Spalten-Grid (Desktop, 1 Spalte Mobile):
- ActiveDevicesPanel: 6 Geraete-Status (Drive, Sensor, LiDAR, Kamera, Hailo, INA260) mit Hz-Raten
- SensorDetailPanel: IMU-Hz, Ultraschall-Range, Cliff-Detection, Sensor-Node-Status
- AudioPanel: ReSpeaker DoA (Azimut), Voice Activity, Sound-Wiedergabe, Lautstaerke-Slider
- RobotInfoPanel: Position, Yaw, Servo-Position, Hardware-Grenzen
Validierung (ValidationPage.tsx)¶
- TestPanel: 15 Validierungstests gruppiert nach Kategorie (Sensorik, Antrieb, Navigation, System), per Klick ausfuehrbar, Live-Statusanzeige (PASS/FAIL/laeuft)
- Kommunikation:
test_list(einmalig) zum Laden der Tests,test_runzum Starten,command_responsefuer Ergebnisse
Sprache (VoicePage.tsx)¶
- ReSpeaker DoA: Azimut-Richtungsanzeige (4 Quadranten)
- Sprach-Transkription: Live-Text aus
voice_transcript - Voice Mute: Mikrofon stummschalten per
voice_mute - E-Stop: Notstopp-Button (identisch mit EmergencyStop)
4. State Management (Zustand)¶
Zentraler Store in src/store/telemetryStore.ts mit 60+ Properties:
| Gruppe | Properties (Auswahl) | Quelle |
|---|---|---|
| Odometrie | x, y, yawDeg, velLinear, velAngular | telemetry (10 Hz) |
| IMU | headingDeg, gzDegS | telemetry (10 Hz) |
| LiDAR | scanRanges[], scanAngleMin/Max/Increment | scan (2 Hz) |
| System | cpuTempC, cpuLoad, ramUsedMb, diskUsagePct, hostIp | system (1 Hz) |
| SLAM-Karte | mapPngB64, mapWidth/Height, mapResolution, robotMapX/Y/Yaw | map (~0.5 Hz) |
| Vision | visionDetections[], inferenceMs, semanticAnalysis | vision_detections (5 Hz), vision_semantics (~0.5 Hz) |
| Batterie | batteryVoltage, batteryCurrent, batteryPower, batteryPercentage | telemetry (10 Hz) |
| Navigation | navStatus, navGoalX/Y/Yaw, navRemainingM | nav_status (1 Hz) |
| Sensoren | imuHz, ultrasonicHz, cliffHz, ultrasonicRange, cliffDetected | sensor_status (2 Hz) |
| Audio | soundDirection, isVoiceActive, audioVolume, voiceTranscript | audio_status (2 Hz), voice_transcript (event) |
| Tests | availableTests, runningTest, testResults | test_list (einmalig), command_response |
| Geraete | esp32Active, lidarActive, cameraActive, hailoDetected, ina260Active | system (1 Hz) |
Pattern: Inkrementelle Updates pro Message-Typ, Shallow Merging, Selectors pro Komponente.
5. Kommunikation¶
WebSocket (Port 9090)¶
- URL:
wss://amr.local:9090(HTTPS) oderws://localhost:9090(HTTP-Fallback) - Auto-Reconnect: Exponentiell (1s, 2s, 4s, 8s)
- Latenz-Tracking:
Date.now() - msg.ts * 1000
Vom Server empfangene Nachrichten¶
| Operation | Rate | Beschreibung |
|---|---|---|
telemetry | 10 Hz | Odometrie, IMU, Batterie, Servo, Hardware-Status |
scan | 2 Hz | LiDAR-Ranges (komprimiert) |
system | 1 Hz | CPU, RAM, Disk, Geraete-Status, Netzwerk |
map | ~0.5 Hz | SLAM-Karte (PNG Base64), Roboter-Position |
vision_detections | 5 Hz | Hailo-8L Erkennungen (BBox, Label, Confidence) |
vision_semantics | ~0.5 Hz | Gemini-Szenenbeschreibung |
nav_status | 1 Hz | Navigationsstatus + Ziel + Restdistanz |
sensor_status | 2 Hz | Ultraschall, Cliff, IMU-Hz |
audio_status | 2 Hz | ReSpeaker DoA, Voice Activity |
voice_transcript | event | Sprach-Transkription (Text + Zeitstempel) |
command_response | — | Antwort auf Freitext-Kommando |
test_list | einmalig | Verfuegbare Tests (Key + Entry-Point) |
vision_status | event | Vision-Pipeline ein/aus Status |
voice_mute_status | event | Mikrofon-Stummschaltung Status |
estop_status | event | E-Stop aktiv/inaktiv Status |
Vom Client gesendete Nachrichten¶
| Operation | Rate | Beschreibung |
|---|---|---|
cmd_vel | 10 Hz | Fahrbefehl (linear_x, angular_z) |
heartbeat | 5 Hz | Deadman-Switch |
servo_cmd | 10 Hz | Pan, Tilt (throttled) |
hardware_cmd | 10 Hz | Motor-Limit, Servo-Speed, LED-PWM (throttled) |
nav_goal | — | Navigationsziel (x, y, yaw) |
nav_cancel | — | Navigationsziel abbrechen |
audio_play | — | Sound-Wiedergabe (sound_key) |
audio_volume | 5 Hz | Lautstaerke (0–100%, throttled) |
vision_control | — | Vision ein/aus |
command | — | Freitext-Kommando |
test_list | einmalig | Verfuegbare Tests anfordern |
test_run | — | Einzelnen Test starten (test_key) |
voice_mute | — | Mikrofon stummschalten (true/false) |
estop | — | Notstopp aktivieren |
estop_release | — | Notstopp freigeben |
MJPEG-Stream (Port 8082)¶
- URL:
https://amr.local:8082/stream - Direkt in
<img src=...>Tag eingebunden - HTTPS via mkcert-Zertifikate (selbe wie WebSocket)
Sicherheitsmechanismen¶
- Geschwindigkeitsbegrenzung: 0.4 m/s linear, 1.0 rad/s angular (hart im Bridge-Node)
- Deadman-Timer: 300 ms ohne Heartbeat → automatischer Stopp
- Single-Controller: Nur ein WebSocket-Client darf
cmd_velsenden - Client-Disconnect: Sofortiger Stopp bei Verbindungsverlust des Controllers
- E-Stop: Sendet 5x Zero-Velocity bei Betaetigung
6. Datenfluss¶
Server → Client:
WebSocket.onmessage → JSON.parse → App.tsx onMessage → Zustand Store → React Re-render
Client → Server:
UI Event → Hook (useJoystick/useWebSocket) → Rate-Limiting/Throttling → WebSocket.send
Rate-Limiting¶
| Nachricht | Max-Rate | Intervall |
|---|---|---|
cmd_vel | 10 Hz | 100 ms |
heartbeat | 5 Hz | 200 ms |
servo_cmd | 10 Hz | 100 ms |
hardware_cmd | 10 Hz | 100 ms |
audio_volume | 5 Hz | 200 ms |
Joystick-Parameter (useJoystick.ts)¶
MAX_LINEAR = 0.4 m/s (aus nav2_params.yaml)
MAX_ANGULAR = 1.0 rad/s
SEND_INTERVAL_MS = 100 (10 Hz cmd_vel)
HEARTBEAT_INTERVAL_MS = 200 (5 Hz heartbeat)
7. HUD-Theme¶
SciFi-Look mit dunklem Hintergrund, definiert in src/index.css:
| Variable | Wert | Verwendung |
|---|---|---|
--color-hud-bg | #0a0e17 | Hintergrund |
--color-hud-panel | #0d1321 | Panel-Hintergrund |
--color-hud-border | rgba(0, 229, 255, 0.2) | Rahmen (Cyan, durchsichtig) |
--color-hud-cyan | #00e5ff | Hauptfarbe |
--color-hud-amber | #ffab00 | Warnung |
--color-hud-red | #ff1744 | Fehler |
--color-hud-green | #00e676 | OK |
--color-hud-text | #e0f7fa | Text |
--font-mono | JetBrains Mono | Schriftart |
Custom Utilities: .hud-glow (Box-Shadow Glow), .hud-scanline (CRT-Effekt).
8. cmd_vel-Routing und Cliff-Safety¶
Nav2 ──► /nav_cmd_vel ──┐
├──► cliff_safety_node ──► /cmd_vel ──► Drive-Knoten
Dashboard ──► /dashboard_cmd_vel ──┘ │
│
Sensor-Knoten ──► /cliff (Bool, Best-Effort, 20 Hz) ──┘
──► /range/front (Range, 10 Hz) ──┘
- Bei
use_cliff_safety:=True(Standard):dashboard_bridgewird per Launch-Remapping von/cmd_velauf/dashboard_cmd_velumgeleitet - Bei
use_cliff_safety:=False:dashboard_bridgepubliziert direkt auf/cmd_vel - Cliff-Safety blockiert bei Cliff (
/cliff= true) ODER Ultraschall < 100 mm (Freigabe > 140 mm, Hysterese)
9. Navigationsziel via Dashboard¶
Klick auf SLAM-Karte → nav_goal per WebSocket → dashboard_bridge → Nav2 NavigateToPose ActionClient → Status-Rueckmeldung per nav_status (1 Hz).
Laufendes Ziel per nav_cancel abbrechbar.
10. Build und Entwicklung¶
cd dashboard/
npm install # Abhaengigkeiten installieren
npm run dev # Entwicklungsserver (https://amr.local:5173)
npm run dev -- --host 0.0.0.0 # Extern erreichbar
npm run build # Produktion (tsc + vite build)
npm run lint # ESLint
npx tsc --noEmit # TypeScript Type-Check
Entwicklungsmodus (zwei Prozesse noetig)¶
- ROS2-Launch:
./run.sh ros2 launch my_bot full_stack.launch.py use_dashboard:=True use_rviz:=False - Vite-Dev:
cd dashboard && npm run dev -- --host 0.0.0.0
HTTPS-Zertifikate (mkcert)¶
amr.local+5.pemundamr.local+5-key.pemindashboard/- Vite, WebSocket-Server und MJPEG-Server nutzen dieselben Zertifikate
- Ohne Zertifikate: Fallback auf HTTP/WS (unverschluesselt)
- Setup-Dokumentation:
planung/https-setup-amr-dashboard.md
TypeScript-Konfiguration¶
strict: true,noUnusedLocals,noUnusedParameters,noFallthroughCasesInSwitch- Target: ES2022, Module: ESNext, JSX: react-jsx
11. Responsive Design¶
- Desktop (lg, >= 1024px): 4er-Grid, Sidebar + 3 Haupt-Panels + 2 Kontroll-Panels
- Mobile/Tablet: Vertikaler Stack, Sidebar versteckt (Toggle-Button), Touch-freundlich
12. Abgrenzung¶
- ROS2-Topics, QoS, TF-Baum:
docs/ros2_system.md - Vision-Pipeline (Hailo, Gemini):
docs/vision_pipeline.md - HTTPS-Setup:
planung/https-setup-amr-dashboard.md - Backend-Node (
dashboard_bridge):amr/scripts/dashboard_bridge.py