approbot-pipeline
Roboter-Pose-Schätzung aus Mehrkamera-ArUco-Bildern.
Input: Kamerabilder (render_*.png) + Kamera-Intrinsiken (render_*.npz) + robot.json
Output: Gelenkwinkel im R⁷ — {x, y, z, a, b, c, e} in mm bzw. Grad
Drei Interfaces, eine Logik
┌──────────────────────────────────────────────────────┐
│ REST API (FastAPI, POST /v1/estimate) │ ← Robotersteuerung, beliebige Sprache
│ CLI (python -m approbot_pipeline <image_dir>) │ ← Terminal, Batch, CI
│ Python (from approbot_pipeline import ...) │ ← direkte Einbindung
└──────────────────────────────────────────────────────┘
Schnellstart
Als Python-Bibliothek
from approbot_pipeline import estimate_from_dir
result = estimate_from_dir("pfad/zu/bildern", robot_json="robot.json")
print(result.joints) # {"x": 50.2, "y": -2.1, "z": 94.8, ...}
print(result.confidence) # {"x": "high", "b": "low", ...}
Als CLI
pip install -e .
python -m approbot_pipeline data/Scene8 --robot robot.json
python -m approbot_pipeline data/Scene8 --robot robot.json --cameras a,b,d
Als REST-Service (Docker / Portainer)
# robot.json in config/ ablegen, dann:
docker compose up
# oder manuell:
docker compose build
docker compose up -d
Anfrage senden:
import requests
resp = requests.post(
"http://localhost:8080/v1/estimate",
files=[
("images", ("render_a.png", open("render_a.png", "rb"))),
("images", ("render_b.png", open("render_b.png", "rb"))),
("intrinsics", ("render_a.npz", open("render_a.npz", "rb"))),
("intrinsics", ("render_b.npz", open("render_b.npz", "rb"))),
],
)
joints = resp.json()["joints"]
Verzeichnisstruktur
approbot-pipeline/
├── approbot_pipeline/ Python-Package
│ ├── __init__.py öffentliche API: estimate_from_dir, PipelineResult
│ ├── pipeline.py Orchestrator (ruft Scripts auf)
│ ├── __main__.py CLI-Einstiegspunkt
│ ├── scripts/ Pipeline-Scripts (kopiert aus dem Haupt-Repo)
│ │ ├── 1_detect_aruco_observations.py
│ │ ├── 2_estimate_camera_from_observations.py
│ │ ├── 3_multiview_bundle_adjustment_v4.py
│ │ ├── 3b_corner_marker_poses.py
│ │ ├── pose_estimation.py
│ │ └── robot_fk.py
│ └── api/
│ ├── __init__.py start_server()
│ ├── __main__.py python -m approbot_pipeline.api
│ └── server.py FastAPI-App
├── config/
│ └── robot.json ← hier die eigene robot.json ablegen
├── doc/
│ ├── README.md diese Datei
│ └── robot_json_pipeline_schema.md
├── tests/
│ ├── test_pipeline.py
│ └── fixtures/
│ └── robot_minimal.json
├── pyproject.toml
└── docker-compose.yaml
Portainer-Stack einrichten
Voraussetzung: Docker Engine 23.0+ / Docker Compose Plugin 2.17+
(prüfen: docker compose version)
Schritt 1 — robot.json auf dem Host ablegen
Auf dem Server, auf dem Portainer läuft, robot.json in einem festen Pfad speichern,
z.B. /opt/approbot/config/robot.json.
Den Volume-Pfad im docker-compose.yaml anpassen:
volumes:
- /opt/approbot/config/robot.json:/config/robot.json:ro
Schritt 2 — Option A: Stack aus Git-Repository
Empfohlen — Portainer zieht automatisch Updates wenn das Repo aktualisiert wird.
- Portainer öffnen → Stacks → + Add stack
- Name vergeben, z.B.
approbot-pipeline - Build method:
Repository - Repository URL: Git-URL des
approbot-pipeline-Verzeichnisses eintragen - Compose path:
docker-compose.yaml - ✅ Re-pull image and redeploy aktivieren (für automatische Updates)
- Deploy the stack klicken
Portainer führt docker compose up --build aus — das Image wird aus dem
dockerfile_inline im Compose-File gebaut.
Schritt 2 — Option B: Stack aus Compose-Datei einfügen
Wenn kein Git-Zugang vorhanden ist:
- Portainer öffnen → Stacks → + Add stack
- Name vergeben
- Build method:
Web editor - Inhalt von
docker-compose.yamlvollständig in den Editor einfügen - Deploy the stack klicken
Portainer baut das Image beim ersten Deploy aus dem
dockerfile_inline-Block. Bei erneutem Deploy wird das Image neu gebaut wenn der Compose-Inhalt geändert wurde.
Schritt 3 — Service prüfen
curl http://<server-ip>:8080/v1/health
# → {"status": "ok", "version": "1.0.0"}
Oder in Portainer unter Containers → approbot-pipeline → Logs nachschauen.
Roboter wechseln (Portainer)
Neue robot.json auf dem Host ablegen (gleicher Pfad), dann in Portainer:
Stacks → approbot-pipeline → Recreate (Container neu starten, kein Rebuild nötig —
die Datei wird per Volume gemountet, nicht ins Image kopiert).
API-Referenz
| Endpoint | Methode | Beschreibung |
|---|---|---|
/v1/estimate |
POST | Bilder → Gelenkwinkel |
/v1/health |
GET | {"status": "ok", "version": "..."} |
/v1/config |
GET | Aktiver pose_estimation-Block aus robot.json |
POST /v1/estimate — Response
{
"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
}
Confidence-Stufen: high | medium | low | none
Konfiguration
Die einzige Konfigurationsdatei ist robot.json — sie beschreibt den Roboter vollständig
für alle Werkzeuge der Umgebung (Pipeline, Renderer, Benchmark).
Die Pipeline liest daraus links, pose_estimation, vision_config, movements und units;
alle anderen Abschnitte werden ignoriert.
Vollständige Dokumentation: robot_json.md API-Integrationsbeispiele (Python, Node.js): api_integration.md
Abhängigkeiten
| Paket | Version | Zweck |
|---|---|---|
| numpy | 1.26.4 | Numerik |
| scipy | 1.13.1 | Bundle Adjustment |
| opencv-contrib-python-headless | 4.10.0.84 | ArUco-Detektion |
| fastapi | 0.115.0 | REST-API |
| uvicorn | 0.30.6 | ASGI-Server |
| python-multipart | 0.0.9 | File-Upload |