162 lines
6.7 KiB
Markdown
162 lines
6.7 KiB
Markdown
# 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`](Homing_0_Camera.md) | Foto → Kamera-Pose → trianguliertes `aruco_marker_poses.json` (Schritte 1–3b) |
|
||
| [`Homing_1_StepByStep.md`](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 |
|
||
|
||
---
|
||
|
||
## 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
|
||
```javascript
|
||
ROBOT_JSON = process.env.ROBOT_JSON || 'scripts/robot_1781069752019.json'
|
||
```
|
||
|
||
**Geplant** (wenn Driver `GET ROBOT_URL/api/robot/config` implementiert):
|
||
```javascript
|
||
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.markers` in `robot.json` — Arm1 + Ellbow eingetragen, Arm2/Hand offen
|
||
- [~] **Erstmals testen**: Homing-Run mit echtem Roboter — Arm1 erkannt (x, y); Ellbow scheitert noch an fehlenden Markern
|
||
- [x] **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
|
||
- [x] **Unit-Test für X-Schätzung** (2026-06-14): reine Geometrie nach `server/homingXEstimate.cjs` ausgelö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** (optional): wenn Driver `GET ROBOT_URL/api/robot/config` bereitstellt
|