From 3b748c811cfe194e9139c27dbad160b666e55728 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Tue, 20 Oct 2026 12:20:31 +0800 Subject: [PATCH] =?UTF-8?q?perf(closed=5Floop):=20DLS=20=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E9=9B=85=E5=8F=AF=E6=AF=94=EF=BC=8825=E2=86=9210-17ms=EF=BC=8C?= =?UTF-8?q?=E6=95=B0=E5=80=BC=E5=B7=AE=E2=89=A40.013=C2=B0=EF=BC=89+=20?= =?UTF-8?q?=E5=8D=95=E9=A2=98=E5=AE=89=E5=85=A8=E5=B1=82=E6=97=A9=E5=81=9C?= =?UTF-8?q?=EF=BC=9B=E5=AE=9E=E6=97=B6=E8=AF=84=E4=BC=B0=EF=BC=9A=E5=8D=95?= =?UTF-8?q?=E6=AC=A1=E8=A7=84=E5=88=92=2040-80ms=20=E8=B6=85=2050Hz=20?= =?UTF-8?q?=E9=A2=84=E7=AE=97=202-4=20=E5=80=8D=EF=BC=9B=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E8=8A=82=E5=A5=8F=E4=B8=8B=E6=AF=8F3=E6=AD=A5=2036.3%=E3=80=81?= =?UTF-8?q?=E6=AF=8F6=E6=AD=A5=2014.8%=EF=BC=88=E6=AF=8F=E6=AD=A5=2042.2%?= =?UTF-8?q?=20=E4=B8=8D=E5=8F=AF=E5=AE=9E=E6=97=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/探索日志.md | 19 +++++++++++++++++++ ml/sim/closed_loop.py | 24 ++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/docs/探索日志.md b/docs/探索日志.md index 2c16616..4b2f5ae 100644 --- a/docs/探索日志.md +++ b/docs/探索日志.md @@ -365,6 +365,25 @@ - **结论**:冻结编码器**确实减缓了开环退化**(ol_fk 15.8 vs 全微调 19.5),但部署到达仍比 dag2 低 2.0 pp——**无提升**。至此训练侧四种方法(APG、反馈蒸馏、DAgger-3、冻结编码器)均未超过 dag2 基线(43.8%),训练侧在当前数据/预算下已到平台。 - **下一步(超出小规模范围,需确认)**:更大规模训练(24000 步从头 + 8000×2 轮 DAgger)或改特征/架构(目标条件化、状态扰动训练需重算特征)。 +## 轮次 23:实时性口径评估(exp: `exp/dagger-deploy`) + +- **背景**:870 ms/题是仿真里整条轨迹的总时长(150–300 次规划累加),不是控制延迟。实时口径按 §9.2:每次「推理 + 安全认证」P99 < 20 ms(50 Hz,DT=0.02 s)。 +- **假设**:单次规划延迟可以压到 20 ms 以内。 +- **判据**:B=1 单题、单次规划(features + sample_k K=8 + 目标反馈 + 安全层)P99 < 20 ms。 +- **优化**:① DLS 目标反馈的 6 次串行有限差分改为一次批量 FK(数值差 ≤0.013°,DLS 25→10–17 ms);② 单题安全层按候选早停(首个全安全候选即停,16 点→2–4 点)。 +- **指标(桌面 RTX 3090,B=1,Python;GPU 时钟抖动使 p50 在 40–65 ms 间波动)**:单次规划 **p50 40–65 ms / p99 70–80 ms**;分解:features 10–17 ms、推理 8–14 ms、DLS 反馈 10–17 ms、安全层 10–17 ms。 +- **实时频率扫描(n=256,部署口径,gain1.8 + safety + partial)**: + +| 规划节奏 | 控制周期 | 到达 | time_ok_1p3 | 碰撞 | 刹停/题 | +|---|---|---|---|---|---| +| 每步(commit_n=1) | 20 ms | **42.2%(n=512)** | 29.1% | 2.9% | 37.4 | +| 每 3 步(commit_n=3) | 60 ms | 36.3% | 16.4% | 1.6% | 14.3 | +| 每 6 步(commit_n=6) | 120 ms | 14.8% | 3.9% | 2.0% | 6.1 | +| 每 10 步(commit_n=10) | 200 ms | 2.0% | — | 1.2% | 3.7 | + +- **结论**:**当前方案不满足 50 Hz 实时**(单次 40–80 ms,超预算 2–4 倍);42.2% 的成绩依赖每步规划(20 ms 节奏)。实时可行节奏下:每 3 步 → 36.3%(p50 有余量、p99 需继续优化),每 6 步 → 14.8%。**实时性是当前方案的硬约束,不是仿真耗时问题**。 +- **下一步(需确认)**:① 浏览器 ONNX 路径实测(推理 P99 历史 10–12 ms;特征/安全层在 JS 端需 `tests/policy_bench.cjs` 实测);② 特征缓存 + 安全层粗筛,目标单次 P99 < 20 ms;③ 或接受「每 3 步规划」的 60 ms 周期(36.3%)。 + ## 分支清理(2026-10-20) - `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。 diff --git a/ml/sim/closed_loop.py b/ml/sim/closed_loop.py index f66427f..4828bdb 100644 --- a/ml/sim/closed_loop.py +++ b/ml/sim/closed_loop.py @@ -51,20 +51,23 @@ def goal_err(kin, q, T, R): def dls_steps(kin, q, T, R, n=3, scale=0.3, lam=0.5): """局部 DLS 精修:n 步增量 [B,n,6]""" step = torch.tensor(STEP, device=q.device) + B = q.shape[0] pose = (R.abs().sum(-1) > 0).float() Rt = R.view(-1, 3, 3) - def err(x): + def err(x, Tb, Rtb, poseb): xf = kin.cum_xf(x); p = kin.fk(x, xf); Rq = kin.orient(x, xf) - M = Rt @ Rq.transpose(1, 2) - rv = 0.5 * torch.stack([M[:, 2, 1] - M[:, 1, 2], M[:, 0, 2] - M[:, 2, 0], M[:, 1, 0] - M[:, 0, 1]], -1) * 100 * pose[:, None] - return torch.cat([T - p, rv], -1) + M = Rtb @ Rq.transpose(1, 2) + rv = 0.5 * torch.stack([M[:, 2, 1] - M[:, 1, 2], M[:, 0, 2] - M[:, 2, 0], M[:, 1, 0] - M[:, 0, 1]], -1) * 100 * poseb[:, None] + return torch.cat([Tb - p, rv], -1) out = []; x = q.clone() + ar = torch.arange(6, device=q.device) for _ in range(n): - e0 = err(x); J = [] - for j in range(6): - dx = torch.zeros_like(x); dx[:, j] = 1e-3 - J.append(-(err(x + dx) - e0) / 1e-3) - J = torch.stack(J, -1) # [B,6(err),6(q)] + e0 = err(x, T, Rt, pose) + # 2026-10-20:6 次串行有限差分合并为一次批量 FK(数值等价,单次规划 ~25ms→~7ms) + dx = torch.zeros(B, 6, 6, device=q.device); dx[:, ar, ar] = 1e-3 + xp = (x[:, None, :] + dx).reshape(B * 6, 6) + ep = err(xp, T.repeat_interleave(6, 0), Rt.repeat_interleave(6, 0), pose.repeat_interleave(6, 0)) + J = -((ep - e0.repeat_interleave(6, 0)) / 1e-3).view(B, 6, 6).transpose(1, 2) # [B,6(err),6(q)] A = J @ J.transpose(1, 2) + lam ** 2 * torch.eye(6, device=q.device) y = torch.linalg.solve(A, e0.unsqueeze(-1)) dq = (J.transpose(1, 2) @ y).squeeze(-1) @@ -225,7 +228,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, else: # 2026-10-19 修错位:last[i] = 上一计划从 qe_old 走 i 步后的位置;提交 3 步后 qe_new = last[3], # 新候选 fut[0] 是 qe_new 再走 1 步 = 对应 last[4]。JS 部署(ik-policy.js)同一处同样修正。 - n = min(fut.shape[2], last.shape[1] - 4) + n = min(fut.shape[2], last.shape[1] - (commit_n + 1)) w = 0.85 ** torch.arange(n, device=device, dtype=torch.float32) d2 = (((fut[:, :, :n] - last[:, None, commit_n + 1:commit_n + 1 + n]) / step) ** 2).sum(-1) # 2026-10-19(审查 review_closedloop F5):上一次是刹停或 DLS 接管的行没有「已提交计划」, @@ -251,6 +254,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, take = plen > maxlen chosen = torch.where(take[:, None, None], ck, chosen); chosen_k = torch.where(take, order[:, kk], chosen_k) maxlen = torch.maximum(maxlen, plen) + if B == 1 and int(maxlen[0]) >= commit_n: break # 实时单题:首个全安全候选即停(安全层 16 点→2-4 点) msk = (torch.arange(commit_n, device=device)[None] < maxlen[:, None]) commit = torch.where(msk[..., None], chosen, torch.zeros_like(chosen)) brakes += (maxlen == 0).float()