Files
appRobotDriver/doc/Info_Koordinaten.md
2026-06-26 15:28:34 +02:00

14 KiB
Raw Blame History

Koordinatensystem, Roboter-Aufstellung & Nullstellung

Diese Datei beschreibt

  1. das Koordinatensystem,
  2. wie der Roboter darin steht,
  3. die angestrebte ideale Nullstellung und
  4. die Schritte, um den Driver auf diese Konvention zu bringen (Weg 2: Modell auf Y).

Status: Phase 1 (y-Flip) ist umgesetzt und am Roboter verifiziert. α/β werden jetzt von y aus gemessen (α=0 → Arm zeigt nach y), passend zu robot.json und appRobotHoming. Offen ist Phase 2 (Handgelenk-/Finger-Nullstellung, B/C).


1. Koordinatensystem

Aus robot.json (coordinateSystem): rechtshändig, Längen mm, Winkel Grad.

Achse Richtung Bedeutung im Aufbau
x rechts entlang der Linearschiene
y „backward" Arm-Arbeitsrichtung: Arm streckt nach y
z oben Höhe

Seitenansicht (Blick entlang +x, also die y/z-Ebene):

  z
  ▲
  |   ■═══════════════●===========●---o   Arm1 ═══ , Arm2 === und Finger --- waagerecht
  |   Schulter (Ursprung, z=0)             ausgestreckt → Fingerspitze o
  └───────────────────────────────►  y  (Arbeitsrichtung)

In der y/z-Seitenansicht liegen beide Handgelenk-Achsen end-on (als Punkt ●), weil sie entlang x verlaufen:

  • linkes ● = a-Achse (Unterarm-Dreher, zwischen Ellbogen und Arm2)
  • rechtes ● = b-Achse (Hand-Knick-Achse)

Draufsicht (Blick von oben auf z; x nach oben, y nach rechts):

  x
  ▲          │                  │
  │     ■════╪═══ Arm1 ═════════╪══ Arm2 ══----o     o = Fingerspitze (y ≈ 590)
  │   Schulter                                  
  └────────────────────────────────────────────►  y

  │ = a-Achse (Ellbogen↔Arm2) bzw. b-Achse (Hand-Knick).
  Bei a=0 laufen BEIDE parallel zur x-Achse (hier als senkrechte Striche).

2. Wie der Roboter darin steht

Gelenk-Kette (robot.json links): Board → Base (Schiene x) → Arm1 (Oberarm) → Ellbow → Arm2 (Unterarm) → Hand → Palm → Finger.

  • Linearschiene entlang x; die Base fährt darauf (xMotor, mm).
  • Schultergelenk (Base→Arm1, variable y): Drehung in der y-z-Ebene → α. Bei Gelenkwinkel 0 zeigt Arm1 entlang y (skeleton.to = [0,-250,0]).
  • Ellbogen (variable z) → β; Unterarm-Dreher (variable a) → a; Handgelenk-Knick (variable b) → b; Hand-Roll (variable c) → c; Greifer (variable e) → e.
  • z = 0 im Driver-Modell = Schulterachse. (In der Welt liegt sie ~45 mm über dem Brett — robot.json Joint1-Origin z=45 —, der Driver rechnet schulter-relativ.)
  • Armlängen (aus links abgeleitet): l1 = 250 (Oberarm), l2 = 250 (Unterarm), l3 ≈ 90 (Hand). Σ ≈ 590 mm.

3. Ideale Nullstellung (Grundstellung)

Definition (deine Vorgabe):

  1. Arm1 entlang y (Schulter-/y-Gelenk α) → α = 0.
  2. Ellbogen so angewinkelt, dass auch Arm2 entlang y (z-Gelenk β) → β = 0 (β ist absolut, also auch direkt entlang y, nicht relativ zum Oberarm).
  3. a-Achse so gedreht, dass die Hand-Knick-Achse genau in x-Richtung läufta = 0.

Diese drei Bedingungen erfüllt der Driver nach Phase 1 bereits (verifiziert, s.u.):

Achse Nullwert Bedeutung Status
X (frei) Schienenposition xMotor in mm
Y (α) Oberarm waagerecht entlang y Phase 1
Z (β) Unterarm waagerecht entlang y (gestreckt) Phase 1
A (a) Hand-Knick-Achse ∥ x (Code erfüllt es)
B (b) 0° (Ziel) Hand gerade — derzeit ist gerade = 180° Phase 2
C (c) 0° (Ziel) kein Hand-Roll — derzeit neutral ≠ 0 Phase 2
E (e) 0 Greifer geschlossen / Referenz

→ Resultierende Fingerspitze (α=β=a=0, gerade Hand): (xMotor, (l1+l2+l3), 0) ≈ (x, 590, 0). (Beobachtet ~550; Differenz steckt in der l3-Ableitung / Resthandstellung.)


4. Ist-Zustand (nach Phase 1) vs. Ideal

