7.7 KiB
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, einRobotMotorPosition) bleiben fest. Das Framework ist für 6-DOF + Greifer ausgelegt. - Ordner:
robot/kinematics/(nichtinverseKinematics/— 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.jswurde nicht beibehalten, sondern nach Abschluss von Phase 0–2 entfernt. Tests importieren direktkinematics/Arm3SegmentLinearXbzw.RobotBase; Produktivcode geht überKinematicsFactory. Die folgenden Abschnitte zur Transition überRobot.jssind daher historisch.
Wo ist das Interface?
RobotBase ist das Interface — als abstrakte Basisklasse (JavaScript-Idiom).
Es definiert zwei Dinge:
- Die Infrastruktur, die jede Implementierung erbt (State,
sendCommand, ...) - 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:
// 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):
calculateAngles3D() // Workspace → Motorwinkel (schreibt auf this.*)
calculatePositionFromMotorAngles() // Motorwinkel → Workspace (schreibt auf this.*)
robot/RobotBase.jsanlegen — generische Infrastruktur ausRobot.js- Beide Kinematik-Methoden in
RobotBaseals Stub mitthrow new Error('not implemented') - JSDoc: Interface-Vertrag dokumentieren
rotateAroundAxis()wandert inRobotBaseals geschützte Hilfsmethode
Phase 1 — Arm3SegmentLinearX als erste Implementierung
robot/
├── RobotBase.js
└── kinematics/
└── Arm3SegmentLinearX.js ← bisheriger Robot.js-Kinematik-Teil
robot/kinematics/Arm3SegmentLinearX.jsanlegenclass Arm3SegmentLinearX extends RobotBase- Konstruktor:
constructor(l1, l2, l3)→super()+ Längen calculateAngles3D()— unverändert übernommencalculatePositionFromMotorAngles()— unverändert übernommen
robot/Robot.jswurde zunächst Kompatibilitäts-Alias und anschließend entfernt (siehe Update-Hinweis oben). Tests importieren direkt./kinematics/Arm3SegmentLinearX.- Alle bestehenden Tests müssen grün bleiben — kein Verhalten ändert sich
(
Robot.Kinematics.RoundTrip.test.jsist das primäre Sicherheitsnetz)
Phase 2 — Konfiguration über Umgebungsvariable
# docker-compose.yml
environment:
ROBOT_KINEMATICS: arm3segmentlinearx
ROBOT_KINEMATICS_PARAMS: '{"l1": 250, "l2": 264, "l3": 100}'
ROBOT_KINEMATICS— Bezeichner der Kinematik-Klasse (Default:arm3segmentlinearx)ROBOT_KINEMATICS_PARAMS— JSON mit Konstruktor-ParameternKinematicsFactory.js(createRobotFromEnv), eingebunden instartRobot.js:const robot = createRobotFromEnv(processEnv, { l1: 250, l2: 264, l3: 100 });- Unbekannte Kinematik → klare Fehlermeldung beim Start, kein silent fail
- Neue Variablen ins zentrale Config-Modul aufnehmen (koordinieren mit
ToDo_3_Config) → offen:ToDo_3_Configist noch nicht umgesetzt. Die Factory liestprocess.envvorerst direkt (gleicher Stil wieROBOT_DEFAULT_FEEDRATE); das zentrale Config-Modul kann die beiden Variablen später übernehmen. Indocker-compose.yamlbereits dokumentiert.
Phase 3 — Zweite Kinematik-Implementierung
Erst wenn ein konkreter zweiter Roboter definiert ist.
Umgesetzt: Joy-IT „Grab-It" (Robot02) als Arm3SegmentRotaryBase.
- 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. robot/kinematics/Arm3SegmentRotaryBase.jsanlegen — nur die zwei Kinematik-Methoden- RoundTrip-Tests für die neue Implementierung schreiben
(
test/Robot.GrabIt.RoundTrip.test.js) - Prüfen ob die 7 Motor-Slots ausreichen → ja: 6 Slots belegt
(
xMotor, alpha, beta, a, b, eMotor),cbleibt frei.RobotMotorPositionunverändert. - In
KinematicsFactoryregistriert (Bezeichnerarm3segmentrotarybase, Aliasegrabit/robot02). Factory reicht jetzt das vollständigeparams-Objekt als 4. Konstruktor-Argument durch (fürbaseHeight).
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 ausatan2(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_PARAMSsetzen. - ⚠️ 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