Files
appRobotDriver/doc/ToDo_12_InverseKinematikConfig_ROADMAP.md
2026-06-11 07:57:51 +02:00

180 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Roadmap 12 — Austauschbare Kinematik
## Ziel
Der `appRobotDriver` soll als generischer G-Code-Treiber für beliebige Roboterarme
funktionieren. Die Kinematik ist der einzige arm-spezifische Teil — alles andere
(G-Code, WebSocket, Sender, Konfiguration) ist roboter-unabhängig.
## Konventionen (festgelegt)
- **Workspace-Koordinaten:** `x, y, z, phi, theta, psi` + `e` (Greifer) — gilt für alle
6-DOF-Arme mit Greifer. Das ist der implizite Vertrag dieses Frameworks.
- **Motoranzahl:** 7 Slots (`x, y, z, a, b, c, e` in `RobotMotorPosition`) bleiben fest.
Das Framework ist für 6-DOF + Greifer ausgelegt.
- **Ordner:** `robot/kinematics/` (nicht `inverseKinematics/` — enthält beide Richtungen)
- **Klassenname:** beschreibend für die physikalische Struktur, z. B. `Arm3SegmentLinearX`
---
## Dateistruktur (Zielzustand)
```
robot/
├── RobotBase.js ← Interface + generische Infrastruktur
├── KinematicsFactory.js ← lädt Kinematik-Klasse anhand Env-Variable
└── kinematics/
├── Arm3SegmentLinearX.js ← aktuelle Implementierung
└── <NextRobot>.js ← zukünftige Implementierungen
```
> **Update (umgesetzt):** Der ursprünglich geplante dauerhafte Kompatibilitäts-Alias
> `robot/Robot.js` wurde **nicht** beibehalten, sondern nach Abschluss von Phase 02
> **entfernt**. Tests importieren direkt `kinematics/Arm3SegmentLinearX` bzw.
> `RobotBase`; Produktivcode geht über `KinematicsFactory`. Die folgenden Abschnitte
> zur Transition über `Robot.js` sind daher historisch.
## Wo ist das Interface?
**`RobotBase` ist das Interface** — als abstrakte Basisklasse (JavaScript-Idiom).
Es definiert zwei Dinge:
1. Die **Infrastruktur**, die jede Implementierung erbt (State, `sendCommand`, ...)
2. Den **Vertrag**: zwei Methoden, die jede Implementierung überschreiben *muss*
`RobotBase` ist die einzige Klasse, die alle anderen Module (`GCode.js`, `InputWS.js`,
Sender, ...) kennen müssen. Sie importieren nie eine konkrete Kinematik.
## Wie greift der restliche Code auf den Roboter zu?
**Heute und während der Transition:**
```
startRobot.js → require('./robot/Robot') → Robot.js (Alias) → Arm3SegmentLinearX
GCode.js → bekommt robot als Parameter → sieht nur RobotBase-Methoden
InputWS.js → bekommt robot als Parameter → sieht nur RobotBase-Methoden
```
`GCode.js`, `InputWS.js` und alle Sender erhalten `robot` bereits als Parameter —
sie importieren `Robot.js` gar nicht. **Für sie ändert sich nichts.**
**Langfristig (nach Abschluss Phase 2):**
```
startRobot.js → KinematicsFactory → instantiiert Arm3SegmentLinearX (oder anderen)
robot/Robot.js → dauerhafter Alias für RobotBase (nicht mehr für eine Implementierung)
```
`robot/Robot.js` bleibt erhalten, zeigt aber auf `RobotBase`:
```js
// robot/Robot.js — dauerhaft
module.exports = require('./RobotBase');
```
Damit kann externer Code weiterhin `require('./robot/Robot')` schreiben und bekommt
die Basisklasse — z. B. für `instanceof`-Checks oder zum Ableiten in Tests.
---
## Phase 0 — `RobotBase` und Interface-Vertrag
`Robot.js` enthält heute zwei Dinge: generische Infrastruktur und arm-spezifische Kinematik.
Der Schnitt:
**`RobotBase` (generisch, nie überschreiben):**
- Zustandsvariablen: `x, y, z, phi, theta, psi, e, feedrate, moveRelative`
- Motor-Zustand: `xMotor, alpha, beta, a, b, c, eMotor` + Changed-Flags
- `sendCommand()`, `createMotorPosition()`, `calculateSpeeds()`
- `cmdReceivers`, `savedPoints`
**Interface-Vertrag (abstrakt, muss überschrieben werden):**
```js
calculateAngles3D() // Workspace → Motorwinkel (schreibt auf this.*)
calculatePositionFromMotorAngles() // Motorwinkel → Workspace (schreibt auf this.*)
```
- [x] `robot/RobotBase.js` anlegen — generische Infrastruktur aus `Robot.js`
- [x] Beide Kinematik-Methoden in `RobotBase` als Stub mit `throw new Error('not implemented')`
- [x] JSDoc: Interface-Vertrag dokumentieren
- [x] `rotateAroundAxis()` wandert in `RobotBase` als geschützte Hilfsmethode
---
## Phase 1 — `Arm3SegmentLinearX` als erste Implementierung
```
robot/
├── RobotBase.js
└── kinematics/
└── Arm3SegmentLinearX.js ← bisheriger Robot.js-Kinematik-Teil
```
- [x] `robot/kinematics/Arm3SegmentLinearX.js` anlegen
- `class Arm3SegmentLinearX extends RobotBase`
- Konstruktor: `constructor(l1, l2, l3)``super()` + Längen
- `calculateAngles3D()` — unverändert übernommen
- `calculatePositionFromMotorAngles()` — unverändert übernommen
- [x] `robot/Robot.js` wurde zunächst Kompatibilitäts-Alias und anschließend
**entfernt** (siehe Update-Hinweis oben). Tests importieren direkt
`./kinematics/Arm3SegmentLinearX`.
- [x] Alle bestehenden Tests müssen grün bleiben — kein Verhalten ändert sich
(`Robot.Kinematics.RoundTrip.test.js` ist das primäre Sicherheitsnetz)
---
## Phase 2 — Konfiguration über Umgebungsvariable
```yaml
# docker-compose.yml
environment:
ROBOT_KINEMATICS: arm3segmentlinearx
ROBOT_KINEMATICS_PARAMS: '{"l1": 250, "l2": 264, "l3": 100}'
```
- [x] `ROBOT_KINEMATICS` — Bezeichner der Kinematik-Klasse (Default: `arm3segmentlinearx`)
- [x] `ROBOT_KINEMATICS_PARAMS` — JSON mit Konstruktor-Parametern
- [x] `KinematicsFactory.js` (`createRobotFromEnv`), eingebunden in `startRobot.js`:
```js
const robot = createRobotFromEnv(processEnv, { l1: 250, l2: 264, l3: 100 });
```
- [x] Unbekannte Kinematik → klare Fehlermeldung beim Start, kein silent fail
- [ ] Neue Variablen ins zentrale Config-Modul aufnehmen (koordinieren mit `ToDo_3_Config`)
→ **offen:** `ToDo_3_Config` ist noch nicht umgesetzt. Die Factory liest `process.env`
vorerst direkt (gleicher Stil wie `ROBOT_DEFAULT_FEEDRATE`); das zentrale Config-Modul
kann die beiden Variablen später übernehmen. In `docker-compose.yaml` bereits dokumentiert.
---
## Phase 3 — Zweite Kinematik-Implementierung
Erst wenn ein konkreter zweiter Roboter definiert ist.
**Umgesetzt: Joy-IT „Grab-It" (Robot02)** als `Arm3SegmentRotaryBase`.
- [x] Physikalische Spezifikation dokumentieren (DOF, Achsen, Gelenkreihenfolge)
→ im JSDoc von `robot/kinematics/Arm3SegmentRotaryBase.js`. 5 Achsen + Greifer:
Basis-Yaw, Schulter, Ellbogen, Handgelenk-Pitch, Handgelenk-Roll, Greifer.
- [x] `robot/kinematics/Arm3SegmentRotaryBase.js` anlegen — nur die zwei Kinematik-Methoden
- [x] RoundTrip-Tests für die neue Implementierung schreiben
(`test/Robot.GrabIt.RoundTrip.test.js`)
- [x] Prüfen ob die 7 Motor-Slots ausreichen → **ja**: 6 Slots belegt
(`xMotor, alpha, beta, a, b, eMotor`), `c` bleibt frei. `RobotMotorPosition`
unverändert.
- [x] In `KinematicsFactory` registriert (Bezeichner `arm3segmentrotarybase`,
Aliase `grabit` / `robot02`). Factory reicht jetzt das vollständige
`params`-Objekt als 4. Konstruktor-Argument durch (für `baseHeight`).
**Offene Punkte / Annahmen (kein Blocker, aber vor Echtbetrieb zu klären):**
- ⚠️ **5-DOF-Constraint:** `phi` (Hand-Azimut) ist an die Position gekoppelt
(= Basis-Drehung) und nicht frei. In der Inversen aus `atan2(y,x)` abgeleitet.
- ⚠️ **Segmentlängen sind Schätzwerte** (l1=105, l2=98, l3=100, baseHeight=110 mm),
abgeleitet aus Reichweite (300 mm) / Höhe (420 mm). Vor Echtbetrieb am Arm
messen und per `ROBOT_KINEMATICS_PARAMS` setzen.
- ⚠️ **Gelenkmodell** (Pitch/Roll am Handgelenk) folgt der Standardkonfiguration
dieser Arm-Klasse; gegen das physische Gerät / die Kalibrieranleitung prüfen.
---
## Abhängigkeiten
- Phase 1 ist unabhängig von allen anderen ToDos — kann sofort angegangen werden
- Phase 2 koordiniert mit `ToDo_3_Config`
- Phase 3 hat keine zeitliche Vorgabe — wird bei Bedarf aufgenommen