G92-Grad + E-Korrektur
This commit is contained in:
@@ -113,5 +113,22 @@ describe("Robot G92", () => {
|
||||
|
||||
// ("Wenn nur G92 x3 gegeben wird, dann wird trotzdem auch y und z gesendet. schlecht." );
|
||||
});
|
||||
|
||||
test("G92 E: Greifer-Öffnung (mm) → eMotor über Kopplung e - b - c", () => {
|
||||
const robot = new Robot(300, 300, 20);
|
||||
const D = 180 / Math.PI;
|
||||
|
||||
// B/C in Grad rein (→ intern Radiant), E in mm. eMotor muss aus e, b, c abgeleitet
|
||||
// werden — die Greifer-Sehne läuft durchs Handgelenk (Arm3SegmentLinearX-Kopplung).
|
||||
GCode.receiveGCode(robot, "G92 B30 C-45 E10");
|
||||
|
||||
expect(robot.b).toBeCloseTo(30 / D, 6); // 30° → rad
|
||||
expect(robot.c).toBeCloseTo(-45 / D, 6); // -45° → rad
|
||||
expect(robot.e).toBe(10); // mm, unverändert
|
||||
expect(robot.eMotor).toBeCloseTo(10 - robot.b - robot.c, 6); // = e - b - c
|
||||
|
||||
// Konsistenz: identische Kopplung wie der reguläre Bewegungspfad (calculateAngles3D).
|
||||
expect(robot.eMotor).toBeCloseTo(robot.gripperMotorFromOpening(robot.e), 12);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -204,7 +204,8 @@ describe('InfoServer', () => {
|
||||
const httpsOptions = { key, cert, passphrase: 'abcd' };
|
||||
|
||||
const sharedState = { connectedClients: [], lastCommands: [], lastPings: [] };
|
||||
const robot = { x: 10, y: 20, z: 30, phi: 0.1, theta: 0.2, psi: 0.3, xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0 };
|
||||
// e = Greifer-Öffnung (mm) → position.e; eMotor = Greifer-Motorwert → motorCounts.e.
|
||||
const robot = { x: 10, y: 20, z: 30, phi: 0.1, theta: 0.2, psi: 0.3, xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0, e: 2.5, eMotor: 7 };
|
||||
const senders = [];
|
||||
|
||||
server = createInfoServer(httpsOptions, sharedState, robot, GCode, senders);
|
||||
@@ -214,7 +215,8 @@ describe('InfoServer', () => {
|
||||
expect(statusCode).toBe(200);
|
||||
|
||||
const json = JSON.parse(body);
|
||||
expect(json.position).toEqual({ x: 10, y: 20, z: 30, a: 0.1, b: 0.2, c: 0.3 });
|
||||
expect(json.position).toEqual({ x: 10, y: 20, z: 30, a: 0.1, b: 0.2, c: 0.3, e: 2.5 });
|
||||
expect(json.motorCounts.e).toBe(7); // Motorwert (eMotor), nicht die mm-Öffnung
|
||||
});
|
||||
|
||||
test('returns 404 for unknown endpoints', async () => {
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('InputWS API response routing', () => {
|
||||
a.send('M114');
|
||||
|
||||
const parsed = JSON.parse(await aReply);
|
||||
expect(parsed.position).toEqual({ x: 5, y: 6, z: 7, a: 0, b: 0, c: 0 });
|
||||
expect(parsed.position).toEqual({ x: 5, y: 6, z: 7, a: 0, b: 0, c: 0, e: 0 });
|
||||
expect(await bSilent).toBe(true);
|
||||
|
||||
a.close();
|
||||
@@ -123,8 +123,8 @@ describe('InputWS API response routing', () => {
|
||||
|
||||
const aParsed = JSON.parse(await aReply);
|
||||
const bParsed = JSON.parse(await bReply);
|
||||
expect(aParsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0 });
|
||||
expect(bParsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0 });
|
||||
expect(aParsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0, e: 0 });
|
||||
expect(bParsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0, e: 0 });
|
||||
expect(robot.sendCommand).toHaveBeenCalled();
|
||||
|
||||
a.close();
|
||||
|
||||
@@ -88,7 +88,7 @@ describe('InputWS', () => {
|
||||
|
||||
const message = await messagePromise;
|
||||
const parsed = JSON.parse(message);
|
||||
expect(parsed.position).toEqual({ x: 12, y: 34, z: 56, a: 1, b: 2, c: 3 });
|
||||
expect(parsed.position).toEqual({ x: 12, y: 34, z: 56, a: 1, b: 2, c: 3, e: 0 });
|
||||
expect(parsed.motorCounts).toBeDefined();
|
||||
|
||||
client.close();
|
||||
@@ -108,7 +108,7 @@ describe('InputWS', () => {
|
||||
|
||||
const message = await messagePromise;
|
||||
const parsed = JSON.parse(message);
|
||||
expect(parsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0 });
|
||||
expect(parsed.position).toEqual({ x: 1, y: 2, z: 3, a: 0, b: 0, c: 0, e: 0 });
|
||||
expect(robot.sendCommand).toHaveBeenCalled();
|
||||
|
||||
client.close();
|
||||
|
||||
@@ -65,20 +65,38 @@ describe('RobotController (ToDo_6)', () => {
|
||||
expect(robot.sendCommand).toHaveBeenCalledWith('G92');
|
||||
});
|
||||
|
||||
test('applyCommand: G92 verhält sich identisch zu M92 (Bug 3)', () => {
|
||||
test('applyCommand: G92 interpretiert Winkel als Grad (→ rad), X bleibt mm', () => {
|
||||
const robot = createDummyRobot();
|
||||
robot.createMotorPosition = jest.fn();
|
||||
|
||||
// G92 nutzt die G-Code-Konvention (Grad). Intern landen die Winkel in Radiant,
|
||||
// X (lineare Schiene) bleibt unverändert in mm. Vgl. M92 oben (Roh-Radiant).
|
||||
RobotController.applyCommand(robot, { command: 'G92', params: { X: 5, Y: 0.5, A: 0.3 } });
|
||||
|
||||
const DEG2RAD = Math.PI / 180;
|
||||
expect(robot.createMotorPosition).toHaveBeenCalledTimes(1);
|
||||
expect(robot.xMotor).toBe(5);
|
||||
expect(robot.alpha).toBe(0.5);
|
||||
expect(robot.a).toBe(0.3);
|
||||
expect(robot.alpha).toBeCloseTo(0.5 * DEG2RAD, 10);
|
||||
expect(robot.a).toBeCloseTo(0.3 * DEG2RAD, 10);
|
||||
expect(robot.calculatePositionFromMotorAngles).toHaveBeenCalled();
|
||||
expect(robot.sendCommand).toHaveBeenCalledWith('G92');
|
||||
});
|
||||
|
||||
test('applyCommand: G92 E setzt Greifer-Öffnung (mm) und leitet eMotor ab', () => {
|
||||
const robot = createDummyRobot();
|
||||
robot.createMotorPosition = jest.fn();
|
||||
robot.b = 0.2; // Handgelenk-Knick
|
||||
robot.c = -0.5; // Hand-Dreher
|
||||
|
||||
// E ist mm (keine Grad/rad-Umrechnung). eMotor wird über die Greifer-Kopplung
|
||||
// aus e, b, c abgeleitet — sonst bliebe der an FluidNC gesendete Wert stale.
|
||||
RobotController.applyCommand(robot, { command: 'G92', params: { E: 10, B: 0.2 * (180 / Math.PI), C: -0.5 * (180 / Math.PI) } });
|
||||
|
||||
expect(robot.e).toBe(10);
|
||||
expect(robot.eMotor).toBeCloseTo(10 - robot.b - robot.c, 10); // = 10 - 0.2 - (-0.5) = 10.3
|
||||
expect(robot.sendCommand).toHaveBeenCalledWith('G92');
|
||||
});
|
||||
|
||||
test('applyCommand: ungültiger Befehl wird ignoriert', () => {
|
||||
const robot = createDummyRobot();
|
||||
RobotController.applyCommand(robot, null);
|
||||
|
||||
@@ -20,12 +20,17 @@ function createDummyRobot() {
|
||||
a: 0,
|
||||
b: 0,
|
||||
c: 0,
|
||||
eMotor: 0,
|
||||
|
||||
// Geometrie
|
||||
l1: 10,
|
||||
l2: 10,
|
||||
l3: 10,
|
||||
|
||||
// Greifer-Kopplung (RobotBase-Default: keine Kopplung). Konkrete Kinematiken
|
||||
// überschreiben dies; siehe Arm3SegmentLinearX.gripperMotorFromOpening.
|
||||
gripperMotorFromOpening(e) { return e - this.b - this.c; },
|
||||
|
||||
// Methoden → jest.fn erlaubt Call-Tracking
|
||||
calculateAngles3D: jest.fn(),
|
||||
calculatePositionFromMotorAngles: jest.fn(),
|
||||
|
||||
Reference in New Issue
Block a user