Aspekt nach Phase 1 (jetzt) Ideal (nach Phase 2)
α=0 zeigt nach y y
β=0 Unterarm y y
a=0 Hand-Knick-Achse ∥ x ∥ x
G92 der Grundstellung → y 590 590
gerade Hand b = 180° b = 0°
neutraler Roll c: ψ = 90° C (posenabh.) c = 0°

Verifiziert per FK: FK(α=0, β=0, gerade Hand) → (0, 590, 0); bei a=0 bleibt Fingerspitze.x = xMotor konstant, während b variiert (Knick in der y-z-Ebene).


5. Schritte (Weg 2)

Reihenfolge nach Workflow: erst Tests (rot), dann Code, dann grün.

Phase 1 — y-Flip ERLEDIGT (am Roboter verifiziert)

Umgesetzt:

  • Spiegelung an der x-z-Ebene in Arm3SegmentLinearX (_mirrorWorkspaceY): die interne Mathematik (_ikPlusY/_fkPlusY) rechnet weiter in +y, die öffentlichen Methoden spiegeln die Workspace-Pose (y, pY, φ, ψ; θ bleibt) → α=0 zeigt nach y.
  • G28 (Home) auf y umgestellt und Singularität behandelt: die voll ausgestreckte Stellung (|y| = l1+l2+l3) ist eine Handgelenk-Singularität, in der die IK a/c nicht bestimmen kann (Müll wie a=135°, c=45° → Finger schräg). G28 setzt dort die Motorwerte direkt (alpha=beta=a=c=0, b=π = gerade Hand) und füllt die Pose per FK. Interim: G28 lässt den Greifer (e/eMotor) unangetastet — sonst ergäbe eMotor = ebc bei b=π den Wert 180° → Finger-Anschlag-Slam (Phase 2 behebt das mit b=0).
  • Tests: test/Robot.Kinematics.NegativeY.test.js (Grundstellung 590, Homing-Pose in y, Round-Trip in y, a=0 → Knick-Achse ∥ x).
  • Migration: Robot.02_UpperArm und der G28-Test auf y umgestellt.
  • Doku: doc/Info_G92.md Y/Z (und C/A nach der Spiegelung) aktualisiert.

Phase 2 — Handgelenk-/Finger-Nullstellung (B, C, Greifer) — OFFEN

Voraussetzung (User): Erst die Finger visualisieren/prüfen — dort werden noch Fehler vermutet. a-Achse ist bereits korrekt (a=0 → Knick-Achse ∥ x). Phase 2 betrifft B (Knick), C (Roll) und die Greifer-Kopplung.

Ziel-Konvention: gerade Hand → B = 0° (statt 180°); neutraler Roll → C = 0°; Greifer-Kopplung konsistent und gegen die echte Mechanik kalibriert.

Vorab-Erkenntnis aus dem Code (wichtig!)

Die b/c/e-Konvention ist an mehreren Stellen kodiert, die gemeinsam geändert werden müssen. Invariante: solange die Hardware-Nullpunkte nicht neu kalibriert werden, müssen die an FluidNC gesendeten Port-Werte gleich bleiben — eine reine Modell-Umbenennung darf die Hardware-Bewegung nicht verändern.

Fundstellen:

Datei / Stelle aktuelle Kodierung
Arm3SegmentLinearX._fkPlusY vHand = rotate(vecUnterarm, n, b); psi = c acos(n.z)b=π = gerade
Arm3SegmentLinearX._ikPlusY b = acos(cosB) (∈[0,π]); c = acos(cosC) + psic hat posenabh. Offset
Arm3SegmentLinearX.gripperMotorFromOpening eMotor = e b c (b,c in rad) — Greifer-Kopplung #1
RobotController G92/M92 b = B/D, c = C/D, eMotor = gripperMotorFromOpening(e)
RobotController M1 b += B, c += C (relativer Motor-Jog)
RobotController G28 b = π, c = 0 (Phase 1) → nach B-Umstellung auf b = 0 ändern
portInverse.js b = hand.z/D, c = hand.x/D + b (Port→Motor, Hardware-Sync)
TelnetSenderGRBL.execCommand / portValue Hand-Ports: z = b·D, x = (cb)·D; e-Port mit factorTurnLift=1.2Greifer-Kopplung #2

