34 lines
1.8 KiB
JavaScript
34 lines
1.8 KiB
JavaScript
/* 局部可观测时间律(2026-10-17,方案 §4.5)
|
||
* 折线按"巡航时间" τ 参数化:每段 τ = max_j |Δq_j| / VMAX_j(各轴同步、主导轴满速)。
|
||
* 归一化速度 v∈[0,1](1 = 巡航):v ← min(1, v + A·dt, sqrt(2·A·剩余 τ)),只依赖当前速度与剩余路程。
|
||
* 输出 50 Hz(dt = 20 ms)状态序列,末点精确落在终点。 */
|
||
'use strict';
|
||
const VMAX = [60, 60, 90, 90, 120, 180]; // °/s,与 tests/route_bench.cjs 同(轴速比 0.6/0.6/0.9/0.9/1.2/1.8)
|
||
const A = 5.0; // 1/s²:0→巡航约 0.2 s(a_max 待真机参数)
|
||
const DT = 0.02;
|
||
function timeParam(P, opt) {
|
||
opt = opt || {};
|
||
const vmax = opt.vmax || VMAX, a = opt.acc || A, dt = opt.dt || DT;
|
||
const segT = [];
|
||
for (let i = 1; i < P.length; i++) { let m = 0; for (let j = 0; j < 6; j++) m = Math.max(m, Math.abs(P[i][j] - P[i - 1][j]) / vmax[j]); segT.push(m); }
|
||
const cum = [0]; for (const s of segT) cum.push(cum[cum.length - 1] + s);
|
||
const total = cum[cum.length - 1];
|
||
const at = tau => {
|
||
if (tau >= total) return P[P.length - 1].slice();
|
||
let i = 0; while (i < segT.length - 1 && cum[i + 1] < tau) i++;
|
||
const u = segT[i] > 0 ? (tau - cum[i]) / segT[i] : 1;
|
||
return P[i].map((v, j) => v + (P[i + 1][j] - v) * u);
|
||
};
|
||
const out = [P[0].slice()]; let tau = 0, v = 0, guard = 0;
|
||
while (tau < total - 1e-9 && guard++ < 100000) {
|
||
const rem = total - tau;
|
||
const v1 = Math.min(1, v + a * dt, Math.sqrt(2 * a * Math.max(0, rem - 0.5 * v * dt)) + 1e-9);
|
||
let step = 0.5 * (v + v1) * dt;
|
||
if (step <= 1e-9) step = Math.min(rem, a * dt * dt); // 防止末端速度掉到 0 卡住
|
||
tau = Math.min(total, tau + step); v = v1;
|
||
out.push(at(tau));
|
||
}
|
||
return { traj: out, total, steps: out.length - 1 };
|
||
}
|
||
module.exports = { timeParam, VMAX, A, DT };
|