324 lines
9.6 KiB
JavaScript
Executable File
324 lines
9.6 KiB
JavaScript
Executable File
// __tests__/Robot.inverseKinematics.test.js
|
|
const Robot = require('../robot/kinematics/Arm3SegmentLinearX');
|
|
|
|
describe("Robot Kinematics Roundtrip", () => {
|
|
|
|
test("calculateAngles3D() <-> calculatePositionFromMotorAngles() handgelenk 1", () => {
|
|
|
|
// === Instanz A: Vorwärts-Kinematik (XYZ -> Motorwinkel) ===
|
|
const L1 = 300;
|
|
const L2 = 200;
|
|
const L3 = 10;
|
|
|
|
const A = new Robot(L1, L2, L3)
|
|
|
|
// Beispiel-Eingabe
|
|
A.x = 0;
|
|
A.y = 310;
|
|
A.z = 0;
|
|
|
|
A.phi = -Math.PI/2;
|
|
A.theta = Math.PI/2;
|
|
A.psi = 0;
|
|
A.e = 0;
|
|
|
|
A.calculateAngles3D();
|
|
|
|
// Motorwerte aus Instanz A speichern
|
|
const motor = {
|
|
xMotor: A.xMotor,
|
|
alpha: A.alpha,
|
|
beta: A.beta,
|
|
a: A.a,
|
|
b: A.b,
|
|
c: A.c
|
|
};
|
|
|
|
// === Instanz B: Rückwärts-Kinematik (Motorwinkel -> XYZ) ===
|
|
const B = new Robot(L1, L2, L3);
|
|
|
|
B.xMotor = motor.xMotor;
|
|
B.alpha = motor.alpha;
|
|
B.beta = motor.beta;
|
|
B.a = motor.a;
|
|
B.b = motor.b;
|
|
B.c = motor.c;
|
|
|
|
// Diese Funktion rekonstruiert nur x, y, z!
|
|
B.calculatePositionFromMotorAngles();
|
|
|
|
// === Vergleich mit Toleranz ===
|
|
const EPS = 0.01; // 1/1000 mm Genauigkeit
|
|
|
|
expect(B.pY).toBeCloseTo(A.pY, EPS);
|
|
expect(B.pZ).toBeCloseTo(A.pZ, EPS);
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
});
|
|
|
|
test("calculateAngles3D() <-> calculatePositionFromMotorAngles() handgelenk 2", () => {
|
|
|
|
// === Instanz A: Vorwärts-Kinematik (XYZ -> Motorwinkel) ===
|
|
const L1 = 300;
|
|
const L2 = 300;
|
|
const L3 = 10;
|
|
|
|
const A = new Robot(L1, L2, L3)
|
|
|
|
// Beispiel-Eingabe
|
|
A.x = 0;
|
|
A.y = 410;
|
|
A.z = 0;
|
|
|
|
A.phi = -Math.PI/2;
|
|
A.theta = Math.PI/2;
|
|
A.psi = 0;
|
|
A.e = 0;
|
|
|
|
A.calculateAngles3D();
|
|
|
|
// Motorwerte aus Instanz A speichern
|
|
const motor = {
|
|
xMotor: A.xMotor,
|
|
alpha: A.alpha,
|
|
beta: A.beta,
|
|
a: A.a,
|
|
b: A.b,
|
|
c: A.c
|
|
};
|
|
|
|
// === Instanz B: Rückwärts-Kinematik (Motorwinkel -> XYZ) ===
|
|
const B = new Robot(L1, L2, L3);
|
|
|
|
B.xMotor = motor.xMotor;
|
|
B.alpha = motor.alpha;
|
|
B.beta = motor.beta;
|
|
B.a = motor.a;
|
|
B.b = motor.b;
|
|
B.c = motor.c;
|
|
|
|
// Diese Funktion rekonstruiert nur x, y, z!
|
|
B.calculatePositionFromMotorAngles();
|
|
|
|
// === Vergleich mit Toleranz ===
|
|
const EPS = 0.01; // 1/1000 mm Genauigkeit
|
|
|
|
expect(B.pY).toBeCloseTo(A.pY, EPS);
|
|
expect(B.pZ).toBeCloseTo(A.pZ, EPS);
|
|
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
});
|
|
|
|
test("calculateAngles3D() <-> calculatePositionFromMotorAngles() handgelenk 3", () => {
|
|
|
|
// === Instanz A: Vorwärts-Kinematik (XYZ -> Motorwinkel) ===
|
|
const L1 = 300;
|
|
const L2 = 200;
|
|
const L3 = 10;
|
|
|
|
const A = new Robot(L1, L2, L3)
|
|
|
|
// Beispiel-Eingabe
|
|
A.x = 10;
|
|
A.y = 500;
|
|
A.z = 0;
|
|
|
|
A.phi = 0; //-Math.PI/2;
|
|
A.theta = Math.PI/2;
|
|
A.psi = 0;
|
|
A.e = 0;
|
|
|
|
A.calculateAngles3D();
|
|
|
|
|
|
// Motorwerte aus Instanz A speichern
|
|
const motor = {
|
|
xMotor: A.xMotor,
|
|
alpha: A.alpha,
|
|
beta: A.beta,
|
|
a: A.a,
|
|
b: A.b,
|
|
c: A.c
|
|
};
|
|
|
|
// === Instanz B: Rückwärts-Kinematik (Motorwinkel -> XYZ) ===
|
|
const B = new Robot(L1, L2, L3);
|
|
|
|
B.xMotor = motor.xMotor;
|
|
B.alpha = motor.alpha;
|
|
B.beta = motor.beta;
|
|
B.a = motor.a;
|
|
B.b = motor.b;
|
|
B.c = motor.c;
|
|
|
|
// Diese Funktion rekonstruiert nur x, y, z!
|
|
B.calculatePositionFromMotorAngles();
|
|
|
|
// === Vergleich mit Toleranz ===
|
|
const EPS = 0.01; // 1/1000 mm Genauigkeit
|
|
|
|
expect(B.pY).toBeCloseTo(A.pY, EPS);
|
|
expect(B.pZ).toBeCloseTo(A.pZ, EPS);
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
});
|
|
|
|
|
|
test("calculateAngles3D() <-> calculatePositionFromMotorAngles() handgelenk 4", () => {
|
|
|
|
// === Instanz A: Vorwärts-Kinematik (XYZ -> Motorwinkel) ===
|
|
const L1 = 300;
|
|
const L2 = 200;
|
|
const L3 = 10;
|
|
|
|
const A = new Robot(L1, L2, L3)
|
|
|
|
// Beispiel-Eingabe
|
|
A.x = 10;
|
|
A.y = 430;
|
|
A.z = 30;
|
|
|
|
A.phi = Math.PI/7; //-Math.PI/2;
|
|
A.theta = Math.PI/2;
|
|
A.psi = 0;
|
|
A.e = 0;
|
|
|
|
A.calculateAngles3D();
|
|
|
|
|
|
// Motorwerte aus Instanz A speichern
|
|
const motor = {
|
|
xMotor: A.xMotor,
|
|
alpha: A.alpha,
|
|
beta: A.beta,
|
|
a: A.a,
|
|
b: A.b,
|
|
c: A.c
|
|
};
|
|
|
|
// === Instanz B: Rückwärts-Kinematik (Motorwinkel -> XYZ) ===
|
|
const B = new Robot(L1, L2, L3);
|
|
|
|
B.xMotor = motor.xMotor;
|
|
B.alpha = motor.alpha;
|
|
B.beta = motor.beta;
|
|
B.a = motor.a;
|
|
B.b = motor.b;
|
|
B.c = motor.c;
|
|
|
|
|
|
|
|
// Diese Funktion rekonstruiert nur x, y, z!
|
|
B.calculatePositionFromMotorAngles();
|
|
|
|
// === Vergleich mit Toleranz ===
|
|
const EPS = 0.01; // 1/1000 mm Genauigkeit
|
|
|
|
expect(B.pY).toBeCloseTo(A.pY, EPS);
|
|
expect(B.pZ).toBeCloseTo(A.pZ, EPS);
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- phi/theta/psi Round-Trip (psi != 0) ---
|
|
// Prueft ob calculatePositionFromMotorAngles() auch die Orientierung
|
|
// (phi, theta, psi) korrekt zurueckrechnet, nicht nur x/y/z.
|
|
|
|
function roundTrip(L1, L2, L3, x, y, z, phi, theta, psi) {
|
|
const A = new Robot(L1, L2, L3);
|
|
A.x = x; A.y = y; A.z = z;
|
|
A.phi = phi; A.theta = theta; A.psi = psi;
|
|
A.calculateAngles3D();
|
|
|
|
const B = new Robot(L1, L2, L3);
|
|
B.xMotor = A.xMotor;
|
|
B.alpha = A.alpha;
|
|
B.beta = A.beta;
|
|
B.a = A.a;
|
|
B.b = A.b;
|
|
B.c = A.c;
|
|
B.calculatePositionFromMotorAngles();
|
|
return { A, B };
|
|
}
|
|
|
|
test("phi/theta/psi Round-Trip: psi = +45 Grad", () => {
|
|
const { A, B } = roundTrip(300, 200, 10, 10, 430, 30, Math.PI/7, Math.PI/2, Math.PI/4);
|
|
const EPS = 4; // 4 Dezimalstellen, ca. 0.1 mrad
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
expect(B.psi).toBeCloseTo(A.psi, EPS);
|
|
});
|
|
|
|
test("phi/theta/psi Round-Trip: psi = -30 Grad", () => {
|
|
const { A, B } = roundTrip(300, 200, 10, 10, 430, 30, Math.PI/7, Math.PI/2, -Math.PI/6);
|
|
const EPS = 4;
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
expect(B.psi).toBeCloseTo(A.psi, EPS);
|
|
});
|
|
|
|
test("phi/theta/psi Round-Trip: psi = +90 Grad, phi = -90 Grad", () => {
|
|
const { A, B } = roundTrip(300, 200, 10, 0, 450, 0, -Math.PI/2, Math.PI/2, Math.PI/2);
|
|
const EPS = 4;
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
expect(B.psi).toBeCloseTo(A.psi, EPS);
|
|
});
|
|
|
|
|
|
// --- Null-Stellung und reale Positionen in -Y Richtung ---
|
|
// Der Roboter arbeitet mit y < 0 als Hauptrichtung.
|
|
// Null-Stellung: Arm voll ausgestreckt in -Y, Fingerspitze bei y=-(l1+l2+l3), z=0.
|
|
// Hand zeigt ebenfalls in -Y => Handvektor (Fingerspitze->Handgelenk) zeigt in +Y
|
|
// => phi = pi/2, theta = pi/2.
|
|
|
|
test("Null-Stellung: Arm voll ausgestreckt in -Y, y=-(L1+L2+L3), z=0", () => {
|
|
const L1 = 300, L2 = 200, L3 = 10;
|
|
const { A, B } = roundTrip(L1, L2, L3, 0, -(L1+L2+L3), 0, Math.PI/2, Math.PI/2, 0);
|
|
const EPS = 4;
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
expect(B.psi).toBeCloseTo(A.psi, EPS);
|
|
});
|
|
|
|
test("Zwischenposition in -Y: Fingerspitze bei y=-400, z=100", () => {
|
|
const L1 = 300, L2 = 200, L3 = 10;
|
|
// Gleiche Handorientierung wie Null-Stellung: phi=pi/2, theta=pi/2
|
|
const { A, B } = roundTrip(L1, L2, L3, 0, -400, 100, Math.PI/2, Math.PI/2, 0);
|
|
const EPS = 4;
|
|
expect(B.x).toBeCloseTo(A.x, EPS);
|
|
expect(B.y).toBeCloseTo(A.y, EPS);
|
|
expect(B.z).toBeCloseTo(A.z, EPS);
|
|
expect(B.phi).toBeCloseTo(A.phi, EPS);
|
|
expect(B.theta).toBeCloseTo(A.theta, EPS);
|
|
expect(B.psi).toBeCloseTo(A.psi, EPS);
|
|
});
|
|
|
|
}); |