exp+fix: commit2 增益重扫(gain2.2 到达 50.8%,n=512)+ 浏览器安全层三点修复(限位夹紧/部分前缀/离散认证)

- gain 1.8/2.2/2.5/2.8 = 50.4/52.0/49.6/47.7%(n=256),2.2 最佳;n=512 复核 50.8%/1.4% 碰撞
- 积分反馈与反馈隔次复用均为负结论(−4.0/−5.1 pp)
- JS:tick/候选投影夹 LIM(原越限后 inBox 永久拒绝→刹停);safety_partial 移植;segCert 开关(默认严格)
- bench nc10 --seg 0:刹车率 79.5→48.1%、planP50 56.1→33.5ms、ok 0→5/10
This commit is contained in:
cbb
2026-10-20 14:23:59 +08:00
parent 46fe24365b
commit d977be321b
6 changed files with 123 additions and 24 deletions
+63 -18
View File
@@ -138,7 +138,19 @@ function refineStep(S, q, T, Rt, maxScale) {
return r.concat([0.5 * (M[7] - M[5]) * 100, 0.5 * (M[2] - M[6]) * 100, 0.5 * (M[3] - M[1]) * 100]);
};
const e0 = e(q), m = e0.length, h = 1e-3, J = [];
for (let j = 0; j < 6; j++) { const qa = q.slice(); qa[j] += h; const e1 = e(qa); J.push(e1.map((v, i) => -(v - e0[i]) / h)); }
if (S.jac) {
/* 解析几何雅可比(与 ml/sim/closed_loop.py 的 dls_steps(analytic=True) 同口径):
有限差分在 float32/双精度下都会给反馈方向注入 ~1–4% 噪声,闭环里会被慢变误差放大。
J[j][i] = −∂err_i/∂q_j:位置 err=T−p → +Jv;姿态 err=100·rotvec → +100·Jw。 */
const jv = S.jac(q);
for (let j = 0; j < 6; j++) {
const col = [jv.Jv[0][j], jv.Jv[1][j], jv.Jv[2][j]];
if (m === 6) col.push(100 * jv.Jw[0][j], 100 * jv.Jw[1][j], 100 * jv.Jw[2][j]);
J.push(col);
}
} else {
for (let j = 0; j < 6; j++) { const qa = q.slice(); qa[j] += h; const e1 = e(qa); J.push(e1.map((v, i) => -(v - e0[i]) / h)); }
}
// Δq = Jᵀ (J Jᵀ + λ²I)⁻¹ e,m ≤ 6
const lam = 0.5, A = [];
for (let i = 0; i < m; i++) { A.push([]); for (let k = 0; k < m; k++) { let s = i === k ? lam * lam : 0; for (let j = 0; j < 6; j++) s += J[j][i] * J[j][k]; A[i].push(s); } }
@@ -179,14 +191,14 @@ class Runner {
this.o = Object.assign({ K: 8, commit: 3, margin: 5, /* 2026-10-18:运行时投影余量保持 5 mm(真机护栏,不迁就数据分布);数据侧另行过滤认证间隙 < 3 mm 的老师轨迹,APG 训练余量 3 mm */
projMax: 0.5, refineMm: 5, arriveMm: 1, arriveDeg: 0.5,
stuckPlans: 5, lockSec: 1.0, subReachDeg: 5, subTimeout: 1.5, subRule: 'cross', subLeadSec: 0.02, seed: 12345, lookahead: true,
actScale: 1, certBudget: 40, safetyMs: 30, firstMs: 45, fbGain: 0 }, opts || {});
actScale: 1, certBudget: 40, safetyMs: 30, firstMs: 45, fbGain: 0, segCert: true }, opts || {});
/* 2026-10-19:actScale = 模型 meta.act_scale(动作标签按 act_scale·STEP 归一化,旧模型 1);增量 = clamp(act, ±1)·actScale·STEP,
与 ml/sim/closed_loop.py 同口径。subRule:'cross'(默认,越过顶点即切,与训练标签同口径;见 _subAdvance)/ 'timeout'(旧:< subReachDeg 或 subTimeout 超时)。 */ // 每条候选最多 40 次距离查询;安全层总预算 30 ms,第 1 条候选可用到 45 ms(超出按刹停处理)
this.cert = makeCert(S, { leverTable: this.o.leverTable, cap: 20 });
this.rng = makeRng(this.o.seed);
/* 2026-10-18 第六轮(审查 B-1/B-2):耗时拆开记——inferMs 主推理、lookaheadMs 前瞻(第二次推理 + 8 份特征)、safetyMs 只含认证、
planMs 整次规划(R9 口径:必须 < 60 ms 周期);certQ 每次规划新增的距离查询数,certHit 记忆命中数,certWhy 认证失败原因计数 */
this.stats = { plans: 0, inferMs: [], lookaheadMs: [], safetyMs: [], planMs: [], planWarmMs: [], planColdMs: [], certQ: [], certHit: 0, certWhy: {}, brakes: 0, refine: 0, lookahead: 0, locks: 0, projected: 0 };
this.stats = { plans: 0, inferMs: [], lookaheadMs: [], safetyMs: [], planMs: [], planWarmMs: [], planColdMs: [], certQ: [], certHit: 0, certWhy: {}, brakes: 0, refine: 0, lookahead: 0, locks: 0, projected: 0, partial: 0 };
this.reset(null);
}
reset(q0) {
@@ -213,7 +225,10 @@ class Runner {
if (!this.q) return null;
const d = this.queue.shift();
this.qPrev = this.q.slice();
if (d) this.q = this.q.map((v, j) => v + d[j]);
if (d) this.q = this.q.map((v, j) => { // 与 ml/sim/closed_loop.py 的 clamp_box 同口径:限位轴执行时夹到 LIM
const id = this.S.ORDER[j], L = this.S.LIM[id], x = v + d[j];
return L.cont ? x : Math.max(L.lo, Math.min(L.hi, x));
});
this.t += DT;
if (this.goal && !this.queue.length) {
const { e, oe } = this.err(this.q);
@@ -250,16 +265,39 @@ class Runner {
}
if (adv) { this.subPrev = this.subs.shift(); this.subT = this.t; }
}
_certCommit(qe, steps) { // 认证:提交步 + 刹停段(先用离散判定快速剔除,再做距离下界认证)
const pts = [qe.slice()]; let q = qe.slice();
for (const d of steps) { q = q.map((v, j) => v + d[j]); pts.push(q.slice()); }
for (let i = 1; i < pts.length; i++) if (!this.S.collideFree(pts[i])) return { ok: false, tail: [] };
const tail = brakeSteps(steps.length ? steps[steps.length - 1] : [0, 0, 0, 0, 0, 0]);
/* 刹停段各步同向等比减速 → 关节空间里是一条直线,认证终点一段即可 */
if (tail.length) { for (const d of tail) q = q.map((v, j) => v + d[j]); pts.push(q.slice()); }
const c = this.cert.path(pts, this.o.certBudget, this._until);
if (!c.ok) this.stats.certWhy[c.why || 'collide'] = (this.stats.certWhy[c.why || 'collide'] || 0) + 1;
return { ok: c.ok, tail };
_certCommit(qe, steps, partial) { // 认证:提交步 + 刹停段(先用离散判定快速剔除,再做距离下界认证)
/* partial:与 ml/sim/closed_loop.py 的 safety_partial 同口径——整段不过时依次缩短前缀,
执行到第一个不安全步为止(而不是整段拒绝 → 刹停)。2026-10-20:浏览器 bench 刹车率 47% 的主因是整段拒绝。 */
const tryPath = n => {
const use = steps.slice(0, n);
const pts = [qe.slice()]; let q = qe.slice();
for (const d of use) { q = q.map((v, j) => v + d[j]); pts.push(q.slice()); }
if (!this.o.segCert) {
/* 离散判定(步点 + 中点,与 ml/sim/closed_loop.py 安全层同口径):capsule 模型余量已含在 collideFree。
连续区间认证(segCert)~40 ms/次,是 50 Hz 部署的主要瓶颈;关闭后 planP50 预计 <30 ms。 */
let prev = qe.slice();
for (const p of pts.slice(1)) {
const mid = prev.map((v, j) => 0.5 * (v + p[j]));
if (!this.S.collideFree(mid) || !this.S.collideFree(p)) return null;
prev = p;
}
const tail = brakeSteps(use.length ? use[use.length - 1] : [0, 0, 0, 0, 0, 0]);
let qq = q;
if (tail.length) { for (const d of tail) qq = qq.map((v, j) => v + d[j]); if (!this.S.collideFree(qq)) return null; }
return { ok: true, tail: tail, steps: use };
}
for (let i = 1; i < pts.length; i++) if (!this.S.collideFree(pts[i])) return null;
const tail = brakeSteps(use.length ? use[use.length - 1] : [0, 0, 0, 0, 0, 0]);
/* 刹停段各步同向等比减速 → 关节空间里是一条直线,认证终点一段即可 */
let qq = q;
if (tail.length) { for (const d of tail) qq = qq.map((v, j) => v + d[j]); pts.push(qq.slice()); }
const c = this.cert.path(pts, this.o.certBudget, this._until);
if (!c.ok) this.stats.certWhy[c.why || 'collide'] = (this.stats.certWhy[c.why || 'collide'] || 0) + 1;
return { ok: c.ok, tail: tail, steps: use };
};
if (!partial) { const r = tryPath(steps.length); return r || { ok: false, tail: [] }; }
for (let n = steps.length; n >= 1; n--) { const r = tryPath(n); if (r && r.ok) return r; }
return { ok: false, tail: [] };
}
async plan() {
if (!this.needPlan()) return;
@@ -316,7 +354,9 @@ class Runner {
const adj = [];
for (const d of steps) {
let qn = q.map((v, j) => v + d[j]);
const qp = project(S, qn, o.margin, o.projMax, near);
let qp = project(S, qn, o.margin, o.projMax, near);
/* 限位轴先夹到 LIM(与执行 clamp_box 同口径),否则候选会被认证的 inBox 整段拒绝 → 永远刹停 */
qp = qp.map((v, j) => { const L = S.LIM[S.ORDER[j]]; return L.cont ? v : Math.max(L.lo, Math.min(L.hi, v)); });
if (qp !== qn) this.stats.projected++;
adj.push(qp.map((v, j) => v - q[j])); q = qp;
}
@@ -366,16 +406,21 @@ class Runner {
collideFree 1.5 ms、criticalPairs 3.1 ms,推理本身 1.5 ms——耗时在几何查询,不在模型。此前截止只在候选之间检查,
单条候选最多 40 次查询(≈150 ms)不受限。现在墙钟截止下沉到认证内部:第 1 条候选截止 t1+firstMs,其余共享到 t1+safetyMs;
到点判不过 → 沿上次已认证的刹停段停,安全性不变,只是可能多刹停。 */
let picked = null;
let picked = null, pickedSteps = null;
for (const c of pool) {
if (c !== pool[0] && now() - t1 > o.safetyMs) break;
this._until = t1 + (c === pool[0] ? Math.max(o.firstMs, o.safetyMs) : o.safetyMs);
const v = this._certCommit(qe, c.steps); if (v.ok) { picked = c; this.brakeTail = v.tail; break; }
const v = this._certCommit(qe, c.steps, true);
if (v.ok) { picked = c; pickedSteps = v.steps; this.brakeTail = v.tail; break; }
}
this._until = undefined;
this.stats.safetyMs.push(now() - t1);
if (picked) {
this.queue.push(...picked.steps); this.lastPlan = [qe.slice()].concat(picked.fut); this.brakeRun = 0;
if (pickedSteps.length < picked.steps.length) this.stats.partial++;
this.queue.push(...pickedSteps);
/* 部分前缀时实际执行步数 < commit,一致性对齐的偏移不再成立 → 置空(Python 侧同轮次 14 的部分前缀满分对齐留待后续统一) */
this.lastPlan = pickedSteps.length === picked.steps.length ? [qe.slice()].concat(picked.fut) : null;
this.brakeRun = 0;
} else {
this.stats.brakes++; this.brakeRun++;
if (this.brakeTail.length) { this.queue.push(...this.brakeTail); this.brakeTail = []; }