Files
appRobotDriver/doc/ToDo_12_InverseKinematikConfig_ROADMAP.md

157 lines
6.2 KiB
Markdown

# 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
├── Robot.js ← dauerhafter Alias: module.exports = RobotBase
├── KinematicsFactory.js ← lädt Kinematik-Klasse anhand Env-Variable
└── kinematics/
├── Arm3SegmentLinearX.js ← aktuelle Implementierung
└── <NextRobot>.js ← zukünftige Implementierungen
```
## 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` wird zum Kompatibilitäts-Alias für die Übergangsperiode:
```js
module.exports = require('./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.
- [ ] Physikalische Spezifikation dokumentieren (DOF, Achsen, Gelenkreihenfolge)
- [ ] `robot/kinematics/<Name>.js` anlegen — nur die zwei Kinematik-Methoden
- [ ] RoundTrip-Tests für die neue Implementierung schreiben
- [ ] Prüfen ob die 7 Motor-Slots ausreichen; falls nicht → `RobotMotorPosition` anpassen
---
## 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