Phase1 Koordinaten

This commit is contained in:
chk
2026-06-26 10:27:42 +02:00
parent 7205b9d913
commit bd1752f567
5 changed files with 131 additions and 86 deletions

View File

@@ -4,15 +4,11 @@ Diese Datei beschreibt
1. das Koordinatensystem,
2. wie der Roboter darin steht,
3. die angestrebte **ideale Nullstellung** und
4. die nötigen Schritte, um den Driver auf diese Konvention zu bringen (**Weg 2:
Modell auf Y drehen**).
4. die Schritte, um den Driver auf diese Konvention zu bringen (**Weg 2: Modell auf Y**).
> **Hintergrund / Problem:** Das Kinematik-Modell (`Arm3SegmentLinearX`) misst die
> Armwinkel aktuell von **+Y** (α=0 → Arm zeigt nach +y). Der reale Roboter steht und
> arbeitet aber in **Y** (robot.json: `Arm1.skeleton.to = [0,-250,0]`,
> `coordinateSystem.y = "backward"`). Dadurch landet eine fast-waagerechte
> Grundstellung im Driver bei **y ≈ +590** statt **590** — Modell und Hardware sind an
> der y-Achse gespiegelt.
> **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).
---
@@ -20,30 +16,38 @@ Diese Datei beschreibt
Aus robot.json (`coordinateSystem`): **rechtshändig**, Längen **mm**, Winkel **Grad**.
| Achse | Richtung | Bedeutung im Aufbau |
|-------|------------|-------------------------------------------|
| x | rechts | entlang der Linearschiene |
| Achse | Richtung | Bedeutung im Aufbau |
|-------|------------|----------------------------------------------|
| x | rechts | entlang der Linearschiene |
| y | „backward" | Arm-Arbeitsrichtung: Arm streckt nach **y** |
| z | oben | Höhe |
| z | oben | Höhe |
**Seitenansicht** (Blick entlang +x, also die y/z-Ebene):
```
Seitenansicht (Blick entlang +x):
z
| ■══════════════════════● Arm waagerecht ausgestreckt → Fingerspitze
| Schulter (Ursprung, z=0)
| ■═══════════════●===========●---o Arm1 ═══ , Arm2 === und Finger --- waagerecht
| Schulter (Ursprung, z=0) ausgestreckt → Fingerspitze o
└───────────────────────────────► y (Arbeitsrichtung)
```
Draufsicht (Blick von oben, z):
In der y/z-Seitenansicht liegen beide Handgelenk-Achsen **end-on** (als Punkt ●), weil
sie entlang x verlaufen:
- linkes ● = **a-Achse** (Unterarm-Dreher, zwischen Ellbogen und Arm2)
- rechtes ● = **b-Achse** (Hand-Knick-Achse)
y ▲ ● Fingerspitze (Grundstellung y ≈ 590)
│ │
│ │ Arm (Ober- + Unterarm, gestreckt)
───┼────────────────■─────────────► x (Schiene)
Schulter
+y
**Draufsicht** (Blick von oben auf z; x nach oben, y nach rechts):
```
x
▲ │ │
■════╪═══ Arm1 ═════════╪══ Arm2 ══----o o = Fingerspitze (y ≈ 590)
Schulter
└────────────────────────────────────────────► y
│ = a-Achse (Ellbogen↔Arm2) bzw. b-Achse (Hand-Knick).
Bei a=0 laufen BEIDE parallel zur x-Achse (hier als senkrechte Striche).
```
---
@@ -68,82 +72,92 @@ Gelenk-Kette (robot.json `links`):
## 3. Ideale Nullstellung (Grundstellung)
**Definition:** Arm **waagerecht voll ausgestreckt entlang y**, Hand **gerade** in
Verlängerung des Unterarms, Greifer geschlossen.
**Definition (deine Vorgabe):**
**Ziel:** in dieser Stellung sind **alle Gelenkwinkel = 0°**.
1. **Arm1 entlang y** (Schulter-/y-Gelenk α) → **α = 0**.
2. **Ellbogen so angewinkelt, dass auch Arm2 entlang y** (z-Gelenk β) → **β = 0**
(β ist absolut, also auch direkt entlang y, nicht relativ zum Oberarm).
3. **a-Achse so gedreht, dass die Hand-Knick-Achse genau in x-Richtung läuft****a = 0**.
| Achse | Ideal-Wert | Bedeutung |
|--------|-----------|-------------------------------------------|
| X | (frei) | Schienenposition `xMotor` in mm |
| Y (α) | **0°** | Oberarm waagerecht entlang y |
| Z (β) | **0°** | Unterarm waagerecht entlang y (gestreckt) |
| A (a) | **0°** | kein Unterarm-Dreh |
| B (b) | **0°** | Hand gerade |
| C (c) | **0°** | kein Hand-Roll |
| E (e) | **0** | Greifer geschlossen / Referenz |
Diese drei Bedingungen erfüllt der Driver **nach Phase 1 bereits** (verifiziert, s.u.):
→ Resultierende Fingerspitze: **(xMotor, (l1+l2+l3), 0) ≈ (x, 590, 0)**.
| Achse | Nullwert | Bedeutung | Status |
|--------|----------|---------------------------------------------|-------------------------|
| X | (frei) | Schienenposition `xMotor` in mm | — |
| 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° (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)**.
(Beobachtet ~550; Differenz steckt in der l3-Ableitung / Resthandstellung.)
---
## 4. Ist-Zustand vs. Ideal
## 4. Ist-Zustand (nach Phase 1) vs. Ideal
| Aspekt | aktuell (Modell +Y) | ideal (Y) |
|----------------------------|---------------------------|-------------------|
| α=0 zeigt nach | +y (FK: y=+410) | y (y=410) |
| Grundstellung Y (α) | ≈175° (=1804,5) | **0°** |
| gerade Hand B (b) | 180° | **0°** |
| neutraler Roll C (c) | 90° (posenabhängig) | **0°** |
| G92 der Grundstellung → y | ≈ +590 | **590** |
| Aspekt | nach Phase 1 (jetzt) | Ideal (nach Phase 2) |
|------------------------------|-----------------------------|----------------------|
| α=0 zeigt nach | **y** ✅ | y |
| β=0 Unterarm | **y** ✅ | y |
| a=0 Hand-Knick-Achse | **∥ x** ✅ | ∥ x |
| G92 der Grundstellung → y | **590** ✅ | ≈ 590 |
| gerade Hand | **b = 180°** | **b = 0°** |
| neutraler Roll | **c: ψ = 90° C** (posenabh.) | **c = 0°** |
Verifiziert per FK: `α=β=0 → y=+410`, `α=β=180 → y=410`. Die +Y/Y-Spiegelung
entspricht `α→180α, β→180β`.
Verifiziert per FK: `FK(α=0, β=0, gerade Hand) → (0, 590, 0)`; bei `a=0` bleibt
`Fingerspitze.x = xMotor` konstant, während b variiert (Knick in der y-z-Ebene).
---
## 5. Schritte, um das zu erreichen (Weg 2)
## 5. Schritte (Weg 2)
Reihenfolge nach Workflow: **erst Tests (rot), dann Code, dann grün.**
### Phase 1 — y-Flip (behebt den gemeldeten Bug)
### Phase 1 — y-Flip ✅ ERLEDIGT (am Roboter verifiziert)
1. **Tests schreiben** (zunächst rot):
- G92 der Grundstellung (α=β=a=b=c=0) → erwartet **y ≈ 590** (statt +590).
- Round-Trip: `IK(x=0, y=550, z, φ, θ)``FK` → identische Pose zurück.
2. **`Arm3SegmentLinearX` umstellen** (α/β von y aus messen):
- **FK** (`calculatePositionFromMotorAngles`): y-Komponenten von `vecBizeps` und
`vecUnterarm` negieren; Handgelenk-Vektoren (`n`, `vHand`) und `φ = atan2(vHand.y,…)`
konsistent nachziehen.
- **IK** (`calculateAngles3D`): `pY`, `gamma = atan2(pZ, pY)`, die `n`-Konstruktion
und `φ` entsprechend spiegeln.
3. **Bestehende +Y-Tests auf Y migrieren** (erwartete y-Werte spiegeln).
4. **`doc/Info_G92.md`** aktualisieren: Y/Z-Konvention auf „0° = waagerecht entlang y".
5. Suite grün; Round-Trip + Grundstellung grün.
Umgesetzt:
- **Spiegelung an der x-z-Ebene** in `Arm3SegmentLinearX` (`_mirrorWorkspaceY`): die
interne Mathematik (`_ikPlusY`/`_fkPlusY`) rechnet weiter in +y, die öffentlichen
Methoden spiegeln die Workspace-Pose (y, pY, φ, ψ; θ bleibt) → α=0 zeigt nach y.
- **G28** (Home) auf y umgestellt: `y = -(l1+l2+l3)`, `phi = +π/2`.
- **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 — Handgelenk auf Null (optional, für echte All-Zero-Grundstellung)
### Phase 2 — Handgelenk-/Finger-Nullstellung (B, C) — OFFEN
6. **B-Konvention drehen:** gerade Hand = **0°** statt 180° (`b → 180°b` an der
Schnittstelle; FK/IK + Greifer-Kopplung `eMotor = e b c` mit anpassen).
7. **C-Nullpunkt:** neutral = **0°** statt 90°.
⚠️ Der C↔ψ-Offset ist **posenabhängig** (`acos(cos β · sin a)`); ein global sauberes
`c=0=neutral` braucht ggf. eine tiefere Umparametrierung des Handgelenks — **vor**
der Umsetzung bewerten.
8. Tests + `Info_G92.md` nachziehen.
> **Voraussetzung (User):** Erst Visualisierung/Überprüfung der Finger, dort werden noch
> Fehler vermutet. **a-Achse ist bereits korrekt** (a=0 → Knick-Achse ∥ x) — Phase 2
> betrifft nur **B (Knick)**, **C (Roll)** und die **Greifer-Kopplung**.
1. **Finger/Hand visualisieren** und gegen die echte Mechanik prüfen (User).
2. **B-Konvention:** gerade Hand soll **0°** sein (derzeit 180°; physischer Knick = 180° B).
Mapping festlegen (z.B. `b → 180° b` an der Schnittstelle) und FK/IK anpassen.
3. **C-Nullpunkt:** neutral soll **0°** sein (derzeit `ψ = 90° C`).
⚠️ Der C↔ψ-Bezug ist **posenabhängig** (`acos(cos β · sin a)`); ein global sauberes
`c=0 = neutral` braucht ggf. eine tiefere Umparametrierung — **vor** der Umsetzung bewerten.
4. **Greifer-Kopplung** `eMotor = e b c` prüfen: mischt mm (e) mit Radiant (b, c)
— gegen die echte Sehnen-Mechanik validieren.
5. Tests + `Info_G92.md` nachziehen.
### Verifikation (Definition of Done)
- G92 der Grundstellung (alle Winkel 0) → Driver meldet **y ≈ 590, z ≈ 0**.
- appRobotHoming kann die **physisch gemessenen Winkel direkt** senden (ohne Spiegelung).
- Volle Test-Suite grün, inkl. Round-Trip und Grundstellungs-Tests.
- **Phase 1 (erfüllt):** G92 der Grundstellung → Driver `y ≈ 590, z ≈ 0`; appRobotHoming
sendet die gemessenen α/β/a **direkt** (ohne Spiegelung); volle Suite grün.
- **Phase 2 (Ziel):** Grundstellung mit **allen** Gelenkwinkeln 0 (inkl. B=C=0); Finger
visuell korrekt.
---
## Anhang: Stand der bisherigen Arbeit
## Anhang: Stand der Kinematik
- Der atan2-Fix in der IK (`gamma = Math.atan2(pZ, pY)`, [Arm3SegmentLinearX.js:59](../robot/kinematics/Arm3SegmentLinearX.js#L59))
ist umgesetzt und macht die IK mathematisch für Y-Eingaben korrekt — eine
Voraussetzung für Phase 1. Er betrifft **nicht** den G92/FK-Pfad.
- Die Winkel-Konventionen (B/C/E) sind in [doc/Info_G92.md](Info_G92.md) dokumentiert;
die Y/Z-Konvention dort wird in Phase 1, Schritt 4 angepasst.
- **y-Flip (Phase 1):** Spiegelung in `Arm3SegmentLinearX` (`_mirrorWorkspaceY`, genutzt von
`calculateAngles3D` und `calculatePositionFromMotorAngles`). Am Roboter bestätigt.
- **atan2-Fix** in der IK (`gamma = Math.atan2(pZ, pY)`): macht die interne IK für y
mathematisch korrekt — Voraussetzung des y-Flips.
- **Winkel-Konventionen** (Y/Z/A/B/C/E) sind in [doc/Info_G92.md](Info_G92.md) dokumentiert
und nach Phase 1 aktualisiert.