7.2 KiB
Homing – appRobotHoming
Stand: 2026-06-15
Homing läuft bei jedem Einschalten — schnell, vollautomatisch, ohne mechanische Endschalter.
Was ist Homing?
Der Roboter weiss beim Einschalten nicht, wo er steht. Die Kameras schauen auf die ArUco-Marker am Roboter und berechnen daraus die vollständige Pose aller Gelenke.
Homing (dieser Prozess): bei jedem Einschalten, automatisch.
Kalibrierung (doc/Kalibrierung.md): nur nach mechanischen Änderungen.
Dieses Dokument ist der allgemeine Ablauf. Technische Detail-Doku je Teilstrecke:
| Doku | Inhalt |
|---|---|
Homing_0_Camera.md |
Foto → Kamera-Pose → trianguliertes aruco_marker_poses.json (Schritte 1–3b) |
Homing_1_StepByStep.md |
aruco_marker_poses.json → Gelenkwinkel je Link (4b-Kette, Schätz-Methoden/Fallbacks) |
Homing_2_improvement.md (geplant) |
Bekannte Probleme, Genauigkeits-Befunde, Verbesserungsideen |
Homing_5_Pose.md |
Verfeinerungsschritt NACH der 4b-Kette (Bundle-Adjustment, 5_pose_estimation.py) — noch nicht verdrahtet |
Kinematik-Kette
Board (ROOT, fest) ← Referenz aller Kameras
│
├── Base linear x axis=[1,0,0] ← Slider-Position
├── Arm1 revolute y axis=[-1,0,0] ← Schultergelenk
├── Ellbow revolute z axis=[-1,0,0] ← Ellbogen
├── Arm2 revolute a axis=[0,-1,0] ← Unterarm-Drehung
├── Hand revolute b axis=[1,0,0] ← Handgelenk
├── Palm revolute c axis=[0,-1,0] ← Handfläche
└── FingerA/B linear e axis=±[1,0,0] ← Greifer (symmetrisch)
Ergebnis-State: { x_mm, y_deg, z_deg, a_deg, b_deg, c_deg, e_mm }
Voraussetzungen
Homing setzt eine abgeschlossene Kalibrierung voraus:
| Was | Status |
|---|---|
| Kamera-Intrinsik (NPZ) | ✅ |
| Board-Marker-Positionen | ✅ |
| X-Achsen-Richtung | ✅ |
| Arm1 Joint-Origin Y/Z | ✅ Button vorhanden und ausführbar |
| Arm-Marker in robot.json | 🔶 Position manuell eintragen; Spin-Verifikation via Kalibrierung → Tab „Marker" (→ doc/Kalibrierung_Marker.md) |
Ablauf
Foto alle Kameras
│
▼
1_detect_aruco_observations.py (pro Kamera, mit NPZ)
│
▼
2_estimate_camera_from_observations.py (pro Kamera)
│
▼
3b_corner_marker_poses.py (einmal, benötigt ≥2 Kamera-Posen)
│ → aruco_marker_poses.json
▼
X-Position aus Marker-Positionen schätzen
│ → x_mm (pro Arm-Marker: beobachtetes_x − Modell_x(slider=0), gemittelt)
▼
4b_revolute_angle.py --link Arm1 --x-mm {x_mm}
│ → state_Arm1.json (accumulated_state)
▼
4b_revolute_angle.py --link Ellbow --from-state state_Arm1.json
│ → state_Ellbow.json
▼
4b_revolute_angle.py --link Arm2 --from-state state_Ellbow.json
│ → state_Arm2.json
▼
4b_revolute_angle.py --link Hand --from-state state_Arm2.json
│ → state_Hand.json ← accumulated_state enthält x,y,z,a,b,c,e
▼
POST ROBOT_URL/api/state
Schritte 1–3b sind dieselbe Board-Pipeline wie in der Kalibrierung.
Sie sind in runBoardPipeline() (server/server.js) als gemeinsame Funktion ausgelagert.
4b-Schleife: sequenziell von root nach tip; jedes Script bekommt den Zustand des
vorherigen Schritts über --from-state. Der erste Aufruf erhält die geschätzte
X-Slider-Position über --x-mm.
Implementierung
| Komponente | Datei | Beschreibung |
|---|---|---|
| Board-Pipeline | server/server.js → runBoardPipeline(runDir, send) |
Foto + Scripts 1, 2, 3b; von Board-Run und Homing genutzt |
| X-Schätzung | server/homingOrchestrator.js → estimateXFromMarkers() |
Pro Arm-Marker beobachtetes_x − Modell_x(slider=0), gemittelt — rechnet den kinematischen Gelenk-Offset (z.B. Arm1.origin.x=110) heraus. Nur x-zuverlässige Ketten (x-Rotation: Arm1/Ellbow). Fallback: roher Mittelwert |
| Homing-Orchestrator | server/homingOrchestrator.js → runHoming() |
Kompletter Ablauf als SSE-Stream |
| Backend-Route | POST /api/homing/run |
SSE-Stream, startet runHoming() |
| State senden | POST /api/homing/send-state |
Weiterleitung an ROBOT_URL/api/state |
| Run-Daten | GET /api/homing/run-data?run=ts |
Debug-Bilder (base64) + finalState |
| Frontend | public/index.html + public/client.js |
Homing-Buttons, Fortschrittsbalken, Tree View; schreibt Teil-Pose als G92-GCode ins Eingabefeld |
| Board-Viewer (Homing) | public/boardViewer.html?mode=homing |
Skelett + Arm-Marker per FK (Three.js): Marker-Quadrat spin-korrekt rotiert + Orientierungszeiger zu Ecke 0 (Modell-Seite); gemessene Marker als Kugeln + Fehlerlinien; progressiver Update je erkanntem Gelenk |
Lauf-Verzeichnisse: data/homing/{timestamp}/
SSE-Event-Typen
Das Backend streamt während des Homing-Laufs folgende Events:
type |
Felder | Bedeutung |
|---|---|---|
log |
text |
Zeile aus Script-Ausgabe |
step |
step, total, text |
Fortschritt (1–6) |
analysis |
key, value |
Zwischenergebnis (x_mm, state_Arm1, …) |
error |
text |
Fehler (Script-Exit ≠ 0 o.ä.) |
done |
exitCode, state?, runDir |
Abschluss; state nur bei Erfolg |
robot.json — Ladestrategie
Aktuell: Lokale Datei
ROBOT_JSON = process.env.ROBOT_JSON || 'scripts/robot_1781069752019.json'
Geplant (wenn Driver GET ROBOT_URL/api/robot/config implementiert):
async function loadRobotConfig() {
if (ROBOT_URL) {
const res = await fetch(new URL('/api/robot/config', ROBOT_URL));
return res.json();
}
return JSON.parse(await fs.readFile(ROBOT_JSON, 'utf8'));
}
Auswirkung: nur ROBOT_JSON-Variable ändern — alle Scripts bekommen automatisch
die aktuelle Konfiguration.
Offene Punkte
- [~] Arm-Marker eintragen (Nutzer):
links.Arm1/Ellbow/Arm2/Hand.markersinrobot.json— Arm1 + Ellbow eingetragen, Arm2/Hand offen - [~] Erstmals testen: Homing-Run mit echtem Roboter — Arm1 (x, y) und Ellbow (z) laufen durch; Arm2/Hand noch zu prüfen. Ellbow brach zunächst ab (4b: nur 2 Marker sichtbar, deren Verbindungsvektor parallel zur Gelenkachse liegt → keine Baseline), seit 2026-06-16 über Fallback-1/-2 in
4b_revolute_angle.pygelöst — Details:Homing_1_StepByStep.md - X-Schätzung verfeinern (2026-06-14):
estimateXFromMarkers()rechnet den kinematischen Gelenk-Offset heraus statt rohem Mittelwert — behebt den ~110 mm Versatz der Modell-Marker - Unit-Test für X-Schätzung (2026-06-14): reine Geometrie nach
server/homingXEstimate.cjsausgelöst,test/homingXEstimate.test.js(9 Tests, inkl. Regression gegen den Offset-Bug) - y-Restfehler (~2°): erkannt 30° → ausgegeben 28°; vermutlich X-Rest-Rauschen + 4b-Fit-Residuum, noch zu untersuchen
- robot.json via Driver-API (2026-06-17):
server/robotConfig.js—fetchRobot()/pushRobot()/robotCachePath; automatischer Fallback auf lokale Datei wennROBOT_URLnicht gesetzt