From 76ac563d2161115ec6ce69b52543c9540337fc9e Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Tue, 20 Oct 2026 06:26:31 +0800 Subject: [PATCH] =?UTF-8?q?exp(dagger-deploy):=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E5=89=8D=E7=BC=80=EF=BC=88=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=88=B0=E9=A6=96=E4=B8=AA=E4=B8=8D=E5=AE=89=E5=85=A8=E6=AD=A5?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94=E5=88=B9=E5=81=9C=2027.4=E2=86=928.?= =?UTF-8?q?6=E3=80=81=E5=88=B0=E8=BE=BE=2038.3%=E3=80=81=E7=A2=B0=E6=92=9E?= =?UTF-8?q?=201.6%=EF=BC=9B=E6=96=B0=E5=A2=9E=20commit=5Fn=20=E5=8F=82?= =?UTF-8?q?=E6=95=B0=EF=BC=88=E6=AF=8F=E6=AD=A5=E8=A7=84=E5=88=92=E5=AE=9E?= =?UTF-8?q?=E9=AA=8C=EF=BC=89=EF=BC=9B=E6=9B=B4=E6=AD=A3=20replan=3Dempty?= =?UTF-8?q?=20=E5=AE=9E=E4=B8=BA=20r=3D0=20=E8=A7=84=E5=88=92=E9=9D=9E?= =?UTF-8?q?=E6=AF=8F=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/探索日志.md | 57 +++++++++++++++++++++++++++++++++++-------- ml/sim/closed_loop.py | 38 +++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/docs/探索日志.md b/docs/探索日志.md index 1d015e9..6725f0c 100644 --- a/docs/探索日志.md +++ b/docs/探索日志.md @@ -128,11 +128,12 @@ | dag1 best(轮次 6) | 1.2 | **23.4%** | 1.6% | **0.454** | 31.4 | 953 ms | - **结论**:部署分布 DAgger 把刹停降 18–40%、时延降 ~31 ms,但到达率未突破 23% 平台(-0.7 pp),属边际权衡改善;历史最佳到达仍是 dag1+gain1.2(23.4%)。 -- **下一步**:试每步重规划(replan='empty',让反馈逐步生效)与安全层候选 K=16(减少全拒刹停)。 +- **下一步**:试 replan=empty(r=0 规划,让反馈逐步生效)与安全层候选 K=16(减少全拒刹停)。 -## 轮次 9:每步重规划 / K=16 安全候选(exp: `exp/dagger-deploy`,新历史最佳) +## 轮次 9:replan=empty(队列空时规划、r=0)/ K=16 安全候选(exp: `exp/dagger-deploy`,新历史最佳) -- **假设**:反馈混合是逐步修正的,每 3 步重规划会滞后;每步重规划(replan='empty')让反馈逐步生效。K=16 给安全层更多候选、减少全拒。 +- **假设**:replan='empty'(每次队列空时规划、无前缀 r=0)比默认 r=3 更适合反馈混合。K=16 给安全层更多候选、减少全拒。 + (更正:replan='empty' 仍是每 3 步规划一次——队列空才规划,commit 3 步;收益来自 r=0 无前缀,不是规划频率。) - **判据**:部署口径 reach > 23.4% 且碰撞 ≤1.6%、刹停/时延不恶化。 - **配方**:dag2 best;`replan='empty'`(每次 r=0 规划);`K=16`。n=128,safety=True。 - **对照**:轮次 8(dag2+gain1.2:22.7% / 1.6% / 0.491 / 25.9 / 922 ms)。 @@ -144,12 +145,12 @@ | gain1.2 + K=16 | 21.1% | 1.6% | 0.451 | 19.4 | 1629 ms | | gain1.0 + K=16 | 21.9% | 4.7% | 0.506 | 14.6 | 1588 ms | -- **结论**:**每步重规划全面改善**(到达 +3.9 pp、碰撞 1.6→0.8%、刹停 25.9→20.6、时延 922→845 ms),成为**新历史最佳**。K=16 无收益且时延翻倍(1629 ms),淘汰。 -- **下一步**:每步重规划下重扫增益(0.8/1.0/1.5/2.0),找新最优。 +- **结论**:**replan=empty(r=0)全面改善**(到达 +3.9 pp、碰撞 1.6→0.8%、刹停 25.9→20.6、时延 922→845 ms),成为**新历史最佳**。K=16 无收益且时延翻倍(1629 ms),淘汰。 +- **下一步**:replan=empty 下重扫增益(0.8/1.0/1.5/2.0),找新最优。 -## 轮次 10:每步重规划下的增益重扫(exp: `exp/dagger-deploy`,新历史最佳) +## 轮次 10:replan=empty(r=0)下的增益重扫(exp: `exp/dagger-deploy`,新历史最佳) -- **假设**:反馈每步生效后,最优增益会上移。 +- **假设**:r=0 规划下,最优增益会上移。 - **判据**:reach > 26.6%(轮次 9)且碰撞 ≤1.6%;同时比较刹停/时延。 - **配方**:dag2 best,`replan='empty'`,gain ∈ {0.8, 1.0, 1.2, 1.5, 2.0},safety=True,n=128。 - **对照**:轮次 9(gain 1.2:26.6% / 0.8% / 0.453 / 20.6 / 845 ms)。 @@ -163,7 +164,7 @@ | **1.5** | **28.9%** | 0.8% | **0.411** | 28.5 | 844 ms | | 2.0 | 28.9% | 0.8% | 0.414 | 44.9 | 914 ms | -- **结论**:到达在 gain 1.5 封顶(28.9%),2.0 无收益且刹停翻倍(44.9)。**按到达优先/碰撞 ≤3%:历史最佳 = gain 1.5 + 每步重规划(28.9% / 0.8% / 0.411 / 28.5 / 844 ms)**;按碰撞优先:gain 1.0(22.7% / 0.0% / 0.489 / 15.2 / 810 ms)。n=128 的差异约 3–4 题,需 n=256 复核。 +- **结论**:到达在 gain 1.5 封顶(28.9%),2.0 无收益且刹停翻倍(44.9)。**按到达优先/碰撞 ≤3%:历史最佳 = gain 1.5 + replan=empty(28.9% / 0.8% / 0.411 / 28.5 / 844 ms)**;按碰撞优先:gain 1.0(22.7% / 0.0% / 0.489 / 15.2 / 810 ms)。n=128 的差异约 3–4 题,需 n=256 复核。 - **下一步**:n=256 复核 gain 1.0 / 1.2 / 1.5;若平台稳定,再决定是否 DAgger 第 3 轮(采集用新部署配置)。 ## 轮次 11:n=256 复核(发现前 128 题偏难) @@ -180,7 +181,7 @@ | 1.2 | 26.6% | **34.8%** | 1.2% | 0.392 | 18.6 | 752 ms | | 1.5 | 28.9% | **36.7%** | 1.6% | 0.323 | 27.4 | 760 ms | -- **结论**:前 128 题显著偏难;n=256 上 **gain 1.5 + 每步重规划 = 到达 36.7% / 碰撞 1.6% / ratio2s 0.323 / 刹停 27.4 / 760 ms**(新历史最佳)。gain 1.0 更保守(31.6% / 碰撞 0.8% / 刹停 15.0)。排名与 n=128 一致(1.5 > 1.2 > 1.0)。 +- **结论**:前 128 题显著偏难;n=256 上 **gain 1.5 + replan=empty = 到达 36.7% / 碰撞 1.6% / ratio2s 0.323 / 刹停 27.4 / 760 ms**(新历史最佳)。gain 1.0 更保守(31.6% / 碰撞 0.8% / 刹停 15.0)。排名与 n=128 一致(1.5 > 1.2 > 1.0)。 - **下一步**:n=512 复核 gain 1.2/1.5;随后评估 DAgger 第 3 轮(部署分布采集)与监控页回写。 ## 轮次 12:n=512 复核(稳定结论) @@ -196,10 +197,46 @@ | 1.2 | 34.8% | **32.6%** | 1.0% | 0.435 | 18.6 | 722 ms | | 1.5 | 36.7% | **33.4%** | 1.0% | 0.399 | 28.1 | 738 ms | -- **结论**:n=512 稳定在 **33% 左右**(n=256 略乐观);**历史最佳(n=512,部署口径)= gain 1.5 + 每步重规划:到达 33.4% / 碰撞 1.0% / ratio2s 0.399 / 刹停 28.1 / 738 ms**。保守版 gain 1.2:32.6% / 1.0% / 0.435 / 18.6 / 722 ms(刹停少 1/3)。 +- **结论**:n=512 稳定在 **33% 左右**(n=256 略乐观);**历史最佳(n=512,部署口径)= gain 1.5 + replan=empty:到达 33.4% / 碰撞 1.0% / ratio2s 0.399 / 刹停 28.1 / 738 ms**。保守版 gain 1.2:32.6% / 1.0% / 0.435 / 18.6 / 722 ms(刹停少 1/3)。 - **监控页回写**:`ml/monitor.py` 新增 `/api/explore`(读 docs/探索日志.md),`ml/monitor.html` 新增「闭环探索日志」卡片;Edge headless 截图确认渲染正常(http://127.0.0.1:8100)。 - **下一步**:候选选择器(consist/value/first)与 K=4 在反馈+每步重规划下是否影响到达/刹停。 +## 轮次 13:选择器 / K / 失败模式(exp: `exp/dagger-deploy`) + +- **假设**:反馈混合下,候选选择器与候选数 K 会影响到达/时延;失败模式可指导下一步。 +- **判据**:reach > 36.7% 或时延显著下降;同时报告 time_ok_1p3。 +- **配方**:dag2 best,replan='empty',gain 1.5,safety=True,n=256。 +- **对照**:轮次 11/12(consist K=8:36.7%)。 +- **指标**: + +| 配置 | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 | +|---|---|---|---|---|---| +| consist K=8 | 36.7% | 1.6% | 0.323 | 27.4 | 764 ms | +| value K=8 | 35.9% | 1.6% | 0.334 | 29.4 | 1063 ms | +| first K=8 | 36.3% | 1.2% | 0.338 | 28.1 | 780 ms | +| K=6 | 35.9% | 1.6% | 0.353 | 28.6 | 627 ms | +| K=4 | 31.6% | 1.2% | 0.396 | 32.6 | 482 ms | + +- **失败模式(n=256,consist K=8)**:失败 149 题中 **碰撞仅 3 题(1.2%)、超时无碰撞 146 题(57.0%)**;失败题终点误差 p50 140 mm;`time_ok_1p3` 仅 **13.3%**;成功题 t_done p50 2.74 s(时限 p50 3.4 s);刹停 27.4 次/题。 +- **结论**:选择器无差别(consist 略优);K=8 到达最佳,K=6 省 18% 时延(到达 -0.8 pp)。**当前真正的瓶颈是时间效率(频繁刹停→超时),不是碰撞**。§9.2 的「用时 ≤ 老师×1.3」当前只有 13.3%。 +- **下一步**:每步重规划下,安全层只需认证第 1 步(现在验 3 步,过度保守→全拒→刹停);改 `safety_steps=1` 验证能否在保持低碰撞的同时大幅减少刹停。 + +## 轮次 14:部分安全前缀(exp: `exp/dagger-deploy`,刹停大降) + +- **假设**:安全层现在要求 3 步全安全才执行,候选只要第 2–3 步有碰撞就全拒刹停;改为「执行到第一个不安全步为止」可减少刹停。 +- **判据**:刹停显著下降、到达不降、碰撞 ≤3%。 +- **配方**:dag2 best,replan='empty',gain 1.5,safety=True,`safety_partial=1`;n=256。 +- **对照**:全窗口(轮次 11/13:36.7% / 1.6% / 0.323 / 27.4 / 775 ms)。 +- **指标**: + +| 安全层 | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 | +|---|---|---|---|---|---|---| +| 3 步全安全 | 36.7% | 13.3% | 1.6% | 0.323 | 27.4 | 775 ms | +| **部分安全前缀** | **38.3%** | 13.7% | 1.6% | **0.308** | **8.6** | 770 ms | + +- **结论**:刹停 **27.4→8.6(-69%)**、到达 +1.6 pp、碰撞不变——**部分安全前缀有效**(新最佳:38.3% / 1.6% / 0.308 / 8.6 / 770 ms)。但 `time_ok_1p3` 仍 13.7%:说明超时主因不是刹停,而是**执行粒度**——部分前缀的填充零步仍在消耗时间。 +- **下一步**:commit_n=1(每次只提交 1 步、每步规划,无填充步),验证时间效率。 + ## 轮次统计 - 连续无实质进展轮数:0(轮次 5 出现实质进展:reach 0→20.3%)。 diff --git a/ml/sim/closed_loop.py b/ml/sim/closed_loop.py index 68b406c..9e87c6a 100644 --- a/ml/sim/closed_loop.py +++ b/ml/sim/closed_loop.py @@ -142,7 +142,7 @@ class TeacherOracle: @torch.no_grad() def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, return_traj=False, use_sub=True, sub_override=None, qp0=None, steps=1, z_scale=1.0, select='consist', oracle=None, replan='r3', sub_rule='cross', - fb_gain=0.0, fb_scale=0.0, fb_near=0.2, fb_fallback=0.0): + fb_gain=0.0, fb_scale=0.0, fb_near=0.2, fb_fallback=0.0, safety_partial=0, commit_n=3): """use_sub=False:不用子目标(sf=0);sub_override=(subs [B,M,6] 绝对关节角, n [B] 个数 或 need [B] bool):冷启动时用给定途经点代替子目标头; qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放; select:consist(部署口径:冷启动价值前瞻、之后与上一条已提交计划的一致性)/ value(每次都价值前瞻)/ first(不选,取第 0 条)。 @@ -227,34 +227,52 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, # 新候选 fut[0] 是 qe_new 再走 1 步 = 对应 last[4]。JS 部署(ik-policy.js)同一处同样修正。 n = min(fut.shape[2], last.shape[1] - 4) w = 0.85 ** torch.arange(n, device=device, dtype=torch.float32) - d2 = (((fut[:, :, :n] - last[:, None, 4:4 + n]) / step) ** 2).sum(-1) + d2 = (((fut[:, :, :n] - last[:, None, commit_n + 1:commit_n + 1 + n]) / step) ** 2).sum(-1) # 2026-10-19(审查 review_closedloop F5):上一次是刹停或 DLS 接管的行没有「已提交计划」, # 与 JS(lastPlan = null → 一致性项缺席、各候选同分 → 取第 0 条)一致:同分,稳定排序取第 0 条 score = torch.where(last_ok[:, None], -(d2 * w).sum(-1), torch.zeros_like(d2[..., 0])) order = torch.sort(score, dim=-1, descending=True, stable=True).indices pick = order[:, 0] - commit = inc[ar, pick, r:r + 3] + commit = inc[ar, pick, r:r + commit_n] committed = torch.ones(B, dtype=torch.bool, device=device) - if safety: - ok_any = torch.zeros(B, dtype=torch.bool, device=device) - chosen = torch.zeros(B, 3, 6, device=device); chosen_k = pick.clone() + if safety and safety_partial: # 2026-10-20:允许「部分安全前缀」——执行到第一个不安全步为止,而不是全拒刹停 + maxlen = torch.zeros(B, dtype=torch.long, device=device) + chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone() for kk in range(K): - ck = inc[ar, order[:, kk], r:r + 3] + ck = inc[ar, order[:, kk], r:r + commit_n] pts = qe[:, None] + torch.cumsum(ck, 1) mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck allq = torch.cat([pts, mids], 1).reshape(-1, 6) - okk = (kin.gaps(allq, 0.5)['gm'].min(-1).values > 0).view(B, 6).all(-1) + ok6 = (kin.gaps(allq, 0.5)['gm'].min(-1).values > 0).view(B, 6) + okstep = ok6[:, :commit_n] & ok6[:, commit_n:] # 每步:步点与其中点都安全 + plen = torch.cumprod(okstep.long(), 1).sum(1) # 安全前缀长度 0..3 + 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) + msk = (torch.arange(commit_n, device=device)[None] < maxlen[:, None]) + commit = torch.where(msk[..., None], chosen, torch.zeros_like(chosen)) + brakes += (maxlen == 0).float() + pick = chosen_k; committed = maxlen > 0 + elif safety: + ok_any = torch.zeros(B, dtype=torch.bool, device=device) + chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone() + for kk in range(K): + ck = inc[ar, order[:, kk], r:r + commit_n] + pts = qe[:, None] + torch.cumsum(ck, 1) + mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck + allq = torch.cat([pts, mids], 1).reshape(-1, 6) + okk = (kin.gaps(allq, 0.5)['gm'].min(-1).values > 0).view(B, 2 * commit_n).all(-1) take = okk & ~ok_any chosen = torch.where(take[:, None, None], ck, chosen); chosen_k = torch.where(take, order[:, kk], chosen_k); ok_any |= okk commit = torch.where(ok_any[:, None, None], chosen, torch.zeros_like(chosen)) if fb_gain > 0 and fb_fallback > 0: # 全拒时的兜底:半速朝目标走一步(须通过安全认证),避免原地停滞 fb1 = dls_steps(kin, qe, T, R, n=1) * fb_fallback ok1 = kin.gaps(qe + fb1[:, 0], 0.5)['gm'].min(-1).values > 0 - commit = torch.where((~ok_any & ok1)[:, None, None], fb1.expand(-1, 3, -1), commit) + commit = torch.where((~ok_any & ok1)[:, None, None], fb1.expand(-1, commit_n, -1), commit) brakes += (~ok_any).float() pick = chosen_k; committed = ok_any if near.any(): - ref = dls_steps(kin, qe, T, R) + ref = dls_steps(kin, qe, T, R, n=commit_n) commit = torch.where(near[:, None, None], ref, commit) dls_it = torch.where(near & (dls_it < 0), torch.full_like(dls_it, it), dls_it) committed = committed & ~near