Aufgaben

  1. Finger visualisieren (User) → Soll-Bild, gegen das kalibriert wird.

  2. Greifer-Kopplung — aktuell aktiv (identifiziert): Bei der realen Verkabelung (hand.axes = ['c','e','b']) liegt der Greifer auf dem y-Port. Gesendet wird daher mNew.e · D = eMotor · D = (e b c) · D — die Kopplung steckt in gripperMotorFromOpening (→ eMotor), der Sender hängt nur noch ·180/π dran.

    • Die x-Port-Variante (e + 1.2·b·D c·D, mit factorTurnLift = 1.2) greift nur bei anderer Verkabelung → derzeit toter Pfad. (factorOpenTurn = 1.92 ungenutzt.)
    • Folge / Slam: bei b = π (Phase-1-„gerade Hand") wird eMotor = ebc = −π → 180° an den Finger-Motor gesendet → er fährt an den Anschlag und verdreht über die Sehne die ganze Hand. Phase 2 (b = 0 = gerade) behebt das automatisch (eMotor = 0).
    • Aufgabe: Kopplung gegen die echte Sehnenmechanik validieren, toten x-Port-Pfad + factorOpenTurn aufräumen, Vorzeichen je nach Motor-Verkabelung prüfen.
  3. B-Konvention (gerade = 0°).

    Verifizierter Mismatch Homing ↔ Driver: Das appRobotHoming meldet für die gerade Hand B ≈ 0 (Messung: B=-6.92), der Driver rechnet aber mit gerade = b = 180°. Der Driver interpretiert das empfangene b≈0 daher als Knick 1800 = 180° (fast voll zurückgeklappt) → im Modell zeigt der Finger nach +y (rückwärts) statt y. D.h. nach dem Homing ist der interne Hand-Zustand des Drivers falsch (gefaltet), was Folge-Moves verfälscht. Das ist das beobachtete „Driver interpretiert als B=180". → Konsequenz: Driver auf B=0=gerade umstellen (passt dann ohne Umrechnung zum Homing), oder appRobotHoming sendet B+180. (C ist konsistent: Homing C=90=flach = Driver c=90=flach, also C=0=aufrecht — kein Versatz.)

    Umstellung durchgängig:

    • FK/IK in Arm3SegmentLinearX (b-Definition / acos-Zweig),
    • gripperMotorFromOpening nachziehen (behebt auch den G28-Greifer-Slam),
    • G92-Eingabe (b = B/D) + M1 + G28,
    • portInverse.js (Umkehrung),
    • Sender-Formeln so kompensieren, dass die FluidNC-Ports unverändert bleiben — ODER bewusst die Hardware-Nullpunkte neu kalibrieren (Entscheidung dokumentieren).
  4. C-Nullpunkt (neutral = 0°). Der c↔ψ-Bezug ist posenabhängig (ψ = acos(cos β · sin a) c). Ein konstantes c=0=neutral ist nicht global möglich, ohne die Hand-Parametrierung zu ändern. Bewerten: c als reinen Gelenkwinkel führen (Offset herausrechnen) oder die ψ-Definition anpassen.

  5. l3-Ableitung korrigiert (RobotConfig.js): l3 kommt jetzt aus Hand + Finger (|Hand.to[1]| + |FingerA.to[1]| = 35 + 60 = 95) statt aus dem Ellbogen-Versatz (90). Zusätzlich sind kinematics.l1/l2/l3 in robot.json explizit überschreibbar (Vorrang vor der Ableitung) — zum Kalibrieren auf die gemessene Reichweite. Reichweite damit 595 — passt zur aktuellen Hardware (60 mm Finger). Die früher beobachteten ~550 stammten von kürzeren 50 mm-Greifern. Bei Greifer-Wechsel kinematics.l3 per Override anpassen (oder Finger-Geometrie in robot.json pflegen).

  6. Tests + Doku nachziehen: Round-Trip mit neuer Konvention, Greifer-Kopplung, G92-Referenztabellen in Info_G92.md, sowie diese Datei.

Ansatz-Entscheidung (vor Umsetzung)

  • Klein/lokal: nur die G92-Eingabe umrechnen (appRobotHoming sendet B=0/C=0 für gerade/neutral, Driver mappt intern auf die alte Konvention). Wenig Risiko, aber das interne Modell bleibt „unsauber".
  • Groß/sauber: interne Konvention durchgängig umstellen (alle Fundstellen oben) mit Hardware-Port-Invariante. Sauberes All-Zero-Home, aber koordinierter Eingriff.

Verifikation

Jede B/C/Greifer-Änderung gegen Visualisierung UND einen Hardware-Test (eine Achse isoliert) prüfen — das Modell allein genügt hier nicht, weil es um die Hardware-Abbildung geht.

Verifikation (Definition of Done)

  • Phase 1 (erfüllt): G92 der Grundstellung → Driver y ≈ 590, z ≈ 0; appRobotHoming sendet die gemessenen α/β/a direkt (ohne Spiegelung); G28 fährt sauber gestreckt nach y (a=0, kein Singularitäts-Müll); volle Suite grün.
  • Phase 2 (Ziel): Grundstellung mit allen Gelenkwinkeln 0 (inkl. B=C=0); Greifer- Kopplung vereinheitlicht; Finger visuell korrekt.

Anhang: Stand der Kinematik

  • y-Flip (Phase 1): Spiegelung in Arm3SegmentLinearX (_mirrorWorkspaceY, genutzt von calculateAngles3D und calculatePositionFromMotorAngles). Am Roboter bestätigt.
  • G28-Singularität (Phase 1): voll ausgestreckt setzt RobotController die Motorwerte direkt (statt der singulären IK) → Finger sauber entlang y.
  • atan2-Fix in der IK (gamma = Math.atan2(pZ, pY)): macht die interne IK für y mathematisch korrekt — Voraussetzung des y-Flips.
  • Winkel-Konventionen (Y/Z/A/B/C/E) sind in doc/Info_G92.md dokumentiert und nach Phase 1 aktualisiert.