Files
2024-02-03 18:01:21 +01:00

398 lines
8.1 KiB
JavaScript

export default class Kinematic {
l1;
l2;
l3;
l4;
L;
W;
constructor() {
this.l1 = 50;
this.l2 = 20;
this.l3 = 120;
this.l4 = 155;
this.L = 140;
this.W = 75;
}
bodyIK(omega, phi, psi, xm, ym, zm) {
const { cos, sin } = Math;
const Rx = [
[1, 0, 0, 0],
[0, cos(omega), -sin(omega), 0],
[0, sin(omega), cos(omega), 0],
[0, 0, 0, 1],
];
const Ry = [
[cos(phi), 0, sin(phi), 0],
[0, 1, 0, 0],
[-sin(phi), 0, cos(phi), 0],
[0, 0, 0, 1],
];
const Rz = [
[cos(psi), -sin(psi), 0, 0],
[sin(psi), cos(psi), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
];
const Rxyz = this.matrixMultiply(this.matrixMultiply(Rx, Ry), Rz);
const T = [
[0, 0, 0, xm],
[0, 0, 0, ym],
[0, 0, 0, zm],
[0, 0, 0, 0],
];
const Tm = this.matrixAdd(T, Rxyz);
const sHp = sin(Math.PI / 2);
const cHp = cos(Math.PI / 2);
return [
this.matrixMultiply(Tm, [
[cHp, 0, sHp, this.L / 2],
[0, 1, 0, 0],
[-sHp, 0, cHp, this.W / 2],
[0, 0, 0, 1],
]),
this.matrixMultiply(Tm, [
[cHp, 0, sHp, this.L / 2],
[0, 1, 0, 0],
[-sHp, 0, cHp, -this.W / 2],
[0, 0, 0, 1],
]),
this.matrixMultiply(Tm, [
[cHp, 0, sHp, -this.L / 2],
[0, 1, 0, 0],
[-sHp, 0, cHp, this.W / 2],
[0, 0, 0, 1],
]),
this.matrixMultiply(Tm, [
[cHp, 0, sHp, -this.L / 2],
[0, 1, 0, 0],
[-sHp, 0, cHp, -this.W / 2],
[0, 0, 0, 1],
]),
];
}
legIK(point) {
const [x, y, z] = point;
const { atan2, cos, sin, sqrt, acos } = Math;
let F;
try {
F = sqrt(x ** 2 + y ** 2 - this.l1 ** 2);
if (isNaN(F)) throw new Error("F is NaN");
} catch (error) {
F = this.l1;
}
const G = F - this.l2;
const H = sqrt(G ** 2 + z ** 2);
const theta1 = -atan2(y, x) - atan2(F, -this.l1);
let theta3;
try {
theta3 = acos(
(H ** 2 - this.l3 ** 2 - this.l4 ** 2) / (2 * this.l3 * this.l4)
);
if (isNaN(theta3)) throw new Error("theta3 is NaN");
} catch (error) {
theta3 = 0;
}
const theta2 =
atan2(z, G) -
atan2(this.l4 * sin(theta3), this.l3 + this.l4 * cos(theta3));
return [theta1, theta2, theta3];
}
matrixMultiply(a, b) {
const result = [];
for (let i = 0; i < a.length; i++) {
const row = [];
for (let j = 0; j < b[0].length; j++) {
let sum = 0;
for (let k = 0; k < a[i].length; k++) {
sum += a[i][k] * b[k][j];
}
row.push(sum);
}
result.push(row);
}
return result;
}
multiplyVector(matrix, vector) {
const rows = matrix.length;
const cols = matrix[0].length;
const vectorLength = vector.length;
if (cols !== vectorLength) {
throw new Error(
"Matrix and vector dimensions do not match for multiplication."
);
}
const result = [];
for (let i = 0; i < rows; i++) {
let sum = 0;
for (let j = 0; j < cols; j++) {
sum += matrix[i][j] * vector[j];
}
result.push(sum);
}
return result;
}
matrixAdd(a, b) {
const result = [];
for (let i = 0; i < a.length; i++) {
const row = [];
for (let j = 0; j < a[i].length; j++) {
row.push(a[i][j] + b[i][j]);
}
result.push(row);
}
return result;
}
calcLegPoints(angles) {
const [theta1, theta2, theta3] = angles;
const theta23 = theta2 + theta3;
const T0 = [0, 0, 0, 1];
const T1 = this.vectorAdd(T0, [
-this.l1 * Math.cos(theta1),
this.l1 * Math.sin(theta1),
0,
0,
]);
const T2 = this.vectorAdd(T1, [
-this.l2 * Math.sin(theta1),
-this.l2 * Math.cos(theta1),
0,
0,
]);
const T3 = this.vectorAdd(T2, [
-this.l3 * Math.sin(theta1) * Math.cos(theta2),
-this.l3 * Math.cos(theta1) * Math.cos(theta2),
this.l3 * Math.sin(theta2),
0,
]);
const T4 = this.vectorAdd(T3, [
-this.l4 * Math.sin(theta1) * Math.cos(theta23),
-this.l4 * Math.cos(theta1) * Math.cos(theta23),
this.l4 * Math.sin(theta23),
0,
]);
return [T0, T1, T2, T3, T4];
}
calcIK(Lp, angles, center) {
const [omega, phi, psi] = angles;
const [xm, ym, zm] = center;
const [Tlf, Trf, Tlb, Trb] = this.bodyIK(omega, phi, psi, xm, ym, zm);
const Ix = [
[-1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
];
return [
this.legIK(this.multiplyVector(this.matrixInverse(Tlf), Lp[0])),
this.legIK(
this.multiplyVector(
Ix,
this.multiplyVector(this.matrixInverse(Trf), Lp[1])
)
),
this.legIK(this.multiplyVector(this.matrixInverse(Tlb), Lp[2])),
this.legIK(
this.multiplyVector(
Ix,
this.multiplyVector(this.matrixInverse(Trb), Lp[3])
)
),
];
}
vectorAdd(a, b) {
return a.map((val, index) => val + b[index]);
}
matrixInverse(matrix) {
const det = this.determinant(matrix);
const adjugate = this.adjugate(matrix);
const scalar = 1 / det;
const inverse = [];
for (let i = 0; i < matrix.length; i++) {
const row = [];
for (let j = 0; j < matrix[i].length; j++) {
row.push(adjugate[i][j] * scalar);
}
inverse.push(row);
}
return inverse;
}
determinant(matrix) {
if (matrix.length !== matrix[0].length) {
throw new Error("The matrix is not square.");
}
if (matrix.length === 2) {
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
}
let det = 0;
for (let i = 0; i < matrix.length; i++) {
const sign = i % 2 === 0 ? 1 : -1;
const subMatrix = [];
for (let j = 1; j < matrix.length; j++) {
const row = [];
for (let k = 0; k < matrix.length; k++) {
if (k !== i) {
row.push(matrix[j][k]);
}
}
subMatrix.push(row);
}
det += sign * matrix[0][i] * this.determinant(subMatrix);
}
return det;
}
adjugate(matrix) {
if (matrix.length !== matrix[0].length) {
throw new Error("The matrix is not square.");
}
const adjugate = [];
for (let i = 0; i < matrix.length; i++) {
const row = [];
for (let j = 0; j < matrix[i].length; j++) {
const sign = (i + j) % 2 === 0 ? 1 : -1;
const subMatrix = [];
for (let k = 0; k < matrix.length; k++) {
if (k !== i) {
const subRow = [];
for (let l = 0; l < matrix.length; l++) {
if (l !== j) {
subRow.push(matrix[k][l]);
}
}
subMatrix.push(subRow);
}
}
const cofactor = sign * this.determinant(subMatrix);
row.push(cofactor);
}
adjugate.push(row);
}
return this.transpose(adjugate);
}
transpose(matrix) {
const transposed = [];
for (let i = 0; i < matrix.length; i++) {
const row = [];
for (let j = 0; j < matrix[i].length; j++) {
row.push(matrix[j][i]);
}
transposed.push(row);
}
return transposed;
}
}
class ForwardKinematics {
l1;
l2;
l3;
l4;
constructor() {
this.l1 = 50;
this.l2 = 20;
this.l3 = 120;
this.l4 = 155;
}
calculateFootpoint(theta1, theta2, theta3) {
const { cos, sin } = Math;
const x =
this.l1 * cos(theta1) +
this.l2 * cos(theta1) +
this.l3 * cos(theta1 + theta2) +
this.l4 * cos(theta1 + theta2 + theta3);
const y =
this.l1 * sin(theta1) +
this.l2 * sin(theta1) +
this.l3 * sin(theta1 + theta2) +
this.l4 * sin(theta1 + theta2 + theta3);
const z = 0;
return [x, y, z];
}
calculateFootpoints(angles) {
const footpoints = [];
for (let i = 0; i < angles.length; i += 3) {
const theta1 = angles[i];
const theta2 = angles[i + 1];
const theta3 = angles[i + 2];
const footpoint = this.calculateFootpoint(theta1, theta2, theta3);
footpoints.push(footpoint);
}
return footpoints;
}
}