Revert B=180-B (Phase 2): gerade Hand ist b=pi, nicht 0

Die Phase-2-Umstellung (Commits 549d10b, 2197a89) war falsch: sie
machte gerade Hand = b=0. Hardware-Daten zeigen aber gerade = B~180
(z.B. G92 ... B179.20). Folge: G28 fuhr nach b=0 -> ~179 Grad
Einklappen -> Hand schlug in den Arm (Crash).

Zurueck auf den verifizierten Stand 933a017:
- IK: this.b = Math.acos(cosB)
- FK: rotateAroundAxis(vecUnterarm, n, this.b)
- G28: robot.b = Math.PI

Verifiziert mit echten Daten: G92 B179.20 -> b=179.20; G28 -> b=180
(Weg 0.8 Grad, kein Slam); IK-Round-Trip exakt; 452/452 Tests gruen.
Logs nicht enthalten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
chk
2026-06-26 16:30:10 +02:00
parent 549d10b9c0
commit f6a752cf58
5 changed files with 39 additions and 44 deletions

View File

@@ -6,9 +6,9 @@ Diese Datei beschreibt
3. die angestrebte **ideale Nullstellung** und
4. die Schritte, um den Driver auf diese Konvention zu bringen (**Weg 2: Modell auf Y**).
> **Status:** **Phase 1 (y-Flip) ✅** und **Phase 2 (B-Konvention) ✅** umgesetzt.
> α=0 → Arm zeigt nach y; **b=0 = gerade Hand** (homing-konsistent).
> Offen: C-Nullpunkt, Greifer-Kopplung-Validierung.
> **Status:** **Phase 1 (y-Flip) ist umgesetzt und am Roboter verifiziert.** α/β werden
> jetzt von **y** aus gemessen (α=0 → Arm zeigt nach y), passend zu robot.json und
> appRobotHoming. Offen ist **Phase 2** (Handgelenk-/Finger-Nullstellung, B/C).
---
@@ -87,8 +87,8 @@ Diese drei Bedingungen erfüllt der Driver **nach Phase 1 bereits** (verifiziert
| Y (α) | **0°** | Oberarm waagerecht entlang y | ✅ Phase 1 |
| Z (β) | **0°** | Unterarm waagerecht entlang y (gestreckt) | ✅ Phase 1 |
| A (a) | **0°** | Hand-Knick-Achse ∥ x | ✅ (Code erfüllt es) |
| B (b) | **0°** | Hand gerade = b=0, Knick = b>0 | Phase 2 |
| C (c) | 0° (Ziel) | kein Hand-Roll — **derzeit neutral ≠ 0** | ⏳ Phase 2 Rest |
| B (b) | 0° (Ziel) | Hand gerade **derzeit ist gerade = 180°** | Phase 2 |
| C (c) | 0° (Ziel) | kein Hand-Roll — **derzeit neutral ≠ 0** | ⏳ Phase 2 |
| E (e) | **0** | Greifer geschlossen / Referenz | — |
→ Resultierende Fingerspitze (α=β=a=0, gerade Hand): **(xMotor, (l1+l2+l3), 0) ≈ (x, 590, 0)**.
@@ -104,7 +104,7 @@ Diese drei Bedingungen erfüllt der Driver **nach Phase 1 bereits** (verifiziert
| β=0 Unterarm | **y** ✅ | y |
| a=0 Hand-Knick-Achse | **∥ x** ✅ | ∥ x |
| G92 der Grundstellung → y | **590** ✅ | ≈ 590 |
| gerade Hand | **b = 0°** | b = 0° |
| gerade Hand | **b = 18** | **b = 0°** |
| neutraler Roll | **c: ψ = 90° C** (posenabh.) | **c = 0°** |
Verifiziert per FK: `FK(α=0, β=0, gerade Hand) → (0, 590, 0)`; bei `a=0` bleibt
@@ -126,25 +126,19 @@ Umgesetzt:
Stellung (`|y| = l1+l2+l3`) ist eine Handgelenk-Singularität, in der die IK `a`/`c` nicht
bestimmen kann (Müll wie `a=135°, c=45°` → Finger schräg). G28 setzt dort die Motorwerte
**direkt** (`alpha=beta=a=c=0`, `b=π` = gerade Hand) und füllt die Pose per FK.
**Interim:** G28 lässt den Greifer (`e`/`eMotor`) **unangetastet** — sonst ergäbe
`eMotor = ebc` bei `b=π` den Wert 180° → Finger-Anschlag-Slam (Phase 2 behebt das mit `b=0`).
- **Tests:** `test/Robot.Kinematics.NegativeY.test.js` (Grundstellung 590, Homing-Pose
in y, Round-Trip in y, a=0 → Knick-Achse ∥ x).
- **Migration:** `Robot.02_UpperArm` und der G28-Test auf y umgestellt.
- **Doku:** `doc/Info_G92.md` Y/Z (und C/A nach der Spiegelung) aktualisiert.
### Phase 2 — B-Konvention ✅ ERLEDIGT
### Phase 2 — Handgelenk-/Finger-Nullstellung (B, C, Greifer) — OFFEN
**gerade Hand = b = 0** (war b = π = 180°). Homing sendet B≈0 für gerade → Driver jetzt konsistent.
> **Voraussetzung (User):** Erst die Finger visualisieren/prüfen — dort werden noch Fehler
> vermutet. **a-Achse ist bereits korrekt** (a=0 → Knick-Achse ∥ x). Phase 2 betrifft
> **B (Knick)**, **C (Roll)** und die **Greifer-Kopplung**.
Umgesetzt:
- **IK** (`_ikPlusY`): `this.b = Math.PI Math.acos(cosB)` (statt `acos(cosB)`).
- **FK** (`_fkPlusY`): Rotation um `Math.PI this.b` (statt `this.b`).
- **G28**: `robot.b = 0` (statt `Math.PI`).
- `gripperMotorFromOpening = e b c` bleibt; mit b=0 für gerade ist b-Beitrag Null → kein Slam mehr.
- Tests: 452/452 grün.
**Noch offen (Phase 2 Rest):** C-Nullpunkt, Greifer-Kopplung-Validierung, toter x-Port-Pfad.
**Ziel-Konvention:** gerade Hand → **B = 0°** (statt 180°); neutraler Roll → **C = 0°**;
Greifer-Kopplung konsistent und gegen die echte Mechanik kalibriert.
#### Vorab-Erkenntnis aus dem Code (wichtig!)
@@ -182,7 +176,13 @@ Fundstellen:
- Aufgabe: Kopplung gegen die echte Sehnenmechanik validieren, toten x-Port-Pfad +
`factorOpenTurn` aufräumen, **Vorzeichen** je nach Motor-Verkabelung prüfen.
3. **B-Konvention (gerade = 0°) ✅ ERLEDIGT** — siehe oben (Phase 2).
3. **B-Konvention (gerade = 0°).** Durchgängig:
- FK/IK in `Arm3SegmentLinearX` (b-Definition / acos-Zweig),
- `gripperMotorFromOpening` nachziehen,
- G92-Eingabe (`b = B/D`) + M1 + G28,
- `portInverse.js` (Umkehrung),
- **Sender-Formeln so kompensieren, dass die FluidNC-Ports unverändert bleiben** —
ODER bewusst die Hardware-Nullpunkte neu kalibrieren (Entscheidung dokumentieren).
4. **C-Nullpunkt (neutral = 0°).** Der `c↔ψ`-Bezug ist **posenabhängig**
(`ψ = acos(cos β · sin a) c`). Ein konstantes `c=0=neutral` ist **nicht global** möglich,
@@ -193,9 +193,8 @@ Fundstellen:
(`|Hand.to[1]| + |FingerA.to[1]|` = 35 + 60 = **95**) statt aus dem Ellbogen-Versatz (90).
Zusätzlich sind `kinematics.l1/l2/l3` in robot.json **explizit überschreibbar** (Vorrang
vor der Ableitung) — zum Kalibrieren auf die gemessene Reichweite.
Reichweite damit 595 — passt zur aktuellen Hardware (60 mm Finger). Die früher beobachteten
~550 stammten von **kürzeren 50 mm-Greifern**. Bei Greifer-Wechsel `kinematics.l3` per
Override anpassen (oder Finger-Geometrie in robot.json pflegen).
⚠️ Geometrie liefert Reichweite 595, beobachtet wurden **~550** → l3 (oder l1/l2) sollte
per `kinematics.l3` explizit kalibriert werden (deutet auf l3 ≈ 50, falls l1=l2=250 stimmen).
6. **Tests + Doku** nachziehen: Round-Trip mit neuer Konvention, Greifer-Kopplung,
G92-Referenztabellen in `Info_G92.md`, sowie diese Datei.