Files
Robot_Arm/ml/gen/consistency.cjs
T

48 lines
3.3 KiB
JavaScript

/* M1 门禁:老师一致性(方案 §4.7 / R1)
* node ml/gen/consistency.cjs [N=100] [seed=4242]
* 取 B/C 类(需要绕行)任务,起点不变、目标位置扰动 ±5 mm 造 4 个邻居,与原任务老师路径比较:
* 两条路径按巡航时间 τ 各重采样 32 点,逐点取各轴最大差;最大偏差 < 30° 视为同一绕行同伦类。
* 门禁:一致率 ≥ 90%。另报告同一任务重复求解的确定性。 */
'use strict';
const N = +process.argv[2] || 100, SEED = +process.argv[3] || 4242;
const { S, ArmIK } = require('../../tests/route_bench_setup.cjs')();
const sm = require('./sampler.cjs')(S, SEED);
const teach = require('./teacher.cjs')(S, ArmIK);
const PD = require('./pathdev.cjs');
const fz = (r, t) => (t.mode === 'pos' && r.ptsJ6Frozen) ? r.ptsJ6Frozen : r.pts; // 与 gen 同口径:pos 任务冻结 J6(gen 里认证不过才回退)
const { VMAX } = require('./timelaw.cjs');
function resample(P, n) {
const seg = []; 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]); seg.push(m); }
const tot = seg.reduce((a, b) => a + b, 0) || 1; const out = [];
for (let k = 0; k < n; k++) { let tau = tot * k / (n - 1), i = 0; while (i < seg.length - 1 && tau > seg[i]) { tau -= seg[i]; i++; }
const u = seg[i] > 0 ? Math.min(1, tau / seg[i]) : 1; out.push(P[i].map((v, j) => v + (P[i + 1][j] - v) * u)); }
return out;
}
const devAx = (A, B) => { const a = resample(A, 32), b = resample(B, 32); const m = [0, 0, 0, 0, 0, 0]; for (let k = 0; k < 32; k++) for (let j = 0; j < 6; j++) m[j] = Math.max(m[j], Math.abs(a[k][j] - b[k][j])); return m; };
const dev = (A, B) => Math.max(...devAx(A, B));
const axisHist = [0, 0, 0, 0, 0, 0]; let sameNoJ6 = 0;
let tasks = 0, pairs = 0, same = 0, detSame = 0, detN = 0; const devs = [], byCat = {};
while (tasks < N) {
const t = sm.sample(); if (t.cat !== 'B' && t.cat !== 'C') continue;
const base = teach(t); if (!base.ok || !base.reached) continue; base.pts = fz(base, t);
tasks++;
const again = teach(t); if (again.ok) { detN++; if (dev(base.pts, fz(again, t)) < 1e-6) detSame++; }
for (let k = 0; k < 4; k++) {
const u = [sm.rnd() - 0.5, sm.rnd() - 0.5, sm.rnd() - 0.5], n = Math.hypot(...u);
const T2 = t.T.map((v, i) => v + 5 * u[i] / n);
const r = teach(Object.assign({}, t, { T: T2 }));
if (!r.ok || !r.reached) continue;
const da = devAx(base.pts, fz(r, t)), d = Math.max(...da); devs.push(d); pairs++;
if (PD.sameClass(base.pts, fz(r, t), t.mode)) sameNoJ6++;
if (d >= 30) axisHist[da.indexOf(d)]++;
const c = byCat[t.cat] = byCat[t.cat] || { pairs: 0, same: 0 }; c.pairs++;
if (d < 30) { same++; c.same++; }
}
if (tasks % 10 === 0) process.stderr.write(`${tasks}/${N}\n`);
}
devs.sort((a, b) => a - b);
console.log(JSON.stringify({ tasks, pairs, consistentRate: +(same / pairs).toFixed(3), gate: same / pairs >= 0.9 ? 'PASS' : 'FAIL',
devP50: +devs[devs.length >> 1].toFixed(1), devP90: +devs[Math.floor(devs.length * 0.9)].toFixed(1),
byCat: Object.fromEntries(Object.entries(byCat).map(([k, v]) => [k, +(v.same / v.pairs).toFixed(3)])),
consistentByGateRule: +(sameNoJ6 / pairs).toFixed(3), gateByRule: sameNoJ6 / pairs >= 0.9 ? 'PASS' : 'FAIL', worstAxisOfInconsistent: axisHist, deterministic: detSame + '/' + detN }));