# ToDo 6a — Speed-Steuerung ## Ist-Zustand und Defizite Die Feedrate-Logik ist auf zwei Ebenen defekt: ### Ebene 1: `calculateSpeeds()` berechnet NaN `Robot.calculateSpeeds(oldPos, newPos)` liest `oldPos.xMotor`, `oldPos.alpha`, `oldPos.beta` — diese Properties existieren in `RobotMotorPosition` **nicht**. Dort heißen sie `x`, `y`, `z`. Ergebnis: alle `motorSpeeds`-Werte sind `NaN`. Die Methode ist seit ihrer Einführung kaputt und fällt nur nicht auf, weil `ROBOT_USE_SPEED_CALC` standardmäßig `false` ist. ```js // Zeile ~214 Robot.js — falsch: this.motorSpeeds.x = (this.xMotor - oldPos.xMotor) / time; // oldPos.xMotor → undefined this.motorSpeeds.y = (this.alpha - oldPos.alpha) / time; // oldPos.alpha → undefined this.motorSpeeds.z = (this.beta - oldPos.beta) / time; // oldPos.beta → undefined // Richtig (RobotMotorPosition-Felder): this.motorSpeeds.x = (this.xMotor - oldPos.x) / time; this.motorSpeeds.y = (this.alpha - oldPos.y) / time; this.motorSpeeds.z = (this.beta - oldPos.z) / time; ``` ### Ebene 2: `motorSpeeds` werden von keinem Sender gelesen Selbst wenn `calculateSpeeds()` korrekte Werte lieferte, ignorieren beide Sender die berechneten Geschwindigkeiten vollständig: - **`TelnetSenderGRBL`**: sendet `mNew.feedrate` (Kartesisch, mm/min) an alle Achsen gleich - **`WSSenderGrbl`**: sendet immer `this.maxSpeedF`, ignoriert `mNew.feedrate` komplett Das ist grundsätzlich falsch: Der Roboter hat drei unabhängige GRBL-Controller. Damit die Fingerspitze mit `F1000 mm/min` fährt, muss jede Achse mit einer **anderen** Winkelgeschwindigkeit drehen — je nachdem, wie weit sie sich für diesen Schritt bewegt. Alle Achsen mit derselben Feedrate zu befehlen führt zu nicht-linearen Werkzeugbahnen. ### Ebene 3: `FPoint` kodiert Feedrate hart ```js // GCode.js FPoint — immer f1000, egal was robot.feedrate ist: var strGCode = `G90 G1 x${robot.x} ... f1000` ``` --- ## Konzept: Korrekte Feedrate-Verteilung Gesamtziel: Die Kartesische Feedrate `F` (mm/min) des G-Code-Befehls bestimmt, wie lange der Bewegungsschritt dauert. Diese Zeit wird auf alle Achsen aufgeteilt. ``` Zeit = kartesische_Distanz / F_mm_per_min Achsen-Feedrate[i] = Achsen-Delta[i] / Zeit (in Grad/min, da GRBL-Achsen typisch in Grad konfiguriert) ``` Der Sender kennt seine Achse (`xAxisGrbl`, `yAxisGrbl`, `zAxisGrbl`) und kann die passende Geschwindigkeit aus `motorPosition.speeds` lesen, wenn diese korrekt befüllt sind. --- ## Pakete ### Paket 1: `calculateSpeeds()` reparieren - [ ] Property-Namen korrigieren: `oldPos.x/y/z/a/b/c/e` statt `oldPos.xMotor/alpha/beta/...` - [ ] Einheit klären: `motorSpeeds` in rad/min oder direkt in Grad/min? - Sender wandeln Motorwinkel bereits von rad → Grad (`* 180/π`) für Positionen - Einheitlichste Lösung: `motorSpeeds` ebenfalls in Grad/min speichern, oder die Umrechnung konsistent im Sender vornehmen - [ ] Grenzfall: Wenn kartesische Distanz null ist (reine Gelenkbewegung), Distanz über Handgelenk-Punkt verwenden — das ist bereits so angelegt, aber mit den falschen Property-Namen - [ ] Unit-Test: `calculateSpeeds` mit bekannten Werten, prüfen dass keine NaN entstehen ### Paket 2: `motorSpeeds` in den Sender durchreichen - [ ] `motorPosition.speeds` korrekt befüllen (passiert bereits via `this.motorPosition.speeds = {...this.motorSpeeds}` nach `calculateSpeeds`) - [ ] Sender-Interface (`execCommand`): wenn `ROBOT_USE_SPEED_CALC` aktiv und `motorPosition.speeds[achse]` verfügbar und > 0 → diesen Wert als `F` verwenden - [ ] Jeder Sender (`TelnetSenderGRBL`, `WSSenderGrbl`) kennt seine Achse und holt die passende Geschwindigkeit aus `speeds`: ```js // Beispiel: Sender ist für Achse "a" zuständig const f = (useSpeedCalc && mNew.speeds.a > 0) ? mNew.speeds.a : mNew.feedrate; data += ` f${f.toFixed(2)}`; ``` - [ ] Fallback: wenn `ROBOT_USE_SPEED_CALC=false` oder Speeds nicht berechnet → `mNew.feedrate` wie bisher (Rückwärtskompatibilität) ### Paket 3: `WSSenderGrbl` Feedrate-Handling vereinheitlichen - [ ] `WSSenderGrbl.execCommand()` auf dasselbe Feedrate-Schema wie `TelnetSenderGRBL` umstellen - aktuell: immer `this.maxSpeedF` → ignoriert das `F` aus dem G-Code-Befehl komplett - richtig: `mNew.feedrate` verwenden (bzw. per-Achse aus Paket 2) - [ ] `maxSpeedF` als Obergrenze behalten (Clamp), nicht als feste Ausgabe ### Paket 4: Kleinere Korrekturen - [ ] `FPoint` in `GCode.receiveFC()`: `robot.feedrate` statt hardcodierten `1000` speichern - [ ] `ROBOT_USE_SPEED_CALC`-Flag dokumentieren: was ändert sich mit/ohne Flag? Klarer Kommentar in `Robot.js` und im README ## Betroffene Dateien - `robot/Robot.js` — `calculateSpeeds()` bugfix, Einheitenwahl - `robot/RobotMotorPosition.js` — ggf. `speeds`-Feld klarer benennen - `robot/TelnetSenderGRBL.js` — per-Achse Feedrate aus `speeds` - `robot/WSSenderGrbl.js` — Feedrate auf `mNew.feedrate` umstellen + Clamp - `robot/GCode.js` — `FPoint`: `robot.feedrate` statt `1000` - `test/GCode.speed.test.js` — Tests für NaN-freie Berechnung ergänzen