# ToDo 6a — Speed-Steuerung > ## ✅ Status: erledigt (Pakete 1–2) > > - **Schalter `ROBOT_SPEED_MODE`** (`legacy` Default = exakt wie bisher, `correct` = koordiniert) > - **`calculateSpeeds()`-NaN-Bug behoben** + `moveTime` eingeführt > - **Koordinierte Feedrate** im `TelnetSenderGRBL` (Korrekt-Modus), Legacy-Pfad unangetastet > - Tests: `test/Robot.calculateSpeeds.test.js`, `test/Sender.Telnet.speedMode.test.js`, > `test/Speed.coordination.test.js` > > **Offen:** WS-Sender (Paket 3), `FPoint`-Feedrate (Paket 4 → ToDo_6b). ## Der Schalter: `ROBOT_SPEED_MODE` Die Speed-Regelung ist über **eine einzige Umgebungsvariable** umschaltbar: | Wert | Bedeutung | |---|---| | `legacy` (Default) | **Alte Speed-Regelung — alles läuft exakt wie bisher.** Jeder Sender sendet die kartesische Feedrate `F` (aus dem G-Code bzw. `robot.feedrate`) unverändert an alle seine Achsen. | | `correct` | **Korrekte Speed-Regelung.** Jeder Sender erhält eine eigene, koordinierte Feedrate, sodass alle Controller die Bewegung gleichzeitig beenden. | ```yaml # docker-compose.yml environment: ROBOT_SPEED_MODE: legacy # oder: correct ``` ### Garantie für `legacy` Im Legacy-Modus wird der **Sende-Pfad der Sender nicht angefasst**. Die Feedrate-Zeile entsteht über exakt denselben Code wie bisher. Die bestehenden Sender-Tests (`Sender.Telnet.test.js`, `Sender.Telnet.caseBackward.test.js`) prüfen die Ausgabe zeichengenau und sind das Sicherheitsnetz: solange sie grün sind, ist Legacy byte-identisch zu vorher. Der Korrekt-Modus fügt **nur zusätzlich** eine alternative Feedrate-Berechnung hinzu, die ausschließlich greift, wenn `ROBOT_SPEED_MODE=correct`. ### Abgrenzung zu `ROBOT_USE_SPEED_CALC` `ROBOT_USE_SPEED_CALC` bleibt der interne Schalter dafür, ob `Robot.calculateSpeeds()` überhaupt rechnet. Der Korrekt-Modus aktiviert diese Berechnung automatisch. `ROBOT_USE_SPEED_CALC` allein ändert **nicht** die Sender-Ausgabe — nur `ROBOT_SPEED_MODE=correct` tut das. Damit bleibt bestehendes Verhalten erhalten. --- ## Hintergrund: Warum die alte Regelung falsch ist Der Roboter hat drei unabhängige GRBL/FluidNC-Controller. Im Legacy-Modus bekommen alle dieselbe kartesische Feedrate (z. B. `f1000`) — unabhängig davon, wie weit sich die jeweilige Achse in diesem Schritt tatsächlich bewegt. Folge: die Achse mit kurzer Bewegung ist viel früher fertig als die mit langer Bewegung, die Bewegungen laufen nicht koordiniert, und die Werkzeugbahn der Fingerspitze ist nicht linear. **Korrekt:** Die kartesische Feedrate `F` bestimmt die Gesamt-Zeit des Schritts: ``` Bewegungszeit = kartesische_Distanz / F Sender-Feedrate = Distanz_dieses_Senders / Bewegungszeit ``` So braucht jeder Controller dieselbe Zeit → die Bewegungen sind koordiniert. --- ## Behobene Defizite (Implementierung dieses ToDos) ### 1. `calculateSpeeds()` berechnete NaN — behoben `Robot.calculateSpeeds()` las `oldPos.xMotor`, `oldPos.alpha`, `oldPos.beta` — Felder, die in `RobotMotorPosition` nicht existieren (dort `x`, `y`, `z`). Ergebnis: `NaN`. Korrigiert auf `oldPos.x/y/z`. Zusätzlich speichert die Methode jetzt die `Bewegungszeit` (`moveTime`), die der Sender für die koordinierte Feedrate braucht. ### 2. Koordinierte Feedrate im Sender (Korrekt-Modus) `TelnetSenderGRBL` berechnet im Korrekt-Modus die Feedrate als `Distanz_dieses_Senders / moveTime`. Die Distanz wird über `portValue()` ermittelt — eine reine Funktion, die für jede (GRBL-Port, Roboter-Achse)-Zuordnung den gesendeten Wert liefert (dieselben Formeln wie der Sende-Pfad, aber als isolierte, testbare Funktion). --- ## Pakete ### Paket 1: `calculateSpeeds()` reparieren — ✅ ERLEDIGT - [x] Property-Namen korrigiert: `oldPos.x/y/z/a/b/c/e` - [x] `moveTime` berechnen und auf `motorPosition` ablegen - [x] Unit-Tests: NaN-Freiheit, exakte Werte, Handgelenk-/Finger-Zweige, Guards (`test/Robot.calculateSpeeds.test.js`) ### Paket 2: Schalter + koordinierte Feedrate — ✅ ERLEDIGT - [x] `ROBOT_SPEED_MODE`-Schalter (`legacy` Default, `correct`) - [x] `TelnetSenderGRBL`: koordinierte Feedrate im Korrekt-Modus, Legacy-Pfad unverändert - [x] `portValue()` als isolierte Funktion, per Kreuzprobe gegen den echten Sende-Pfad getestet - [x] Korrekt-Modus-Tests + Koordinations-Invariante über alle drei Sender (`test/Sender.Telnet.speedMode.test.js`, `test/Speed.coordination.test.js`) ### Paket 3: `WSSenderGrbl` — ⬜ OFFEN (Folge-Schritt) - [ ] `WSSenderGrbl` auf denselben Schalter + koordinierte Feedrate bringen - WS-Sender ist aktuell nicht in `startRobot.js` aktiv → kein Produktiv-Risiko - Legacy-Verhalten des WS-Senders zunächst unverändert lassen ### Paket 4: Aufräumen — ⬜ OFFEN (Folge-Schritt) - [ ] `FPoint` in `GCode.receiveFC()`: `robot.feedrate` statt hardcodiertem `1000` → gehört thematisch zur Datei-Logik, wird in **ToDo_6b** behandelt - [ ] `portValue()` perspektivisch auch im Sende-Pfad nutzen (Dedupe der Formeln); bewusst aufgeschoben, um den Legacy-Pfad in diesem Schritt nicht anzufassen ### Bekannte Grenze des Korrekt-Modus v1 Die koordinierte Feedrate bildet die euklidische Norm über die Achsen eines Senders. Mischt ein Sender lineare (mm) und winklige (Grad) Achsen, mischt die Norm diese Einheiten — dieselbe Vereinfachung, die GRBL bei gemischten Achsen ohnehin macht. Für eine spätere, einheiten-saubere Behandlung ggf. eigenes ToDo. ## Betroffene Dateien - `robot/Robot.js` — `calculateSpeeds()` Fix, `moveTime`, Schalter→`useSpeedCalc` - `robot/RobotMotorPosition.js` — `moveTime`-Feld - `robot/TelnetSenderGRBL.js` — `portValue()`, koordinierte Feedrate, Schalter - `test/Robot.calculateSpeeds.test.js` — neu - `test/Sender.Telnet.speedMode.test.js` — neu