4b kind-marker für winkel beachten
This commit is contained in:
@@ -14,6 +14,14 @@ 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
|
||||
|
||||
34
doc/Homing_0_Camera.md
Normal file
34
doc/Homing_0_Camera.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Homing 0 – Kamera & Triangulation
|
||||
|
||||
> Technische Detail-Doku zu [`Homing.md`](Homing.md) — dieser Teil: Foto → trianguliertes
|
||||
> `aruco_marker_poses.json`.
|
||||
> Status: Gerüst, wird sukzessive ausgebaut.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
Schritte 1–3b der Homing-Pipeline (siehe `Homing.md` → Ablauf), a.k.a. „Board-Pipeline":
|
||||
|
||||
| Schritt | Script | Pro | Ergebnis |
|
||||
|---|---|---|---|
|
||||
| 1 | `1_detect_aruco_observations.py` | Kamera | 2D-Marker-Ecken (mit NPZ-Intrinsik entzerrt) |
|
||||
| 2 | `2_estimate_camera_from_observations.py` | Kamera | Kamera-Pose relativ zum Board (`cam*_camera_pose.json`) |
|
||||
| 3b | `3b_corner_marker_poses.py` | einmal, ≥2 Kamera-Posen | `aruco_marker_poses.json` (3D-Weltpositionen aller Marker) |
|
||||
|
||||
Ausgelagert in `runBoardPipeline()` (`server/server.js`) — gemeinsam genutzt von
|
||||
Kalibrierung und Homing.
|
||||
|
||||
## Offene Punkte / noch zu dokumentieren
|
||||
|
||||
- [ ] Kamera-Intrinsik-Format (NPZ-Inhalt, Verzeichnis)
|
||||
- [ ] Wie `3b` mehrere Kamera-Beobachtungen eines Markers kombiniert (Triangulation/Mittelung, `num_cameras`-Feld)
|
||||
- [ ] Fehlerquellen: Verdeckung, Belichtung, `num_cameras < 2` → Marker fehlt komplett
|
||||
- [ ] Format von `aruco_marker_poses.json` (Felder `position_mm`, `corners_m`, `normal`, `edge_length_mm`)
|
||||
- [ ] Zusammenhang mit `Kalibrierung.md` / `Kalibrierung_Marker.md`
|
||||
|
||||
## Verweise
|
||||
|
||||
- Allgemeiner Ablauf: [`Homing.md`](Homing.md)
|
||||
- Nächster Schritt (Gelenkwinkel aus den triangulierten Positionen): [`Homing_1_StepByStep.md`](Homing_1_StepByStep.md)
|
||||
- Kalibrierung: `Kalibrierung.md`, `Kalibrierung_Marker.md`
|
||||
77
doc/Homing_1_StepByStep.md
Normal file
77
doc/Homing_1_StepByStep.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Homing 1 – Schritt für Schritt: Gelenkwinkel-Schätzung (4b)
|
||||
|
||||
> Technische Detail-Doku zu [`Homing.md`](Homing.md) — dieser Teil: `aruco_marker_poses.json`
|
||||
> → Gelenkwinkel je Link.
|
||||
> Status: Gerüst, wird sukzessive ausgebaut.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
Die 4b-Kette aus `Homing.md` → Ablauf: `scripts/4b_revolute_angle.py`, sequenziell
|
||||
Arm1 → Ellbow → Arm2 → Hand. Jeder Aufruf bekommt den Zustand des vorherigen Schritts
|
||||
über `--from-state` (`accumulated_state`).
|
||||
|
||||
## Schätz-Methoden (Prioritätsreihenfolge je Gelenk)
|
||||
|
||||
Pro Gelenk wird in dieser Reihenfolge versucht, eine Schätzung zu finden. Jede
|
||||
nächste Stufe ist ein **reiner Fallback** — sie greift nur, wenn die vorherige Stufe
|
||||
**keine einzige** brauchbare Baseline liefert (z. B. Marker nicht sichtbar, oder
|
||||
Marker-Paar zufällig parallel zur Drehachse):
|
||||
|
||||
1. **Primär** — zwei Marker auf dem Ziel-Link selbst. `v_model`/`v_obs`-Differenzvektor,
|
||||
⟂ zur Gelenkachse projiziert, Winkel zwischen beiden gemessen. Braucht nur die
|
||||
Achs*richtung* (aus FK der schon gelösten Vorgänger), nicht die Pivot-*Position*.
|
||||
2. **Fallback-1** *(Konzept 2026-06-16, noch nicht implementiert)* — zwei Marker auf
|
||||
dem **direkten Kind-Link**, deren Verbindungsvektor (im Kind-Lokalframe) parallel
|
||||
zur **eigenen** Achse des Kind-Links liegt → invariant gegen dessen noch
|
||||
unbekannten Winkel, daher als Stellvertreter für „zwei Marker am Ziel-Link" nutzbar.
|
||||
Beispiel: Ellbow (Achse X) ← Arm2-Marker 144↔148 bzw. 143↔146 (Arm2-Achse Y, ⟂ zu X,
|
||||
beide Paare exakt achsparallel in Arm2s Lokalframe). Wie Primär unabhängig von der
|
||||
Pivot-Position — eine separate „ist die nächste Achse senkrecht"-Prüfung ist nicht
|
||||
nötig, das ergibt sich automatisch aus der bestehenden Mindest-Baseline-Prüfung nach
|
||||
der Projektion.
|
||||
3. **Fallback-2** *(implementiert)* — ein einzelner Marker auf dem Ziel-Link gegen den
|
||||
Gelenk-**Pivot** selbst (Pivot + Achse aus FK der Vorgänger). Einzige Stufe, die mit
|
||||
nur 1 sichtbaren Marker funktioniert — aber zusätzlich abhängig von der Pivot-
|
||||
*Position* (also den geschätzten Vorgänger-*Werten*, nicht nur deren Achsrichtung).
|
||||
|
||||
→ Code: `scripts/4b_revolute_angle.py` (`estimate_revolute_angle()`), Konstante
|
||||
`PIVOT_FALLBACK_ID`, Feld `"method"` im Ergebnis-JSON.
|
||||
|
||||
### Befund 2026-06-16 (wichtig für Priorisierung)
|
||||
|
||||
Im Testlauf `test/homing/20260616_120456` waren am Ellbow nur Marker 129/132 sichtbar,
|
||||
deren Verbindungsvektor exakt parallel zur Ellbow-Achse liegt → Primär-Methode liefert
|
||||
nichts, Fallback-2 (Pivot) springt ein und meldet `z ≈ -4.33°` (intern konsistent,
|
||||
Exit 0). Eine unabhängige Gegenrechnung (Least-Squares über alle Ellbow- *und*
|
||||
Arm2-Marker, `z`+`a` frei) zeigt aber ein Minimum bei `z ≈ -38°` — die Fallback-2-Schätzung
|
||||
liegt hier ca. 35–40° daneben. Die (noch nicht implementierte) Fallback-1-Rechnung mit
|
||||
Arm2-Marker 144↔148/143↔146 hätte `z ≈ -44°` ergeben, sehr nah am Least-Squares-Minimum,
|
||||
weil sie (wie Primär) nicht von der Pivot-Position abhängt. → Fallback-1 ist nicht nur
|
||||
„nice to have", sondern in diesem Fall klar genauer als Fallback-2.
|
||||
Detaillierte Aufarbeitung/Entscheidung: siehe `Homing_2_improvement.md` (geplant).
|
||||
|
||||
## robot.json-Struktur (Kurzreferenz)
|
||||
|
||||
- `links.<Name>.parent` — Name des Eltern-Links (Kette/Baum)
|
||||
- `links.<Name>.jointToParent` — `{type, axis, origin, rotation, variable}`
|
||||
- `links.<Name>.markers[]` — `{id, position, normal, size}`; `position` ist relativ
|
||||
zum **Pivot** des eigenen Links, im lokalen, noch nicht eigen-rotierten Frame.
|
||||
- FK-Engine: `scripts/robot_fk.py` (`RobotFK.compute()`, `.joint_origin_world()`,
|
||||
`.joint_axis_world()`, `.marker_world()`).
|
||||
|
||||
## Offene Punkte / noch zu dokumentieren
|
||||
|
||||
- [ ] State-JSON-Schema im Detail (`accumulated_state`, `per_pair`, `method`, `circular_std_deg`)
|
||||
- [ ] `--min-baseline` Tuning / Auswirkung
|
||||
- [ ] Fallback-1 Implementierung (Aufwandsschätzung: klein-mittel, ~2-3h — Kern-Mathematik
|
||||
`_model_spoke_world()`/`_pair_estimate()` bereits vorhanden und wiederverwendbar;
|
||||
neu: Kind-Link-Suche, Achsparallelitäts-Filter, 3-stufige Tier-Logik) + Tests
|
||||
- [ ] y-Restfehler (~2°) aus `Homing.md` → Offene Punkte
|
||||
|
||||
## Verweise
|
||||
|
||||
- Allgemeiner Ablauf: [`Homing.md`](Homing.md)
|
||||
- Vorheriger Schritt (Kamera/Triangulation): [`Homing_0_Camera.md`](Homing_0_Camera.md)
|
||||
- Bekannte Probleme / Ideen: `Homing_2_improvement.md` (geplant)
|
||||
307
doc/Homing_5_Pose.md
Normal file
307
doc/Homing_5_Pose.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Homing 5 – Pose-Schätzung per Bundle-Adjustment (`hybrid`)
|
||||
|
||||
> Technische Detail-Doku zu [`Homing.md`](Homing.md) — **Verfeinerungsschritt NACH
|
||||
> der 4b-Kette** ([`Homing_1_StepByStep.md`](Homing_1_StepByStep.md)), **nicht**
|
||||
> deren Ersatz: `5_pose_estimation.py` braucht den `accumulated_state` von 4b als
|
||||
> Startwert. Ohne guten Startwert läuft die interne Optimierung mangels eigener
|
||||
> verlässlicher Initialisierung leicht in ein lokales Minimum (siehe „Wichtige
|
||||
> Einschränkung" unten).
|
||||
> Status: Skript liegt bereits in `scripts/5_pose_estimation.py`, **noch nicht**
|
||||
> in `homingOrchestrator.js`/`server.js` verdrahtet — und braucht noch einen
|
||||
> kleinen Code-Hook, um den 4b-Startwert überhaupt entgegennehmen zu können
|
||||
> (siehe Offene Punkte).
|
||||
|
||||
---
|
||||
|
||||
## Herkunft
|
||||
|
||||
`scripts/5_pose_estimation.py` ist 1:1 (byte-identisch, per Diff verifiziert)
|
||||
aus dem Schwesterprojekt **`appRobotRendering`** übernommen
|
||||
(`pipeline/pose_estimation.py`, dort Stufe 4 der allgemeinen Pose-Pipeline).
|
||||
Mitgewandert und ebenfalls identisch: `scripts/robot_fk.py`. Dort ist das
|
||||
Verfahren an zehn simulierten Szenen mit bekannter Grundwahrheit validiert
|
||||
(`doc/pipeline.tex` im Rendering-Projekt) — diese Zahlen unten sind **Simulationsergebnisse
|
||||
aus appRobotRendering**, keine Messung an appRobotHoming/echter Hardware.
|
||||
|
||||
`scripts/robot_1781069752019.json` enthält bereits den passenden
|
||||
`pose_estimation`-Konfigurationsblock (identisch zu den Rendering-Defaults:
|
||||
`method: hybrid`, `normal_weight: 100`, `huber_delta_mm: 8.0`, …) — die Datenseite
|
||||
ist also schon vorbereitet, nur die Prozess-Verdrahtung fehlt noch.
|
||||
|
||||
---
|
||||
|
||||
## Einordnung in den Homing-Ablauf
|
||||
|
||||
```
|
||||
1_detect_aruco_observations.py ┐
|
||||
2_estimate_camera_from_observations.py │ = "Board-Pipeline" (Homing_0_Camera.md)
|
||||
3b_corner_marker_poses.py ┘
|
||||
│
|
||||
▼ aruco_marker_poses.json
|
||||
│
|
||||
▼
|
||||
4b_revolute_angle.py × N (sequenziell root→tip, über --from-state verkettet)
|
||||
│ Homing_1_StepByStep.md — liefert pro Gelenk eine Schätzung
|
||||
│ (Primär/Fallback-1/Fallback-2), abhängig von Sichtbarkeit
|
||||
▼
|
||||
accumulated_state {x,y,z,a,b,c,e} ← Startwert, NICHT optional überspringbar
|
||||
│
|
||||
▼
|
||||
5_pose_estimation.py (method=global_ba, accumulated_state als Startwert x0)
|
||||
│ dieses Dokument — EIN gemeinsamer Bündelausgleich über alle 7
|
||||
│ Variablen gleichzeitig, verfeinert/korrigiert den 4b-Zustand global
|
||||
▼
|
||||
robot_state.json { movements: {…}, residual_rms, … }
|
||||
```
|
||||
|
||||
**Wichtig:** `5_pose_estimation.py` ist **kein** Ersatz für die 4b-Kette, sondern
|
||||
ein **Verfeinerungsschritt danach**, der deren `accumulated_state` als Startwert
|
||||
braucht. Lässt man die 4b-Kette weg, fehlt dieser Startwert — die interne
|
||||
Optimierung initialisiert dann faktisch bei `0` für jede Variable, und bei einer
|
||||
beim Einschalten unbekannten Roboterpose ist das ein guter Weg in ein lokales
|
||||
Minimum (Mechanismus s. „Wichtige Einschränkung" unten).
|
||||
|
||||
| Stufe | Eingabe | Aufruf | Ausgabe |
|
||||
|---|---|---|---|
|
||||
| **4b-Kette** (liefert den Startwert) | `aruco_marker_poses.json` + extern geschätztes `x_mm` | N Prozesse, je Link einer, `--from-state` verkettet | `accumulated_state` (flach: `x,y,z,a,b,c,e`) |
|
||||
| **`5_pose_estimation.py`** (verfeinert global) | `aruco_marker_poses.json` **+ `accumulated_state` aus 4b als Startwert** | 1 Prozess | `robot_state.json` → `movements.<var>.{value,unit,observable,confidence,n_markers}` |
|
||||
|
||||
---
|
||||
|
||||
## Wie es funktioniert (kurz)
|
||||
|
||||
Das Skript parametrisiert über **Gelenkvariablen** (nicht Links) und liest pro
|
||||
Marker Position **und gemessene Normale** aus `aruco_marker_poses.json`
|
||||
(3b-Ausgabe). Vier austauschbare Verfahren (`robot.json` → `pose_estimation.method`),
|
||||
`hybrid` ist Standard und kombiniert die letzten beiden:
|
||||
|
||||
1. `sequential_vector` — analytische Winkel aus Marker-Paar-Vektoren (schnell, braucht ≥2 Marker/Gelenk)
|
||||
2. `sequential_fk` — blockweiser nichtlinearer Fit entlang der Kette, vorherige Variablen eingefroren, Multi-Start `{0,60,…,300}°` gegen lokale Minima
|
||||
3. `global_ba` — **einziges** Bündelausgleichsproblem über **alle 7 Variablen gleichzeitig** (`scipy.optimize.least_squares`, Huber-Loss)
|
||||
4. **`hybrid`** = 2 als Startwert → 3 als Verfeinerung
|
||||
|
||||
Die Blockbildung in `analyze_chain()` ist generisch aus der FK-Topologie
|
||||
abgeleitet (keine festen Link-Namen) — passt damit zur Projekt-Konvention
|
||||
„Scripts müssen Szenen/Ketten automatisch erkennen, nichts hartkodieren".
|
||||
Für *dieses* Robot-Modell ergibt sich u. a. der Block `{x, y}`: `Base` (Variable
|
||||
`x`) hat **keine eigenen Marker** (`"markers": []` in `robot_1781069752019.json`)
|
||||
und wird automatisch mit `Arm1` (Variable `y`, 5 Marker) zu einem gemeinsamen
|
||||
Least-Squares-Fit zusammengefasst.
|
||||
|
||||
Jedes Ergebnis kommt mit einer Konfidenz pro Variable (`high/medium/low/none`,
|
||||
abgeleitet aus sichtbaren Markern je Block) — analog zur 4b-Kette, aber pro
|
||||
Block statt pro Einzel-Fallback-Stufe.
|
||||
|
||||
### Wichtige Einschränkung: Startwert und lokale Minima
|
||||
|
||||
`estimate_pose()` ruft für `global_ba`/`hybrid` **immer zuerst selbst**
|
||||
`estimate_sequential_fk()` als „billigen, robusten Init" auf
|
||||
(`scripts/5_pose_estimation.py:471-476`) — es gibt aktuell **keinen** Parameter,
|
||||
um stattdessen einen extern vorgegebenen Startwert (z. B. den `accumulated_state`
|
||||
aus 4b) einzuspeisen, obwohl `estimate_global_ba()` selbst intern bereits ein
|
||||
`x0`-Dict entgegennimmt (`:236-237`).
|
||||
|
||||
`estimate_sequential_fk()` initialisiert jede Variable bei `0.0` und rastert den
|
||||
Multi-Start `{0,60,120,180,240,300}°` **nur über die erste Variable eines
|
||||
Blocks** (`bvars[0]`) — und auch das **nur, wenn diese selbst `revolute`
|
||||
ist** (`:296-304`). Für dieses Robotermodell heißt das konkret:
|
||||
|
||||
- Block `{x, y}` (Base markerlos → mit Arm1 zusammengefasst): `bvars[0]` ist
|
||||
`x` (linear) → `lead_type != "revolute"` → **kein** Multi-Start. `y`
|
||||
(Schultergelenk, Arm1) wird in einem einzigen Lauf ab `0°` gefittet.
|
||||
- Block `{b, c, e}` (Hand/Palm markerlos → mit den Fingermarkern zusammengefasst):
|
||||
nur `b` bekommt den 6-Punkte-Raster; `c` und `e` starten in **jedem** der
|
||||
6 Läufe fix bei `0`.
|
||||
- Einzelvariablen-Blöcke wie Ellbow (`{z}`) oder Arm2 (`{a}`) bekommen den
|
||||
vollen Raster auf sich selbst — dort ist das Risiko deutlich kleiner.
|
||||
|
||||
Liegt die echte Pose in `y`, `c` oder `e` weit von `0` entfernt (beim Homing
|
||||
nach dem Einschalten der Normalfall, nicht die Ausnahme), kann schon die
|
||||
`sequential_fk`-Vorstufe in einem falschen lokalen Minimum landen — die
|
||||
anschließende `global_ba`-Verfeinerung poliert dieses falsche Minimum dann nur
|
||||
noch, statt es zu verlassen. Das deckt sich mit dem in der Validierungstabelle
|
||||
unten sichtbaren großen Abstand zwischen Mittelwert (0,253°) und Schlechtestfall
|
||||
(1,568°) bei sonst niedriger Streuung (0,134°) — ein Muster, das zu „meist
|
||||
gut, gelegentlich falsches Minimum" passt.
|
||||
|
||||
**Konsequenz:** `5_pose_estimation.py` sollte in appRobotHoming **nicht kalt**
|
||||
laufen, sondern mit dem `accumulated_state` der 4b-Kette als Startwert (Details
|
||||
und der dafür nötige Code-Hook: Abschnitt „Integrationsschritte").
|
||||
|
||||
### Validierung im Rendering-Projekt (Simulation, 10 Posen, bekannte GT)
|
||||
|
||||
| Verfahren | Winkel Ø [°] | Winkel schlechtest. [°] | Position Ø [mm] | Position schlechtest. [mm] |
|
||||
|---|---|---|---|---|
|
||||
| `sequential_vector` | 0,315 | 1,717 | 0,144 | 0,712 |
|
||||
| `sequential_fk` | 0,434 | 1,838 | 0,158 | 0,851 |
|
||||
| `global_ba` | 0,253 | 1,568 | 0,103 | 0,390 |
|
||||
| **`hybrid`** | **0,253** | **1,568** | **0,103** | **0,390** |
|
||||
|
||||
(Quelle: `appRobotRendering/doc/pipeline.tex`, Abschnitt „Validierung und Ergebnisse".)
|
||||
|
||||
---
|
||||
|
||||
## Vorteile
|
||||
|
||||
- **Bestes/stabilstes Verfahren im Rendering-Benchmark** (s. Tabelle oben) — unter
|
||||
allen vier Methoden der niedrigste Mittel- *und* Worst-Case-Fehler.
|
||||
- **Überbrückt markerlose Gelenke automatisch.** `Hand` (Variable `b`) und `Palm`
|
||||
(`c`) tragen keine eigenen Marker — `global_ba` zieht die Information aus den
|
||||
Fingermarkern *rückwärts* durch die Kette. Die 4b-Kette braucht dafür explizit
|
||||
einen Fallback pro Gelenk; hier passiert es als Nebenprodukt der gemeinsamen
|
||||
Optimierung.
|
||||
- **Fittet `x` und `y` gemeinsam aus denselben Arm1-Markern** (Block `{x,y}`,
|
||||
weil `Base` markerlos ist) — konsistenter als zwei getrennte Schätzungen.
|
||||
Ersetzt `estimateXFromMarkers()` aber **nicht**: dieser Block ist genau einer
|
||||
der beiden, die ohne guten Startwert anfällig für ein lokales Minimum sind
|
||||
(s. „Wichtige Einschränkung" unten) — die gemeinsame Schätzung ist also ein
|
||||
Mehrwert *nach* einer 4b-Vorschätzung, kein Grund, diese zu überspringen.
|
||||
- **Funktioniert mit nur 1 sichtbarem Marker pro Gelenk**, weil das Residuum
|
||||
Position **und** Normale nutzt (Gl. in `residual_vector()`) — die 4b-Primärmethode
|
||||
braucht dafür mindestens 2.
|
||||
- **Ist die automatisierte Form der bereits manuell durchgeführten Gegenrechnung.**
|
||||
Der Befund vom 2026-06-16 in `Homing_1_StepByStep.md` (Ellbow: Fallback-2 lag
|
||||
35–40° neben einer von Hand gerechneten Least-Squares-Kontrolle über Ellbow- *und*
|
||||
Arm2-Marker) ist exakt das, was `global_ba`/`hybrid` automatisch und für **alle**
|
||||
Gelenke gleichzeitig macht. Ein Lauf hätte den Fallback-2-Fehler vermutlich direkt
|
||||
erkennbar gemacht.
|
||||
- **Robuste Verlustfunktion (Huber)** dämpft einzelne Ausreißer-Marker (Fehldetektion,
|
||||
Verdeckung) automatisch, statt dass ein einzelner schlechter Marker das ganze
|
||||
Gelenk verfälscht.
|
||||
- **Multi-Start über mehrere Startwinkel** hilft dort, wo er greift (Blöcke mit
|
||||
genau einer Variable, z. B. Ellbow/`z`, Arm2/`a`) — für Homing wertvoller als
|
||||
für Kalibrierung, weil beim Einschalten die Pose komplett unbekannt ist. Greift
|
||||
aber **nicht** bei den gekoppelten Blöcken `{x,y}` und `{b,c,e}` (s. u.) — genau
|
||||
dort ist ein externer Startwert aus 4b nötig.
|
||||
|
||||
## Nachteile
|
||||
|
||||
- **Kein verlässlicher eigener Kaltstart — Startwert von außen zwingend nötig.**
|
||||
Wie im Abschnitt „Wichtige Einschränkung" hergeleitet: der interne Multi-Start
|
||||
deckt nur Einzelvariablen-Blöcke ab, nicht die gekoppelten Blöcke `{x,y}` und
|
||||
`{b,c,e}`. Allein aufgerufen (ohne `accumulated_state` aus 4b) ist
|
||||
`5_pose_estimation.py` daher beim Homing real gefährdet, in einem lokalen
|
||||
Minimum zu landen, statt die echte Pose zu finden — kein Rand-, sondern ein
|
||||
Kernfall, weil die Pose beim Einschalten grundsätzlich unbekannt ist.
|
||||
- **`scipy` fehlt aktuell im appRobotHoming-Container.** `docker-compose.yaml`
|
||||
installiert nur `opencv-python-headless numpy`
|
||||
(`pip3 install --quiet --no-cache-dir opencv-python-headless numpy`). Ohne
|
||||
`scipy` greift `HAVE_SCIPY=False`: `estimate_sequential_fk` lässt jeden Block
|
||||
auf `0.0` stehen, `estimate_global_ba` gibt den (dann ebenfalls nullwertigen)
|
||||
Startwert unverändert zurück — **kein Fehler, nur eine `[WARN]`-Zeile auf
|
||||
stdout.** Das ist ein stiller Fehlmodus: muss vor dem ersten Einsatz behoben
|
||||
werden (scipy zur `pip3 install`-Zeile ergänzen).
|
||||
- **Zwei nichtlineare Least-Squares-Läufe statt eines geschlossenen Ausdrucks** —
|
||||
langsamer als `sequential_vector` und langsamer als ein einzelner
|
||||
`4b_revolute_angle.py`-Aufruf. Für „schnell, vollautomatisch" (Anspruch aus
|
||||
`Homing.md`) noch nicht auf echter Hardware gemessen.
|
||||
- **Kein progressives Zwischenergebnis.** Die 4b-Kette liefert nach jedem Link
|
||||
ein SSE-`analysis`-Event und aktualisiert den Board-Viewer live
|
||||
(„progressiver Update je erkanntem Gelenk", `Homing.md` → Implementierung).
|
||||
`estimate_pose()` gibt nur den fertigen Endzustand zurück — für dieselbe UX
|
||||
müsste man zusätzlich die internen Zwischenstände von `estimate_sequential_fk()`
|
||||
exponieren.
|
||||
- **Verliert die dokumentierte Fallback-Diagnostik.** `Homing_1_StepByStep.md`
|
||||
protokolliert pro Gelenk, *welche* Stufe gegriffen hat (`method`: primary /
|
||||
fallback_1 / fallback_2). `5_pose_estimation.py` liefert nur eine
|
||||
Block-Konfidenz (`high/medium/low/none`), nicht *welche* Heuristik intern
|
||||
gewirkt hat — weniger Transparenz beim Debuggen einzelner Gelenke.
|
||||
- **Ausgabeformat passt nicht direkt auf `/api/state`.** Der Endpunkt erwartet
|
||||
ein flaches `{x,y,z,a,b,c,e}` (`accumulated_state`, siehe
|
||||
`server/server.js` → `POST /api/homing/send-state`), `5_pose_estimation.py`
|
||||
schreibt verschachtelt (`movements.<var>.value`). Eine kleine Adapterfunktion
|
||||
ist nötig, kein Drop-in-Ersatz.
|
||||
- **Unbeobachtbare Gelenke werden als `0.0` ausgegeben**, nicht als `null`
|
||||
(Konfidenz `none`/`observable:false` steht nur als Metadatum daneben). Das
|
||||
widerspricht der sonst im Projektverbund befolgten Konvention „Unbekannt bleibt
|
||||
`null`, nie erfundene `0`". Eine Integration muss `observable:false` aktiv auf
|
||||
`null` ummappen, bevor der Zustand weitergereicht wird — sonst wandert eine
|
||||
stille `0°`/`0mm` in Richtung Robotersteuerung.
|
||||
- **Noch nicht an echten Kamerabildern/Markern validiert.** Die Zahlen oben sind
|
||||
Simulation aus appRobotRendering (saubere FK-Marker-Positionen, definierter
|
||||
Renderfehler-Rauschboden). Reale Marker-Ungenauigkeiten (s.
|
||||
`Kalibrierung_Marker.md`) und reale Kameranoise könnten andere `huber_delta_mm`/
|
||||
`normal_weight`-Werte als die übernommenen Defaults verlangen.
|
||||
|
||||
## Besonderheiten
|
||||
|
||||
- **Reiner, unveränderter Import-Stand** — momentan git-`??` (untracked), noch
|
||||
nicht in `homingOrchestrator.js`/`server.js` referenziert (nur `4b_revolute_angle.py`
|
||||
ist dort als `SCRIPT_4B` verdrahtet).
|
||||
- **Schema-Kompatibilität zur lokalen `3b_corner_marker_poses.py` bereits
|
||||
geprüft:** Feldnamen `marker_id`, `position_mm`/`position_m`, `normal`,
|
||||
`num_cameras` stimmen 1:1 — `load_observations()` braucht keine Anpassung.
|
||||
- **Namens-Kollision mit `5_camera_z_refine.py`** — zwei Skripte teilen sich das
|
||||
Präfix `5_`. Entspricht der Konvention aus appRobotRendering, wo mehrere
|
||||
Dateien sich ein Stufen-Präfix teilen (z. B. `3_*`, `4_*`); kein Bug, aber beim
|
||||
Lesen der `scripts/`-Liste leicht zu verwechseln.
|
||||
- **Die `pose_estimation.method`-Option erlaubt gezieltes A/B-Testen** ohne
|
||||
Codeänderung: `--method sequential_vector|sequential_fk|global_ba|hybrid` per
|
||||
CLI-Override, oder dauerhaft über `robot_1781069752019.json` →
|
||||
`pose_estimation.method`. Nützlich, um z. B. `hybrid` parallel zur bestehenden
|
||||
4b-Kette laufen zu lassen und beide Ergebnisse zu vergleichen, bevor irgendetwas
|
||||
ersetzt wird.
|
||||
- **`finger_block_joints`/`per_link_method`** stehen schon (leer) in der
|
||||
robot.json — vorbereitete, aber im Skript bisher ungenutzte Erweiterungspunkte
|
||||
aus appRobotRendering.
|
||||
|
||||
---
|
||||
|
||||
## Aufruf (Stand-alone, zum Testen)
|
||||
|
||||
⚠️ Diese Aufrufe laufen **kalt** (kein externer Startwert — der Code-Hook dafür
|
||||
existiert noch nicht, s. Integrationsschritte). Geeignet, um das Kaltstart-/
|
||||
Lokales-Minimum-Verhalten aus „Wichtige Einschränkung" zu beobachten und zu
|
||||
reproduzieren — **nicht** der vorgesehene Produktionspfad.
|
||||
|
||||
```bash
|
||||
python scripts/5_pose_estimation.py data/homing/<run>/aruco_marker_poses.json \
|
||||
-robot scripts/robot_1781069752019.json \
|
||||
-out data/homing/<run>/robot_state.json
|
||||
# Verfahren erzwingen, z.B. zum gezielten Vergleich einzelner Methoden:
|
||||
python scripts/5_pose_estimation.py data/homing/<run>/aruco_marker_poses.json \
|
||||
-robot scripts/robot_1781069752019.json --method global_ba
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integrationsschritte (Offene Punkte)
|
||||
|
||||
- [ ] **`scipy` in `docker-compose.yaml` ergänzen** (`pip3 install …` Zeile) —
|
||||
ohne das läuft `hybrid` lautlos auf Nullzustand.
|
||||
- [x] **Architektur entschieden:** 4b-Kette läuft zuerst und liefert den
|
||||
`accumulated_state` als Startwert; `5_pose_estimation.py` läuft danach als
|
||||
globaler Verfeinerungsschritt darüber. Kein Ersatz, keine parallele
|
||||
Alternative — siehe „Wichtige Einschränkung" oben.
|
||||
- [ ] **Code-Hook in `5_pose_estimation.py` ergänzen:** aktuell gibt es keinen
|
||||
Weg, einen externen Startwert hineinzugeben. Vorschlag: CLI-Flag analog zu 4b
|
||||
(`--from-state accumulated_state.json`), das den 4b-Zustand als `x0` direkt an
|
||||
`estimate_global_ba()` durchreicht (Parameter existiert dort bereits,
|
||||
`:236-237`) und so den internen `estimate_sequential_fk()`-Kaltstart in
|
||||
`estimate_pose()` (`:471-476`) umgeht bzw. nur als Fallback nutzt, falls kein
|
||||
externer Startwert übergeben wird.
|
||||
- [ ] Adapter `movements.<var>.value` → flaches `{x,…,e}`-State-Objekt für
|
||||
`POST /api/homing/send-state`; dabei `observable:false → null` ummappen.
|
||||
- [ ] Anbindung in `homingOrchestrator.js` (neuer Schritt, analog `runBoardPipeline`/
|
||||
4b-Loop) + SSE-Event(s) für Fortschritt (auch ohne echtes Zwischenergebnis,
|
||||
z. B. ein `step`-Event „läuft" / „fertig").
|
||||
- [ ] Erste echte Messung: `hybrid`-Ergebnis gegen 4b-Kette auf demselben
|
||||
`data/homing/<run>/aruco_marker_poses.json` vergleichen (insbesondere am
|
||||
Ellbow-Fall aus `Homing_1_StepByStep.md`).
|
||||
- [ ] `huber_delta_mm`/`normal_weight` ggf. gegen reale Marker-Genauigkeit
|
||||
nachjustieren (Defaults sind aus appRobotRendering-Simulation übernommen).
|
||||
- [ ] Eintrag in `Homing.md`-Tabelle (Doku-Übersicht) ergänzen, sobald
|
||||
verdrahtet.
|
||||
|
||||
---
|
||||
|
||||
## Verweise
|
||||
|
||||
- Allgemeiner Ablauf: [`Homing.md`](Homing.md)
|
||||
- Vorheriger Schritt (Kamera/Triangulation, liefert den gemeinsamen Input):
|
||||
[`Homing_0_Camera.md`](Homing_0_Camera.md)
|
||||
- Alternative/Ist-Zustand (4b-Kette, dieselbe Aufgabe anders gelöst):
|
||||
[`Homing_1_StepByStep.md`](Homing_1_StepByStep.md)
|
||||
- Ursprung & Validierung: Projekt **`appRobotRendering`**,
|
||||
`pipeline/pose_estimation.py` + `doc/pipeline.tex` (Abschnitte „Pose-Estimation:
|
||||
Vier Schätzverfahren" und „Validierung und Ergebnisse").
|
||||
Reference in New Issue
Block a user