Phase1 Koordinaten
This commit is contained in:
@@ -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° (=180−4,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.
|
||||
|
||||
Reference in New Issue
Block a user