exp(dagger-deploy): 部分安全前缀(执行到首个不安全步)——刹停 27.4→8.6、到达 38.3%、碰撞 1.6%;新增 commit_n 参数(每步规划实验);更正 replan=empty 实为 r=0 规划非每步

This commit is contained in:
cbb
2026-10-20 06:26:31 +08:00
parent de009bd516
commit 76ac563d21
2 changed files with 75 additions and 20 deletions
+47 -10
View File
@@ -128,11 +128,12 @@
| dag1 best(轮次 6) | 1.2 | **23.4%** | 1.6% | **0.454** | 31.4 | 953 ms | | 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%)。 - **结论**:部署分布 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%、刹停/时延不恶化。 - **判据**:部署口径 reach > 23.4% 且碰撞 ≤1.6%、刹停/时延不恶化。
- **配方**:dag2 best;`replan='empty'`(每次 r=0 规划);`K=16`。n=128,safety=True。 - **配方**: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)。 - **对照**:轮次 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.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 | | 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),淘汰。 - **结论**:**replan=empty(r=0)全面改善**(到达 +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 下重扫增益(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%;同时比较刹停/时延。 - **判据**: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。 - **配方**: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)。 - **对照**:轮次 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 | | **1.5** | **28.9%** | 0.8% | **0.411** | 28.5 | 844 ms |
| 2.0 | 28.9% | 0.8% | 0.414 | 44.9 | 914 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 轮(采集用新部署配置)。 - **下一步**:n=256 复核 gain 1.0 / 1.2 / 1.5;若平台稳定,再决定是否 DAgger 第 3 轮(采集用新部署配置)。
## 轮次 11:n=256 复核(发现前 128 题偏难) ## 轮次 11:n=256 复核(发现前 128 题偏难)
@@ -180,7 +181,7 @@
| 1.2 | 26.6% | **34.8%** | 1.2% | 0.392 | 18.6 | 752 ms | | 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 | | 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 轮(部署分布采集)与监控页回写。 - **下一步**:n=512 复核 gain 1.2/1.5;随后评估 DAgger 第 3 轮(部署分布采集)与监控页回写。
## 轮次 12:n=512 复核(稳定结论) ## 轮次 12:n=512 复核(稳定结论)
@@ -196,10 +197,46 @@
| 1.2 | 34.8% | **32.6%** | 1.0% | 0.435 | 18.6 | 722 ms | | 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 | | 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)。 - **监控页回写**:`ml/monitor.py` 新增 `/api/explore`(读 docs/探索日志.md),`ml/monitor.html` 新增「闭环探索日志」卡片;Edge headless 截图确认渲染正常(http://127.0.0.1:8100)。
- **下一步**:候选选择器(consist/value/first)与 K=4 在反馈+每步重规划下是否影响到达/刹停。 - **下一步**:候选选择器(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%)。 - 连续无实质进展轮数:0(轮次 5 出现实质进展:reach 0→20.3%)。
+28 -10
View File
@@ -142,7 +142,7 @@ class TeacherOracle:
@torch.no_grad() @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, 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', 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):冷启动时用给定途经点代替子目标头; """use_sub=False:不用子目标(sf=0);sub_override=(subs [B,M,6] 绝对关节角, n [B] 个数 或 need [B] bool):冷启动时用给定途经点代替子目标头;
qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放; qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放;
select:consist(部署口径:冷启动价值前瞻、之后与上一条已提交计划的一致性)/ value(每次都价值前瞻)/ first(不选,取第 0 条)。 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)同一处同样修正。 # 新候选 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] - 4)
w = 0.85 ** torch.arange(n, device=device, dtype=torch.float32) 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 接管的行没有「已提交计划」, # 2026-10-19(审查 review_closedloop F5):上一次是刹停或 DLS 接管的行没有「已提交计划」,
# 与 JS(lastPlan = null → 一致性项缺席、各候选同分 → 取第 0 条)一致:同分,稳定排序取第 0 条 # 与 JS(lastPlan = null → 一致性项缺席、各候选同分 → 取第 0 条)一致:同分,稳定排序取第 0 条
score = torch.where(last_ok[:, None], -(d2 * w).sum(-1), torch.zeros_like(d2[..., 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 order = torch.sort(score, dim=-1, descending=True, stable=True).indices
pick = order[:, 0] 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) committed = torch.ones(B, dtype=torch.bool, device=device)
if safety: if safety and safety_partial: # 2026-10-20:允许「部分安全前缀」——执行到第一个不安全步为止,而不是全拒刹停
ok_any = torch.zeros(B, dtype=torch.bool, device=device) maxlen = torch.zeros(B, dtype=torch.long, device=device)
chosen = torch.zeros(B, 3, 6, device=device); chosen_k = pick.clone() chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone()
for kk in range(K): 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) pts = qe[:, None] + torch.cumsum(ck, 1)
mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck
allq = torch.cat([pts, mids], 1).reshape(-1, 6) 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 take = okk & ~ok_any
chosen = torch.where(take[:, None, None], ck, chosen); chosen_k = torch.where(take, order[:, kk], chosen_k); ok_any |= okk 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)) commit = torch.where(ok_any[:, None, None], chosen, torch.zeros_like(chosen))
if fb_gain > 0 and fb_fallback > 0: # 全拒时的兜底:半速朝目标走一步(须通过安全认证),避免原地停滞 if fb_gain > 0 and fb_fallback > 0: # 全拒时的兜底:半速朝目标走一步(须通过安全认证),避免原地停滞
fb1 = dls_steps(kin, qe, T, R, n=1) * fb_fallback fb1 = dls_steps(kin, qe, T, R, n=1) * fb_fallback
ok1 = kin.gaps(qe + fb1[:, 0], 0.5)['gm'].min(-1).values > 0 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() brakes += (~ok_any).float()
pick = chosen_k; committed = ok_any pick = chosen_k; committed = ok_any
if near.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) commit = torch.where(near[:, None, None], ref, commit)
dls_it = torch.where(near & (dls_it < 0), torch.full_like(dls_it, it), dls_it) dls_it = torch.where(near & (dls_it < 0), torch.full_like(dls_it, it), dls_it)
committed = committed & ~near committed = committed & ~near