WebCam als schlanke alternative zum appVideoControl
This commit is contained in:
152
doc/01_WebcamRoadmap.md
Normal file
152
doc/01_WebcamRoadmap.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# AppRobotWebcam – Roadmap
|
||||
|
||||
## Ziel
|
||||
|
||||
Sauberer, fokussierter Webcam-Streaming-Service als Docker-Container.
|
||||
Kein Robot-Control, kein ArUco – nur zwei Verantwortlichkeiten:
|
||||
|
||||
1. **Live-Video** mit minimaler Latenz (MJPEG über WebSocket)
|
||||
2. **Standbilder** auf Abruf via HTTP REST (`/api/snapshot/cam{n}`)
|
||||
|
||||
Das Homing-Projekt holt seine Standbilder über den Snapshot-Endpunkt – keine weitere Kopplung nötig.
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
USB Kameras
|
||||
│
|
||||
▼
|
||||
FFmpeg (v4l2 → MJPEG)
|
||||
│
|
||||
▼
|
||||
Node.js Server (Express + ws)
|
||||
├── WebSocket /ws/cam0, /ws/cam1 → Browser (Live-Stream)
|
||||
└── HTTP GET /api/snapshot/cam0 → Homing-Projekt (JPEG)
|
||||
```
|
||||
|
||||
**Stack-Entscheide (bereits umgesetzt):**
|
||||
|
||||
| Komponente | Wahl | Begründung |
|
||||
|------------|------|------------|
|
||||
| Webserver | Node.js + Express | Wartbar, grosses Ecosystem, user-präferiert |
|
||||
| WebSocket | `ws`-Library | Schlank, bewährt, kein Overhead |
|
||||
| Video-Capture | FFmpeg | Stabil, flexibel, MJPEG-passthrough möglich |
|
||||
| Stream-Protokoll | MJPEG über WebSocket | Geringste Latenz, einfach im Browser |
|
||||
| Snapshot-API | HTTP GET → raw JPEG | Einfachste Schnittstelle für Consumer |
|
||||
| Container | `dockerfile_inline` in docker-compose | Kein separates Dockerfile, Portainer-tauglich |
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
### Phase 1 – Grundgerüst ✅ (erledigt)
|
||||
|
||||
- [x] Projektstruktur anlegen
|
||||
- [x] `package.json` mit Abhängigkeiten (express, ws)
|
||||
- [x] `docker-compose.yaml` mit `dockerfile_inline` (Node.js + FFmpeg, kein separates Dockerfile)
|
||||
- [x] `server.js` – HTTP + WebSocket-Server, Graceful Shutdown
|
||||
- [x] `src/deviceDetect.js` – Kamera-Erkennung (env → by-id → /dev/video*)
|
||||
- [x] `src/videoStream.js` – FFmpegStreamer (MJPEG splitten, WebSocket broadcast, Auto-Restart mit Backoff)
|
||||
- [x] `src/snapshotService.js` – REST-Endpunkt: aktuellstes Frame aus laufendem Stream
|
||||
- [x] `public/index.html` + `public/viewer.js` – Basis-Viewer
|
||||
|
||||
### Phase 2 – Deployment & Latenz-Baseline
|
||||
|
||||
- [ ] **Kamera-Zugriff im Container** verifizieren:
|
||||
- `/dev/video0` und `/dev/video2` im Container sichtbar
|
||||
- `group_add: video` greift (Zugriffsrechte)
|
||||
- Fallback auf YUYV422 wenn MJPEG nicht unterstützt
|
||||
- [ ] **Latenz messen** (Baseline):
|
||||
- Uhr auf Kamera richten, Screenshot → Differenz ablesen
|
||||
- Zielwert: <100 ms Ende-zu-Ende (Kamera → Browser-Pixel)
|
||||
- [ ] **Multi-Format-Fallback** implementieren: MJPEG → YUYV422 → RGB24
|
||||
- [ ] **Health-Endpunkt** `/health` erweitern: Kamera-Status, verbundene Clients, FPS
|
||||
|
||||
### Phase 3 – Latenz optimieren
|
||||
|
||||
- [ ] **FFmpeg-Flags tunen** für minimale Latenz:
|
||||
```
|
||||
-fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0
|
||||
```
|
||||
- [ ] **Native MJPEG pass-through** testen:
|
||||
Wenn Kamera MJPEG nativ bei Zielauflösung liefert → `-vcodec copy`
|
||||
(kein Re-Encoding, minimale CPU-Last, minimale Latenz)
|
||||
- [ ] **Canvas-Rendering** im Browser: `createImageBitmap()` statt Blob-URL-Overhead
|
||||
- [ ] **WebRTC evaluieren**: <50 ms möglich, aber STUN/TURN-Komplexität in Docker –
|
||||
sinnvoll erst wenn MJPEG-Latenz >150 ms bleibt
|
||||
|
||||
### Phase 4 – Hochauflösende Snapshots
|
||||
|
||||
**Aktuell**: Snapshot = letztes Frame aus dem Stream (Auflösung = Stream-Auflösung).
|
||||
**Ziel**: Snapshot in originaler Kamera-Auflösung (z.B. 1280×960).
|
||||
|
||||
Drei Optionen (noch offen):
|
||||
|
||||
| Option | Vorgehen | Pro | Contra |
|
||||
|--------|----------|-----|--------|
|
||||
| A | Stream-Frame direkt nehmen | Sofort, kein Aufwand | Auflösung = Stream-Auflösung |
|
||||
| B | Zweite FFmpeg-Pipeline 0.5 FPS High-Res | Immer verfügbar | CPU-Last, pipe:3 Komplexität |
|
||||
| C | Einmaliger `ffmpeg -frames:v 1` on-demand | Hohe Qualität | ~500 ms Delay, Stream-Unterbrechung |
|
||||
|
||||
- [ ] Option wählen und implementieren
|
||||
- [ ] Mit Homing-Projekt testen (Consumer von `/api/snapshot`)
|
||||
- [ ] Snapshot-Metadaten in Response-Headern: Zeitstempel, Auflösung, Kamera-ID
|
||||
- [ ] Optionaler Webhook: POST nach Snapshot an konfigurierbaren Endpunkt
|
||||
|
||||
### Phase 5 – Robustheit & Produktion
|
||||
|
||||
- [ ] Kamera hot-plug: Stream-Neustart wenn `/dev/videoX` verschwindet/wiederkommt
|
||||
- [ ] Resource Limits: `--memory 512m --cpus 1.0` in docker-compose
|
||||
- [ ] HTTPS: Reverse Proxy (nginx/traefik) vorschalten – kein TLS im App-Code
|
||||
- [ ] JSON-Logging mit Level (info/warn/error)
|
||||
- [ ] Kamera-Parameter per env var: `CAM0_WIDTH`, `CAM0_HEIGHT`, `CAM0_FPS`, `CAM0_QUALITY`
|
||||
|
||||
---
|
||||
|
||||
## Abgrenzung zu appRobotVideoControls
|
||||
|
||||
| Feature | appRobotVideoControls | appRobotWebcam |
|
||||
|---------|-----------------------|----------------|
|
||||
| Video-Streaming | ✅ | ✅ (verbessert) |
|
||||
| Snapshots | ✅ (komplex, dual-pipe) | ✅ (HTTP REST, einfach) |
|
||||
| Robot-Control (G-Code) | ✅ | ❌ anderes Projekt |
|
||||
| ArUco / Homing | ✅ (Python+OpenCV) | ❌ anderes Projekt |
|
||||
| Gamepad / Keyboard | ✅ | ❌ |
|
||||
| HTTPS (self-signed) | ✅ | ❌ (Reverse Proxy empfohlen) |
|
||||
| Separates Dockerfile | ✅ (gross, OpenCV-Build) | ❌ (inline in compose) |
|
||||
|
||||
---
|
||||
|
||||
## Ports & Netzwerk
|
||||
|
||||
| Service | Container-Port | Host-Port |
|
||||
|---------|---------------|-----------|
|
||||
| HTTP + WS | 8080 | 8444 |
|
||||
|
||||
```bash
|
||||
# Netzwerk einmalig erstellen (falls noch nicht vorhanden)
|
||||
docker network create appRobotNet
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datei-Struktur
|
||||
|
||||
```
|
||||
appRobotWebcam/
|
||||
├── src/
|
||||
│ ├── deviceDetect.js Kamera-Erkennung (env → by-id → /dev/video*)
|
||||
│ ├── videoStream.js FFmpeg-MJPEG-Streamer + WebSocket-Broadcast
|
||||
│ └── snapshotService.js REST-Router für /api/snapshot
|
||||
├── public/
|
||||
│ ├── index.html Basis-Viewer
|
||||
│ └── viewer.js WebSocket-Client, MJPEG-Rendering
|
||||
├── doc/
|
||||
│ ├── 01_WebcamRoadmap.md (diese Datei)
|
||||
│ └── 05_OptionalToDo_roadmap.md Control-Optionen
|
||||
├── docker-compose.yaml Einzige Docker-Datei (dockerfile_inline)
|
||||
├── package.json
|
||||
└── server.js Einstiegspunkt
|
||||
```
|
||||
87
doc/05_OptionalToDo_roadmap.md
Normal file
87
doc/05_OptionalToDo_roadmap.md
Normal file
@@ -0,0 +1,87 @@
|
||||
## Roadmap - Optionale Punkte ##
|
||||
|
||||
---
|
||||
|
||||
## Control integrieren
|
||||
|
||||
Das aktuelle `appRobotVideoControls` koppelt zwei unabhängige Verantwortlichkeiten:
|
||||
|
||||
1. **Video-Streaming** (USB-Kameras → Browser)
|
||||
2. **Robot-Control** (Browser → G-Code → Robot-Server)
|
||||
|
||||
### Warum wir trennen
|
||||
|
||||
| Problem | Auswirkung |
|
||||
|---------|------------|
|
||||
| Control-Code-Änderung → Video-Server-Restart | Stream unterbricht, Operator verliert Bild |
|
||||
| Grosser Docker-Container | OpenCV + Python + FFmpeg + Control-Libs |
|
||||
| Schwer testbar | Video-Stream nicht einfach simulierbar |
|
||||
| Latenz-Konflikte | Video-Tuning ≠ Control-Anforderungen |
|
||||
| Robot-Server offline | Video-Server wirft Fehler |
|
||||
|
||||
### Vorteil der Kopplung
|
||||
|
||||
Ein Browser-Tab für alles: Video + Steuerung zusammen – sehr bequem für den Operator.
|
||||
|
||||
---
|
||||
|
||||
### Optionen für Control
|
||||
|
||||
**Option A: Getrennt bleiben (empfohlen für Phase 1–2)**
|
||||
|
||||
```
|
||||
appRobotWebcam → Port 8444 (nur Video + Snapshots)
|
||||
appRobotControl → Port 8445 (nur G-Code, Gamepad, Keyboard → Robot)
|
||||
```
|
||||
|
||||
- Browser öffnet beide als Tabs oder iframe-Dashboard
|
||||
- Nachteil: Zwei Services deployen und warten
|
||||
|
||||
**Option B: Control als optionales Modul in appRobotWebcam**
|
||||
|
||||
- Env var `ENABLE_CONTROL=true/false` schaltet Control-Code ein/aus
|
||||
- Control-Code ist in der Codebase, aber inaktiv wenn nicht konfiguriert
|
||||
- Nachteil: Code-Komplexität steigt, trotzdem ein Container
|
||||
|
||||
**Option C: Vollintegration wie bisher**
|
||||
|
||||
- Alles in einem Container – wie `appRobotVideoControls`
|
||||
- Einfachste UX, einfachstes Deployment
|
||||
- Nachteil: Monolith, schwer zu warten und zu testen
|
||||
|
||||
### Empfehlung
|
||||
|
||||
Für Phase 1–2: **Option A** – sauber trennen.
|
||||
Wenn UX zum Problem wird (Operator-Feedback): **Option B** evaluieren.
|
||||
Option C nur wenn Deployment-Einfachheit absolut dominiert.
|
||||
|
||||
---
|
||||
|
||||
## Homing-Projekt Anbindung
|
||||
|
||||
Das Homing-Projekt braucht Standbilder der Kameras für ArUco-Erkennung.
|
||||
|
||||
**Schnittstelle**: `GET /api/snapshot/cam{n}` → JPEG-Bild
|
||||
|
||||
`appRobotWebcam` liefert genau das. Keine weitere Kopplung nötig.
|
||||
Das Homing-Projekt ist Consumer, `appRobotWebcam` ist Producer.
|
||||
|
||||
```
|
||||
Homing-Projekt
|
||||
└── HTTP GET http://approbotwebcam:8080/api/snapshot/cam0
|
||||
└── HTTP GET http://approbotwebcam:8080/api/snapshot/cam1
|
||||
↓
|
||||
ArUco-Erkennung → Kamera-Pose → Weltkoordinaten
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard / UI Konsolidierung
|
||||
|
||||
Wenn mehrere Services (Video, Control, Homing-Status) parallel laufen:
|
||||
|
||||
- **Option**: Leichtes Dashboard als eigene statische Seite (nginx) mit iframes
|
||||
- **Option**: Portainer-UI zeigt alle Container-Status
|
||||
- **Option**: Control-Panel in `appRobotWebcam` einbetten (Option B oben)
|
||||
|
||||
Dies ist bewusst aufgeschoben – erst wenn klar ist welche Services produktiv laufen.
|
||||
Reference in New Issue
Block a user