feat: quaternion relative

This commit is contained in:
2025-06-12 16:16:51 +09:00
parent ba1b18febe
commit 7f4cb6daec
3 changed files with 64 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "computercraft-mutil", "name": "computercraft-mutil",
"version": "1.0.9", "version": "1.0.10",
"description": "", "description": "",
"license": "ISC", "license": "ISC",
"author": "", "author": "",

View File

@@ -2,7 +2,7 @@ export interface PIDParameters {
kP: number; kP: number;
kI: number; kI: number;
kD: number; kD: number;
errorWrap?: (error: number) => number; calculateError?: (setpoint: number, measuredValue: number) => number;
} }
export type PIDReport = { export type PIDReport = {
@@ -46,10 +46,9 @@ export class PID {
public update(measuredValue: number): number { public update(measuredValue: number): number {
const currentTime = os.epoch("utc"); const currentTime = os.epoch("utc");
const rawError = this.setpoint - measuredValue; const error = this.params.calculateError
const error = this.params.errorWrap ? this.params.calculateError(this.setpoint, measuredValue)
? this.params.errorWrap(rawError) : this.setpoint - measuredValue;
: rawError;
let deltaTime = 0; let deltaTime = 0;
if (this.lastTime !== null) { if (this.lastTime !== null) {

View File

@@ -3,14 +3,14 @@ import { Vec3 } from "./vec";
const math = globalThis.math; const math = globalThis.math;
export class Quaternion { export class Quaternion {
public constructor( constructor(
public x: number, public x: number,
public y: number, public y: number,
public z: number, public z: number,
public w: number, public w: number,
) {} ) { }
public static fromXYZ({ static fromXYZ({
x, x,
y, y,
z, z,
@@ -45,6 +45,62 @@ export class Quaternion {
return new Vec3(roll, pitch, yaw); return new Vec3(roll, pitch, yaw);
} }
public normalized(): Quaternion {
const mag = math.sqrt(
this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z,
);
return new Quaternion(
this.x / mag,
this.y / mag,
this.z / mag,
this.w / mag,
);
}
public inverse(): Quaternion {
// assuming unit quaternion
return new Quaternion(-this.x, -this.y, -this.z, this.w);
}
public multiply(q: Quaternion): Quaternion {
const w1 = this.w,
x1 = this.x,
y1 = this.y,
z1 = this.z;
const w2 = q.w,
x2 = q.x,
y2 = q.y,
z2 = q.z;
return new Quaternion(
x1 * w2 + w1 * x2 + y1 * z2 - z1 * y2,
y1 * w2 + w1 * y2 + z1 * x2 - x1 * z2,
z1 * w2 + w1 * z2 + x1 * y2 - y1 * x2,
w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2,
);
}
/** Returns rotation error to target as axis and angle (in radians) */
public getErrorTo(target: Quaternion): { axis: Vec3; angle: number } {
// q_error = q_target * inverse(q_current)
const q_error = target.multiply(this.inverse()).normalized();
const angle = 2 * math.acos(math.min(math.max(q_error.w, -1), 1));
const s = math.sqrt(1 - q_error.w * q_error.w);
let axis: Vec3;
if (s < 1e-6) {
axis = new Vec3(1, 0, 0); // arbitrary
} else {
axis = new Vec3(q_error.x / s, q_error.y / s, q_error.z / s);
}
// Wrap angle to [-PI, PI]
const wrappedAngle = angle > Math.PI ? angle - 2 * Math.PI : angle;
return { axis, angle: wrappedAngle };
}
} }
// fast sign without zero // fast sign without zero