separate pipeline

This commit is contained in:
chk
2026-06-03 19:49:07 +02:00
parent 6d4a61f4d5
commit 222f0e55f7
30 changed files with 5579 additions and 15 deletions

View File

@@ -0,0 +1,266 @@
# Separate Pipeline Solution — Roadmap
**Ziel:** Die Pose-Schätzungs-Pipeline als eigenständige, wartbare,
konfigurierbare Einheit, komplett losgelöst von Simulation und Rendering.
**Input:** Bilder (PNG/JPG) + Kamera-Intrinsiken (npz) + `robot.json`
**Output:** `robot_state.json` — Gelenkwinkel im R⁷-Raum (x,y,z,a,b,c,e)
---
## Status-quo-Analyse
Die Pipeline ist bereits **de facto** sauber trennbar:
- Keine einzige Pipeline-Datei referenziert Simulation, Blender oder Rendering.
- Externe Abhängigkeiten: nur `numpy`, `scipy`, `opencv-contrib-python-headless`.
- Internes Modul: `robot_fk.py`.
- `Dockerfile.pipeline` und `requirements.pipeline.txt` existieren bereits.
**Was heute noch fehlt**, um sie vollständig eigenständig zu machen:
- Kein eigener Package-Einstiegspunkt (kein `setup.py` / `pyproject.toml`)
- Kein stabiles, versioniertes API-Interface
- `robot.json` ist zu stark mit Render-Parametern vermischt
- Kein Health-Check / kein Service-Modus (nur CLI)
---
## Architektur-Entscheidung
Drei Interface-Ebenen, alle parallel nutzbar:
```
┌──────────────────────────────────────────────────────┐
│ REST API (FastAPI, /v1/estimate) │ ← Integration in Robotersteuerung
│ CLI (python -m approbot_pipeline <scene>) │ ← Terminal, Batch, CI
│ Python (import approbot_pipeline; estimate(...)) │ ← direkte Einbindung in Python-Code
└──────────────────────────────────────────────────────┘
alle lesen dieselbe robot.json → robot_fk → pose_estimation
```
Der **Robot.json-Vertrag** ist das einzige Konfigurationsinterface:
- Kinematik (`links`) → *Modell, ändert sich selten*
- `pose_estimation`*Algorithmus-Tuning, ändert sich gelegentlich*
- Keine Render-/Sim-Parameter mehr in der Pipeline-Config (die bleiben in `renderingInfo`)
---
## Paketstruktur (Ziel)
```
approbot-pipeline/ ← eigenständiges Verzeichnis / eigenes Repo
├── approbot_pipeline/ ← Python-Package
│ ├── __init__.py ← öffentliche API: estimate(), estimate_from_dir()
│ ├── pipeline.py ← Orchestrator (run_pipeline.py → Modul)
│ ├── detect.py ← 1_detect_aruco_observations
│ ├── camera_pose.py ← 2_estimate_camera_from_observations
│ ├── triangulate.py ← 3_multiview_bundle_adjustment_v4
│ ├── corner_poses.py ← 3b_corner_marker_poses
│ ├── pose_estimation.py ← pose_estimation (unverändert)
│ ├── robot_fk.py ← robot_fk (unverändert)
│ └── api/
│ ├── __init__.py
│ └── server.py ← FastAPI-Service
├── pyproject.toml ← package metadata, dependencies gepinnt
├── Dockerfile ← = Dockerfile.pipeline (schlankes Image ~200 MB)
├── docker-compose.yml ← Service-Modus, Port-Mapping
├── README.md
└── tests/
├── test_pipeline.py ← End-to-end gegen eine Beispiel-Scene
└── fixtures/ ← kleine Testbilder + robot.json-Minimal
```
---
## Phase 1 — Saubere Python-Package-Struktur
### Aufgaben
* [ ] `pyproject.toml` anlegen (Name: `approbot-pipeline`, Versions-Tag, Dependencies)
* [ ] `approbot_pipeline/__init__.py` mit der öffentlichen API:
```python
from approbot_pipeline import estimate, estimate_from_dir
result = estimate_from_dir("path/to/images", robot_json="robot.json")
# result.joints → {"x": 50.2, "y": -2.1, ..., "e": 3.0}
# result.confidence → {"x": "high", "b": "low", ...}
```
* [ ] `pipeline/run_pipeline.py` → `approbot_pipeline/pipeline.py` (Module statt Subprocess-Kette)
* [ ] Interne Schritte als **direkte Python-Funktionsaufrufe** statt `subprocess.run` —
das eliminiert Process-Overhead und macht Fehler besser fangbar.
### Ergebnis
```bash
pip install -e . # lokal entwickeln
python -m approbot_pipeline data/sim/Scene8 # CLI
```
---
## Phase 2 — robot.json bereinigen
Das Pipeline-Package braucht aus `robot.json` nur zwei Abschnitte:
| Abschnitt | Braucht Pipeline | Braucht Renderer |
|---|:---:|:---:|
| `links` (Kinematik, Marker) | ✅ | ✅ |
| `pose_estimation` | ✅ | ✗ |
| `vision_config` | ✅ | ✅ |
| `units` | ✅ | ✅ |
| `renderingInfo` | ✗ | ✅ |
| `robot_test_poses` | ✗ | ✅ |
| `test_camera_positions` | ✗ | ✅ |
### Aufgaben
* [ ] Minimales **Pipeline-Schema** dokumentieren (welche Felder sind Pflicht,
welche optional-mit-Default): `doc/robot_json_pipeline_schema.md`
* [ ] Validierer: `approbot_pipeline.config.validate(robot_json_path)` →
prüft Pflichtfelder, gibt sinnvolle Fehlermeldungen
* [ ] `robot.json` bleibt eine Datei (kein Split), aber das Package liest nur
seinen Teil — Renderer-Felder werden einfach ignoriert.
---
## Phase 3 — REST API (FastAPI)
Für die Integration in die Robotersteuerung: die Steuerung postet Bilder,
bekommt Gelenkwinkel zurück.
### Interface
```
POST /v1/estimate
Content-Type: multipart/form-data
- robot_json: file (oder als Pfad-Config beim Server-Start)
- images[]: file (N Kamerabilder, benennt nach cam-ID: render_a.png etc.)
- intrinsics[]: file (N npz-Dateien, gleiche Reihenfolge wie images)
Response 200:
{
"joints": {"x": 50.2, "y": -2.1, "z": 94.8, "a": 20.1, "b": 59.9, "c": 9.0, "e": 3.0},
"confidence": {"x": "high", "y": "high", "z": "high", "a": "high", "b": "low", "c": "low", "e": "low"},
"residual_rms": 1.45,
"n_markers": 56,
"processing_ms": 1240
}
GET /v1/health → {"status": "ok", "version": "1.2.0"}
GET /v1/config → aktuelle robot.json-Konfiguration (pose_estimation-Block)
```
### Aufgaben
* [ ] `approbot_pipeline/api/server.py` mit FastAPI
* [ ] Temporäres Verzeichnis pro Request (keine Race-Conditions bei parallelen Anfragen)
* [ ] `docker-compose.yml` für den Service-Modus:
```yaml
services:
pipeline:
image: approbot/pose-pipeline:latest
ports: ["8080:8080"]
volumes:
- ./robot.json:/config/robot.json:ro
command: ["python", "-m", "approbot_pipeline.api", "--robot", "/config/robot.json"]
```
* [ ] `GET /v1/health` für Container-Health-Check
---
## Phase 4 — Tests & CI
### Aufgaben
* [ ] `tests/test_pipeline.py`: End-to-end-Test auf minimaler Fixture-Scene
(kleine PNGs + robot.json-Minimal) ohne Blender → CI-tauglich in < 30s
* [ ] Regression gegen GT: `eval_pose.py` als pytest-Assertion
(`assert mean_error_deg < 0.5`) — fängt Code-Regressions wie den fy-Bug automatisch
* [ ] GitHub Actions (oder äquivalentes CI): `docker build` + Test auf jedem Commit
* [ ] Versions-Tag: `approbot-pipeline==1.0.0` → Pipeline-Verhalten ist reproduzierbar
---
## Phase 5 — Deployment
### Variante A: Docker-Service auf dem Roboter-PC (empfohlen)
```bash
docker run -d --rm \
-p 8080:8080 \
-v ./robot.json:/config/robot.json:ro \
approbot/pose-pipeline:latest \
python -m approbot_pipeline.api --robot /config/robot.json
```
Robotersteuerung (beliebige Sprache) postet Bilder per HTTP:
```python
import requests
resp = requests.post("http://localhost:8080/v1/estimate",
files=[("images", open(img, "rb")) for img in cam_images])
joints = resp.json()["joints"]
```
### Variante B: Python direkt eingebunden (ohne Container)
```python
from approbot_pipeline import estimate_from_dir
result = estimate_from_dir(image_dir, robot_json="robot.json")
move_robot(result.joints)
```
### Variante C: Portainer / verteilte Nodes
Schnittstellt nahtlos an die Docker-Roadmap (Phase 58) an:
- `approbot/pose-pipeline` ist das Image für Nomad-Batch-Jobs
- Job-Payload: `{"scene_dir": "/mnt/data/sceneX"}` oder die Bilder direkt
---
## Konfigurationsvertrag (stabiles Interface)
Der einzige Eingabepunkt für Algorithmus-Tuning:
```json
"pose_estimation": {
"method": "hybrid",
"marker_observation": "corner_pose",
"use_normals": true,
"normal_weight": 100.0,
"robust_loss": "huber",
"huber_delta_mm": 8.0,
"max_iterations": 200,
"min_cameras_per_marker": 2,
"per_link_method": {}
}
```
Dieses Schema ist **versionsstabil**: neue Parameter haben immer Defaults,
alte werden nie entfernt (nur deprecated-markiert). So laufen Roboter-Deployments
mit einer älteren `robot.json` unverändert weiter.
---
## Vorgehen / Empfohlene Reihenfolge
1. **Phase 1** (pyproject.toml + `__init__.py` + Funktionsaufrufe statt Subprocess)
— bringt sofort sauberere Fehlerbehandlung und elimiert ~2s Process-Startup pro Schritt.
2. **Phase 2** (Schema-Validierer) — frühzeitige, verständliche Fehlermeldungen
statt kryptischen Abbrüchen in Schritt 2.
3. **Phase 3** (REST API) — sobald die Robotersteuerung integriert werden soll.
4. **Phase 4** (CI) — parallel zu Phase 2/3.
5. **Phase 5** (Deployment) — läuft auf Phase 3 auf.
Phases 1+2 sind **unabhängig von der Docker-Roadmap** und können sofort beginnen.
Phase 35 passen in die Docker-Roadmap Phase 48.
---
## Was sich NICHT ändert
- `robot.json` ist und bleibt die einzige Konfigurations-Datei.
- Die Algorithmen (pose_estimation.py, robot_fk.py) bleiben unberührt.
- Bestehende `run/run_pipeline.bat` und `pipeline/run_pipeline.py` bleiben parallel
lauffähig — das Package ist eine sauberere Schicht *darüber*, kein Ersatz.
- Benchmark-Tools (`benchmark/*.py`) arbeiten weiterhin direkt gegen das Dateisystem.