9.5 KiB
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.pipelineundrequirements.pipeline.txtexistieren 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.jsonist 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.tomlanlegen (Name:approbot-pipeline, Versions-Tag, Dependencies)approbot_pipeline/__init__.pymit der öffentlichen API: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
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.jsonbleibt 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.pymit FastAPI- Temporäres Verzeichnis pro Request (keine Race-Conditions bei parallelen Anfragen)
docker-compose.ymlfür den Service-Modus: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/healthfü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.pyals 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)
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:
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)
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 5–8) an:
approbot/pose-pipelineist 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:
"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
- Phase 1 (pyproject.toml +
__init__.py+ Funktionsaufrufe statt Subprocess) — bringt sofort sauberere Fehlerbehandlung und elimiert ~2s Process-Startup pro Schritt. - Phase 2 (Schema-Validierer) — frühzeitige, verständliche Fehlermeldungen statt kryptischen Abbrüchen in Schritt 2.
- Phase 3 (REST API) — sobald die Robotersteuerung integriert werden soll.
- Phase 4 (CI) — parallel zu Phase 2/3.
- 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 3–5 passen in die Docker-Roadmap Phase 4–8.
Was sich NICHT ändert
robot.jsonist und bleibt die einzige Konfigurations-Datei.- Die Algorithmen (pose_estimation.py, robot_fk.py) bleiben unberührt.
- Bestehende
run/run_pipeline.batundpipeline/run_pipeline.pybleiben parallel lauffähig — das Package ist eine sauberere Schicht darüber, kein Ersatz. - Benchmark-Tools (
benchmark/*.py) arbeiten weiterhin direkt gegen das Dateisystem.