# Homing – appRobotHoming

> Stand: 2026-06-14  
> 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.

---

## 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 | 🔶 Nutzer trägt ein (`links.Arm1/Ellbow/Arm2/Hand.markers`) |

---

## 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 (Durchschnitt x der Nicht-Board-Marker)
        ▼
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()` | Mittelwert x der Nicht-Board-Marker aus `aruco_marker_poses.json` |
| 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 |

**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`
- [ ] **Erstmals testen**: Homing-Run mit echtem Roboter und eingetragenen Markern durchführen
- [ ] **X-Schätzung verfeinern** (optional): `estimateXFromMarkers()` könnte gelenk-spezifischere Logik nutzen statt einfachem Mittelwert
- [ ] **robot.json via Driver-API** (optional): wenn Driver `GET ROBOT_URL/api/robot/config` bereitstellt
