Files
Robot_Arm/docs/探索日志.md
T
cbb 219ed19de4 exp+eval: 轮次51-52——2x2 补全 + 逐题老师用时落盘;1.3x 口径的时限收益其实是显著的
轮次51 (2x2 缺失格 K16+pre):最好格 到达 0.5664 / 时限内 0.3594 / 2s误差比 0.1382 /
行程比 2.4142 / 碰撞 0 / 超VMAX 0。主效应:pre 在 K8 +6.64pp、K16 +6.25pp(时限),
K16 在 endpoint +3.13pp、pre +2.73pp ⇒ 时限可加无交互;到达饱和(pre 在 K16 上净+1题 p=1.0)。
最好格 vs 历史控制逐题赢20丢9 净+11,McNemar p=0.061 ⇒ 到达仍未确立。

轮次52 (仪表):dump 补 t_exp/tr_ref(题目属性,与臂无关 ⇒ 一条臂即可给全部历史臂补配对)。
改完落盘代码先自证行为未动:同配方控制臂 22/22 老键逐位相同;limit=2*t_exp 实测对上
⇒ "到达率自带 2x 时限"从推断变成实测。

逐题配对(vs K8 endpoint, 判据=ok 且 t_done<=1.3*t_exp):
K16pre 27赢3丢 净+24 p=8.4e-6;K8pre 22/5 净+17 p=0.0015;门控5 23/6 p=0.0023;
门控20 22/6 p=0.0037;h=4 15/5 p=0.041;K16endpoint 17/9 p=0.169;h=8 11/7 p=0.481;
tie=1 3/0 p=0.25。
⇒ 自我纠正:轮次46-51 我一直拿 success(那道 2x 门)做配对,把"时限内"写成没有配对支持。
现在两栏分开:到达未确立(p>=0.06),时限内已确立(p=0.0015/8.4e-6)。
副产物:K16+endpoint 的 +3.1pp 时限当初没有配对检验,实测 p=0.169 不显著,已在轮次52记下。
2026-10-21 01:48:46 +08:00

1389 lines
184 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 探索日志:Transformer 机械臂闭环控制
长期锚点:`docs/Transformer轨迹拟合训练方案.md` §9.2(部署形态 = 策略 + 安全层)。
验收口径(§9.2):实际执行碰撞 = 0;held-out 可达任务到达率 ≥ 98%;到达用时 ≤ 老师 ×1.3;关节行程 ≤ 现有规划器 ×1.10;冷启动 < 10 ms;每次推理 + 安全认证 P99 < 20 ms;刹停发生率 < 2% 的推理周期;策略单独(关安全层)碰撞率 < 3%。
记录顺序:**倒序**,最新一轮在最上面(2026-10-20 起,用户要求:打开就看到最近一轮)。
评测约定:val 任务、部署节奏(r = 3 重规划、1 段采样、K=8);「纯策略」= 关安全层;「部署」= 开安全层。时延:桌面 RTX 3090 上 `sample_k(B=64, K=8)` 墙钟均值(均值,非 P99);浏览器 ONNX P99 以 §9.2 R4 的独立测量为准。
**口径更正(2026-10-20,轮次 29 复核;下列历史原始数值保留):**
- 轮次 24–28 的“50 Hz 可行/连续无饥饿”结论撤回。同步 `await plan(); tick()` 不是实际 20 ms 调度;`starveTicks` 只是耗时除以 20 ms 的估算。组件分位数相加不等于整次规划 P99,实测 62–68 ms 也超过 commit=3 的 60 ms。仍须严格认证下的真查看器 R9 测量,且不放松 §9.2 的 P99 < 20 ms。
- Python 端点/中点检查不等于连续认证;JS `--seg 0` 是放松安全的消融,不是部署验收。开安全层碰撞必须为 0,不能套用“纯策略 <3%”判据。既有到达率允许途中碰撞,新指标 `collision_free_success` 才是时限内无碰撞到达率。
- `replan=empty` 是同步仿真的零前缀配方,不是已验证的异步执行配置。所有任务(含 D)继续保留在历史对照分母内;不能与 held-out 可达任务 ≥98% 直接比较。
- 轮次 26 的几何角速度雅可比不等于远离目标时 skew 姿态残差的精确导数,因而既改变控制律又消除差分误差;不能把全部收益归因于去噪。3.58% 是单个位置雅可比样本对拍,不是普遍噪声界。`check_dls.py` 中标 B=256 的测量实际仅有 64 行,不能引用为 B=256 性能。
- `err_fail_med` 是批次结束时未按时到达任务的末端误差,不是每题截止时误差;仿真墙钟/题也不是单次规划延迟。查看器默认选择器仍为 consist,endpoint 仅是可选实验。
## 轮次统计
- 连续无实质进展轮数:0。
- **当前最好(n=256 val,同 seed 同批)**:`ml/runs/base_dag2_dag1/best.pt` + `K=8, select=endpoint, replan=empty, commit_n=2, fb=2.2, fb_block_gain=0.5, dls_safety=1, safety=1` → **到达 57.4%、无碰撞到达 57.4%、时限内(≤1.3×老师) 39.1%、刹停 19.25/题、行程比 2.445、2 s 误差比 0.210**(轮次 33 base)。
- **但这组数含超速**:`speed_ratio_p50=1.660`、`over_vmax_rate=1.000` ⇒ 每轴实际最大速度是 VMAX 的 1.66 倍(反馈叠在 ±1 限幅之后,轮次 30 定位)。按合法限速 `--inc_cap 1` 重测是 **到达 52.3% / 时限内 26.6%**(轮次 33 cap)。**验收要引用后者**,两者的差就是"以前的分数有一部分是超速换来的"。
- n=512 复核在轮次 28–30 给过 50.8–52.1%;当前配方(含 `inc_cap`)尚未在 n=512 重跑。
- **合法限速口径下的当前最好格(轮次 51,2×2 补全后)**:`K=16 + select=pre + commit_n=2 + fb=2.2 + inc_cap=1 + cert_batch=1` → **到达 56.6%、时限内 35.9%、2 s 误差比 0.138、行程比 2.414、刹停 14.20/题、碰撞 0、超 VMAX 0**。相对历史控制(K8 endpoint 0.5234/0.2656)逐题赢 20 丢 9 = 净 +11 题,**McNemar p=0.061 ⇒ 到达仍未确立**;`goal_ratio_2s` 201:55(p=1.0e−20)是这条线最硬的证据。两条杠杆**在时限上可加(无交互)、在到达上饱和**(K16 之上再加 `pre` 只净 +1 题,p=1.0)。
- **口径提醒(对所有历史数字生效)**:`ok = done & (t_done ≤ max(2×老师用时, 3 s))` ⇒ 本链引用的"到达率"本身就带 2× 时限,`time_ok_1p3` 才是 §9.2 的 1.3× 口径,两道门不能混着引用。
- **实时性(GPU,B=1、K=8、一步)**:整周期 P50 33.5 ms、P99 64.7 ms;批量认证把最坏情况从 80.2 → 37.1 ms(轮次 34)。仍未进 §9.2 的 P99 < 20 ms,瓶颈是几何查询不是推理(推理 7.2 ms)。
- **安全层口径**:线上只做**端点 + 中点**点检(`gaps(...).min(-1) > 0`)。轮次 30 那条"认证通过仍碰撞"(−0.149 mm)已被连续段认证抓住(`certify_uniform` n≥8、二分 5 次查询),但它进不了实时预算(轮次 31–32)。
- **失败结构(轮次 38,147 到达 / 109 未到)**:**63 题是走一半被冻住**(离目标 >100 mm 且刹车 >30 次)、23 题方向错、位置差 20~100 mm 15 题、差 5~20 mm 5 题、姿态没到位 3 题、起步失败 0;未到组活动窗内 37% 的步是零运动。
- **与 §9.2 的差距**:到达 52.3% vs ≥98%;时限内 26.6% vs 100%;刹停远大于 2% 的推理周期;P99 未达标。碰撞口径已达标(执行 0)。
- **已试过的训练法(轮次 19–22)都没超过 dag2 基线**:APG(管线修复但尺度失控)、反馈蒸馏(未内化)、DAgger-3(39.5%)、冻结编码器(41.8%)⇒ 再提到达率优先走"推理/选择/安全层"这条,而不是先重训。
- **规矩**:大规模训练与扩数据都要用户放行;不许用放松碰撞/安全/精度/实时来换分数;负结论与本表一起留档(本文件就是报告系统,`ml/monitor.py` 的 `/api/explore` 直接渲染它)。
## 算法优化候选(按实测证据强度排序,2026-10-20)
| 序 | 候选 | 支撑证据 | 状态 |
|---|---|---|---|
| 0 | ~~把"沿路最小间隙"接进候选打分~~(极端版 = `--tie 1`:只在"前缀最长"的并列集合内按余量定夺,零额外几何) | 轮次 45 两臂:到达 −1.95 pp(净 −5 题,McNemar p=0.125 与零不可分)、时限 +1.17 pp、刹停 −9%、行程 −0.076、**碰撞仍 0**;且分辨率被 `gcap=0.5 mm` 截顶锁死(并列 79% 但只有 5.6% 可分辨,与轮次 44 离线 5.6% 对上) | **判掉**:买余量卖进度,且作用在 `maxlen>0` 的人群——失败主人群是全拒(`maxlen=0`)。前置条件见 #28 |
| 0b | **#28 抬宽相位截顶 `gcap` 并与 `gap_margin` 解耦** | 现在 `--gap_margin 1.5` 会同时抬截顶值和验收门限,动一个必动另一个;`_coarse` 保留 `d ≤ cp_lim + cap` 的胶囊对 ⇒ 成本随壳层体积涨,而 GPU 周期 P50 已 33.5 ms | 未实现(要新增只改数值精度的参数,不许碰判据) |
| 1 | **候选数翻倍 K=8→16** | 轮次 39 实测:到达 **+3.9 pp**、时限内 **+3.1 pp**、**刹车 −30%**(19.55→13.77)、碰撞仍 0、行程比不变 ⇒ 很多"全拒"是**没备选**而不是真没路 | **正向**;认证/打分成本要用独占微基准量完才能谈部署 |
| 2 | ~~全拒时缩步顶上~~(`--backoff`) | 四臂实测:转化率 7.7%→37%(能救回停步),但到达 0.5234→0.5156 **不赚**;且它单独把 `brake_rate` 改善 16% 而行为没变好 ⇒ **可以绕过 §9.2 的刹停判据**,这是仪表漏洞不是收益 | **已判掉**(轮次 43);同时把 §9.2 的刹停口径标记为"不可由缩步单独满足" |
| 3 | ~~白费比例惩罚~~(`select=ep_waste --len_w`) | 三臂实测行程比不降反升,给出原因:**罚的是没被执行的那段地平线**(16 步打分地平线 vs 实际提交 2 步) | **已判掉**(轮次 42);其诊断直接引出 #29(把打分地平线换成真会执行到的位置) |
| 3c | **#29 打分地平线 = 被执行的前缀**(`select=pre`:用 `fut[:,:,commit_n]` 的终点误差代替 `fut[:,:,-1]`) | 两 seed 独立复现:2 s 误差比符号检验 192:64(p=5e−16)/ 191:65(p=1.5e−15),时限内 +6.64/+4.30 pp,行程比 −0.171/−0.171,**碰撞仍 0**;到达只 +5/+3 题(McNemar p=0.42/0.68 ⇒ 噪声);代价是到达题末态误差 0.22→0.42 mm(判据壳内但余量 4.6×→2.4×) | **正向(时限/进度显著、到达未显著)**,作可选口径入库,不改默认。下一试 #30 |
| 3d | ~~#30 前瞻长度曲线~~(`pre_h`,h=2/4/8/16) | 轮次 48 四点实测:时限内 0.2656/0.2813/0.3047/**0.3320**,2 s 误差比符号检验随 h 缩短单调增强(p=2.4e−6/1.8e−12/4.9e−16),**到达四点全在噪声内**;末态误差 0.2175/0.2255/**0.3301/0.4151** ⇒ 代价集中在 h≤4;h=8 是坏点(行程比 2.6749 反而最差) | **已完成**:形状=进度单调、精度阶跃在 h≤4。作废 #26,派生 **#31** |
| 3e | **#31 门控地平线**(远端用短前瞻、近终段用整条地平线,一个阈值参数) | 轮次 48 的曲线直接给出机制:时限收益来自短前瞻,精度损失来自"近终段没有终端代价"。若两者能解耦,`time_ok` 与 `err_success_med` 就不必此消彼长 | 待实现(`pre_h` 已在,只差按当前误差选 h) |
| 3b | ~~行程惩罚 `ep_len`(罚候选累计行程)~~ | 三臂实测行程比 2.6216/2.6548/2.6719 vs 对照 2.6158 ⇒ **不降反升**,只换来 +2 pp 时限 | **负结论**,被 3 取代 |
| 4 | **一步角点的标定**(E1 那条线:`mf_target='mid'` 把自举导数移到中点) | 解析高斯场判据:`sq` 只线性收敛(结构性少修一半)、`mid/lin` 二次收敛;实测部署角偏差 MF 3.851 → `mid` 2.364(0.61×);但**线上一步输出比自身两段采样离老师远 10 倍**(4.326 vs 0.434) | 开关已落、默认关;要付训练,且受"一步与认证/DAgger 共同标定"约束 |
| 5 | **认证进 20 ms**(#16/#18) | GPU 周期 P50 33.5 ms,几何宽相位占大头;f32 认证需先抬 eps 到 2e−4 mm | 未解,是验收硬门槛 |
| 6 | 速度/余量口径(#11/#14) | `--inc_cap 1` 代价 −5.1 pp 到达、−12.5 pp 时限;`gap_margin 0.5 mm` 只付 3 题 | 已量化,投影式余量未移植 |
| 7 | 循环内共享 FK(#19)、FP16/BF16、流水线复用 | 实测分别只有 0.03 ms/次、负收益、−3.1 ms 且扰动特征 | **已判掉** |
## 轮次 39:两条没人碰过的算法杠杆——候选档位与行程惩罚(机制已单测,A/B 排队)
- **为什么是这两条**:轮次 38 的失败分类说清楚了两件事——① 到达率的缺口在"K 条候选全被拒 ⇒ 原地冻住"(63/109),② §9.2 要"关节行程 ≤ 规划器×1.10",实测行程比中位 **2.4~2.6**,而现在**没有任何机制在管行程**(选择器只看末态误差,安全层只看间隙)。另外 `--steps` 那组数顺带给了一条线索:多段推理把行程比从 2.62 拉到 1.87,说明行程主要由候选选择/动作形状决定,不是物理必然。
- **杠杆 1:候选档位与多样性(#15)**。`ml/eval/eval_cl.py` 补 `--z_scale`(rollout 早就有这个参数,只是评估没接)。四臂对照轮次 33 cap:`K=16`、`K=8 --safety_try 3`、`K=8 --safety_try 1`、`K=8 --z_scale 1.5`。要回答的是:**全拒是"真没路"还是"没备选"**——若 K=16 或 z_scale 拉大能把到达/刹车买回来,那是候选池的问题;若 `safety_try 1` 几乎不掉到达,那说明第 1 条候选就够,后面试 7 条只是在花几何查询的钱(这对 20 ms 预算直接相关)。
- **杠杆 2:行程惩罚(`select='ep_len'` + `--len_w`)**。分数从 `-末态误差` 改成 `-末态误差 − len_w·Σ_t Σ_j |Δ_j|/STEP_j`(每轴按自己的满速步长归一,所以 6 轴可比)。
- 机制单测 2 条(`tests/test_eval_cl.py::TravelPenaltySelectTest`,40 项全过):造两条**末态相同、累计位移相同**的候选,一条来回抖(归一行程 10.8)一条匀速(3.6),`goal_err` 打成常数 ⇒ 只有行程能把它们分开。实测 `len_w=0` 选到抖动那条(第 1 步 1.08°)、`len_w=1.0` 选到匀速那条(第 1 步 0.27°);另有一条**同分零权重必须与 `select='endpoint'` 逐位相同**的回归断言(rtol=atol=0),保证默认行为没被这次改动碰到。
- 待测:`len_w ∈ {0.1, 0.3, 1.0}` 三臂,看行程比能压到多少、到达与时限各付多少。这条杠杆与"到达率"是相互冲突的目标(惩罚行程=主动走慢),所以必须成对汇报,不能只报行程变好。
- 单位口径要说清:`len_w` 的量纲是"mm 误差 / 一个归一 STEP 的累计行程",16 步满速在 3 条轴上大约是 48,所以 0.1~1.0 对应的惩罚是 5~50 mm 当量——这是扫这三个值的原因,不是随手取的。
- 测试 `tests/test_eval_cl.py` 38→40 项、全套 43→45 项(`python -m unittest discover -s tests`,2.2 s 全绿)。GPU 队列顺序:backoff A/B → `--safety 0` 判别 → 候选档位 → 行程惩罚。
- **等价闸门先过了**:`--backoff 0 --cert_batch 1 --inc_cap 1` 的 256 题与轮次 33 cap(`--cert_batch 0`)**逐项完全相同**(`docs/experiment_round38_backoff0.json` 对 `docs/experiment_round33_cap.json`,把两个 metrics 字典做键并集比较,差异集合为空,含 success 0.523438、time_ok 0.265625、brakes 19.55…)⇒ 轮次 36 我把批量认证分支重写(加缩步档那一段)没有改动任何行为。
- **两个我自己的操作失误,如实记在这里**:
1. 新加的 `backoff` 指标一度在产物里根本没有——`ml/eval/eval_cl.py:run()` 的 `out` 是**自己显式列键**的,不是从 `rollout` 返回的字典通抄,所以我往 `res` 加的 `backoff_mean` 被无声丢掉。已改成从逐题 `per['backoff']` 汇总(`backoff_mean` + `reject_all_mean = backoff+brakes`,后者才是与"原来全拒次数"可比的量)。**教训:加指标要顺着输出链路走到底,别只改生产端。**
2. 停后台 GPU 作业时,`TaskStop` 之后 `bash` 脚本链会活下来并继续拉起下一个 python;而 `nvidia-smi --query-compute-apps` 在这台机器上列不出这些 python(只列 ShellHost/wallpaper32 之类),据此判断"卡已空"会误判。我第一次按 PID 杀子进程后,链条 30 秒后又起了一个新的 eval。第二次我按进程树杀,但**把父子关系读反了**(Windows 的 PPID 在父进程已死时是陈旧值),结果误杀了新链的第一臂。正确做法:重启前用命令行正则列出全量候选、数 python 个数确认单租户,重启后再数一次;杀之前先按 `StartTime` 判新旧。backoff 那一臂因此要补跑。
## 行程惩罚 ep_len 三臂读数:时限变好,但行程比没降(轮次 39 续,负结论)
- 同一 256 题、同 seed、都带 `--inc_cap 1`,只改 `select=ep_len --len_w`:
| 臂 | 到达 | 时限内(≤1.3×老师) | 行程比(中位) | 刹车/题 | 成功题末态误差 |
|---|---|---|---|---|---|
| 对照(endpoint) | 0.5234 | 0.2656 | 2.6158 | 19.55 | 0.218 mm |
| len_w=0.1 | 0.5117 | 0.2695 | 2.6216 | 19.49 | 0.244 mm |
| len_w=0.3 | 0.5195 | 0.2852 | 2.6548 | 20.00 | 0.219 mm |
| len_w=1.0 | 0.5273 | **0.2891** | 2.6719 | 19.35 | — |
(`docs/experiment_round39_lenw{0.1,0.3,1.0}.json`)
- **两条相反的结论**:① 时限内**单调变好**(0.2656→0.2891,+2.4 pp = 6 题),到达在噪声内(0.5117/0.5195/0.5273,最大偏离对照 −1.2/+1.2 pp,n=256 下这就是噪声量级);② **行程比一点没降**(2.6216/2.6548/2.6719 vs 对照 2.6158,+0.006/+0.039/+0.056,只有最后一个勉强出噪声、且方向是"变差")。
⇒ "候选地平线内的累计行程"不是"实际执行行程"的好代理:罚它选出的候选在**近端少过冲**(所以收得快、时限内变好),但整段任务要更多次规划才能走完,累计行程不降反升。这条是我原先的假设被打掉,不是被确认;`ep_len` 只值 +2 pp 时限,且对 §9.2 的行程判据无用。
- 顺着这个读数,真正该罚的是**同一轴来回**而不是总长:已实现 `select='ep_waste'`,罚 `(1 − 同轴净位移/累计行程) × 当前误差`(乘当前误差是为了近端不压死进度项——上一版的形状问题正在这里)。三臂 `len_w=0.1/0.3/1.0` 排在 `ml/runs/_waste_sweep.sh`。
- 换向率指标 `reverse_p50` 从这一批之后才会出现在产物里(前面的臂是在改动前加载的模块跑的),口径已与离线分解同分母,预期对照臂应复现 **23.5%** 量级——**对不上就要查口径,不能各自报各自的**。
## 换向率进了评估指标(轮次 39 续)
- 分解既然指到"来回抖",每个臂都要直接报这个数,不能每次回头读转储:`rollout` 里按轴记方向,`reverse_rate` = 同轴方向翻转的步数 / 活动步数(站住的步不重置方向,所以"停了再同向走"不算换向);`eval_cl` 报 `reverse_p50`/`reverse_mean`。
- **正对照**(`tests/test_eval_cl.py`,42 项全绿):抖动轨迹报 >0.4、匀速轨迹报 ≈0——指标本身要证明有灵敏度,不然等于没测。
- 待交叉验证:新指标在对照臂上应复现转储离线算的 **23.5%**(p50);对不上就是两个口径有一个错。
- 生产端改了、输出端没改=没改:上一次 `backoff_mean` 就是这么丢的,这次加指标同时改了 `closed_loop.per` 与 `eval_cl` 的显式键列表。
## 行程超出的成因分解(轮次 39 续,纯 CPU 读轮次 33 base 转储)
- 口径先对齐:只在"有老师参考且到达"的题上算,自测 **2.425** 与 eval 报的 2.445 同量级(差别在我按 `t_done/DT` 取窗端,eval 逐步累加),后面的数都在这个口径上。
- **超出主要是同一根轴来回抖,不是绕远路**:净位移/总行程中位 **0.445**(完美单调应为 1.0)、各轴最大 excursion/行程 0.507、旧口径换向中位 23.5%(p25 15.7%、p75 30.3%)。
- **撤回一句我自己的假象(并且这条也要再撤回一次,见下一条)**:上面原来写"到达组抖得更厉害(26.1% vs 未到达 14.7%)",**错在口径**。旧定义 `(dd[t+1]·dd[t] < 0).mean()` 要求相邻两步都非零,遇到"前进→停住→后退"就漏计;而未到达题恰恰大量站住,于是被系统性地少计。
- **用与定义无关的量重算(净位移/总行程,同一转储、同一窗口)**:**到达组 0.478、未到达组 0.352** ⇒ 未到达组白费比例更高(走完 738° 只前进 255°;到达组是 569°/267°)。这条稳。
- **但换向率的组间比较我第二次算错,现纠正**:上一次我用"未到达 24.0% vs 到达 14.4%"来说组间差异,那个算法把**到达题到达之后的冻结步**也算进了分母(`act` 只按 `tn<=limit`,没排除已到达),把到达组稀释了。按与 `rollout` 完全同语义的窗口(到达题截到 `t_done`)重算,得到的是**两组差不多**:
| 转储(口径) | reverse_p50 全体 | 到达组 | 未到达组 |
|---|---|---|---|
| `round33_cap`(=轮次33 cap,限幅) | **0.2474** | 0.265 | 0.233 |
| `round33_base`(不限幅) | 0.257 | 0.275 | 0.240 |
(`docs/experiment_round39_reverse_offline.json`)⇒ **换向是全局现象(约 1/4 的(步,轴)对在反向),不是失败题的特征**;失败题的特征是"白费行程比例高 + 大量站住"。这两件事先前被我混成一句,现在分开。
- **可证伪预测(等 backoff 对照臂回来对表)**:同一配方下 in-eval 的 `reverse_p50` 应当 ≈ **0.247**。差得多就说明线上实现与离线同语义重算不一致,得先查仪表再谈算法。
- **口径敏感要常驻**:分组/比较类断言之前,先换一种定义把同一份数据再算一遍;两种定义都给同方向才写进日志。分母必须与指标定义一致(`act_ = 未 done 且 在时限内`),否则"到达题窗短"会系统性稀释任何按步数归一的指标。
- **对得上训练侧的事实**:预训练本来就有 `w_smooth=0.05` 的二阶差分正则,但闭环仍大量换向 ⇒ 训练期的平滑项没管住执行期的抖动。
- **第一条 A/B 读数(`len_w=0.1`,256 题、`--inc_cap 1`)**:到达 0.5117(对照 0.5234,−1.2 pp)、时限内 0.2695(+0.4 pp)、**行程比 2.6216 几乎没动**(对照 2.6158)、成功题末态误差 0.2438(对照 0.2175,变差)。
原因是量级不匹配,不是方向错:`0.1 × 累计归一行程`(16 步满速在 3 条轴上约 48)只当量 ~5 mm,而早段末态误差是几百 mm ⇒ 罚不动。等 `len_w=0.3/1.0` 两臂看行程比是否真的开始下降;若仍压不住,惩罚对象应从"总行程"换成**换向/二阶差分**(直接打这个 23.5%)。
## 执行余量与前缀完整性审计(轮次 39 续,纯 CPU 读轮次 33 转储)
- 256 题 × 195 次同步规划、commit_n=2 ⇒ 169,216 个**真正被执行过的**检查点(每步的端点+中点,间隙在 0.5 mm 截顶)。
- **执行点的间隙分布**:≥0.5 mm(=宽裕)占 **91.0%**;0.2–0.5 mm 占 5.6%;<0.1 mm 占 0.84%;<0.02 mm 占 0.25%。每题"最险的那个执行点"的中位数只有 **0.029 mm**。
⇒ 两件事:① 大多数执行是宽裕的,所以"加 0.5 mm 余量门限"只花 3 题(轮次 33 margin 已实测)是自洽的;② 但每题都会至少一次贴到 **30 µm** 级——这是胶囊模型内部的"贴",真机的运动学与装配误差远大于这个数,**搬到实机时这条是实质风险**,不能当成"我们有余量"。
- **前缀完整性**:满前缀 79.4%、部分前缀 10.8%、全拒 9.9%。分组看全拒率——**到达题 0.88%,未到达题 22.0%**(未到达组因此 44% 的步是零运动,与时间账量的 37% 同量级:那 37% 是"活动窗内",这里是"×commit_n"的换算)。
- **对 `backoff` 的预期与判据**:全拒意味着"连第 1 步的端点或中点已经穿进障碍",缩步能不能救取决于**穿得有多深**(穿 0.1 mm 救得回,穿 3 mm 救不回)。这个数转储里没记(只记被选中那条),所以直接由 `backoff_mean`/`reject_all_mean` 来量:
`reject_all_mean ≈ 原全拒次数`(=backoff_mean+brakes_mean,与轮次 33 cap 的 19.55 可比),`backoff_mean/(backoff_mean+brakes_mean)` 就是"缩步能救回的比例"。这是补跑(#22)要读的第一个数。
## 轮次 52:补上逐题老师用时这把尺子之后,整条"打分地平线"线的结论要翻面——1.3× 口径上的时限收益是显著的
- **仪表缺的是什么**:`--dump` 的逐题行里没有老师用时,所以 §9.2 的 `time_ok_1p3`(≤1.3×老师)此前**只能报整批比例,做不了逐题配对**。本轮把 `t_exp`(老师用时)与 `tr_ref`(老师行程)写进每一行——它们是**题目属性、与臂无关**,所以只要有一条带新字段的控制臂落盘,就能按题号 join 给**所有历史臂**补上配对能力。
- **改了落盘代码必须先自证没动行为**:重跑同配方控制臂 ⇒ **22/22 老键逐位相同**(`docs/experiment_round52_ctl_ex.json` vs `round45_tie0`)。顺带核对 `limit = 2×t_exp`(3.48 s ⇒ 6.96 s),把轮次 51 那句"到达自带 2× 时限"从推断变成实测。
- **逐题配对(vs K8 `endpoint` 控制,n=256、seed=0;判据 = ok 且 `t_done ≤ 1.3·t_exp`)**:
| 臂 | 新增达标 | 失去达标 | 净 | 精确 McNemar p |
|---|---|---|---|---|
| **K16 `pre`(最好格)** | **27** | **3** | **+24** | **8.4e−6** |
| K8 `pre` | 22 | 5 | +17 | **0.0015** |
| 门控 5 mm | 23 | 6 | +17 | 0.0023 |
| 门控 20 mm | 22 | 6 | +16 | 0.0037 |
| K8 `pre` h=4 | 15 | 5 | +10 | 0.041 |
| K16 `endpoint` | 17 | 9 | +8 | 0.169 |
| K8 `pre` h=8 | 11 | 7 | +4 | 0.481 |
| `tie=1`(轮次 45) | 3 | 0 | +3 | 0.250 |
- **这条表纠正的是我自己的判读,不是数据**:轮次 46–51 我一直拿 `success`(自带 2× 预算的那道门)做逐题配对,于是写成"到达与时限收益都在噪声内"。换成 §9.2 真正的 1.3× 口径,`pre` 系列的收益是 **p=0.0015 与 8.4e−6**——整条线最硬的一条证据。⇒ **"到达率没显著变化"仍然成立**(2× 那道门上是净 +1/+5/+11、p≥0.06),但**"时限内到达"这道门是明确被买到的**。两栏必须分开写,之前混过一次。
- **同时被这张表重新定位的两条旧结论**:`K16+endpoint` 的时限收益(+8 题)其实不显著(p=0.169)——轮次 39 报的"+3.1 pp 时限"当时没有配对检验;h=8 那臂在 1.3× 口径下也是 p=0.48 ⇒ 轮次 48 说 h=8 是坏点,现在有了第二重证据。`tie=1` 在 1.3× 上只有 +3 题(p=0.25)且到达净 −5 ⇒ 判掉不变。
- **待办**:seed=1 的 `t_exp` 正在补落盘(轮次 53),以便把轮次 47/50 那句"整批 +4.3 pp / +0.39 pp"也换成配对 p 值。
- **口径纪律**:`--pre_near`、`--pre_h`、`--tie` 都是可选口径,默认仍是 `endpoint`。§9.2 的验收栏仍按"到达未确立、时限内已确立(seed0 配对)"来写,等 s1 配对完成再决定是否给到达之外的第二栏正式换配方。
## 轮次 51:2×2 补全(K∈{8,16} × select∈{endpoint,pre})——两条正向杠杆在时限上可加、在到达上饱和
- **为什么补这格**:`K=16`(#27/#1,轮次 39/44 验证)与 `select=pre`(#29,轮次 46 验证)是链上仅有的两条正向杠杆,但从未同时测过。§9.2 要的是**同一份配方**同时满足到达与时限,所以可加性必须直接量,不能各自报喜。
- **四格(全部 n=256、seed=0、同配方、同批口径;缺的那格是本轮新跑)**:
| 格 | 到达 | 时限内(≤1.3×老师) | 2 s 误差比 | 到达题末态误差 | 刹停/题 | 行程比 | 碰撞 / 超 VMAX |
|---|---|---|---|---|---|---|---|
| K8 `endpoint`(历史控制) | 0.5234 | 0.2656 | 0.2792 | 0.2175 | 19.55 | 2.6158 | 0 / 0 |
| K8 `pre` | 0.5430 | 0.3320 | 0.1702 | 0.4151 | 19.84 | 2.4449 | 0 / 0 |
| K16 `endpoint` | 0.5625 | 0.2969 | 0.2800 | 0.1595 | 13.77 | 2.6243 | 0 / 0 |
| **K16 `pre`(新最好格)** | **0.5664** | **0.3594** | **0.1382** | 0.2882 | 14.20 | **2.4142** | **0 / 0** |
- **主效应(这就是可加性的算法)**:
- 时限:`pre` 在 K8 上 **+6.64 pp**、在 K16 上 **+6.25 pp**;`K16` 在 endpoint 上 +3.13 pp、在 pre 上 +2.73 pp ⇒ **四个方向同量级、无交互**。时限这块确实是两个独立瓶颈(候选容量、打分口径)各自买一份。
- 到达:`pre` 在 K8 上 +1.95 pp、在 K16 上只 **+0.39 pp**(逐题 16 赢 15 丢,净 +1,**McNemar p=1.000**)⇒ **到达饱和**:K16 已经把"有活路可选"这件事买完了,再把打分口径改对,救不回新题。
- 最好格 vs 历史控制(K8 endpoint)逐题:赢 20 丢 9,净 **+11 题**(0.5234→0.5664),**McNemar p=0.061** ⇒ 这是整条"选择/认证"线上**最接近显著**的一次到达收益,但按规矩 p=0.061 就是"尚未确立",要在 n=512 或第二个 seed 上复现才算。翻转按类别:B +8/−2、A +8/−4、C +3/−2、Dr +1/−1 ⇒ 增益仍然主要在 B(直线被挡)。
- `goal_ratio_2s` 逐题:201 好 / 55 差,**p = 1.0e−20** ⇒ 全程进度是这条线最硬的证据(比任何单臂都强)。
- **一个所有历史数字都被我漏说的口径**:`ok = done & (t_done ≤ limit)`,而 `limit = time_limit(ex) = max(2×老师用时, 3 s)`。**⇒ 本链引用的"到达率"本来就带 2× 时限**,不是"最终到没到"。`time_ok_1p3` 才是 §9.2 的 1.3× 口径。两者是两道门,引用时不能再混。轮次 52 已把 `t_exp/tr_ref` 落盘,之后 1.3× 口径能做逐题配对。
- **状态**:合法口径(碰撞 0、超 VMAX 0)下的当前最好配方更新为 **K=16 + select=pre + commit_n=2 + fb=2.2 + inc_cap=1 + cert_batch=1** → 到达 56.6%、时限内 35.9%。§9.2 的缺口仍然是**到达 −41.4 pp**(时限 −64.1 pp 是另一栏)。
- **判据没被放松的证据**:四格 `collision_rate` 与 `over_vmax_rate` 全 0,`n_plans_mean` 全 195.0 ⇒ 规划次数不变;K16 的额外成本只在每颗规划的候选数(认证与打分的批量宽度),这条**必须用独占微基准量**才能进部署账(#16/#28 仍未结)。
## 轮次 50:门控 20 mm 的 seed=1 复现——方向对、幅度不稳:时限收益塌了,精度代价只是减小没消除
- **一条臂就够**(控制臂复用 `round47_ep_s1`,同 seed 同配方,已在盘)。
- **两 seed 并排**(都是 n=256、`collision_rate=0`、`over_vmax_rate=0`、`n_plans_mean=195.0`):
| 读数 | s0 控制 | s0 门控 20 | s1 控制 | s1 门控 20 |
|---|---|---|---|---|
| 到达 | 0.5234 | 0.5352 | 0.5313 | **0.5313** |
| 时限内(≤1.3×老师) | 0.2656 | **0.3281** (+6.25 pp) | 0.2969 | 0.3008 (**+0.39 pp**) |
| 2 s 误差比 | 0.2792 | 0.1702 | 0.2750 | **0.2011** |
| 到达题末态误差中位 | 0.2175 | 0.2248 | 0.2228 | 0.2286 |
| 行程比中位 | 2.6158 | 2.5146 | 2.6369 | 2.6468 |
- **复现矩阵(什么稳、什么不稳)**:
- **稳**:`goal_ratio_2s` 大幅改善(两 seed 都到 0.170/0.201,控制 0.279/0.275)⇒ "打分地平线 = 执行地平线"买**路上的进度**是这条轴唯一稳健的效应;末态误差**中位数**几乎不动(+3.4%/+2.6%)⇒ 门控确实把 `pre` 那个 +91% 的精度损失压掉了大部分。
- **不稳**:时限收益从 **+6.25 pp 塌到 +0.39 pp**;到达两 seed 合并赢 24 丢 21(净 +3,**p=0.766**)⇒ 到达率在这条轴上彻底没有可用信号。
- **合并后要更正轮次 49 的一句话**:到达题组内末态误差 vs 控制的符号检验,s0 是 67 差/57 好(p=0.419)、s1 是 74/51(**p=0.049**)、两 seed 合并 **141 差/108 好,p=0.042** ⇒ 正确说法是"**精度代价被大幅减小(未门控的 `pre` 是 82/42,p=3.1e−4)但没有消除**",不是"与控制不可分"。中位数与逐题分布在这里给出相反的印象,正是因为门控把尾部拉近了中位、但把大量题各推一点点。
- **判读规则的兑现**:轮次 49 预注册写的是"若 s1 变成显著变差,则本轮结论降级为阈值恰好躲过一次复现"。时限那一半也塌了 ⇒ **降级为**:门控不是免费午餐,它是"要不要用时限收益换末态余量"的开关;`pre_near=20` 只在 seed=0 上看起来两者都要。
- **本轮另外修掉的一处统计错**:精确 McNemar/符号检验我用的是 `2·Σ_{i≤min(k,n−k)} C(n,i)/2ⁿ`,在 **k = n/2 的中间点会算出 1.168(>1)**。改成定义式 `p = min(1, 2·min(P(X≤k), P(X≥k)))`(X~Bin(n,½))后重算了本轮全部已上报的 p 值:**除中间点那一例外,其余数值全部不变**(0.125/0.424/0.678/0.455/0.327/0.042/4.9e−16/1.8e−12/2.4e−6 都对得上)。中间点(s1 到达 11 赢 11 丢)的正确值是 **p=1.0**。
- **状态**:`--pre_near` 作可选口径入库,默认仍 `endpoint`。**这条轴的可推荐形态是 ungated `pre`**(时限/进度两 seed 都稳),门控留给"末态余量优先"的场景。
- **下一轮(组合而非继续调这条轴)**:`pre` 与 K=16 是两条各自验证过的杠杆,但没一起测过。§9.2 要的是同一份配方同时满足到达与时限,所以**可加性**必须直接量:跑 `K=16 + select=pre`(对照 `K16+endpoint` 与 `K8+pre` 都已在盘)。若不可加,说明两条买的是同一个瓶颈(候选被拒后的"没活路"),那是重要的机理结论。
## 轮次 49:#31 门控地平线成立——终端段盖到 20 mm 时,时限收益与精度代价被解耦(预注册判据由该臂满足)
- **假设(由轮次 48 的四点曲线给出,不是拍脑袋)**:时限收益来自"打分地平线 = 执行地平线",精度代价来自"近终段没有终端代价"。两件事若真能分开,就应该存在一个终端段长度,使得短前瞻的时限收益基本不损、而末态误差回到控制水平。
- **做法**:`select=pre --pre_near <mm>`——当前误差 ≤ 阈值时打分改用**整条地平线**末端,远端仍用提交窗口点。多一次 `goal_err(B×K 点)` 只在门控口径下发生。
- **四列对照(全部 seed=0、同配方同批;控制=`round45_tie0`,pre=`round46_pre256`)**:
| 读数 | 控制 h=16 | `pre` h=2 | 门控 5 mm | 门控 20 mm |
|---|---|---|---|---|
| 到达 | 0.5234 | 0.5430 | **0.5469** | 0.5352 |
| 时限内(≤1.3×老师) | 0.2656 | **0.3320** | **0.3320** | 0.3281 |
| 2 s 误差比 | 0.2792 | 0.1702 | 0.1702 | 0.1702 |
| 到达题末态误差中位 | 0.2175 | 0.4151 | 0.3768 | **0.2248** |
| 行程比中位 | 2.6158 | 2.4449 | **2.4218** | 2.5146 |
| 刹停/题 | 19.55 | 19.84 | 19.84 | 19.64 |
| 执行碰撞 / 超 VMAX | 0 / 0 | 0 / 0 | **0 / 0** | **0 / 0** |
| `n_plans_mean` | 195.0 | 195.0 | 195.0 | 195.0 |
- **逐题配对(vs 控制)**:到达 门控5 赢 16 丢 10(净 +6,p=0.327)、门控20 赢 13 丢 10(净 +3,p=0.678)、`pre` 净 +5(p=0.424)⇒ **到达率在这条轴上任何一点都没跨过显著**。到达题组内的末态误差符号检验才是分界:`pre` 82 差/42 好 **p=4.2e−4**、门控 5 mm 79/45 **p=0.0029**(仍显著变差)、门控 20 mm 67/57 **p=0.42 ⇒ 与控制不可分**。
- **预注册判据的落点**(规则在看到数据前写在 `ml/runs/_r49_gate.sh`):成立 = `err_success_med` 回到接近控制(≈0.22 mm) 且 `time_ok ≥ 0.31`。**门控 20 mm 两条都满足**(0.2248、0.3281)⇒ 假设成立。门控 5 mm 只满足时限一半都不到(0.3768 仍显著差)⇒ 不成立。
- **5→20 mm 的分级响应是机理证据,不是调参**:终端段太窄(5 mm ≈ 最后一两步)只回收 20% 的精度差,20 mm(≈ 最后 3–5 步)才回收全部。⇒ 病名的正确写法是**"终端代价的作用区间不够长"**,而不是"缺终端代价"。这也说明 `pre` 的精度损失不是"打分变笨"的随机副作用,而是近终段路径选择被 2 步口径主导的直接后果。
- **两条臂各自的代价(不许只报好的那半)**:门控 20 mm 把 A 类到达打到 **94/128**(控制 96、门控5 也是 96),换来精度归零;门控 5 mm 到达最高 0.5469 且行程比最好(2.4218),但末态误差仍显著变差。⇒ **不存在同时占优的阈值**,本轮只证明"可解耦",不证明"已找到最优点"。
- **口径纪律**:不改为默认。§9.2 的"时限内"最好读数现在是 **0.3320(pre h=2 / 门控5)**,"到达"最好读数是 **0.5469(门控5)**——两者都还没显著超过控制,所以验收栏仍写控制的 0.5234/0.2656,另注可选口径。
- **下一步(唯一还没做的关键检验)**:门控 20 mm 在 seed=1 复现(控制臂 `round47_ep_s1` 已在盘 ⇒ 只需一条臂)。若 p=0.42 那个"与控制不可分"变成"显著变差",本轮结论降级为"阈值恰好躲过一次复现"。
**【轮次 50 已做,结果是降级】**:seed=1 上该符号检验是 74 差/51 好 **p=0.049**,两 seed 合并 **141 差/108 好 p=0.042** ⇒ "与控制不可分"不成立,正确说法是**精度代价被大幅减小但没有消除**;同时时限收益在 seed=1 上从 +6.25 pp 塌到 +0.39 pp。详见下一轮。
## 轮次 48:#30 打分地平线四点曲线——进度收益是剂量-反应关系,而精度代价只集中在 h≤4;h=8 是个坏点
- **设计**:`select=pre` 加 `--pre_h`(0=commit_n)。同 seed=0、同配方(`K=8/commit_n=2/fb=2.2/inc_cap=1/cert_batch=1`),只改打分取 `fut` 的第几步。h=2 与 h=16 两点已在盘(轮次 46 / 45,且控制臂已证跨窗口逐位复现),本轮补 **h=4、h=8**。
- **曲线(n=256 全部四点)**:
| 打分地平线 | 时限内(≤1.3×老师) | 2 s 误差比 | 到达题末态误差中位 | 到达 | 行程比中位 | 刹停/题 | 执行碰撞 |
|---|---|---|---|---|---|---|---|
| h=16(现 `endpoint`) | 0.2656 | 0.2792 | 0.2175 mm | 0.5234 | 2.6158 | 19.55 | **0** |
| h=8 | 0.2813 | 0.2661 | 0.2255 mm | 0.5195 | **2.6749** | 20.27 | **0** |
| h=4 | 0.3047 | 0.2350 | 0.3301 mm | 0.5391 | 2.5619 | 20.41 | **0** |
| h=2(=提交窗口) | **0.3320** | **0.1702** | 0.4151 mm | 0.5430 | **2.4449** | 19.84 | **0** |
- **逐题配对(对同一控制)**:`goal_ratio_2s` 符号检验 h=8 **166 好/90 差 p=2.4e−6**、h=4 **184/72 p=1.8e−12**、h=2 **192/64 p=4.9e−16**(轮次 46)——三个 h 都显著、显著性随 h 缩短单调增强。到达则全在噪声内:h=8 赢 8 丢 9(净 −1,p=1.0)、h=4 赢 10 丢 6(净 +4,p=0.45)、h=2 赢 15 丢 10(净 +5,p=0.42)。全程末态误差的符号检验 h=8 是 142/114(p=0.091)、h=4 是 130/126(p=0.85)⇒ **末态误差的总体分布没被推动,被推动的是"到达题组内到达那一刻的误差"**。
- **三条读法(比"哪个 h 最好"有用)**:
1. **进度/时限关于 h 是单调的**(前瞻越短 ⇒ 时限内越多、2 s 时越贴近目标)。这是**剂量-反应关系**,比单点 A/B 强:不是一次涨落,而是整条轴同向。⇒ §9.2 的"到达用时 ≤ 老师×1.3"这一栏的真瓶颈之一确实是**打分地平线长于执行地平线**。
2. **精度代价不是均摊的,几乎全在 h≤4**:h=8 相对控制的末态到达误差只 +3.7%(0.2175→0.2255),h=4 就 +52%(→0.3301)、h=2 +91%(→0.4151)。⇒ 病因不是"前瞻短",而是**近终段没有前瞻**(MPC 缺 terminal cost 的教科书症状)。正确的形态是**前瞻长度随剩余距离收缩**(#31),不是在 h 上挑一个折中值。
3. **h=8 是曲线上的坏点**:时限只 +1.56 pp、到达 −1 题、**行程比反而最差**(2.6749 > 控制 2.6158)。⇒ "把地平线砍一半"这种渐进方案被自己的中间点否掉了:要么收到提交窗口附近,要么别动。
- **按类别**:h=4 时 A 类回到控制水平(96/128),B +2、C +2;h=2 时 A −1、B +2、C +3。⇒ 轮次 45 那个"短前瞻在开阔区纯亏"的担忧在 h=4 上消失(`tie` 是另一码事:它换成的是余量而非进度)。
- **判据状态**:四点全部 `collision_rate=0`、`over_vmax_rate=0`、`start_fail=0`;`speed_ratio_p50/p95/max` 与 `accel_ratio_max` **四点的值逐位相同**(1.000002/1.000010/1.000010/1.000010)⇒ 收益不是放松安全或超速换来的。成本侧的**实测**(不是推算)证据:`n_plans_mean` 四点都是 **195.0**,即每颗题的规划次数没变 ⇒ 认证与几何调用的次数不变,改变的只是同一批点里取哪一个来排序。`dls_share` 0.613/0.633/0.629/0.637 不单调,暂不作结论。
- **预注册判读规则的落点**:规则写的是"若 time_ok/2 s 误差比随 h 单调变差 ⇒ 超出提交窗口的前瞻有害,#26 一并作废"。观测正是单调 ⇒ **#26(在前缀口径上再加白费惩罚)作废**;改为 **#31:门控地平线**(远端用 `pre`、近终段用整条地平线),一个参数、机制由本曲线直接给出。
- **状态**:`pre_h` 作可选口径入库,默认仍是 `endpoint`。§9.2 的"时限内"读数最好的一栏从 0.2969(K=16 endpoint)换到 **0.3320(K=8 h=2)**,但到达没有显著变化,所以**不换主口径**。
## 轮次 46–47:#29 把候选打分的地平线换成「这一轮真会执行到的位置」——链上第一个正向杠杆,两个 seed 独立复现
- **诊断(不是新想法,是旧负结论给出的原因)**:`select='endpoint'` 按**整条 16 步地平线末端**的终点误差给 K 条候选排序,而每轮只提交 `commit_n=2` 步。对被安全层截断的候选,那个终点**根本到不了**;对满前缀候选,它是 14 步开环外推。轮次 42 判掉 `ep_len/ep_waste` 时写的就是同一件事——"罚的是没被执行的那段地平线"。`select='pre'` 把打分点换成 `fut[:, :, commit_n-1]`(这一轮真正会走到的位置),**同一次 `goal_err` 只换点集,机制上不增加量级**。
- **实现账**:分支 + 文档串 + `--select pre`;两条可手算的正对照(夹具造一对"前缀好但末端差"/"末端好但前缀差"的候选 ⇒ 两口径必须选**相反**的那条)+ 一条不变式(`commit_n=16` 时 pre 的索引落到地平线末端 ⇒ 必须与 endpoint 选同一条)。49 项测试绿。仪表灵敏度:n=16 冒烟上 `pre` 与 `endpoint` 已给出不同轨迹(刹停 24.94→21.94、行程比 2.43→2.67),不是惰性口径。
- **对照臂的可比性先证再引用**:本轮控制臂 = `docs/experiment_round45_tie0.json`(`tie=0`、同配方),它与轮次 42 的 `round42_K8cb1` **22/22 键逐位相同** ⇒ 该配方跨窗口确定性复现,单臂对照成立(这是第一次在新杠杆上省掉控制臂,前提是复现证据已落盘)。
- **seed=0(n=256,同 seed 同批)** 与 **seed=1 独立复现(两臂同窗口背靠背)**:
| 读数 | s0 控制 | s0 `pre` | Δ | s1 控制 | s1 `pre` | Δ |
|---|---|---|---|---|---|---|
| 执行碰撞率 | 0.0 | **0.0** | — | 0.0 | **0.0** | — |
| 到达 | 0.5234 | 0.5430 | +1.95 pp | 0.5313 | 0.5430 | +1.17 pp |
| 时限内(≤1.3×老师) | 0.2656 | **0.3320** | **+6.64 pp** | 0.2969 | **0.3398** | **+4.30 pp** |
| 2 s 误差比(中位) | 0.2792 | **0.1702** | −39% | 0.2750 | **0.2011** | −27% |
| 行程比中位 | 2.6158 | 2.4449 | −0.171 | 2.6369 | 2.4661 | **−0.171** |
| 超 VMAX 题占比 | 0.0 | 0.0 | — | 0.0 | 0.0 | — |
| 到达题末态误差中位 | 0.2175 | 0.4151 | **+0.198 mm** | 0.2228 | 0.3425 | **+0.120 mm** |
- **统计口径(这条线真正的证据在哪)**:
- `goal_ratio_2s` 逐题符号检验:s0 **192 好 / 64 差(p=4.9e−16)**,s1 **191 好 / 65 差(p=1.5e−15)**。⇒ 显著、且两个 seed 的计数几乎一样。
- 到达:s0 逐题丢 10 赢 15(净 +5,McNemar **p=0.424**)、s1 丢 10 赢 13(净 +3,**p=0.678**)。⇒ **到达率的提升在两个 seed 上都只是噪声**。诚实的写法是"时限内到达显著变多、总到达未见显著变化",即它把路上的时间买回来了,没有把通路买回来。
**【轮次 52 补验】**这句里"时限内显著变多"当时还只是整批读数(0.2656→0.3320),没有逐题配对。补上每题老师用时后:**K8 `pre` 在 1.3× 口径上逐题 22 赢 5 丢,净 +17,精确 p=0.0015**;`K16+pre` 是 27/3、p=8.4e−6。⇒ 那句话现在有据了,而且比原来写的更强。注意 `success` 那道门自带 **2×** 预算(`limit = max(2·t_exp, 3 s)`),所以"到达 p=0.42"与"时限内 p=0.0015"是两道不同门的读数,不矛盾。
- 末态误差逐题:s0 130 好 / 126 差(p=0.85)、s1 121/135(p=0.42)⇒ 全程不显著;但**到达题组内**的中位误差两 seed 都涨(0.22→0.42、0.22→0.34 mm)。这是"打分只看 2 步"的必然代价:近目标处不再为最后那几毫米承诺路径。判据仍未违反(到达定义是 `e<1 mm 且 oe<0.5°`,最差的 0.42 mm 也在壳内),但**这条余量从 4.6 倍缩到 2.4 倍**。
- **按类别(两个 seed 方向一致)**:A 类 −1,**B 类 +2、C 类 +3/+2**。单个切片在这个分母上都不显著,但方向与轮次 44 的"#27:候选容量救不了 B/C"**不矛盾**——那条测的是"多给同分布候选",这条改的是"用哪个终点评价候选"。真正的判据留给下一轮的地平线曲线。
- **成本**:两臂墙钟 317.6/319.2 s(s0)与 315.1/314.4 s(s1)——同量级;但**墙钟差不构成成本结论**,要说"零额外开销"得用独占微基准把 `goal_err` 在 `B×K` 个点上的两次调用分开量(记为待办)。
- **状态**:**不改为默认口径**(到达未显著 + 到达精度余量减半),把 `select='pre'` 作为已验证的可选口径入库。下一步 #30:把打分步长做成参数 `pre_h`,扫 h∈{2,4,8,16}(16=现在的 endpoint),量的是"前瞻长度 → 时限内到达 / 末态精度"这条**机理曲线**而不是挑系数;若精度代价随 h 恢复而时限收益不塌,正确的形态是"前瞻长度随剩余距离收缩"。
## 轮次 45:`tie`(等长安全前缀按沿路最小间隙定夺)判掉——它买的是余量,卖的是进度,而且作用在错的人群上
- **做法**:`cert_batch=1` 已经在同一次几何调用里算了 K 条候选各 `2·commit_n` 个点的间隙,但这些信息只用来判通过/拒绝。`--tie 1` 让"前缀最长"的并列集合改由**沿路最小间隙**定夺;零额外几何调用,判据一个字没动(仍是 `gap > gap_margin`)。
- **先测触发率再花钱**(阈值预注册:<1% 直接判掉,≥5% 才跑两臂)。n=64 探针:并列率 `tie_opp_rate=74.8%`、真的改选率 `tie_changed_rate=7.4%` ⇒ 过线,才投 256 题两臂。
- **两臂 A/B(n=256、同 seed、同窗口背靠背,配方=轮次 42 的 `K8 + cert_batch=1 + inc_cap=1`)**:
| 读数 | A `tie=0` | B `tie=1` | 差 |
|---|---|---|---|
| 到达 / 无碰撞到达 | 0.5234 | 0.5039 | **−1.95 pp** |
| 时限内(≤1.3×老师) | 0.2656 | 0.2773 | +1.17 pp |
| 执行碰撞率 | 0.0 | **0.0** | 判据未放松(硬门槛通过) |
| 刹停/题 | 19.55 | 17.79 | −9.0% |
| 卡住题占比(刹停>20%) | 23.4% | 20.3% | −3.1 pp |
| 行程比中位 | 2.6158 | 2.5399 | −0.076 |
| 到达均值用时 | 2.487 s | 2.359 s | −0.128 s |
- **统计诚实**:逐题配对**丢 6 题、赢 1 题,净 −5**;McNemar 精确双侧 **p = 0.125** ⇒ 到达差异在这个分母上与零不可分。所以结论不是"它显著变差",而是**没有任何正向证据,且方向为负**;而刹停/行程/用时全部朝"更保守"的方向动,与到达同向恶化是一个自洽的签名,不是噪声。丢的 6 题里 **5 题在 A 类(开阔均匀区)**,唯一赢的那题在 C 类(贴立柱)。
- **仪表与独立离线数对上了(这条比 A/B 本身更有说服力)**:本轮 256 题的改选率 **5.58%**,与轮次 44 纯离线量到的"执行点间隙落在 0.2–0.5 mm 的占比 **5.6%**"逐位同量级;并列率 79.0% 对上"满前缀 79.4%"。⇒ **余量能分辨的上界就是"有多少执行点真的贴进 0.5 mm 以内"**,被宽相位截顶 `gcap = max(0.5, gap_margin+0.5)` 锁死(凡沿途间隙都 >0.5 mm 的候选,`min_gap` 恒等于 0.5,排序上完全并列)。所以 5.6% 是这个家族在 cap=0.5 下的**天花板**,不是实现没调好。
- **机理判读**:并列集合里"余量最大"的那条通常也是"位移最小"的那条。默认口径由选择器分数(终点误差)定夺 = 用进度换余量的风险由策略承担;`tie=1` 把判据整个换成余量 ⇒ 在根本没有障碍的 A 类纯亏进度。正确形态若要保留这条线,只能是"进度 + 余量"的加权,而加权又要一个新的自由系数——在当前 −5 的背景下不值得一轮。
- **结构性发现(否掉的是实现,更是提问方式)**:#24 从提出起就**作用在错的人群**。失败主人群是"全拒(`maxlen=0`)"——未到达题 22.0% 的规划连第 1 步都不安全;而 `tie` 只在 `maxlen>0` 时触发。全拒人群的杠杆是 `backoff`(轮次 43 已判掉:能救回停步、救不回通路)。⇒ "选哪条活路"这件事,对已经走通的路影响很小,对走不通的路毫无影响。
- **本轮踩到并修掉的实现洞**:tie 分支里我用了局部名 `nmax`,把主循环的"总步数"顶掉 ⇒ 进度回调 `%d` 收到张量直接炸(第一次 n=64 探针就是这么失败的)。修法是改名 `ntile`,并补一条守卫单测(tie=1 必须带 `progress` 跑通且 `total` 保持标量);**守卫做过变异检查**——把 `ntile` 换回 `nmax`,守卫立刻变红。计数器的正对照也可手算:夹具里两条候选永远全安全 ⇒ `tie_opp = tie_changed = n_plans`、两个比率恒 1。47 项测试全绿。
- **不采纳**。前置条件记为 #28:先把"数值截顶 `gcap`"与"验收门限 `gap_margin`"解耦(现在 `--gap_margin 1.5` 会同时抬截顶值和门限,动一个必动另一个),再量 cap=0.5/2/5 mm 下的分辨率与几何耗时(`_coarse` 保留 `d ≤ cp_lim + cap` 的胶囊对,成本随壳层体积涨)。**但即使分辨率上来,人群错配也不会被修好**,所以这条只作为"贴边题(C 类)里选哪条活路"的低优先候选保留。
## #27:K=16 的增益**不集中在 B/C**——B 类缺口是策略能力,不是候选容量(轮次 44,逐题 dump)
- K=8 与 K=16(都 `--cert_batch 1 --inc_cap 1`,同 256 题同 seed)逐题对齐后按类别:
| 类别 | 题数 | K8 到达 | K16 到达 | 差 | K8 刹停/题 | K16 刹停/题 |
|---|---|---|---|---|---|---|
| A 均匀 | 128 | 75% | 78% | +3.1 pp | 7.5 | 4.9 |
| B 直线被挡 | 74 | 27% | **32%** | +5.4 pp | 38.1 | **27.0** |
| C 贴立柱 | 32 | 34% | 38% | +3.1 pp | 33.3 | 24.5 |
| D 不可达 | 13 | 0% | 0% | 0 | 5.2 | 4.8 |
| Dr | 9 | 78% | 89% | +11.1 pp | 10.2 | 6.4 |
逐题翻转:**新救回 12 题(A 6、B 4、C 1、Dr 1),反而丢 2 题(都在 A)**,净 +10 题 = 总体 0.5234 → 0.5625。
- **统计诚实**:分类切片里 1–4 题的变化在这个分母上不构成显著(B 类 +4 题 = +5.4 pp,单看会高估);**稳的只有两条**——总体 +3.9 pp(净 10 题)与刹停整体 −29%(B 从 38.1 降到 27.0 次/题,是 74 题上的均值移动)。
- **关键读法(决定预算投向)**:候选容量翻倍把 B 的**拒绝次数**砍掉近三成,但 B 的到达只从 27% 到 32%,离 A 类的 78% 还差 46 pp。⇒ **"少被拒"不等于"会绕"**:B/C 的缺口是策略在这些状态下**根本不产出绕行轨迹**,不是被闸门挡住的。继续在排序/认证口径上打磨救不了 B/C;要动的是数据侧(在受阻状态下采集教师轨迹,即 DAgger 第三轮,需要放行)或明确把 B/C 列为当前能力边界之外。
- 由此更新候选清单:#24 修正版(`tie='gap'`,等长前缀时按沿路最小间隙定夺)仍然值得测——它影响的是"选了哪条活路",会影响后续拍的存活率;但它**不能**被期待修好 B/C 的 46 pp,那属于训练侧。
## 分层指标上线并被三重交叉验证(轮次 43)
- 对照配方(轮次 33 cap:1 步、`--inc_cap 1`)重跑一次带分层指标的评估:**原有 22 个指标逐位不变**,新增键全部产出(`docs/experiment_round43_control_strat.json`):
| 新读数 | 值 | 独立离线预测 |
|---|---|---|
| `success_reachable`(排除按定义不可达的 D 类) | **0.5514** | 0.5514(逐题 dump 手算)✓ |
| `by_cat` 到达 | A 96/128=75%、B 20/74=27%、C 11/32=34%、D 0/13、Dr 7/9 | 与离线一致 ✓ |
| `brake_rate` mean / p50 / **p90** | 10.03% / 0.51% / **31.54%** | 均值与手算 19.55/195 一致 ✓ |
| `stuck_share`(brake_rate>0.2 的题占比) | **23.4%** | 与"最卡前 20% 题占 72.4% 刹停事件"同量级 ✓ |
| `reverse_p50` / `reverse_moving_p50` / `moving_share_p50` | 0.2474 / 0.3258 / 0.9587 | 离线预测 0.247 ✓ |
- 三处独立来源(逐题 dump 手算、轨迹转储离线重算、评估内建指标)在四个数上完全对齐 ⇒ 这批分层读数可以当判据用,不只是看板。
- **为什么现在必须有 p90/stuck_share**:对照口径的均值刹停率 10.03% 看着"差 5 倍",但真相是 **p50 只有 0.51%、p90 高达 31.5%、23.4% 的题踩在卡住线上**。以前只有均值时,`backoff` 这类"专治尾巴症状"的改动能把判据刷好而行为不变(已实测:brake_rate −16%、到达 −0.8 pp)——**没有 p90/stuck_share 就看不出这种背离**,这就是 #25 存在的理由。
## `backoff` 四臂判掉:转化率能买到刹停率,买不到到达——而且它能**绕过 §9.2 的判据**(轮次 43)
- 同一 256 题同 seed、`--inc_cap 1 --cert_batch 1`,只改缩步档数(`docs/experiment_round38_backoff{0,1,2,3}.json`):
| 档数 | 到达 | 时限内 | 刹停/题 | 缩步顶上/题 | 全拒事件合计 | 转化率 | `brake_rate` mean |
|---|---|---|---|---|---|---|---|
| 0(对照) | 0.5234 | 0.2656 | 19.55 | 0 | — | — | **10.03%** |
| 1(1/2) | 0.5273 | 0.2695 | 18.78 | 1.57 | 20.35 | **7.7%** | 9.63% |
| 2(+1/4) | 0.5234 | 0.2617 | 18.12 | 5.00 | 23.11 | 21.6% | 9.29% |
| 3(+1/8) | **0.5156** | **0.2578** | 16.48 | 9.70 | **26.18** | 37.1% | **8.45%** |
- **预注册判读命中低那一支**(转化率 <60%):把全拒变成"慢一拍的动作"确实随档数上升(7.7%→37.1%),但**到达和时限一点没赚**(0.5234/0.2656 → 0.5156/0.2578,方向还是负的)。⇒ 被全拒的那些状态不是"差一点就能过",而是**这个方向本来就不通**:缩步只是慢一点去撞同一堵墙。
- 更值得警惕的是第二列和最后一列的**背离**:`brake_rate`(§9.2 要 <2% 的那个判据)从 10.03% 一路降到 8.45%,`brakes_mean` 降 16%,**但真实结果没变好**。也就是说**缩步档可以只改善刹停率指标而不改善行为**——判据本身能被绕过。而且"全拒事件合计"反而从 20.35 涨到 26.18:缩步把机械臂带进了更容易被拒的几何里。
⇒ 规则:**任何降低刹停率的改动,必须同时报到达与时限**,只看 `brake_rate` 会奖励错误行为。这条已加进 #25 的分层读数要求。
- 处置:`backoff` 保持默认关、**作为到达率杠杆判掉**;它在"减少原地站住"这件事上确实有效(−16% 刹停),所以留作实机上"不许完全停住"这类需求的安全侧选项,不再投入档位扫描。
- 下一步(链已结束,可以改库):#25 分层指标(可达分母 / 按类别到达率 / `brake_rate_p90` / `stuck_share`)→ 用它重读 K=16(#27:+3.9 pp 到底买在哪一类)→ 才轮到 #24(几何信息进打分,直接打 B/C 类"方向不通")。
## 控制臂对表:22/22 键逐位复现 + 仪表预测精确命中(轮次 42)
- `--backoff 0 --cert_batch 1 --inc_cap 1` 重跑轮次 33 cap 配方:**两文件共有的 22 个指标键全部相等,差异集合为空**(第三次确认批量认证分支重写没有改变行为)。
- **仪表互验精确命中**:这个控制臂的 `reverse_p50 = 0.2474`,而我先前用轮次 33 cap 转储、按 `rollout` 同窗口离线重算的预测值是 **0.247**(那次还给出"到达组 0.265 / 未到达组 0.233")⇒ 线上指标与离线重算在三个小数位上对齐,之前 1.575 那批读数确实只是"未除 6 的旧口径"。
- 控制臂的 §9.2 判据读数(第一次用真分母):**`brake_rate_mean = 10.03%`、`p50 = 0.51%`、`n_plans = 195`、`moving_share = 95.9%`** ⇒ "<2% 推理周期"在均值上差 5 倍、在中位上已达标,再次是长尾。
- 待读(正在跑):`backoff=1/2/3` 的 `backoff_mean / reject_all_mean` 转化率,按预注册规则判:10–20% ⇒ 缩步只是慢一点撞,到达不动,主攻转向 #24;>60% ⇒ 直接吃到那 63/109 的冻住时间。
## "冻住"的真实形态:不是堵死,是每题约 30 次、每次 1.8 步的反复试探(轮次 42,纯读转储)
- 先自纠一次框架:我按"≥10 步连续零运动"找长冻结,结果 B 类 74 题里只有 9 题有这样的段、C 类 32 题只有 3 题。原因很直白:`commit_n=2` 时**一次全拒只站 2 步**,要连成 10 步得连续 5 次全拒。所以"长冻结"这个说法对这份配置基本不成立,改测"停顿事件的累计与分布":
| 类别 | 题数 | 到达率 | 零步占比 | 停顿段数/题 | 平均段长(步) | 刹停/题 |
|---|---|---|---|---|---|---|
| A 均匀 | 128 | 80% | **7.9%** | 9.5 | 0.59 | 7.4 |
| B 直线被挡 | 74 | 34% | **28.7%** | **34.1** | 1.81 | 37.2 |
| C 贴立柱 | 32 | 41% | 25.2% | 29.2 | 1.57 | 33.3 |
| D 不可达 | 13 | 0% | 2.8% | 2.2 | 0.15 | 5.4 |
| Dr | 9 | 78% | 7.5% | 8.1 | 0.47 | 9.7 |
(`ml/runs/round33_base.pt` + 逐题 dump;这是 steps=1、不限幅口径,用于看**形态**不是看成绩)
- **结论**:B/C 的失效不是"走进死路被堵死",而是**反复试探**——每次规划全拒、站约 2 步、下次换个方向再试,一题平均 30 多次,累计把 **29%** 的时间窗花在停走上。它们最终能脱困(段长只有 1.6–1.8 步),只是每次都白站两拍。
- **这条把杠杆的预期钉准了**:
- `backoff`(全拒→缩慢一步)正好打在"每次白站 2 步"上:若 30 多次停顿各省 1–2 步,等于省 30–60 步 ≈ **时限内 20–30% 的时间**,但**不会**把"方向本来就不通"的候选变成通路。
- 真正要拿到 B 类到达(34%→A 类那样的 80%),得让**被拒之前的排序**就能避开必然穿模的方向(#24:把已经算出来的沿路间隙喂进打分),而不是靠事后缩步。
- D 类几乎不停(2.8% 零步、2.2 段),是"直接开去停靠位、永远不满足判据"——再次印证它们不该在到达率分母里。
## 缺口几乎全在"直线被挡 / 贴立柱"两类(轮次 41,按任务类别分层)
- 六个已跑口径(1/2 步 × cap/margin/both,同一 256 题同 seed)的逐题 dump 交叉比对:
- **95 题(37%)在任何口径下都到达**;**106 题(41%)在任何口径下都不到达**;只有 55 题是"口径敏感"的。
- 卡住题集在四个一步口径之间高度重合(Jaccard 0.87–0.91),换 cap/margin/反馈档**换的不是同一批题**;而 `steps=2` 的卡住集(135 题)**完整包含**一步的 71 题(交集 71=全集)⇒ 多段是"新增"了一批卡住题,而不是换了一批,与候选塌缩的机理一致。
- **按类别的到达率**(`--inc_cap 1` 合法限速口径;类别定义见方案 §采样:A 均匀 45%、B 直线被挡 30%、C 贴立柱 15%、D 目标不可达 10%):
| 类别 | 1 步限幅 | 2 步限幅 |
|---|---|---|
| A 均匀(无障碍) | **75%** (96/128) | 61% |
| B 直线被挡 | **27%** (20/74) | 16% |
| C 贴立柱 | **34%** (11/32) | 16% |
| Dr | 78% (7/9) | 67% |
| D 目标不可达 | 0% (0/13) | 0% |
- **读法**:总到达 52% 的缺口几乎全部来自 **B(30% 的题,只有 27% 做到)与 C(15% 的题,34%)**——无障碍的 A 类已经 75%。⇒ 剩下要拿的分**不是"普遍再准一点",而是"直线被挡住时会绕"**。这与前面所有证据自洽:安全层全拒集中在尾巴题、多段推理因候选塌缩而更拒、K 加倍最赚(多给几条活路)。
- **一处必须纠正的分母口径**:D 类是"目标不可达、标签为停靠"的题,**按定义不可能到达**,却一直留在我的到达率分母里(§9.2 原文要的是"held-out **可达**任务到达率 ≥98%")。去掉 13 道 D 后:一步限幅 0.5234 → **0.5514**、一步不限幅 0.5742 → 0.6049、两步 0.3945 → 0.4156。差 **+2.8 pp**,不大但方向是系统性偏高/偏低混合,**以后必须两个数一起报**。历史轮次保留含 D 的分母(那是当初记在文件头的约定),但从下一轮起新增 `success_reachable` 与按类别到达率,别让"98%"和含不可达题的分母对话。
- 待办(链结束后改库):`eval_cl` 补 `success_reachable`(排除 cat=D)、`by_cat` 到达率、`brake_rate_p90`、`stuck_share`。这四格是让"对尾巴/对 B 类有效"的杠杆能被看见的前提。
## 刹停是极端长尾:一半题零刹停,两成题占 72% 事件(轮次 41,纯读已落盘逐题数据)
- 轮次 33 base(一步、不限幅)的逐题 dump:256 题共 4928 次全拒,均值 19.25/题。
| 分组 | 刹停均值 | 刹停中位 |
|---|---|---|
| 到达的 147 题 | **1.72** | **0** |
| 未到达的 109 题 | 42.89 | 45 |
分位数:**48.8% 的题一次都不刹**;最卡的前 5% 题占全部刹停事件 **24.5%**(92.8 次/题),前 10% 占 **42.9%**,前 20% 占 **72.4%**;后 80% 只占 27.6%(均值 6.6)。>30 次的 71 题(27.7%)几乎承担了整条判据缺口。
- **这改变了"怎么评判一条杠杆"**:§9.2 的"刹停 <2% 推理周期"用**均值**报,会被这 71 题拖死(K=16 均值 7.06%、中位 0.0),而大多数题其实早就满足。⇒ 以后每条臂都要**分层报**:`brake_rate_p90` 与"卡住题占比(brake_rate>0.2)"要进指标,否则一条只对尾巴有效的杠杆(#24、backoff)会在总均值上看起来"没动"。
- 与失败分类完全对上:63/109"走一半被冻住"就是这批题;**它们同时也是到达缺口的全部来源**(可拿的 +25 pp 区间就在这批题里)。所以攻它们不是锦上添花,是主攻方向。
- 待办(等链跑完再改库,避免重犯 14:45 那次事故):给 `eval_cl` 补 `brake_rate_p90` 与 `stuck_share`(判据 `brake_rate > 0.2` 的题占比)。
## `ep_waste` 三臂也是负结论,但给出了原因:罚的是没被执行的那段地平线(轮次 42)
- 同一 256 题同 seed、`--inc_cap 1`,只改 `select=ep_waste --len_w`(罚 `1 − 同轴净/总` × 当前误差):
| 臂 | 到达 | 时限内 | 行程比 | 刹车/题 | 刹停率 mean / p50 | 换向率 |
|---|---|---|---|---|---|---|
| 对照(endpoint,1 步限幅) | 0.5234 | 0.2656 | 2.6158 | 19.55 | ≈0.099 / — | 离线 0.247 |
| waste 0.1 | 0.5156 | 0.2695 | 2.6697 | 19.39 | 0.0995 / 0.005 | 0.2527 |
| waste 0.3 | 0.5156 | 0.2656 | **2.5349** | 19.63 | 0.1007 / 0.005 | 0.2511 |
| waste 1.0 | **0.4883** | **0.2383** | 2.7083 | 20.03 | 0.1027 / 0.010 | 0.2490 |
(`docs/experiment_round40_waste{0.1,0.3,1.0}.json`)
- **行程比没有随权重单调下降**(2.67 → 2.53 → 2.71 vs 对照 2.62):0.3 那一格看着像改善,但两侧都比它差 ⇒ 不是趋势,不能拿单点报"行程降了 0.08"。权重加到 1.0 时到达 −3.5 pp、时限 −2.7 pp,纯亏。
- **原因(这条比结论更有用)**:打分用的浪费比例是在**候选的 16 步地平线**上算的,而每拍真正提交只有 `commit_n=2` 步。排序被"后面 14 步会不会来回抖"牵着走,执行的前 2 步基本不变 ⇒ 改了顺序改不了行为。**要影响执行,惩罚必须只算在会被提交的那段前缀上。**
- 与 `ep_len` 三臂合起来看:**选择期的"路径形状类"惩罚(总行程 / 浪费比例)在两个版本上都是负结论**,而选择期的"多给活路"(K=16 +3.9 pp)和"几何信息进打分"(#24,待测)才是对的方向。差别在于前者动的是没被执行的那段,后者动的是被拒/被选的那 2 步。
- 内部一致性:`brake_rate_mean` 0.0995/0.1007/0.1027 与 `brakes_mean/n_plans` = 19.4/195 ≈ 0.0995 逐位对得上 ⇒ 刚补的分母口径没接错。
## K=16 串行 vs 批量认证等价 + 第一次真正核算"刹停率"(轮次 41)
- `--K 16 --cert_batch 1` 与 `--K 16`(串行)跑同一 256 题同 seed:`success / time_ok_1p3 / brakes_mean / travel_ratio_median / collision_*` **全部逐位相同**,差异键只有 7 个、且全是我这几轮新加/改口径的报表键(`reverse_*` 的两种口径、`n_plans`、`brake_rate`、`moving_share`)。⇒ **轮次 39 的"试候选数 1/3/8/16 曲线"同样描述批量口径**,也就是说 K=16 到达 0.5625 这个收益不依赖串行早停这个巧合。
(`docs/experiment_round40_K16cb1.json` vs `docs/experiment_round39_K16.json`)
- **整批墙钟 573 s → 469 s(−18%)**:批量把 K 次小调用并成 1 次大调用,在 B=256 的评估里本来就更快。这只说明趋势,**单次规划的 P99 要用独占微基准另测**(修好的探针排在链尾)。
- **`brake_rate`(刹停 / 规划次数)第一次有读数**:K=16 口径 `mean = 7.06%`,但 **`p50 = 0.0`**,`n_plans_mean = 195`,`moving_share_p50 = 98.7%`。
⇒ §9.2 要"<2% 推理周期",均值差 3.5 倍,但**中位任务一次都不刹**——全部刹停集中在一小撮题上。这与失败分类完全一致(63/109 是"走一半被冻住",而它们就是那撮尾巴)。
**含义**:继续在"普遍少刹"上花力气收益有限,真正的问题是**那批卡住的题里安全层没有可行候选**——正是 K=16(+3.9 pp)与 #24(把间隙喂进打分)要打的靶子。
- 仪表互验:K=16 批量臂 `reverse_p50 = 0.2625` 与我离线同窗口重算的 K=8 对照 0.247 同量级(K=16 略高,与它到达更多、动得更多一致);`reverse_moving_p50 = 0.3194` > 全步口径 ✓ 方向正确(除掉站住的步之后更抖)。之前那批 1.x 的读数确系修分母前的旧口径。
## #24 的设计定稿:一次几何调用同时供打分与认证(不放松判据、不加成本)
- 表面看有循环依赖:打分要用的"沿路间隙"目前只在安全层里算,而安全层只认证**排完序**的候选。破法是把顺序倒过来——**先对全部 K 条候选算一次 `2·commit_n` 点的间隙矩阵**,然后:
- 打分:`score = −e_end − gap_w · relu(gap_ref − 该候选沿路最小间隙)`(间隙不足的候选先降序,但**不淘汰**它,判据仍是后面的 `gap > 0`);
- 认证:直接用同一份间隙矩阵算 `plen`,**不需要再算第二次**。
- 于是代价是**零额外几何调用**,但口径上要求走批量认证(`cert_batch=1`)——串行那条靠"第一条全安全就早停"省掉后面 K−1 次调用,一旦要按间隙排序就必须先看全部候选。这跟实时性是同一个方向:批量口径本来就是为压最坏情况(轮次 34:80.2→37.1 ms)而做的,所以这条杠杆顺带把部署口径推到批量那侧。
- 读法预设(免得事后挑数):主判据是 `brake_rate`(全拒/规划次数,刚补上的分母)与 `success`/`time_ok_1p3`;`gap_ref` 取 0.5 mm 起(宽相位截顶值以内,见轮次 33 的口径教训:余量门限高于 cap 会恒假)。
- A/B 脚本 `ml/runs/_gapscore_sweep.sh`(`gap_w = 0.5 / 2.0 / 8.0`,三档差一个数量级,看是否有拐点)。等链跑完、库能改之后实现+单测再跑。
## 代价探针的第一版量错了对象(仪表缺陷,已修)
- 第一版 `ml/runs/_kcost_probe.py` 把"整次 `rollout(..., max_sec=0.041)` 调用"当成"一次规划"来量,得到 `K8/cb0 P50 = 144.8 ms`、`K16/cb0 = 139.6 ms` 这种**比轮次 34 的 33.5 ms/规划大四倍**的数——那里面包含每次调用的固定开销(`tasks_to_tensors`、生成器、首段同步),2 步的调用只做 1 次规划,所以固定开销占了大头。绝对值完全不可比,我先差点把它当"K 翻倍不花钱"的证据。
- 修法是**用两个长度解出固定开销**:`max_sec` 取 2 步(1 次规划)与 8 步(4 次规划),`每次规划成本 = (T8 − T2)/3`。这样才与 §9.2 的"每次推理+认证 P99 <20 ms"同一口径。
- 顺带一条**不能混的机理**:串行认证在 B=1 有早停(第一条全安全候选就 `break`),所以它对 K **不敏感**——第一版那组 144.8 vs 139.6 其实主要是这个早停在起作用,不是"K 免费";批量认证没有早停,K 从 8 到 16 是真的多算点(第一版里 150.7 → 169.1 ms)。两条口径对 K 的响应相反,报数时必须分开写,否则会把"串行下 K 便宜"错当成"批量下也便宜"。
- 修好的探针排在当前批次结束后单独跑(不改正在被 bash 逐行读取的 `_next_batch.sh`,只跑独立脚本)。
## 到达率的上限在哪(把 #21 的数读成"还剩多少可拿")
- 两条界都在同一 256 题、`--inc_cap 1` 口径下量得:
- **不要求无碰撞**:纯策略到达 **0.7773**(一步)/ 0.8164(两段)——这是"控制器本身能把活干完"的上限。
- **要求无碰撞**:纯策略只有 **0.3594**(一步)/ 0.3750(两段)——这是"完全没有安全层"时的下限。
- 当前部署口径(开安全层、K=8)= **0.5234**;K=16 = **0.5625**。
- 所以可拿的区间是 **0.5234 → 0.7773**(+25.4 pp),而它不是靠放松判据拿:这段全部是"被拒的候选里其实有安全的路"。#24(间隙进打分)与候选数(#1)是同一件事的两个方向——前者让排序避开死路,后者多给几条活路。
- 反过来,纯策略无碰撞到达 0.359 也说明**不能靠"少拒一点"来吃这 25 pp**:拒得越少,穿模越多。只有"选得更准"才吃得到,这也是为什么把已有的间隙数喂给打分比再加余量门限更值钱。
## 判别定案(#21):两段动作不是"更危险",是"没有备选";顺带量出安全层值多少到达
- 关安全层(`--safety 0`,同 256 题、`--inc_cap 1`,只改段数):
| 口径 | 到达 | **无碰撞到达** | 碰撞率 | 碰撞步/题 | 时限内 | 行程比 |
|---|---|---|---|---|---|---|
| pure steps=1 | 0.7773 | 0.3594 | 0.5859 | 58.34 | 0.5352 | 2.375 |
| pure steps=2 | **0.8164** | **0.3750** | **0.5586** | **45.07** | **0.6836** | **1.594** |
| 开安全层 steps=1(对照) | 0.5234 | 0.5234 | 0 | 0 | 0.2656 | 2.616 |
| 开安全层 steps=2 | 0.3945 | 0.3945 | 0 | 0 | 0.2969 | 1.844 |
(`docs/experiment_round40_safetyoff_steps{1,2}.json`)
- **结论 1(判据 #21 定案)**:关掉安全层以后,两段在**每一格都不更差**——到达更高(+3.9 pp)、时限内高一大截(+14.8 pp)、**碰撞步反而更少**(58.3→45.1)、行程比更接近老师。⇒ "两段动作更危险"被否;两段在开环与纯策略口径都更好,它唯一输的地方是"开安全层时刹车多 4.5 倍"。
- **结论 2(机理)**:这正好是候选塌缩的必然结果——`steps=2` 时 K 条候选的一阶步标准差从 0.852 掉到 0.0245(35 倍),"塌成一条"以后,那条在采样点上穿模就等于**全拒**;`steps=1` 时候选彼此差得远,总有一条能过。所以"没有备选"是主因,不是"界太保守"(线上只有端点+中点点检,没有位移界)。
- **结论 3(安全层值多少到达,首次定量)**:一步口径下安全层把到达从 0.7773 压到 0.5234(**−25.4 pp**),换来碰撞从 58.6% 到 0;纯策略的**无碰撞到达只有 0.3594**,比开安全层的 0.5234 低 16.4 pp ⇒ **安全层确实在净收益上是赚的**,不是白拿到达。
- **对"把一步角点训准"这条训练路线的直接警告**:一步候选的多样性,恰恰来自角点的**偏差/欠缩放**(每条 ε 被削到不同的衰减幅度)。如果训练把一步映射成两段那样的收敛解,候选可能一起塌缩 ⇒ **按当前安全层口径,到达率可能不升反降**(实测就是这个方向:steps=2 开闸 −12.9 pp)。所以任何"修角点"的训练都必须**同时**处理候选多样性(更大 K、或给候选注入受控多样性),否则就是拿开环收益去换闭环到达。
- 顺带修一处历史口径:§9.2 的"策略单独碰撞率 <3%"这一格,现在才有合法读数 **58.6%**(一步)/ **55.9%**(两段)——差距巨大,且以前从没在同一配方下报过。
## §9.2 记分板(2026-10-20 现状,最好的合法口径 = `--inc_cap 1` + K=16)
| 判据(§9.2) | 要求 | 当前 | 差距 |
|---|---|---|---|
| 实际执行碰撞 | = 0 | **0**(`collision_rate 0`、`collision_free_success` = `success`) | **达标** |
| 超 VMAX 的题占比 | = 0(本轮新增的硬口径) | **0**(`over_vmax_rate 0.000`,开 `--inc_cap 1` 后;不开则 1.000、峰值 1.660×VMAX) | **达标**(代价:到达 −5.1 pp) |
| held-out 可达任务到达率 | ≥ 98% | **0.5625**(K=16)/ 0.5234(K=8) | 差 **41.8 pp** |
| 到达用时 ≤ 老师×1.3 | 全部到达题 | 达标比例 **0.2969**(K=16)/ 0.2656(K=8) | 到达题里也只有约三成按时 |
| 关节行程 ≤ 规划器×1.10 | ≤1.10 | 行程比中位 **2.62** | 超 **1.4 倍**,且罚候选行程是负结论(见上) |
| 冷启动 | <10 ms | 未单独测(周期 P50 9.0 ms 是稳态推理,含首段编码) | 待测 |
| 推理 + 安全认证 P99 | <20 ms | 现口径周期 P50 33.5 / P99 64.7 ms;批量认证把最坏 80.2→37.1 ms | 差 **2~3 倍** |
| 刹停 < 2% 推理周期 | <2% | 估算 **≈9.9%**(19.25 次全拒 ÷ 195 次规划;K=16 时 13.77/195 ≈ 7.1%) | 差 **3.5~5 倍** |
- 刹停率这一格现在是**手算估算**:`eval` 产物里以前没存每题规划次数。已补 `n_plans` 与 `brake_rate`(并因此踩了 `per['plans']` 键名冲突的坑),当前批次之后的臂会直接给这个数,届时替换估算。
- 记分板脚本 `ml/runs/_scoreboard.py`(读所有产物 + 轮次 33/38 的 GPU 微基准)。**注意它的输出在 GBK 控制台下会因 `⇒` 崩**,要带 `PYTHONIOENCODING=utf-8` 跑——这是我自己的老坑第 N 次复现,脚本以后只打 ASCII。
- 读法:判据之间是相互拉扯的(限速 −5.1 pp 到达、多段推理 +2.4 pp 时限但 −15 pp 到达、放大噪声 −8.2 pp 时限),所以**任何单格变好都要同批报其他格**,这正是记分板存在的理由。
## 多段推理的代价实测(B=1、K=8、逐轮交替、奇数轮反序)
- `docs/experiment_round38_steps_cost.json`(24 轮 × 每轮 40 次,取每档的中位):
| 段数 | 推理 P50 | 相对 1 段增量 |
|---|---|---|
| 1 | 9.02 ms | — |
| 2 | 14.98 ms | **+5.97 ms** |
| 4 | 26.72 ms | +17.71 ms |
| 8 | 51.07 ms | +42.05 ms |
- 每多一段 ≈ 多一次解码器前向(编码器 `mem` 只算一次),所以增量近似线性;9.02 ms 这一档比轮次 33 报的 7.2 ms 高,是不同会话的时钟/上下文差别,**只用同批内的差值,不用绝对值跨轮比**。
- 合到整周期上:轮次 34 的现口径 P50 ≈ 33.5 ms,两段就是 ≈39.5 ms(25 Hz),四段 ≈51 ms(19.6 Hz)——**离 20 ms 预算更远**,所以"靠多段推理把一步的偏差补回来"在部署上不成立,只能作为开环诊断口径。
- 与 K 这条轴叠加会更糟(K=16 认证点翻倍),所以 `K=8/16 × cert_batch=0/1` 的四格周期微基准排在当前批次里(`ml/runs/_kcost_probe.py`),这是判断"候选数能不能买到部署里的到达率"的关键一列。
## 候选为什么会"塌":两段积分把 K 条候选吸成一条(轮次 39 探针,机理定案)
- `ml/runs/_cand_div.py`(256 题特征、同一批噪声 z、K=8,只改段数):
| steps | 候选间一阶步的标准差 spread | 两两平均距离 pairwise | 第 1 步模长 | ±1 饱和率 |
|---|---|---|---|---|
| 1 | **0.8518** | 5.3647 | 1.1565 | 5.2% |
| 2 | **0.0245** | 0.3024 | 1.0956 | 3.9% |
| 4 | 0.0211 | 0.2861 | 1.0970 | 6.9% |
⇒ **多段积分把 K 条候选的差别抹掉了 35 倍**(0.85 → 0.024)。这不是"动作更大更危险"那么简单:安全层是在 K 条**几乎相同**的候选里挑一条能走的前缀,挑无可挑就刹停。
- 这条与轮次 38 的实测严丝合缝:`steps=2/4/8` 到达掉 13–15 pp、刹车 ×4.5,但**开环误差与行程比都更好**——动作质量提升是真的,到达率下降是"没有备选"造成的。
- **但"人为制造多样性"不是解法**(`z_scale=1.5`,同 K=8):刹车确实砍一半(19.55→10.51),可到到达掉到 0.4961、时限内从 0.2656 崩到 **0.1836**、行程比恶化到 3.5243、换向升到 0.32。⇒ 放大噪声换来的"能过的候选"是**分布外**的候选:安全层放行它,但它走的是绕、抖、慢的路。**K=16 的收益来自"同分布内更多候选",不是"多样性本身"**——这两个数放在一起才把这条轴的边界说清楚。
- 判据状态:#21 的两个分支现在这样分:**"动作更危险"不是主因,"候选塌缩"是主因**(spread 35 倍 + try/K 单调曲线 + z_scale 反例三条独立证据)。剩下要量的只是成本口径(K=16 在批量认证下贵多少)。
## 事故:我在链式作业排队期间编辑了被 import 的库,两个臂直接没跑
- 时间线:`z15` 于 14:46:38 结束,链接着拉起 `safety=0 steps=1`(14:46:38)——而我为了让 `brake_rate` 有分母,正在 14:45:5x 用脚本改 `ml/sim/closed_loop.py`,一次替换把 `per = {...}` 的字典提前闭合,留下 `IndentationError: unexpected indent`。**新起的 python 进程 import 到的是半成品文件**,两个 safety-off 臂各 2 秒就退出(日志里 `=== safety=0 steps=1 done 14:46:40 ===` 的"done"只是 bash 循环走到下一行,不代表成功)。修好在 14:46:52,48 项测试全绿。
- **教训(比我原先记的"别改正在跑的脚本"更进一层)**:链式作业会在**未来某个时刻**新起解释器,所以"只要不改正在跑的那一个进程"是不够的。规则改为:**链没结束就不编辑被 import 的库;要编辑,先等通知或先让链跑完**。并且 `=== done ===` 这种循环日志不能当成功凭据——**判成功要看产物文件在不在**(这次 `docs/experiment_round38_safetyoff_steps{1,2}.json` 根本没生成,一看便知)。
- 两个 safety-off 臂与 `ep_waste`/`backoff`/K 代价一起并入下一批重跑(`ml/runs/_next_batch.sh`)。
- 顺带补了验收判据缺的分母:`rollout` 现在记 `n_plans`(每题规划次数)并给 `brake_rate = brakes/n_plans`、`eval_cl` 报 `brake_rate_p50/mean` 与 `n_plans_mean`——§9.2 要的是"刹停 <2% **推理周期**",以前只有绝对次数,**这条判据实际上从来没被真正核算过**。命名上躲开一个坑:`per` 里原本有个 `'plans'` 键是转储用的逐规划字典,新键必须叫 `n_plans`,否则 `flat` 的过滤 `if k not in ('traj','plans')` 会把它静默吃掉。
## 试候选数的成本-收益曲线(轮次 39,四档齐全,#15 主结果)
- 同一 256 题、同 seed、`--inc_cap 1`、其余配方(`select=endpoint, replan=empty, commit_n=2, K=8, fb=2.2`)不变,只改**安全层允许试几条候选**:
| 试候选数 | 到达 | 无碰撞到达 | 时限内 | 刹车/题 | 行程比 | reverse_p50 | 整批墙钟 |
|---|---|---|---|---|---|---|---|
| 1 | 0.4023 | 0.4023 | 0.2227 | 60.44 | 2.5107 | 0.1863 | 112 s |
| 3 | 0.4570 | 0.4570 | 0.2383 | 37.75 | 2.5297 | 0.2244 | 170 s |
| 8(对照) | 0.5234 | 0.5234 | 0.2656 | 19.55 | 2.6158 | —(改动前口径) | 329 s |
| 16 | 0.5625 | 0.5625 | 0.2969 | 13.77 | 2.6243 | 1.5750(旧口径,÷6=0.2625) | 573 s |
(`docs/experiment_round39_try{1,3}.json`、`..._K16.json`、对照 `docs/experiment_round33_cap.json`)
- **读法**:到达 0.402→0.562(+16 pp)、刹车 60.4→13.8(÷4.4)单调随候选数变好;代价是串行认证下 gaps 调用次数与候选数成正比,整批墙钟 112→573 s(×5.1,与题数无关的部分就是几何调用)。碰撞全程 0、限速照开 ⇒ **这不是放松判据换来的分数**。
- `reverse_p50` 随"站住更多"而**下降**(try1 的 0.186 < 对照 0.247),是稀释而不是变平滑 ⇒ 这个指标只能和 `moving_share` 一起读(后两个键从 14:41 之后的臂才有)。
- **部署口径的未决项**:上面的墙钟是**串行认证**(`cert_batch=0`)口径;批量认证把 K 条候选并成 1 次调用,理论上能把候选数带来的成本从"线性调用"变成"一次更大的调用"。所以 `K=8/16 × cert_batch=0/1` 的四格 B=1 周期微基准(`ml/runs/_kcost_probe.py`)是这条杠杆能否上线的关键读数——**跨轮次墙钟不算证据**,本表的墙钟只用来说明趋势量级。
## try3 反向档位 + 换向指标的稀释修正(轮次 39 续)
- `--safety_try 3`(只允许安全层试按分数排序的前 3 条候选,其余不变、仍 `--inc_cap 1`):到达 **0.4570**(对照 0.5234,−6.6 pp)、时限内 0.2383(−2.7 pp)、**刹车 37.75**(对照 19.55,+93%)、行程比 2.530、碰撞仍 0(`docs/experiment_round39_try3.json`)。
- 把"允许试几条候选"这条轴摆开,是**单调**的:
| 试候选数 | 到达 | 时限内 | 刹车/题 |
|---|---|---|---|
| 3 | 0.4570 | 0.2383 | 37.75 |
| 8(对照) | 0.5234 | 0.2656 | 19.55 |
| 16 | 0.5625 | 0.2969 | 13.77 |
⇒ "全拒是没备选而不是真没路"这条假设有**双向**证据(减档更差、加档更好),不只是单点相关。边际收益在递减:3→8 值 +6.6 pp,8→16 只值 +3.9 pp。
- **但不能当部署结论**:认证点数与候选数成正比(K=8 每次规划 32 个点 → K=16 是 64 个),而 GPU 周期 P50 已经是 33.5 ms、超 20 ms 预算。所以接下来必须先量 `K=8 vs 16` 的 B=1 独占微基准(`ml/runs/_kcost_probe.py`,已验证调用签名);如果 K=16 把周期推到 45 ms 以上,这条路就只能靠"一次批量调用覆盖全部候选"把常数项压回去(轮次 34 已证批量把最坏 80.2→37.1 ms)。
- **换向指标被刹停稀释,已修**:try3 报 `reverse_p50=0.2244`,比对照的离线同窗口值 0.247 **还低**,而它恰恰是站住最多的臂——因为分母 `nact` 把站住的步也算进去了。补了两个口径:`reverse_rate_moving`(只算动过的步)与 `moving_share`(动步占比,=1−站住比例)。
配套写了**可手算的负对照**:`+0.9 → 站一拍 → −0.9` 这一条轨迹给 1/18(全步口径)vs 1/12(动步口径)、moving_share=2/3 ⇒ 稀释效应是真的,不是猜的。写夹具时又踩一次自己的坑:`commit_n=1` 时每拍都重新规划、永远只执行候选的第 0 步,序列里的 0 与 −0.9 根本没被执行到(末步报 3.24° 而不是 0°)——夹具必须按"一次规划提交 3 步"来构造。测试 42→43 项全绿。
## K16:候选数翻倍是本轮第一个正向结果(轮次 39 续)
- 同一 256 题、同 seed、`--inc_cap 1`、其余配方不变,只把 `K` 从 8 加到 16(`docs/experiment_round39_K16.json`):
| 口径 | 到达 | 无碰撞到达 | 时限内 | 刹车/题 | 行程比 |
|---|---|---|---|---|---|
| 对照 cap(K=8) | 0.5234 | 0.5234 | 0.2656 | 19.55 | 2.6158 |
| **K=16** | **0.5625** | **0.5625** | **0.2969** | **13.77** | 2.6243 |
⇒ 到达 **+3.9 pp**、时限内 **+3.1 pp**、**刹车 −30%**,碰撞仍为 0,行程比不变。这一条直接支持失败分类的判断:**很多"全拒"不是真没路,而是 8 条候选里没有一条能走**;把候选池翻倍,安全层就找得到可行前缀。这是本轮(36–39)四个杠杆里第一个正向、且不放松任何判据的结果(限速 `inc_cap=1` 照开、间隙判据照旧)。
- **代价没量之前不许上线**:K=16 意味着每次规划的候选点从 32 个(8×2×2)到 64 个,认证与打分都要翻倍——这正好是要用独占微基准量的东西(跨轮次墙钟不算证据:这批整批墙钟 573 s vs 对照 325 s,但同期我在同一台机器跑了 CPU 分析,**不能当成本**)。`steps_cost` 探针之后要补一个 `K=8 vs 16` 的 B=1 周期微基准。
- **仪表对上了**:K16 报 `reverse_p50 = 1.5750`,是**我修分母之前**的口径(每步换向轴数,0–6),除以 6 = **0.2625**;离线按 rollout 同窗口算的对照臂预测是 **0.247** ⇒ 差 6%,在"K 更大→走得更顺"的合理方向内。读数规则写死:**产物里的 `reverse_p50` 早于 14:28 的一律先除 6**,之后的才是 [0,1] 的分数。
- 之后三臂(`safety_try 3/1`、`z_scale 1.5`)在跑:`safety_try` 是**成本方向**的对照(只试前 3/1 条候选会掉多少到达),`z_scale` 是多样性方向。
## 指标链路端到端自证 + 预注册一条判读(轮次 39 续)
- **CPU 冒烟(4 题、`--select ep_waste --len_w 0.3 --cert_batch 1 --backoff 2 --inc_cap 1`)**:产物里 `reverse_p50 / reverse_mean / backoff_mean / reject_all_mean` 四个新键**都在**(`ml/runs/_smoke_waste.json`)。这是在验证"生产端改了、消费端也要改"那条教训落地——上次 `backoff_mean` 就是这样静默丢掉的。同时证明 `ep_waste` 与 `backoff` 在真实运动学管线上能一起跑,不靠打桩。
- **预注册判读**(读 256 题四臂之前先把解释规则写死,免得事后挑数):这次 4 题小样本给的是 `backoff_mean 11.25 / reject_all_mean 88.25` ⇒ 缩步转化率约 **13%**。
- 若 256 题也只在 10–20% 量级:结论是**"冻住"的根因是候选方向本身对着障碍**,缩步只是慢一点撞——那 `backoff` 只留作安全侧兜底(它确实把零运动换成慢运动,但不买到达),到达率这条路要转向"围绕受阻状态重采集数据"(训练侧,需要放行)。
- 若转化率到 60% 以上:`backoff` 直接吃到失败分类里 63/109 那组,值得继续投预算(并与 `ep_waste` 组合再测)。
- 两个分支都要同时报 `brakes_mean` 与到达/时限,不能只报转化率。
## 轮次 38:把 `--steps` 打通之后,闭环反而变差——瓶颈从"动作质量"移到了"安全层拒绝率"
- **动机**:E1-b 实测同一个模型的一步输出比它自己的两段采样离老师远 **10 倍**(4.33 vs 0.43)。那就先别急着训练——直接把推理段数当自变量,量闭环。`ml/eval/eval_cl.py` 加 `--steps`(默认 1,发布口径不变),同一 256 题、同 seed、同配方只改段数:
| 口径 | 到达 | 时限内(≤1.3×老师) | 刹车/题 | 到点耗时 | 行程比(中位) | 成功题末态误差 | DLS 接管份额 |
|---|---|---|---|---|---|---|---|
| steps=1 cap=0(=轮次33 base) | 0.5742 | 0.3906 | 19.25 | 2.31 s | 2.445 | 0.189 mm | 0.625 |
| steps=2 cap=0 | 0.4258 | 0.3555 | **86.31** | 1.67 s | **1.571** | 0.243 mm | 0.453 |
| steps=1 cap=1(=轮次33 cap) | 0.5234 | 0.2656 | 19.55 | 2.49 s | 2.616 | 0.218 mm | 0.613 |
| steps=2 cap=1 | 0.3945 | 0.2969 | 86.16 | 1.93 s | 1.844 | 0.293 mm | 0.445 |
| steps=4 cap=1 | 0.4062 | 0.3047 | 79.65 | 1.91 s | 1.882 | **0.135 mm** | 0.461 |
| steps=8 cap=1 | 0.4102 | **0.3242** | 79.70 | 1.93 s | 1.870 | 0.253 mm | 0.469 |
(`docs/experiment_round36_steps{2,4,8}_cap1.json`、`docs/experiment_round36_steps2_cap0.json`)
**段数趋势(都在 `--inc_cap 1` 的合法限速口径)**:到达 0.523 → 0.395 → 0.406 → 0.410(一步之后就先掉再平),时限内 0.266 → 0.297 → 0.305 → **0.324**(单调变好),行程比 2.62 → 1.84 → 1.88 → 1.87(单调变好、贴近老师),刹车 19.6 → 86 → 80 → 80(一步之后翻 4 倍就不再降)。
⇒ 多段推理把"更快、更贴老师、更省行程"这三样都买到了,唯独买不到到达率——那道门的拒绝率把它顶住了。段数从 4 到 8 几乎不再变化,说明瓶颈不在采样精度。
- **失败分类**(不占卡:读轮次 33 base 的 256 题轨迹转储,CPU 重算末态位姿误差,`docs/experiment_round38_fail_taxonomy.json`)。147 到达 / 109 未到,未到题按"末态离目标多远 + 被拒多少次"分桶:
| 桶 | 题数 |
|---|---|
| 位置 >100 mm 且刹车 >30 次(走一半被冻住) | **63** |
| 位置 >100 mm 但刹车 ≤30 次(方向错 / 绕圈) | 23 |
| 位置 20~100 mm | 15 |
| 位置 5~20 mm | 5 |
| 位置 <5 mm 但姿态没到位 | 3 |
| 起步失败 | 0 |
未到组中位数:位置误差 255 mm、姿态误差 11.0°、**活动窗内 37% 的步是零运动**。
⇒ 到达率的缺口不在精度或姿态判据(这两类合计只有 8 题),而在**走一半被冻住**:76% 的失败离目标还 >100 mm,其中多数是安全层反复全拒。这正是 `backoff`(全拒时缩步顶上)对着的那块;也说明 #15(候选数与安全档位)比"往打分里加剩余时限"更值钱。
- **反直觉但自洽的一组数**:两段推理**开环更贴老师**(E1-b:0.43 vs 4.33)、**行程比更接近 1**(1.571 vs 2.445)、**到了的题还更快**(1.67 vs 2.31 s),但闭环到达掉 14.8 个百分点。逐题对照:108 题两者都到、**39 题只有 1 步能到、只有 1 题是 2 步独有** ⇒ 不是"换了一批题",是同一批题里 1 步原本能做对的那部分被卡住了。
- **卡点在安全层不在策略**:刹车从 19.25 → 86.3/题(4.5 倍),未到达组的刹车中位数从 45 → 176(几乎每步都被拒 ⇒ 原地冻结,末态误差 p50 也从 230 → 406 mm)。也就是说**发布的那 57.4% 是与"一步那套偏置且被削弱过的动作"共同标定出来的**:策略、反馈增益、认证口径、DAgger 采集分布四者都围着它。
- **两种解释必须分开,否则下一步会走错**:
- (A) 两段动作确实更危险(一段更长 ⇒ 真的经过障碍附近);
- (B) 拒绝来自口径而非几何。
**先把我自己写的 (B) 改正一次**:线上安全层里没有"位移界随步长放大"这回事——它只做点检(每步的端点 + 中点,`gaps(...).min(-1) > 0`,见 `ml/sim/closed_loop.py` 的 `okstep = ok6[:, :commit_n] & ok6[:, commit_n:]`)。确定性界只在我离线做的连续认证里用。所以 (B) 不可能以"界太松/太紧"的形式成立;能以 (B) 解释的只剩两种:**点检欠采样**(那会让它偏松而不是偏严)与**候选多样性塌了**(两段积分把 K 条候选吸到一起,没有备选可退)。
于是判别实验定为三件:`--safety 0` 的 steps=1 vs steps=2 对照(看真实碰撞率差多少 ⇒ (A) 的直接证据)、候选多样性探针、steps 的 B=1 代价实测。脚本 `ml/runs/_safetyoff_ab.sh`,结果见下面补记。
- **对 K-MF 那条训练路线的直接含义**:把角点"训准"会让动作分布变成现在两段那样 ⇒ 按当前认证口径,到达率可能**先降**。所以训练不是下一步,先做 (A)/(B) 判别;若是 (B),收紧认证(细分/更紧的界)比训模型便宜得多,而且是 50 Hz 也必须的那块。
- **共同标定的第三处证据(回代码确认)**:`ml/dagger/collect.py:48` 调 `rollout` 时也不传 `steps` ⇒ DAgger 采集的状态分布同样是一步走出来的。所以"一步偏置的动作"同时被三处围着标定:策略微调数据、反馈增益、认证口径。改任一处都要另两处跟上,不能指望"只换推理段数"就白拿 10 倍开环增益。
## 轮次 37:K-MF(运动学 MeanFlow)方案核查——先测"一步 vs 多步",结果比论文那条更要命
- **交来的方案里那几条 file:line 断言,逐条回代码核过(都成立)**:`ml/model/meanflow.py:38-39` 是标准 Eq.4 单段自举(`jvp` 切向量 (v,0,1),目标 `v − (t−r)·du/dt`);`ml/train.py:162` 调 `meanflow_loss` 时**没接 ada_p** ⇒ 自适应加权一直是默认 `ada_p=1.0`(配置里改不了,与方案说的一致);`p_neq` 在第 `p_neq_warm=5000` 步从 0 硬切到 0.5;`p_edge=0.1` 把 10% 样本钉在部署角 (r=0,t=1);`policy.sample_k(..., steps=1)` 是默认,`ml/model/policy.py:153` ⇒ **历史发布的所有数字都是一步推理**。
- **方案里过时的两条要先改掉,否则会拿旧靶子做新实验**:「闭环 0% 到达、79% 碰撞」是 DAgger 之前的状态,现在是 256 题到达 57.4%、实际执行碰撞 0(轮次 30/33);「DAgger 全量是主线,先做」也已完成(`base_dag2_dag1` 就是两轮的产物)。所以 E 系列的判据不能再用"能不能从 0 救起来"。
- **E1 第一版判据是自证的,作废**:我先拿"网络自己的 r≠t 切片走 32 段"当平均速度真值——那正是 MF 损失直接训练的切片,等于用被测公式的收敛解当真值。改成**与损失无关的参考**:平均速度的定义是瞬时场(r=t 切片,由纯流匹配监督)的流图 `u_true = (z_t − Φ_{t→r}(z_t))/(t−r)`,128 段欧拉;离散化自证:128 段与 512 段的真值差 0.019(相对 **0.18%**)⇒ 参考本身稳。
- **E1 结果(`docs/experiment_round36_kmf_e1.json`,256 个 val 样本,λ=0.5,‖u_true‖ 均值 10.38)**:目标与真值的距离(越小越好)
| 目标写法 | (r=0,t=1) 部署角 | 最差 ‖du/dt‖ 处 (t=0.05) |
|---|---|---|
| MF(现行 Eq.4) | **3.851** | 2.629 |
| K-MF 按方案抄的 (1−λ)²/λ² | 7.187(1.87×,更差) | 3.808 |
| K-MF 把平方换成线性权重 (1−λ)/λ | 14.661(更差) | 2.106 |
| 把整条修正移到中点时刻 `du(z_c,r,c)/dc` | **2.364(0.61×)** | 2.468 |
| 网络现在的一步输出 u(ε,0,1) | 3.906 | — |
- **两条硬结论**:
1. **方案抄来的 Eq.7 系数在收敛解上不自洽**:λ=0.5 时两项系数和 = (1−λ)²+λ² = 0.5,不动点变成 `v − 0.5(t−r)u'`,不是平均速度;实测也印证——部署角 `‖du(z_t,c,t)/dt‖ = 28.0` 而 `‖du/dt‖ = 4.24`(把 r 槽钉在中点后对 t 的导数大 6.6 倍),组合被这一项主导,所以偏差反而更大。**K-MF 仓库只有 README,论文只能引公式,这条是我们自己的数**:要么抄漏了子区间时长因子,要么 c 的定义不同。判据「K-MF 偏差 ≤ MF 的一半」在这个写法下不成立(1.87×)。
2. **自举污染是真的,而且就在部署角**:网络一步输出与自己的流图差 3.906(相对 38%),而 MF 目标的偏差 3.851 几乎一样 ⇒ 训练把目标的偏差原样灌进了线上那一步。`‖du/dt‖` 在两端炸开(t=0.05 为 156.6、t=0.95 为 64.3,中段只有 1.6–4.1),与论文 Fig.1a 的形态一致(他们报 76×,我们这里 0.05↔0.4 是 **66×**)。
- **E1-b(`docs/experiment_round36_kmf_e1b.json`,512 样本,比的是"离老师多远",归一化动作块 L2)**:一步 **4.326**;同模型 2 段 **0.434**、4 段 0.429、32 段 0.601;流图(r=t,128 段)0.665。**一步比多步差 10 倍**,而 2 段就基本到位(再多段反而偏离老师——那是模型自身场的偏差,不是采样误差)。
- **这一条比 K-MF 本身更要命**:57.4% 的到达是在"线上一步输出偏离自己场 10 倍"的前提下拿到的,安全层/反馈/DAgger 都在替这个角点擦屁股。所以先把 `--steps` 打通,量它对闭环到达的影响,再决定要不要为"把一步训好"付一次训练。
- **不动点判决补一个解析实验**(`tests/test_meanflow_targets.py`,3 项,CPU/float64,与已训模型无关):1-D 高斯数据 `x~N(0,σ²)` 时 PF-ODE 是线性的,流图 `z_r = z_t·sqrt(s²(r)/s²(t))` 有闭式 ⇒ 平均速度真值有闭式。把**精确场**代进四种写法:
- 仪表自证:Eq.4 代精确场逐位还原真值(绝对偏差 <1e−12,σ=0.5/1/2 三档、(r,t) 三组都过)⇒ 后面的数可信。
- **收敛阶分开"有偏"与"无偏"**(区间长度 0.2→0.1→0.05,σ=2 的相对偏差):`sq`(按方案抄的 (1−λ)²/λ²)0.426→0.133→0.057,比值 ≈0.4 ⇒ **只有线性收敛 = 结构性少修一半**;`lin`(换成系数和为 1 的线性权重)0.042→0.015→0.004、`mid`(整条修正移到中点)0.147→0.042→0.010,比值 ≈0.25 ⇒ 二次收敛,只剩积分误差。
- 跨整段 (0,1) 时**所有**单点导数写法都不可信(σ=2:sq 1.92、lin 1.84、mid 4.15 倍偏差),跨半段就降到 0.44–0.85 ⇒ 与 E1-b 的实测同向:**一步跨完本身难,两段就便宜一个量级**。这不是训练的毛病,是写法本身的量级。
- 测试 35→38 项(缩步档 3 项)+ 新文件 `tests/test_meanflow_targets.py` 3 项 ⇒ 共 41 项。
- **目标写法开关已落地但默认不启用**(`ml/model/meanflow.py` 新增 `avg_velocity_target(..., target='mf'|'lin'|'sq'|'mid', lam)`,`ml/train.py:162` 从配置读 `mf_target`/`mf_lam`):
- 默认 `mf` 分支与原实现**逐位相同**(测试用同一批输入把老写法重构出来对拍 `u`、`u_tgt`、`z` 三个张量,rtol=atol=0)。
- 接线自证:把解析高斯场做成桩喂进 `avg_velocity_target`,float32 下三种写法的偏差与 float64 解析值逐位到小数四位一致(sq 0.4262→0.1332→0.0567、lin 0.042→0.0149→0.0038、mid 0.1465→0.0422→0.0104)⇒ z_c、切向量、r 槽换位没接错。
- 加这个开关不是为了训它,是为了"E2 若要训,先有一条不改默认路径的口子"。要不要付这次训练,等 #21 的判别结果(见轮次 38)。
## 时间账:限幅之后 time_ok 的损失在"站住"上,不在巡航速度上(2026-10-20,轮次 36 续)
- **口径**:直接吃轮次 33 的 256 题轨迹转储(`ml/runs/round33_cap.pt`、`round33_base.pt`,离线分析 `ml/runs/_time_audit.py`,产物 `docs/experiment_round36_time_audit.json`),按每题的活动窗把每一步按「该步最大关节位移 / VMAX·DT」分四档:零运动、<0.5、0.5–0.95、≥0.95。切片时踩了一个自己的坑:`per['traj']` 是 `torch.stack(traj, 1)` 出来的 **[B,T,6]** 而不是 [T,B,6],按列切成了"同一时刻不同题",比值全是 70~230 的 nonsense;加了 `traj[i][0] == q0` 的断言之后才挡住这类错。
- **实测(占活动窗的比例)**
| 组 | 题数 | 零运动 | <0.5 | 0.5–0.95 | ≥0.95 | 刹停段/题 | 平均段长 |
|---|---|---|---|---|---|---|---|
| base 到达且按时 | 100 | 1.9% | 4.0% | 19.4% | 74.8% | 1.2 | 1.5 步 |
| base 到达但超时 | 47 | 10.5% | 7.2% | 25.5% | 56.8% | 10.1 | 1.7 步 |
| base 未到达 | 109 | 32.6% | 9.9% | 32.8% | 24.7% | 38.6 | 2.1 步 |
| cap(限幅) 未到达 | 122 | 30.1% | 9.7% | 31.0% | 29.1% | 35.9 | 2.1 步 |
- **判读**:限幅并没有把动作整体拖慢——未到达组的 ≥0.95 档反而从 24.7% 升到 29.1%;真正吃掉时限的是**零运动**(占活动窗 30%,平均每题 1.54 s)和**长刹停段**(最长 41 步 = 0.82 s 原地不动)。所以 #17 不该往"打分里加剩余时限"这种间接方向做,直接打"全拒⇒站住"这一条。
- **改动(默认关,`--backoff`)**:全拒时不再站住,把首选候选按 1/2、1/4、1/8…(档数 = backoff)**并进同一次批量认证**(零额外几何调用),只在原候选连第 1 步都不安全时顶上;缩步候选不参与"前缀更长者优先"的比较,所以正常路径的选择逐位不变。新增 `backoff_mean` 指标记录缩步次数。测试 35→38 项(全拒→慢爬、有安全前缀时插手无效、未开批量认证时报错)。
- **待测**:256 题 A/B(`--inc_cap 1 --cert_batch 1 --backoff 0/1/2/3`)——GPU 先让给 K-MF E1,这轮排在其后。
## 轮次 36:规划起点队列长度实测——#19(循环内共享 FK)判掉
- **要判的问题**:轮次 35 留下"循环内唯一可省项"是 `qe == q` 时把 features 已算的 `cum_xf` 共享给反馈/打分,实测那份 `cum_xf` 在 B=1 值 **3.631 ms**。但它只在规划时队列为空(`r = queue.shape[1] == 0`)才成立,而部署口径是 `replan='r3'`。先量 r 的分布再决定,不靠推测。
- **做法**:`rollout(..., return_traj=True)` 顺带记每次规划的 `r`(`plans['r']`,纯诊断,不参与任何判据)。32 题同批、K=8、fb 2.2、dls_safety 1、`docs/experiment_round36_plan_r_hist.json`:
| 口径 | 规划次数 | r 分布 | r==0 占比 | 平摊可省 |
|---|---|---|---|---|
| empty / commit 2 / 不限幅 | 175 | r=0 全部 | 1.000 | 3.631 ms/次 |
| **r3 / commit 3 / 不限幅** | 118 | r=3:116、r=2:1、r=0:1 | **0.0085** | **0.031 ms/次** |
| r3 / commit 3 / inc_cap 1 | 118 | 同上 | 0.0085 | 0.031 ms/次 |
| empty / commit 3 / inc_cap 1 | 117 | r=0 全部 | 1.000 | 3.631 ms/次 |
- **结论(不做)**:部署 r3 稳态每次规划都从队列第 3 步之后开始,`qe` 是前缀末端而不是 `q`,与 features 的姿态不同 ⇒ 共享率 1.7%,平摊收益 0.03 ms,为它加一条按 r 分支的参数面不值。**评测口径(empty)能省 3.6 ms 这件事不能当成部署收益汇报**——发布的那批 57.4% 数字是在 empty 下跑的,若将来把部署也改成"队列空了才规划",这条要重开。
- **顺带记下的实测**:同一批 32 题上 empty/commit2 到达 0.469、r3/commit3 0.438、加 inc_cap 后 0.406(n=32,只作方向参考,不足以定论;256 题口径见轮次 33)。规划次数差 1.5 倍(175 vs 118)——r3 每次只新增 commit 步却已含 3 步前缀,所以同样墙钟时间内规划得更少,这正是它更贴近部署的原因。
- 测试 35 项仍全过(`plans['r']` 只增不改)。
## 轮次 35:半精度、流水线复用与 float32 认证的实测(exp: `exp/dagger-deploy`,三条路两条被自己否掉)
- **按用户指定的方向先量显卡精度档**(FP32 / FP16 / BF16,`docs/experiment_round34_precision_ab.json`;B=1、K=8、steps=1,25 次预热 + 120 次计时):
| 精度 | 推理 P50 | 推理 P99 | 整周期 P50 | 整周期 P99 | 动作最大偏差 |
|---|---|---|---|---|---|
| FP32 | 7.68 ms | 10.63 | 36.51 | 46.57 | — |
| FP16 | 8.31 ms(慢 8%) | 12.03 | 36.16 | **55.60(更差)** | 2.1e−3 归一化 = **0.0050°** |
| BF16 | 8.58 ms(慢 12%) | 14.17 | — | — | 1.7e−2 = **0.0377°** |
结论:**这个网络在这个批量下不是算力受限**,半精度换不来时间,还要付动作偏差与更差的尾部 ⇒ 推理保持 FP32。(未测更大 batch,那里的结论不下。)
- **float32 能不能做认证**(`docs/experiment_round34_f32_cert.json`,48 段 × 9 网格点 × 15 组对,宽相位 cap 2 mm):f32 与 f64 间隙差 p50 **0**、p99 3.66e−5、p999 7.44e−5、**max 1.32e−4 mm**;有符号误差 max +1.32e−4(确有 1 例高估)、min −1.09e−4。同一判据下 f64/f32 都接受 24/48 段,"仅 f32 判安全"= 0、符号翻转 = 0。⇒ f32 不是不能用,但**现用 eps=1e−4 已被实测越界**,要用就得把 eps 抬到 ≥2e−4 mm;且这只量了间隙值误差,没覆盖 f32 下 FK/界自身的误差,因此暂不放宽 dtype 守卫。
- **流水线复用特征间隙——否掉**(`docs/experiment_round35_pipeline_gaps.json`):想法是"下一次规划的 q 正好是本次认证已算过的末点"。第一版给出 42.1→31.8 ms,但**等价性检查不通过**(缓存行与新算差 3.19 mm)——是我取行索引用错(`full[CN-1]` 取到第 2 条候选的第 1 步,而非第 1 条候选走完 commit 步)。修好后收益只剩 **34.8→31.7 ms(−3.1 ms)**:复用要求认证调用改用 cap=40 的宽相位(特征所需),省下的被吃回大半;且缓存行与新算**不是逐位相同**(差 6.3e−5 mm,源于不同 batch 下矩阵乘的结合序),会扰动特征输入。⇒ 不为这 3 ms 改口径,放弃。
- **剩下的量级判断**:一个周期 ≈ 推理 7.2 + 特征 13.1(宽相位 8.2 + FK 4.0)+ 末态打分 4.3 + DLS 6.5 + 认证 7.4 ≈ 33 ms,其中 **4–5 次调用各自重做同一个宽相位筛选**(67,256 个簇对)。下一步是把一次筛选的半径按位移界(`cert_lever`)扩到整个 commit 地平线,供同周期所有点共用——这才是有量级的候选,而不是再抠精度。
- **再一次自我撤回(方法论)**:为了找"合并几何调用"的收益,我写了三个变体的探测:①两次独立调用 17.62 ms、②共享一次 `cum_xf` 13.32 ms、③合成一次 cap=40 调用 9.51 ms(交错 90 轮,特征行逐位一致)。**②③ 都不成立**:探测里认证点直接取自轨迹、与特征无关,而真实循环的顺序是 特征(q) → 推理 → 候选 → 认证点(q+Δ),认证点在特征算完之前不存在 ⇒ 同一次规划内既不能共享 `cum_xf`,也不能把两次 `gaps` 合并。只有 ①→轮次 34 已落地的 `gm_groups` 复用 `xf`(同一姿态内部,−3.4 ms)是真的。教训:**效率探测必须按真实数据依赖排布,否则测的是不存在的调用图**。
- **循环内还剩的可复用点**:`replan='empty'` 且队列为空时 `qe == q`,此时目标反馈 DLS 与末态打分的 FK 和特征用的是同一姿态,可以共享一份 `cum_xf`(部署 `r=3` 时不成立,要按 r 分支)。这条待测(#19)。
- **守卫补一个**:`gaps()` / `gm_groups()` 现在拒绝行数与 `q` 不符的 `xf`(之前会静默按别的行做宽相位,实测触发 CUDA device-side assert 或给出错间隙),测试 34→35 项;`python -m ml.eval.check_parity` 仍 PASS(2000 样本,间隙差 2.52e−13 mm、碰撞布尔差 0、特征差 5.96e−08)。
## 轮次 34:GPU 周期的可实现优化——复用 FK 与压认证最坏情况(exp: `exp/dagger-deploy`)
- **起因与一次自我撤回**:轮次 33 记下"合并几何查询看能不能进 20 ms"后,先按 `docs/experiment_round34_fused_cycle.json` 测出"整周期 41.9→24.5 ms"。这个数**作废**,两个独立原因:
1. 第一版的"合并"把 `xf=` 传给了包含 5 行的 `gaps` 调用,而 `gaps` 的 `xf` 是按行对应的 → 只有第 0 行走完宽相位,认证点 4 行全部退回截顶值 40.0 mm,即**少干了活**(表现为认证矩阵差 31.17 mm)。修好后同一 cap 下矩阵逐位一致(差 0.0 mm)。
2. 更根本:真实管线顺序是 特征 → 推理 → 候选 → 认证,**认证点依赖网络输出**,所以"特征用的 q 间隙与认证点间隙合成同一次调用"在实现上做不到。该文件保留作记录,但不能当作可实现收益。
- **只测可实现的口径**(`docs/experiment_round34_geom_opt.json`;B=1、K=8、commit 2;7 个变体逐轮交替、奇数轮反序,80 轮;等价性两项都 `True`:批量认证矩阵与逐条循环逐位一致、features 复用 xf 前后逐位一致):
| 变体 | 含义 | P50 | P95 | P99 |
|---|---|---|---|---|
| A_today | 现口径 | 33.49 | 46.14 | 64.69 |
| B_xf_reuse | `gm_groups` 复用 features 已算的 `cum_xf` | **30.12** | 42.35 | 66.37 |
| C_batch_cert | K 条候选的认证点并成 1 次 gaps | 38.05 | 50.17 | 59.86 |
| D_both | B + C | 34.87 | 39.81 | 48.26 |
| E_both_cont_n8 | B + C + 连续认证 n=8 | 43.73 | 51.08 | 65.68 |
| F_worst_loop8 | **最坏情况**:所有候选被拒(今天逐条串行 K 次调用) | 80.22 | 88.44 | 89.55 |
| G_worst_batch | 同一最坏情况但并成 1 次调用 | **37.14** | 46.06 | 52.22 |
- **复用 FK 是无条件收益**:省一次 `cum_xf`(约 3.4–3.7 ms),特征输出逐位一致,已直接实现进 `ml/common/kin_torch.py` 的 `gm_groups(..., xf=)` 与 `ml/common/features.py`(默认行为不变,新参数仅在调用方已算过 xf 时传)。
- **批量认证是"典型换尾部"**:常见路径(第一条候选就通过)贵 4.6 ms;但今天一旦前面的候选被拒就要串行调用,实测最坏 **80.2 ms(12.5 Hz)→ 37.1 ms**,P99 64.7→48.3 ms(D vs A)。硬实时看的是最坏情况,所以这条值得做,但要按"典型略变慢、尾部大幅变稳"来汇报,不能只报 P50。
- **仍未到 20 ms**:即便 D 档 P50 34.9 ms、P99 48.3 ms。连续认证挂在 D 上是 43.7 ms(E)。所以 GPU 侧下一步只能是更结构性的:认证降 float32(需先批量量化 f32/f64 判据差异,不能凭 3 个点下结论)、把宽相位做成可增量复用的核、或 `torch.compile`/CUDA Graph 把碎 kernel 合批。
- **等价性闸门(纯效率改动不许改变任何结果)**:
- 复用 FK:同一 256 题、同 seed、同配方重跑(`docs/experiment_round34_xf_reuse_check.json`),**22 个指标逐项与轮次 33 base 完全相同**(含 success 0.57421875、time_ok 0.390625、brakes 19.25、speed_ratio_max 1.660003662 等)。整批墙钟 296.3 → 318.0 s,是两次独立运行的差,**不能当作开销结论**:省下的那次 `cum_xf` 只在 B=1 交错实测里量到 −3.4 ms,批量评估里一次调用已覆盖 256 行,摊薄后不足以抵消运行间波动。
- 批量认证(`--cert_batch 1`,默认 0 不变):同一 256 题重跑 **22 项指标与 base 逐项相同**(`docs/experiment_round34_cert_batch_check.json`,success 0.57421875、time_ok 0.390625、speed_ratio_max 1.660003662 等全部一致),整批墙钟 296.3 → 306.9 s(批量口径把 8 次串行调用换成 1 次 K×256 点的大调用,均值略涨符合预期,与 B=1 交错实测方向一致)。合成夹具里也断言两条路径选到同一条候选、同一前缀长度、逐位相同的轨迹与证书。
- **测试 32→34 项**:`CertBatchEquivalenceTest` 两条(候选 0 前缀不完整 → 选候选 1;所有候选第 2 步都不安全 → 两条路径都停在第 1 步)。写夹具时踩到一个自己的坑:桩里掩码写成 `q[:, :1]` 再 `[:, None]` 变成 3 维,广播出 (4,4,49) 才报"size 16",是**测试桩错**不是库错——先证形状再改代码。
## 轮次 33:把速度做成可观测的硬约束,再测限幅与余门的代价(exp: `exp/dagger-deploy`)
- **仪表先行**:`rollout()` 现在逐题记录**实际执行**的速度比 `max_j|Δq_j|/STEP`(STEP=VMAX·DT,所以 1.0 = VMAX)和按控制步的速度变化比;`eval_cl` 汇总 `speed_ratio_p50/p95/max`、`over_vmax_rate`、`accel_ratio_p95/max`,并把逐题值放进 `per`(极值/分位不进按 n 加权的批聚合)。
- **仪表自证**:用第30轮 after 的 trace 独立重算得 max **1.660004×VMAX**、超速度题占比 **1.0**,与新指标在同一批上逐位一致(`speed_ratio_max` 1.660003662、`over_vmax_rate` 1.0);这是"先证判据有灵敏度",不是拿新数当结论。
- **默认路径未变**:`--inc_cap 0 --gap_margin 0`(新默认)跑出的每个旧指标与第30轮 after 完全相同——success 0.57421875、无碰撞到达 0.57421875、time_ok 0.390625、碰撞 0、刹停 19.25、行程比 2.4449527263641357、ratio2s 0.20997506380081177、成功题误差 0.18899640440940857、dls_share 0.625(`docs/experiment_round33_base.json`)。整批墙钟 296.3 s 与第30轮 248.8 s 不可比:这期间我在同机跑了 CPU 分析,属自我污染,仪表开销需独占另测,不作为结论。
- **两个新开关**:`inc_cap=1` 在**反馈叠加之后**再按每轴 VMAX·DT 限幅(此前 `inc=clamp(act,±1)·astep` 之后又加 `gain×fb`,所以 1.66× 是结构性的);`gap_margin>0` 让安全层要求间隙 > 余量而非 >0,并把宽相位 cap 自动抬到 `margin+0.5`——不抬的话 `gaps(allq, 0.5)` 的截顶值 0.5 会让任何 >0.5 的余量门限恒假。`col`(真实碰撞计数)仍按 `gap≤0` 判,不随余量改变,所以两者不会互相顶替。
- **部署侧同病**(只读核查,未改 JS):`webapp/js/ik-policy.js:355` 也是 `inc[s][j] += fbGain*fbStep[j]` 在 ±1·STEP 之后叠加,所以浏览器控制器同样能超 VMAX;JS 的余量是 `o.margin=5` 的**投影式**护栏(`project()` 每轴 ≤projMax,只处理 gap<margin 的近距对),与本轮的"低于余量就刹停"不是同一机制;`ACC=5.0` 只用于刹停段长度,不是每步加速度上限(§9.2 也没给数值化的加速度门槛,所以 `accel_ratio` 只作变体间比较)。
- **间隙分布的口径坑**:第30轮 `plans['gaps']` 是在 cap=0.5 下记录的,所以"最小间隙 ≥1 mm 的题占 0"是截顶假象。可说的是:逐题最小记录间隙 p5 **0.0005**、p25 0.0064、中位 **0.046** mm,57.4% 的题曾低于 0.5 mm——即策略实际贴障碍飞,而查看器护栏是 5 mm。要把 0.5 以上那段分开,必须抬 cap 重算(本轮后续做)。
- **四组单变量对照**(同 256 题、同 seed、其余按第30轮配方):base / `inc_cap=1` / `gap_margin=0.5` / 两者同开。结果见下表。
- **四组单变量对照结果**(同 256 题、同 seed、K=8、commit 2、fb 2.2、fb_block_gain 0.5、dls_safety 1、select endpoint、replan empty、partial 1;`docs/experiment_round33_{base,cap,margin,both}.json`):
| 变体 | 到达 | 无碰撞到达 | time_ok_1p3 | 真实碰撞 | 刹停/题 | 速度比 p50 | 超 VMAX 题占比 | 2 s 误差比 |
|---|---|---|---|---|---|---|---|---|
| base | 147/256 = 0.5742 | 0.5742 | 0.3906 | 0 | 19.25 | 1.660 | 1.000 | 0.2100 |
| `--inc_cap 1` | 134/256 = 0.5234 | 0.5234 | 0.2656 | 0 | 19.55 | 1.000 | 0.000 | 0.2790 |
| `--gap_margin 0.5` | 144/256 = 0.5625 | 0.5625 | 0.3789 | 0 | 20.05 | 1.660 | 1.000 | 0.1817 |
| 两者同开 | 133/256 = 0.5195 | 0.5195 | 0.2617 | 0 | 19.97 | 1.000 | 0.000 | 0.2870 |
- **速度限幅是有代价的硬约束**:每轴增量限回 VMAX 后到达 −5.1 pp、time_ok −12.5 pp、2 s 误差比 0.210→0.279。也就是说 base 的 57.4% 里**有一部分是用超速换来的**:反馈在限幅之外叠加,实际替模型补了速度。不能把 base 当作"满足速度约束的成绩"。
- **机理**:base 速度比 p50=1.660 恰等于 `1+0.3×fb_gain 2.2`;限幅后刹停几乎不变(19.25→19.55),但 time_ok 掉得比到达更多,说明损失集中在**逼近目标末段被剥夺了超速修正**,不是更容易刹停。所以出路不是调低增益(第29/30轮已测:增益小到达就掉),而是把该由前馈承担的速度还给模型——在限幅后的分布上重采数据(DAgger,需批准),或用不违反速度约束的前馈(#14 的余量投影 + 按时限打分)。
- **0.5 mm 余量门很便宜**:只少 3 题、刹停 +0.8/题,2 s 误差比反而更好(0.182)。但这不等于裕度够用——它只说明"要求 0.5 mm 不明显拖慢"。5 mm(查看器护栏)是否同样便宜,取决于策略能否找到 ≥5 mm 的路径,必须抬 cap 重算真实间隙分布才能回答(第30轮 `plans['gaps']` 在 0.5 mm 截顶,算不出这一条)。
- 四组 `collision_rate` 都是 0,但这仍是端点/中点离散口径;`gap_margin` 不改 `col` 判据(仍 `gap≤0`),余量与碰撞计数是两条独立的线,不互相顶替。
- **回归**:`python -m unittest tests.test_eval_cl` **30 项通过**(新增 4 项):未限幅时实测速度比确实 =3.4/1.2>1(灵敏度)、限幅后恰为 1.0、0.3 mm 间隙在余量 0 时通过而余量 0.5 时刹停、5 mm 间隙在余量 0.5 时仍通过(证明 cap 抬升生效,否则该测试会恒假)。
- **GPU 上的一个控制周期(按 GPU 口径实测)**:诊断档与逐项分解见 `docs/experiment_round33_gpu_cycle.json`,真实配方(steps=1)见 `docs/experiment_round33_gpu_cycle_steps1.json`,RTX 3090、B=1、float32 推理 + float64 认证,每项 20 次预热 + 120 次计时并 `cuda.synchronize`。
- **前提更正(重要)**:`eval_cl` 从不传 `steps`,`rollout` 默认 `steps=1`,而 `ml/model/policy.py:156` 写明"steps=1 即一步生成,steps>1 是诊断用"。所以第27轮以来的所有评估成绩一直是**单步推理**;本节先用 steps=16 测出的"当前配方 116 ms ≈ 8.6 Hz"是口径错误,已作废并在下面用真实配方重测。
- **真实配方(steps=1, K=8)的分解**:
| 环节 | P50 | P95 | P99 |
|---|---|---|---|
| 推理 `sample_k` steps=1,K=1 / 4 / 8 | 7.11 / 7.08 / 7.19 | 9.45 / 10.17 / 9.95 | 11.91 / 14.00 / 11.34 |
| 特征 83 维(总 / 其中宽相位间隙 / 其中 FK) | 13.11 / 8.18 / 4.00 | 19.57 / 16.57 / 7.51 | 21.17 / 19.43 / 8.47 |
| 目标反馈 DLS 1 步 | 6.53 | 9.32 | 12.62 |
| 现有离散安全层 4 点(cap 1.0) | 7.41 | 11.34 | 13.80 |
| `certify_uniform` 2 根 n=8 / n=16 | 9.70 / 16.07 | 14.40 / 20.95 | 15.75 / 24.42 |
| 组合周期(同一位姿,顺序测;跨行比较受漂移影响,只作量级) | P50 | P95 | P99 |
|---|---|---|---|
| K=8 + 离散安全层(真实配方) | **36.68 → 27.3 Hz** | 43.27 | 49.17 |
| K=4 / K=1 + 离散安全层 | 37.71 / 40.36 | 55.17 / 48.46 | 76.53 / 52.06 |
| K=8 + 连续认证 n=8 | 45.34 → 22.1 Hz | 49.68 | 52.20 |
| K=8 + 连续认证 n=16 | 49.55 → 20.2 Hz | 56.85 | 59.06 |
| (对照)诊断档 steps=16 的同一周期 | 116.5 | 134.5 | 142.6 |
- **归因更正(先证仪表再下结论)**:空算子 P50 0.028 ms、空 `synchronize` 0.005 ms、一次 `encode` 2.27 ms,所以**不是启动/同步地板**。先前口算的"5–10 ms 是 GPU 启动开销"错了,在此更正:特征 13.1 ms、认证 7.4 ms、DLS 6.5 ms 是 B=1 下真实的小算子工作量——即 **GPU 在单臂批量=1 时利用率极低**,而不是被框架拖慢。
- **推理不是瓶颈,几何才是**:单步推理 7.2 ms,且候选数几乎免费(K 1→8 不涨);一个周期里几何类工作合计约 27 ms(特征宽相位 8.2 + 特征 FK 4.0 + 认证 7.4 + DLS 6.5),并且这几次调用各自重复做 `cum_xf` 与宽相位筛选。所以 50 Hz 的路径是**合并几何查询**(`features()` 已支持 `gm=`/`xf=` 复用,`gm_groups` 文档保证与整表 `gaps` 数值一致),而不是继续压推理。
- **连续认证的增量成本可接受**:在同一位姿上 n=8 只比现有离散层贵约 8.7 ms(整周期 36.7→45.3 ms),n=16 贵约 12.9 ms。也就是说"段内连续证明"在 GPU 上不是奢侈项,真正卡住 20 ms 的是那 ~27 ms 的重复几何。合并后再看是否进得了 20 ms——这一步正在做交错复测(跨行顺序测的数字受漂移影响,不作最终结论)。
- **50 Hz 结论(当前状态)**:真实配方一个规划周期 **36.7 ms(P99 49.2)≈ 27 Hz**,仍不满足"P99<20 ms";且必须记住这是在**不限幅**(可超速 1.66×)的策略分布下测的——限幅后的到达率代价见上表,两者不能混着谈。
- f32 与 f64 的间隙在 3 个点上完全相同(最大偏差 0.0 mm、无符号翻转),但 **3 个点不足以支持"认证可以改用 float32"**,需按真实轨迹批量对比才能下结论。
## 轮次 32:均匀细分认证与确定界的反证(exp: `exp/dagger-deploy`,认证仍进不了 20 ms,但定位到真正的成本杠杆)
- **本轮假设**:第31轮的二分认证把成本花在**多次依赖的 gaps 调用**上(B=1 时每次约 7 ms 起步)。若改成固定层数的一次均匀细分,同样的判据应只付一次调用;同时需要单独证明"这个确定界本身没被低估",否则判据再快也没意义。
- **实现**:`Kin.certify_uniform(a, b, n, eps, cap=None)`——每根段取 n 个子段、一次 gaps 调用查 n+1 个点,对**每个子段**用与二分版完全相同的判据(子段两端间隙之和 > 子段位移界 + 2eps;界对 Δq 线性,故子段界 = 整段界/n)。没有"未决"状态:证不出就是 `unproven`(拒绝),不存在把未知当安全。未接入 rollout,未改查看器默认。
- **确定界的反证**(`docs/experiment_round32_bound_falsification.json`):对 48 段每段取 121 个网格点,逐组对比"实测最小间隙"与"预测下限 (ga+gb−bound)/2"。720 项检验**0 违例**;但只有 **61 项有信息量**(预测下限 < 观察 cap 2 mm 才可能被证伪;其余组对的预测下限中位数 15.9 mm、最大 19.96 mm,都被钳制恒等式自动满足)。61 项里最紧的一项仍比预测下限高 **0.035 mm**,p10 高 0.544 mm。因此这是"未能在真实运动上证伪该界",不是"证明了该界";浮点求距误差仍未做形式化上界。
- **接受率曲线**(48 段单根,`docs/experiment_round32_uniform_validation.json`):
| 子段数 n | 判安全 | 判碰撞 | 证不出 | 二次加密(2n+1点)反证的误放行 |
|---|---|---|---|---|
| 8 | 26 | 1 | 21 | 0 |
| 16 | 30 | 1 | 17 | 0 |
| 32 | 34 | 1 | 13 | 0 |
| 64 | 36 | 1 | 11 | 0 |
| 128 | 38 | 1 | 9 | 0 |
第180题 1.08–1.10 s 那段(第30轮加密回放检出 −0.14927 mm、原离散证书 `certified=true`)在**每个 n≥8 都判碰撞**。
- **宽相位 cap 的正确公式**(对第31轮一条负结论的更正):第31轮用 `bound/2 + eps`,那个值恰好**会把本可通过的子段钳成不可证**,所以既没提速也可能少接受;本轮改成"最小不挡住判定的宽相位"= `最大子段界 + 2eps`,n=4 时是 5.92 mm、n=128 时 0.37 mm。第31轮那句"自适应 cap 无收益"只在旧公式下成立,新公式的实测收益见下表(原记录保留,不回改)。
- **计时 A/B**(`docs/experiment_round32_uniform_latency.json`;B=2 根=部署 commit2 口径,24 对根逐对按正序/逆序**交替**测全部配置,每配置 3 次预热 + 7 次计时 = 168 次;CUDA float64):
| 配置 | 宽相位 mm | P50 | P90 | P99 | >20 ms 次数 | 安全/碰撞/拒(48根) |
|---|---|---|---|---|---|---|
| 均匀 n=8 auto | 5.92 | **9.68** | 13.03 | 15.16 | 0/168 | 26/1/21 |
| 均匀 n=16 auto | 2.96 | **14.14** | 18.45 | 20.87 | 5/168 | 30/1/17 |
| 均匀 n=32 auto | 1.48 | 20.21 | 27.88 | 32.27 | 87/168 | 34/1/13 |
| 均匀 n=64 auto | 0.74 | 31.43 | 48.75 | 57.70 | 165/168 | 36/1/11 |
| 均匀 n=128 auto | 0.37 | 59.76 | 89.06 | 109.09 | 168/168 | 38/1/9 |
| 均匀 n=8 cap20 | 20 | 13.13 | 16.44 | 20.96 | 3/168 | 26/1/21 |
| 均匀 n=32 cap20 | 20 | 28.41 | 39.58 | 53.02 | 153/168 | 34/1/13 |
| 均匀 n=64 cap20 | 20 | 50.85 | 75.51 | 88.36 | 168/168 | 36/1/11 |
| 均匀 n=128 cap20 | 20 | 98.46 | 139.98 | 167.04 | 168/168 | 38/1/9 |
| 二分 budget32 | 20 | 54.88 | 82.15 | 117.20 | 133/168 | 37/1/10 |
| 二分 budget65 | 20 | 71.83 | 108.56 | 122.53 | 133/168 | 41/1/6 |
- auto 与 cap20 的**决策列完全相同**,只是快 1.36–1.65×(n=8 13.13→9.68、n=128 98.46→59.76):这次是交错实测的真收益,不是第31轮那种顺序漂移。
- 单次调用的均匀细分在同样接受率下比二分便宜 5 倍以上(n=8 9.68 ms vs 二分 54.88 ms),但二分在同样时间能多证出 11 根——两条路的取舍由下一段的结论决定,不单看耗时。
- **负结论(部署口径)**:只有 n≤16 能把认证本身压进 20 ms(P50 9.68–14.14 ms),而这档只能证出 48 根里的 26–30 根(54–63%),其余必须刹停;n=32 起 P50 就已经吃满 20 ms,还没算推理。所以**连续段认证按现有几何口径不能同时满足"P99<20 ms"和"不牺牲到达"**。这些数是 CUDA float64 的单次调用,不能外推到浏览器 CPU float32 的 50 Hz 链路。
- **机理定位与下一步**:成本 = 单次调用固定开销 × 由 `log(bound/间隙)` 决定的层数。要同时拿到"证得出"和"证得起",必须把**间隙抬起来**(安全层要求正余量,而不是 `gap>0`)并把**每步位移压回去**(反馈叠加在 ±1 限幅之后,导致第30轮实测最大 1.66×VMAX,界也随之变大)。顺带记下一个坑:安全层现在是 `kin.gaps(allq, 0.5)`,截顶值 0.5 mm 低于任何 >0.5 mm 的余量门限,直接把余量判据变成恒假——加余量必须同时抬这个 cap。
- **回归与判据灵敏度**:`python -m unittest tests.test_eval_cl` **26 项通过**(新增 3 项均匀细分)。含解析对照:合成恒间隙段(界 1 mm/度、间隙 0.1 mm)在 n=5 必判证不出、n=6 必判安全,且 `cap_required_mm` 等于 `1/n+2eps`;薄障碍在 n=4/8/64 判碰撞而 n=2 只判证不出(不误放行);`n` 给成张量被类型门拒绝。二次加密反证在 n=8..128 全部 0 误放行。
## 轮次 31:连续段认证的确定界与成本实测(exp: `exp/dagger-deploy`,判据变强,实测进不了 20 ms)
- **假设与判据**:轮次 30 的端点/中点检查漏掉段内碰撞(第180题 1.08–1.10 s,段内 −0.14927 mm,端点/中点均正)。若用可证明的全姿态运动界对整段做 Lipschitz 判据,应当(a) 在同一个漏检段上给出碰撞或拒绝,(b) 预算不足时只会拒绝而不会放行,(c) 不放宽 `>0` 判据。判据失败或耗时超预算都算负结论,必须实测不能外推。
- **确定界替代随机采样界**:JS 侧 `leverTable(4000)`/`leverBounds(20000)` 是随机姿态最大值再 ×1.05,不是全姿态上界。本轮按三角不等式给闭式界:组 g 的胶囊端点到关节 k(<g) 的最大位移 = (Σ_{j=k}^{g-2}‖P[j+1]−P[j]‖ + max_e‖e−P[g−1]‖)·|Δq_k|(k=g−1 时末项换成端点到该轴的垂直距离),组对 (ga,gb) 只累加 ga..gb−1,取组内最小仍保留 Lipschitz 性质。Python 端 `Kin.cert_lever`(15×6)即此表,未改 `lever`,也未改 JS。
- **界的量级**(STEP=[1.2,1.2,1.8,1.8,2.4,3.6]°):单整步在各组对上的位移界 **5.93–48.86 mm**,0 层通过要求端点间隙之和 > 界,即单端间隙至少 3.0–24.4 mm。实际执行段的间隙普遍只有 0.001–0.5 mm,所以“必须细分”是结构性的,不是实现缺陷。
- **基础求距 bug 与影响范围**:模型里有 1 个退化胶囊(索引 686,组 3)。旧 `seg_seg_dist` 在第二条线段退化成点时固定取该点为第二条起点,没把点投影到第一条线段上——[0,2] 段对点 1 的实测距离旧值 1 mm、反向调用约 1e−9 mm、真值 0。修复后在已有 2000 个对拍姿态的前 64 个姿态上,670 个“退化胶囊作第二条”的细对里 **20661/42880 个距离变小(旧值偏大,最大 86.003 mm)**,正反调用一致。但这批变化没有进入判定:把轮次 30 修复后 48 段的 7586 个 0.01° 加密点分别用修复前(95da491)与修复后内核重算,**组最小间隙逐位相同(最大差 0.0 mm)、碰撞布尔差 0**;`python -m ml.eval.check_parity` 仍 PASS(2000 样本,fk 4.55e−13 mm、间隙 2.52e−13 mm、特征 5.96e−08、梯度相对误差 1.85e−06、碰撞布尔差 0)。即这轮修复是正确的,但不解释轮次 30 的漏检,也不改变既有成绩。
- **认证实现**:`Kin.certify_segments` 为 float64 批量前沿二分,每根段独立查询预算,端点间隙用 `min(gap,cap)` 故钳制只会更保守;判据 `ga+gb > bound+2eps` 且两端 >eps,任一子段碰撞即整根判碰撞;超深度、超预算、非有限间隙、越出关节盒一律拒绝(`unresolved`/`outside_box`),不放行未知。宽相位 cap 可按本层界收到 `bound/2+eps`(`adaptive_cap`)。**尚未接入 rollout,未改查看器默认。**
- **48 段固定样本**(`docs/experiment_round31_certificates.json`,沿用轮次 30 的 12 题,每题取首/中/末移动段 + 原证书最小间隙段 + 显式指定 180:54;cap=20、eps=1e−4、max_depth=12):
| 每根查询预算 | 判安全 | 判碰撞 | 未知拒绝 | 查询数 p50/max |
|---|---|---|---|---|
| 2 | 20 | 0 | 28 | 2 / 2 |
| 8 | 26 | 1 | 21 | 4 / 8 |
| 32 | 37 | 1 | 10 | 4 / 32 |
- 已知漏检段(180 题执行段 54)**判碰撞,仅用 5 次查询**;该段原证书 `certified=true`、原内部 `col=0`。48 段里 0.01° 加密采样检出碰撞 1 段,认证接受该类段 **0 段**;整批与逐根调用结果逐位一致。
- 拒绝不等于安全:预算 2 时 28 段全部落 `unresolved`,实现不会把未知当通过。
- **成本模型对上实测**(`docs/experiment_round31_cert_cost_model.json`):按最紧组对的界,所需层数 `L=ceil(log2(bound_pair/(ga+gb)))`、查询数 `2^L+1`。37 个判安全段需要 2–17 次(L=0..4),10 个未知段需要 **33 或 65 次**(L=5..6),与预算 32 恰好卡在中间一致;碰撞段因提前撞到负间隙只用 5 次。结论:认证成本由 `bound/间隙` 的对数决定,**降低界或提高间隙余量**才是可行杠杆,加预算只是线性买单。
- **耗时负结论**(CUDA float64、B=1、驻留输入、每段4次预热+9次计时、变体按段交错,`docs/experiment_round31_adaptive_cap.json`):单段认证 **P50 22.93 ms、P90 58.43 ms、P99 101.57 ms,432 次里 225 次 > 20 ms**;自适应 cap 交错重测 P50 23.21 ms、P99 99.10 ms,**决策完全相同、耗时无收益**。先前顺序测得的“1.6×、整批 72→14.8 ms”经交错复测判定为 GPU 时钟/顺序漂移,不作为结论(48 根整批复测 260→212 ms 仍受同一偏差影响,且部署不会出现 48 根同时认证)。因此**连续认证按当前实现不能进 20 ms,更不能宣称 50 Hz**。
- **回归与判据灵敏度**:`python -m unittest tests.test_eval_cl` **23 项通过**(轮次 30 为 14 项)。新增段认证 8 项覆盖:薄障碍段内碰撞被界抓到而清晰段被接受、预算/深度耗尽只拒不放、恒间隙合成段上预算 4/8/9 对应查询 3/5/9(验证细分层数与查询数的解析关系)、批量与逐根及乱序一致、空批与全拒前沿、NaN/±Inf 间隙拒绝、越界拒绝、退化距离对称性与梯度。**变异实测**:把类型门去掉后 `max_depth=torch.tensor(255,dtype=torch.uint8)` 会回绕成 `range(0)`、整段判据被跳过,真实间隙 −0.05 mm 的段直接返回 `safe=true`;加门后同一调用报 `certification budgets must be plain ints`。这是本轮抓到的第二个误放行路径(第一个是轮次 30 的未夹紧认证)。
- **口径与边界**:认证只在 float64 下进行,浮点求距误差没有形式上界(`seg_seg_dist` 距离下限约 1e−9 mm),eps=1e−4 mm 是工程容差不是证明;48 段取自轮次 30 修复后的执行轨迹,是固定样本不是新策略闭环;连续认证未接入 rollout,实际执行仍走端点/中点离散检查;速度/加速度超限(轮次 30 实测最大 1.66×VMAX)本轮未处理。
- **结论与下一步**:判据确实变强(抓到离散检查漏掉的段内碰撞、未知只拒不放),但成本与 `bound/间隙` 的对数绑定,**当前实现不能部署,也不得把“认证通过”说成零碰撞**。下一步两条:(1) 把逐层依赖的二分改成**一次均匀细分**(固定层数、单个 gaps 调用覆盖全部子段),其实时可行性取决于几何查询成本随批大小是否近似平坦;(2) 把余量要求移到规划/速度侧——实际轨迹贴着 0.001–0.02 mm 间隙走,任何认证方案都会退化成几十次查询,而反馈叠加后的速度约束本来就是未完成项。
## 轮次 30:认证与执行统一逐步限位(exp: `exp/dagger-deploy`,局部修复有效,仍未零碰撞)
- **假设与判据**:安全层认证的是未夹紧累加轨迹,执行器却逐步夹紧 J5,导致认证通过后碰撞。必须同时证明原证书安全、执行点碰撞、记录命令经执行算式能准确重建实际点;修复后保持同任务、同种子、同模型、同增益和同排序,到达不下降,并用不依赖原 `col` 标记的加密回放检验。
- **固定配方**:`python -u -m ml.eval.eval_cl --n 256 --commit 2 --fb 2.2 --partial 1 --replan empty --select endpoint --fb_block_gain 0.5 --dls_safety 1`;K=8、seed=0、analytic DLS、dag2 checkpoint、fallback=0,其余参数默认;分别附 `--out docs/experiment_round30_before.json --trace ml/runs/round30_before.pt --dump ml/runs/round30_before_rows.json` 和对应的 `after` 路径。未训练、未扩题、未改 JS 默认。
- **可比性**:取证版 before 与轮次29首批256题完整轨迹逐元素完全相同(最大差 0°),ok/col/brakes/err/t_done/first_hit 也完全相同;before/after 配置仅输出路径不同。任务 SHA256=`249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772`,checkpoint SHA256=`f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1`,kin_fp=`2ebcbf17`。
- **直接证据**(`docs/experiment_round30_certificates.json`):旧版7次首撞的原认证端点/中点在 float32 与 float64 均为正间隙,实际执行段均有负间隙;`clamp(previous + recorded_command)` 与实际端点逐位一致。原证书与实际点主要差在 J5,首撞段差 0.340°–4.317°;其他轴仅有最高约 7.6e-6° 的累加舍入差,不足以解释毫米级穿入。全部有效认证步中,5298/34069 步偏差 >0.001°,最大 7.587°。
- **修复范围**:`execution_points` 按执行器相同顺序逐步夹紧,端点和中点都由实际路径计算;整段、部分前缀和近目标 DLS 共用。r3 的旧队列终点和特征前缀改为真实执行结果。fallback 保持实验默认关闭,但两分支统一为“认证一步后补零”,不再把只验过一步的动作重复执行;端点和中点都认证。候选排序和反馈增益未改,0.5 仍只是间隙截顶,正间隙判据没有放宽。
| 指标(同256题) | 修复前 | 修复后 |
|---|---|---|
| 按时到达 | 147/256 = 57.4% | 同147题,57.4% |
| 原端点/中点口径无碰撞到达 | 145/256 = 56.6% | 147/256 = 57.4%(下述加密审计否定其“全程无碰撞”含义) |
| time_ok_1p3 | 39.1% | 39.1% |
| 原端点/中点口径碰撞题 | 7/256 | 0/256 |
| 成功题末端误差中位数 | 0.1951 mm | 0.1890 mm |
| 成功题平均时间 | 2.2993 s | 2.3076 s |
| 2 s 误差比中位数 | 0.2100 | 0.2100 |
| 刹停/题 | 19.2383 | 19.2500 |
| 成功题指令行程比中位数 | 2.4450 | 2.4450 |
| 从实际轨迹重算成功题行程比 | 2.4251 | 2.4251 |
| 整批同步仿真墙钟 | 247.515 s | 248.834 s |
- **执行一致性复核**(`docs/experiment_round30_comparison.json`):修复后34107个有效认证步,认证端点/中点与实际执行点均逐位一致,最大差 0°;有效时段没有未经认证的移动步。这里“认证”仍仅指离散端点/中点,不能推出连续安全。CPU B=1、commit=2 的预测函数单独测量:100次预热、1000次采样、单线程,P50 0.0388→0.0518 ms,P99 0.0691→0.0939 ms;不是完整规划延迟,不能证明50 Hz。
- **独立加密审计的正反对照**:预先固定12题 `0,21,84,128,145,164,175,180,197,215,224,255`,其中包含全部7题旧首撞及5题其他任务;不按新版 `col` 过滤。逐题回放至自身时限或到达,含起点,每个插值段最大轴步距≤0.05°并包含中点,float64、每批64点。命令:`python -u -m ml.eval.diag_collision --trace ml/runs/round30_before.pt --dense_indices 0,21,84,128,145,164,175,180,197,215,224,255 --max_step_deg 0.05 --device cuda --out docs/experiment_round30_dense_before.json`,after 同理;串行运行。
- before:56118点,**7/12题检出碰撞**,14.870 s;证明探针确实能抓旧碰撞。
- after:56566点,**仍有1/12题碰撞**,15.057 s;其余11题采样点间隙为正。这是小样本有限采样,不是完整256题的连续认证。
- 剩余第180题,执行段1.08–1.10 s:上一点/中点/端点 float64 间隙为 **0.5 / 0.11723 / 0.06521 mm**,都安全,但段内约76.6%处为 **−0.14927 mm**;该段0.01°再加密185点仍检出,见 `docs/experiment_round30_missed_segment.json`。因此这次残余是端点/中点漏检,而非认证与执行错位;它比旧离散首次碰撞1.14 s还早。不得把内部计数0当成修好了;已知真实无碰撞到达至多146/256=57.0%,其余未加密任务仍未知。
- **回归与判据灵敏度**:`python -m unittest tests.test_eval_cl -v` 14项通过。覆盖整段/部分限位拒绝、只保留真实安全前缀、DLS被拒后保留候选、r3旧队列起点和特征前缀及逐点证书、fallback单步/中点拒绝。把预测函数在内存中替回旧算式,限位、DLS和r3测试重新失败。加密探针另有合成正对照:原端点/中点全安全且 `col=0`,仍检出四分之一处薄障碍;安全负对照、截止后/到达后碰撞排除均通过。
- **新增硬约束证据**:before/after 两组256题实际轨迹都出现超速,最大 **1.660004×VMAX**(来源是动作限幅后叠加0.3×gain2.2反馈)。实际各轴最大速度约 `[99.600,99.600,149.400,149.400,199.200,298.800]°/s`,并非只看原始指令推测。行程仍约参考2.425倍;速度、加速度和真实控制时延均未达部署验收。历史 `travel` 累积的是指令而非夹紧后位移,本轮已同时列出实际轨迹重算值,不悄悄换分母。
- **结论与下一步**:逐步限位一致性修复有效,未牺牲本批到达率;但加密审计仍检出段内碰撞,**不能部署、不能宣称零碰撞或50 Hz合格**。下一轮先验证更强的段内认证,再单独评估反馈后的速度/加速度约束与真实端到端调度;每次保持小样本单变量对照,继续保留失败证据。
## 轮次 29:安全层全拒后减弱目标反馈(exp: `exp/dagger-deploy`)
- **假设**:目标反馈将绕障候选推向障碍,连续全拒会反复停在同处;仅在上一计划被全拒时减小下一计划反馈,可能保留正常段收敛速度并减少重复刹停。
- **判据**:同任务、同 seed、同 K、同认证下到达和 time_ok 不下降,碰撞不增加;部署门槛仍是零碰撞及真实时延达标。所有实验不重训、不扩大数据。
- **固定配方**:`python -m ml.eval.eval_cl --n 256 --K 8 --commit 2 --fb 2.2 --partial 1 --replan empty --select endpoint --seed 0`;dag2 checkpoint、几何雅可比。下表仅改列出的参数,默认 `fb_block_gain=1,dls_safety=0`。512 题按两个 256 批、每批 seed=0,沿用轮次 28 顺序。
| 改动(n=256) | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 |
|---|---|---|---|---|---|
| 原配置复跑 | 52.7% | 37.5% | 2.3% | 0.210 | 34.6 |
| `--dls_safety 1` | 52.7% | 37.5% | 2.0% | 0.210 | 34.6 |
| `--fb_clear 20 --fb_cmin 0.3` | 32.0% | 5.9% | 1.2% | 0.323 | 3.2 |
| `--sub_refresh 1` | 51.6% | 35.5% | 2.0% | 0.279 | 33.8 |
| `--select adapt` | 52.7% | 37.9% | 2.3% | 0.210 | 34.6 |
| `--fb_block_gain 0.3` | 56.2% | 38.7% | 3.1% | 0.178 | 17.2 |
| `--fb_block_gain 0.5` | **57.4%** | **39.1%** | 3.1% | 0.210 | 19.3 |
| `--fb_block_gain 0.7` | 55.5% | 38.3% | 2.3% | 0.189 | 25.0 |
| `--fb_block_gain 0.5 --dls_safety 1` | **57.4%** | **39.1%** | 2.7% | 0.210 | 19.2 |
- **单独减半反馈 n=512**:到达 55.1% / time_ok 36.1% / 碰撞 2.5% / ratio2s 0.251 / 刹停 19.5;相对轮次 28 同规模基线 52.1% / 35.2% / 2.0% / 0.285 / 35.7,到达 +3.0 pp、碰撞也 +0.5 pp,不满足“碰撞不增加”判据。
- **为何不采用其他变体**:所有近障碍状态都衰减反馈会明显拖慢到达;子目标重预测无收益;adapt 全拒后旧计划本来就失效,一致性分数全为 0,实质变成等分选首条而非“继续绕行”。
- **原配置失败分解(n=256)**:135 题按时到达,121 题失败(其中 20 题后来才到,101 题始终没到)。A/B/C/D/Dr 分别 128/74/32/13/9 题,B/C 失败题刹停中位数约 96/93 次;D 留在分母,不用删题提高成绩。
- **碰撞定位**:原配置共 6 题碰撞,只有 224 号首次碰撞紧跟 DLS 接管;另外 5 题首次碰撞早于接管或根本未接管。为 DLS 加端点/中点检查仅减少 1 题,不能解释剩余碰撞。候选认证的未夹紧累加点与执行的逐步夹紧点不同是待验证假设,尚不能断言根因。
- **计时**:n=256 减半反馈+DLS检查整批墙钟 243.6 s,单独减半 n=512 为 443.4 s;这些不是规划时延,不能证明 50 Hz。反馈条件分支未增加几何查询,但其实际延迟收益未测。
- **记录改进**:评估输出完整参数、checkpoint/任务 SHA256、批大小和随机种子规则;增加时限内无碰撞到达率;修正 n_ref、参考时间占比和条件指标的跨批汇总。可保存实际轨迹供不重新采样的回放,NaN 在 JSON 中写为 null,执行进度每 50 步输出。
- **回归检查**:`python -m unittest tests.test_eval_cl -v` 六项通过,覆盖跨批分母、空条件集、非有限数、首撞探针正对照、精修被拒后保留安全候选、只有全拒后才减弱反馈。精修对照在关闭检查时确实碰撞,打开后保留候选,证明门会执行;这是合成测试,不替代真实几何验收。
- **仍未修复的边界**:`fb_fallback=0` 保持关闭;非部分前缀分支仍有“只验一步却重复执行”问题,不能开启部署。速度限制在反馈叠加前而非叠加后,尚未验证速度/加速度符合硬约束。
- **减半反馈 + DLS 检查的 n=512 复核(完整输出 `docs/experiment_round29.json`)**:到达 **282/512 = 55.1%**、时限内无碰撞到达 **280/512 = 54.7%**、time_ok **36.1%**、碰撞 **9/512 = 1.8%**、ratio2s **0.251**、刹停 **19.23/题**。到达题末端误差中位数 **0.196 mm**;有参考且到达题的行程比中位数 **2.425**(远高于 1.10 要求)。整批墙钟 **488.4 s**,非控制延迟。
- 完整命令:`python -u -m ml.eval.eval_cl --n 512 --commit 2 --fb 2.2 --partial 1 --replan empty --select endpoint --fb_block_gain 0.5 --dls_safety 1 --out docs/experiment_round29.json --dump ml/runs/round29_rows.json --trace ml/runs/round29_trace.pt`。
- 与轮次 28 同规模端点基线相比,到达 +3.0 pp、time_ok +0.9 pp、碰撞少 1 题(10→9),不足以宣称稳定安全改善;n=256 碰撞仍由 6→7 题。
- **首撞回放(`docs/experiment_round29_collisions.json`)**:`python -m ml.eval.diag_collision --trace ml/runs/round29_trace.pt --out docs/experiment_round29_collisions.json`。9 题首次碰撞在 CPU float32/float64 均复现,均位于 **J5 限位**;q0 与首次碰撞前一点均为正间隙,首撞采样点负间隙约 **−0.017 至 −0.936 mm**。这排除了这 9 次碰撞由起点本就碰撞或 float32 单独翻号造成,但还需要记录原认证点才能证明夹紧的因果作用;0.01° 加密仅覆盖首撞段,不是整条轨迹安全审计。
- 双精度运动学/间隙另与已有 JS 2000 个样本对拍:`python -m ml.eval.check_parity` PASS,碰撞布尔差 0、间隙最大差 2.53e−13 mm。首撞回放仍共用碰撞模型,不替代真机或独立连续认证。
- **结论与下一步**:减少连续刹停确有小样本收益,但碰撞、行程和真实时延仍未过门槛;保留为实验选项,不更改查看器默认。下一轮记录原认证点与 J5 夹紧后实际点,修复后用同批同 seed A/B 和独立细采样复核,禁止靠关掉认证提速。
## 轮次 28:端点进度选择——时间效率大幅提升(exp: `exp/dagger-deploy`,新历史最佳)
- **假设**:一致性选择奖励"与上一条计划一致",但失败主因是超时(轮次 13:失败 57% 是超时)。改成直接按候选块末态离目标的距离选(`select='endpoint'`),应提升时间效率与精度。
- **判据**:time_ok_1p3 显著上升、到达不降、碰撞 ≤3%。
- **配方**:dag2 + 解析 J,gain2.2,commit=2,replan=empty,safety_partial;n=256 → n=512。选择器用 `goal_err(fut[-1])`(只做一次 K 端点 FK,不额外推理)。
- **指标(n=256,commit=2)**:
| 选择器 | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 |
|---|---|---|---|---|---|
| consist(轮次 27) | 52.0% | 29.7% | 1.6% | 0.329 | 35.3 |
| **endpoint** | **52.7%** | **37.5%** | 2.3% | **0.210** | 34.6 |
| mix(consist − 0.1·端点mm) | 50.0% | 30.5% | 2.3% | 0.300 | 35.1 |
- **n=512 复核(endpoint,commit=2)**:**到达 52.1% / time_ok 35.2% / 碰撞 2.0% / ratio2s 0.285 / 刹停 35.7**(consist n=512:50.8% / 28.9% / 1.4% / 0.343 / 35.9)。
- **节奏曲线(endpoint,n=256)**:commit=1 → **59.4% / 42.6% / 3.9% / 0.133**;commit=2 → 52.7% / 37.5% / 2.3% / 0.210;commit=3 → 47.7% / 27.0% / 1.2% / 0.245。
- **负结论**:
- `safety_try=1/2`(只认证前 1/2 条候选):41.4% / 44.9%(全 K 52.0%)——候选兜底对到达贡献 ~10 pp,不能省。
- K=4:47.3%(K=8 52.0%)——少采样同样损失到达。
- DLS 兜底半步(全拒时朝目标走一步并认证):52.3%(基线 52.0%),碰撞 1.6→2.7%,无收益;重复多步版本碰撞 45.7%(第二步未认证),已回退为单步且默认关闭。
- 增益调度(远 2.8/近 2.2):47.3% / time_ok 32.8%,等同 gain2.8 常数;高增益在远处就造成过冲,按距离衰减无效。
- **浏览器同步**:`select='endpoint'` 进 Runner/bench(`--select endpoint`),冷启动跳过 8 份前瞻(省 ~80 ms P99,冷启动价值评分被端点排序替代)。nc10 bench(--seg 0 --select endpoint):pos ok 4/5 reached、pose ok 2/4,刹车率 32.1%/25.5%(consist 48.1%/31.6%),planP50 31.9/36.5 ms、P99 63.5/67.5 ms。
- **同步 bench 耗时与队列耗尽估算(不是 50 Hz 调度实测)**:Runner 的 `starveTicks = max(0,ceil(planMs/20) − 规划时队列步数)`,不含真实 tick 时间戳。
| 集合(endpoint, commit=2, --seg 0) | 到达 ok/reached | 刹车率 | planP50/P99 | 估算饥饿 tick/次规划 |
|---|---|---|---|---|
| near_column_pos (10) | 4/5 | 32.7% | 32.0/62.1 ms | 0.87 |
| near_column_pose (10) | 2/3 | 25.6% | 36.8/68.0 ms | 0.78 |
| route40 (20) | 12/13 | 40.4% | 44.3/68.1 ms | 1.26 |
- commit=2 对应 40 ms 队列,route 集 planP50 44 ms 已超过;估算提示可能饥饿,但该 bench 等规划完成才 tick,没有观测实际空转。
- commit=3 对应 60 ms,仍低于上述 P99,不能据此宣称连续 50 Hz。其 Python 同步结果:endpoint n=256 → 47.7% / 27.0% / 1.2% / 0.245;gain2.5 为 45.7% / 24.6%。
- **结论(经轮次 29 更正)**:端点选择 + 解析 J + gain2.2 + commit=2 + safety_partial,在 n=512 同步仿真为 **到达 52.1% / time_ok 35.2% / 碰撞 2.0% / ratio2s 0.285**。历史 42.2% 使用 commit=1 与 FD,非单变量对照。commit=1 到达 59.4% 但碰撞 3.9%;无论 2.0% 还是 3.9%,开安全层均未通过零碰撞要求,实时性亦未通过。
- **下一步**:先查碰撞来源,保持严格认证和查看器默认;再测真实异步调度及认证成本,不因 Python 收益直接切换部署默认。
## 轮次 27:解析 J 后重扫增益 + 积分/缓存反馈(负结论)+ 浏览器安全层对齐(exp: `exp/dagger-deploy`)
- **目标**:解析雅可比改变了反馈噪声结构,重扫增益;同时修浏览器部署路径的安全层(轮次 25 遗留:bench 刹车 47%、到达 0)。
- **增益重扫(commit=2,empty,analytic,n=256)**:
| gain | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 |
|---|---|---|---|---|---|
| 1.8 | 50.4% | 27.0% | 2.0% | 0.287 | 22.1 |
| **2.2** | **52.0%** | 29.7% | 1.6% | 0.329 | 35.3 |
| 2.5 | 49.6% | 34.4% | 1.2% | 0.334 | 47.9 |
| 2.8 | 47.7% | **34.4%** | 2.0% | 0.362 | 59.8 |
- **n=512 复核 gain 2.2(commit=2)**:**到达 50.8% / time_ok 28.9% / 碰撞 1.4% / ratio2s 0.343 / 刹停 35.9**。**新历史最佳(50 Hz 节奏)= gain 2.2 + commit=2**。
- **负结论**:
- 积分反馈(fb_ki=0.3,decay0.9,限幅0.5·STEP):到达 48.0%(gain2.2 单独 52.0%),−4.0 pp。慢变偏差用积分项补偿会过冲,不采用。
- 反馈隔次复用(fb_every=2,省 ~3.5 ms/次规划):到达 46.9%(−5.1 pp)。误差方向变化比预期快,缓存不成立。
- **浏览器部署修复(有效)**:三个与 Python 仿真不一致的 bug 修掉后(`webapp/js/ik-policy.js`):
1. `tick` 不夹关节限位(Python `clamp_box` 每步夹)→ q 一旦越限,认证的 `inBox` 永远拒绝所有候选,原地刹停到超时(certWhy.box 2650 次);现执行与候选投影都夹 LIM。
2. 安全层整段拒绝 → 刹停;移植 `safety_partial`:整段不过时依次缩短前缀,执行到第一个不安全步。
3. 新增 `segCert` 开关(默认 true):连续区间认证(距离下界递归)~40 ms/次,是 50 Hz 预算的主要超支项;关闭后改为离散判定「每步端点+中点 collideFree」(与 Python 安全层同口径),默认仍走严格认证,bench 用 `--seg 0` 测部署口径。
- JS bench(nc10,fb2.2,commit2;`node tests/policy_bench.cjs ... --seg 0`):
| 集合 | 修复前 ok/reached | 修复后 ok/reached | 刹车率 | safety P50 | plan P50 / P99 |
|---|---|---|---|---|---|
| near_column_pos | 0/—(轮次 25) | **5/7** | 79.5→**48.1%** | 41.6→**19.8 ms** | 56.1→**33.5 / 61.1 ms** |
| near_column_pose | 0/— | **3/3** | 80.4→**31.6%** | 43.4→**22.8 ms** | 57.3→**37.6 / 66.3 ms** |
- **结论**:实时配置定为 `dag2 + 解析 J + gain2.2 + commit2 + safety_partial + replan=empty`:n=512 到达 **50.8%**、碰撞 **1.4%**。浏览器端从「0 到达 + 47% 刹车」恢复到 nc 集可达,plan P50 33.5/37.6 ms;P99 仍 61–66 ms,commit=2 的 40 ms 队列会偶发饥饿,需继续压 P99(K/安全候选数/JS 特征)。
- **下一步**:① Python 测 `safety_try=1`(只认证首选候选)对准确率的影响,若无损则 JS 同步(省多次 collideFree);② K=4/6 的准确率-时延权衡;③ JS `gapMatrix` 同步只算 8 组特征;④ commit=2 异步部署的饥饿率实测。
## 轮次 26:解析雅可比 DLS——去掉差分噪声,准确率大幅提升(exp: `exp/dagger-deploy`,新历史最佳)
- **背景**:DLS 目标反馈此前用 6 次批量有限差分(ε=1e-3°)求雅可比。float32 下旋转项信噪比只有 ~1%,位置项 ~3.6%(与 autograd 对拍,见下),相当于每次规划往反馈方向注入 2–4% 的随机扰动。
- **假设**:改用几何雅可比(一次 FK)可同时去噪 + 提速;闭环准确率应不降、延迟应下降。
- **判据**:① 雅可比与 autograd 一致(rel < 1e-4);② n=256 commit=3 到达 ≥ 36.3%(轮次 25 基线,不劣化);③ DLS 单次延迟下降。
- **配方**:`Kin.jac` 几何雅可比(Jv_k = a_k×(p−o_k),Jw_k = a_k,度单位);`dls_steps(analytic=True)` 默认;其余同轮次 25(dag2、K=8、gain1.8、safety_partial、replan=empty)。
- **数值对拍**(`ml/eval/check_dls.py`):
- 位置项:解析 J 与 autograd 逐项一致(rel 0.00000);有限差分 J 自身 rel 误差 **3.58%**。
- 姿态项:解析 J 与差分 DLS 输出 max 0.20°/rel 5%(差的部分是 rotvec 的二阶项 Jr(e),非噪声)。
- DLS 输出:pos-only max 0.12°,pos+rot max 0.20°。
- **延迟**(B=1,3090):n=1 **9.3→7.1 ms**,n=3 **27.7→19.5 ms**(每次 DLS 少 6 次批量 FK)。
- **闭环(replan=empty,analytic,n=256)**:
| commit | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 |
|---|---|---|---|---|---|
| 3(FD 对照,完全复现轮次 25) | 36.3% | 16.4% | 1.6% | 0.277 | 14.3 |
| 3(解析 J) | 46.5% | 16.4% | 1.6% | 0.337 | 13.5 |
| 2(解析 J) | **50.4%** | 27.0% | 2.0% | 0.287 | 22.1 |
| 1(解析 J) | **57.4%** | **31.6%** | 3.1% | 0.251 | 39.1 |
- **n=512 复核(commit=2,解析 J)**:到达 **49.2%** / time_ok **26.2%** / 碰撞 **2.3%** / ratio2s 0.351 / 刹停 21.0(n=256 的 50.4% 在噪声内)。
- **结论**:
- 解析雅可比**既快又准**:commit=3 到达 36.3→46.5%(+10.2 pp),碰撞不变;commit=2 达 **49.2%**(历史最佳 42.2% 是 commit=1 + FD,现在 commit=2 就超过),碰撞 2.3%。
- 机理:DLS 差分雅可比在 float32 下的 2–4% 噪声被慢变误差闭环放大(轮次 4/闭环根因结论:σ>0.02×STEP 即崩);解析 J 去掉噪声后失败题里"能到 50 mm 却到不了"的那批能收敛了(err_fail 中位数 51→217 mm:失败集两极分化,成功集变大)。
- ratio2s 在 commit=3 变差(0.277→0.337)但到达更多:慢收敛题现在"最终到得了",不是"更快"。
- **新历史最佳(50 Hz 目标节奏,commit=2)= 49.2% / 碰撞 2.3% / ratio2s 0.351 / time_ok 26.2%**;非实时上限(commit=1)= 57.4%(n=256,待 n=512 复核)。
- **下一步**:① n=512 复核 commit=1;② 积分反馈(fb_ki,针对慢变偏差)在 commit=2 上叠加;③ 浏览器 JS 端同步解析 J;④ commit=2 的 40 ms 周期实测规划 P99(Python/JS)。
## 轮次 25:实时节奏下的准确率探索 + 推理提速(exp: `exp/dagger-deploy`)
- **目标**:在 50 Hz 可行的节奏(commit=3,60 ms 周期)下提升准确率。
- **效率优化(有效)**:
- `Kin.gm_groups`:特征只算 8 组间隙(数值 diff 0),features 10.4→**7.1 ms**;
- **onnxruntime CPU 推理 3.9 ms**(PyTorch 11.1 ms,部署端本来就是 ONNX);
- 单次规划 ≈ 7.1(features)+3.9(推理)+11(DLS)+8(安全)+2 ≈ **32 ms p50 / ~45 ms p99**;
- 异步部署下 tick(执行步 + 碰撞检查)2–3 ms → **50 Hz 控制可行**;commit=3(60 ms 队列)可覆盖规划 p99。
- **准确率尝试(n=256,commit=3,gain1.8,safety_partial)**:
| 方法 | 到达 | time_ok | 碰撞 | ratio2s | 刹停 |
|---|---|---|---|---|---|
| dag2 + consist(基线) | **36.3%** | 16.4% | 1.6% | 0.277 | 14.3 |
| dag2 + 候选平均(select=average) | 29.7% | 20.3% | 0.4% | 0.432 | 55.9 |
| 冻结编码器模型(dag3_fe) | 34.4% | 14.8% | 2.0% | 0.332 | 15.8 |
| dag2 + commit=4 | 28.9% | 8.2% | 3.5% | 0.339 | 10.5 |
- **结论**:候选平均、冻结编码器、更稀疏规划**均未超过 36.3%**。**50 Hz 节奏下的最佳配置 = dag2 + commit=3 + consist + gain1.8 + safety_partial:到达 36.3% / 碰撞 1.6% / ratio2s 0.277**(相对非实时的 42.2% 差 5.9 pp)。
- **下一步**:若要把实时准确率再拉高,需要把单次规划再压到 ~22 ms 以支持 commit=2(40 ms 周期,预计 ~39%);或提升模型本身精度(训练侧)。
## 轮次 24:实时性优化与新方法(exp: `exp/dagger-deploy`)
- **目标**:准确率更高 + 效率满足 50 Hz(20 ms 控制步)。
- **① 混合节奏(规划每 3 步 + 执行时每步反馈)——负结论**:plan fb1.8 + exec fb1.8 → 36.3%(与纯规划反馈的 commit_n=3 完全相同);plan fb0 + exec fb1.8 → 仅 1.6%。执行时反馈不带来额外收益(规划时的反馈已是主要贡献),此捷径不成立。
- **② 特征几何优化(有效)**:特征只用 8 组间隙(GP),但原实现算全部 49 组(67256 簇对)。新增 `Kin.gm_groups(q, GP, cap)`:按 7 连杆组对过滤簇对(簇索引 → `cl_g` → 组对 min/max),只算 8 组。
- 数值验证:与 `gaps()['gm'][:, a*7+b]` 逐项一致(diff 0)。
- 延迟:gm 计算 10.4→**7.1 ms**(p50),单次规划省约 3 ms。
- **③ 浏览器部署**:导出 `webapp/models/traj_policy.onnx`(dag2 best,19.8 MB,对拍误差 3.1e-6,act_scale 1.0);`ik-policy.js` 加全程目标反馈(`fbGain`)+ `commit` 可配;`ik-policy-glue.js` 支持 `?fb=&commit=`(默认 fb=1.8、commit=3)。
- JS bench(Node,10 题,fb1.8+commit1):到达 0、**刹车率 47.3%**(每 2 次规划刹停一次)、planP50 43.8 / P99 70.5 ms。JS 安全层(5 mm 护栏 + 距离认证)比 Python(0.5 mm gap)保守,反馈+每步规划被大量拒绝。
- **④ 实时口径的关键认识**:部署形态是**异步规划**(tick 20 ms 只执行队列;`plan()` 后台跑),所以 50 Hz 的约束是「tick < 20 ms + 规划在队列耗尽前完成」,不是「单次规划 < 20 ms」。
- JS 的 tick 只做队列执行 + 投影/碰撞检查(~1.5–3 ms)→ 50 Hz 可行;
- 规划 P99 70 ms → 需要 commit ≥ 4(80 ms 队列)才不饥饿;commit=3(60 ms)会偶发饥饿。
- 准确率与规划频率的 trade-off:commit1 42.2%(不可 50Hz)、commit3 36.3%、commit6 14.8%。
- **下一步**:① 在 commit=3/4 的实时节奏下找准确率提升(候选平均、dag3_fe 冻结编码器、更高 gain);② 把 gm_groups 同步到 JS(gapMatrix 只算 8 组);③ 降低 JS 安全层拒绝率(margin/认证预算调参)。
## 轮次 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%)。
## 轮次 22:冻结编码器微调(exp: `exp/dagger-deploy`,负结论)
- **假设**:微调导致的分布退化来自表征漂移;冻结编码器(enc + enc_norm)、只调解码器/头,可保留开环精度同时吸收 DAgger-3 的部署分布样本。
- **判据**:部署口径 success > 43.8%(dag2)且 ol_fk 不退化(≤16 mm)。
- **配方**:`train.py --freeze-enc`(优化器只含 requires_grad 参数),数据 = 主数据 + DAgger-3 样本,lr 3e-4、5000 步,从 dag2 best 初始化;n=256 部署口径。
- **对照**:dag2 + gain1.8(43.8% / 28.9% / 1.6% / 0.216 / 40.0 / 956 ms)。
- **指标**:
| 模型 | gain | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 | ol_fk |
|---|---|---|---|---|---|---|---|---|
| 冻结编码器 | 1.5 | 39.8% | 22.3% | 1.2% | 0.293 | 25.4 | 936 ms | 15.8 mm |
| 冻结编码器 | 1.8 | 41.8% | 26.2% | 2.3% | 0.274 | 41.4 | 940 ms | 15.8 mm |
| dag2(基线) | 1.8 | **43.8%** | **28.9%** | 1.6% | **0.216** | 40.0 | 956 ms | 15.7 mm |
- **结论**:冻结编码器**确实减缓了开环退化**(ol_fk 15.8 vs 全微调 19.5),但部署到达仍比 dag2 低 2.0 pp——**无提升**。至此训练侧四种方法(APG、反馈蒸馏、DAgger-3、冻结编码器)均未超过 dag2 基线(43.8%),训练侧在当前数据/预算下已到平台。
- **下一步(超出小规模范围,需确认)**:更大规模训练(24000 步从头 + 8000×2 轮 DAgger)或改特征/架构(目标条件化、状态扰动训练需重算特征)。
## 轮次 21:DAgger-3(最终部署配置采集)(exp: `exp/dagger-deploy`,负结论)
- **假设**:用最终部署配置(每步规划 + 部分安全前缀 + 反馈 gain1.8 + 安全层)采集,训练分布与部署完全一致,可继续提升。
- **判据**:部署口径 success > 43.8%(dag2 基线,n=256)。
- **配方**:`collect --fb_gain 1.8 --safety 1 --replan empty --commit_n 1 --safety_partial 1`(500 题、73 起点)→ 老师重规划 → 打包 → 从 dag2 best 微调 15000 步(lr 3e-4,配置 base_dag3)。
- **对照**:dag2 + gain1.8(n=256:43.8% / 28.9% / 1.6% / 0.216 / 40.0 / 956 ms)。
- **指标(n=256,部署口径)**:
| 模型 | gain | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|---|
| dag3 | 1.5 | 39.8% | 16.0% | 1.2% | 0.360 | 14.7 | 912 ms |
| dag3 | 1.8 | 39.5% | 21.1% | 1.2% | 0.320 | 24.5 | 935 ms |
| dag2(基线) | 1.8 | **43.8%** | **28.9%** | 1.6% | **0.216** | 40.0 | 956 ms |
- **结论**:DAgger-3 到达 **-4.3 pp**(39.5 vs 43.8),time_ok -7.8 pp;碰撞/刹停略好但主指标下降。开环进一步退化(ol_fk 19.5 mm vs dag2 15.7)。**负结论**:继续微调(15000 步 lr 3e-4)会退化主分布,训练侧收益递减。
- **下一步**:冻结编码器、只微调解码器(假设:微调退化来自表征漂移),用同一份数据与预算验证。
## 轮次 20:反馈蒸馏(exp: `exp/dagger-deploy`,负结论)
- **假设**:用最终部署配置收集 (特征, 复合动作 = 模型输出 + 目标反馈) 样本,微调后模型可内化反馈(部署时降低/去掉反馈仍能到目标)。
- **判据**:部署口径下 `fb_gain=0~0.9` 时 success 接近/超过基线(43.8%,n=256),且 time_ok 不降。
- **配方**:`rollout` 加 collect 钩子(与部署一致:safety、replan='empty'、safety_partial=1、commit_n=1、fb_gain=1.8)收集 500 题 → **17.7 万样本**;`train.py --config distill.json`(lr 1e-4、3000 步、w_value=0、w_sub=0、w_fk=0.1、w_smooth=0.01)+ 主数据 BC 锚定,从 dag2 best 微调。
- **对照**:dag2 + gain 1.8(n=256:43.8% / 28.9% / 1.6% / 0.216 / 40.0 / 956 ms)。
- **指标(n=256,部署口径)**:
| 模型 | gain | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|---|
| distill | 0.0 | **0.8%** | — | 0.0% | 0.708 | 2.1 | 762 ms |
| distill | 0.9 | 36.7% | 10.5% | 1.2% | 0.421 | 12.4 | 916 ms |
| distill | 1.8 | 43.4% | 27.7% | 2.0% | 0.228 | 47.4 | 925 ms |
| dag2(基线) | 1.8 | 43.8% | 28.9% | 1.6% | 0.216 | 40.0 | 956 ms |
- **结论**:**反馈未内化**——`gain=0` 时到达仅 0.8%(与基线纯策略一样);`gain=1.8` 时与基线持平(43.4 vs 43.8,差约 1 题),碰撞/刹停略差。蒸馏样本占比仅 8.5%、标签仍以主数据增量为主,模型没有学会"看目标修正"。此配方不成立。
- **下一步**:与最终部署配置一致的 DAgger 第 3 轮(采集时已用 safety+fb 1.2,但仍是 3 步提交/全窗口;现在部署是每步规划+部分前缀)。
## 轮次 19:APG 闭环策略梯度试跑(exp: `exp/dagger-deploy`,管线修复但暂不采用)
- **假设**:用闭环目标/碰撞损失直接微调策略(把"朝目标走"内化),减少对执行层反馈的依赖。
- **判据**:纯策略闭环 success 上升、碰撞铰链下降;部署口径(+反馈/安全层)不降。
- **配方**:`ml.apg.apg_finetune --init base_dag2_dag1/best.pt --steps 2`(先冒烟;原计划 500 步)。
- **修复的两个既有 bug**(APG 此前从未实测):
1. `kin_torch._cat_gaps` 的 `p['b'] += s` 是 in-place,而 `b` 被 autograd 索引反向图引用 → backward 报 version 错;改为 `p['b'] = p['b'] + s`。
2. APG 段边界 `queue.detach()` 让下一段执行的动作无梯度;改为只 detach 状态并重置队列(下一 plan 走 r=0 重新提交)。
- **指标(2 步冒烟)**:EVAL0(纯策略)success 0 / 碰撞 31.8%;第 0 步 col(碰撞铰链均值)**409508**、gnorm 473119(被裁剪)、tgt 206.6、smooth 645;EVAL@2 success 0 / 碰撞 26.0%。
- **结论**:管线已能跑,但**损失尺度失控**(碰撞铰链均值 40 万,远大于 margin 3mm 的合理量级);且 APG 展开是纯策略口径(无反馈/安全层/每步规划),与当前部署形态不一致。**暂不采用**,转「反馈蒸馏」。
- **下一步**:反馈蒸馏——用最终部署配置收集 (特征, 复合动作=模型输出+目标反馈) 样本,微调模型把反馈内化。
## 轮次 18:n=512 复核 gain 1.8(收尾)
- **假设**:轮次 17 的 43.8%(n=256)需更大样本确认。
- **判据**:reach 与 n=256 差异 <5 pp、碰撞 ≤3%。
- **配方**:dag2 best,replan='empty',commit_n=1,safety_partial=1,gain 1.8;n=512。
- **对照**:轮次 17(n=256:43.8% / 28.9% / 1.6% / 0.216 / 40.0 / 956 ms);轮次 16(gain 1.5 n=512:40.8% / 25.0% / 2.1% / 0.309 / 25.3 / 852 ms)。
- **指标**:到达 **42.2%**、time_ok_1p3 **29.1%**、碰撞 **2.9%**、ratio2s **0.270**、刹停 37.4、墙钟 **870 ms/题**。
- **结论**:gain 1.8 在 n=512 稳定(42.2%),到达/时间效率优于 gain 1.5(+1.4 pp / +4.1 pp),但碰撞 2.1→2.9%(贴近 3% 线)、刹停 25.3→37.4。**历史最佳(到达优先,n=512)= gain 1.8;碰撞优先 = gain 1.5**。
- **下一步**:本轮探索收尾(监控页已回写);进入大规模训练需用户确认 §9.2 锚点。
## 轮次 17:每步规划下的更大增益(exp: `exp/dagger-deploy`)
- **假设**:每步规划下更大的反馈增益可进一步提升到达与时间效率。
- **判据**:time_ok 上升、到达不降、碰撞 ≤3%。
- **配方**:dag2 best,replan='empty',commit_n=1,safety_partial=1,gain ∈ {1.8, 2.2, 3.0},n=256。
- **对照**:轮次 15(gain 1.5:41.8% / 21.5% / 2.3% / 0.259 / 26.9 / 967 ms)。
- **指标**:
| gain | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|
| **1.8** | **43.8%** | 28.9% | 1.6% | 0.216 | 40.0 | 956 ms |
| 2.2 | 41.0% | **32.4%** | 2.0% | 0.174 | 64.4 | 976 ms |
| 3.0 | 36.3% | 32.8% | 0.8% | **0.153** | **123.2** | 994 ms |
- **结论**:gain 1.8 到达最高(43.8%);2.2/3.0 精度与时间效率更好但到达下降、刹停暴涨(3.0 达 123 次/题,过冲)。**历史最佳按到达优先(碰撞 ≤3%):gain 1.8 = 到达 43.8% / 碰撞 1.6% / ratio2s 0.216 / time_ok 28.9% / 956 ms(n=256)**。保守/时间优先可选 2.2。
- **下一步**:n=512 复核 gain 1.8;随后收尾。
## 轮次 16:n=512 复核每步规划(稳定结论)
- **假设**:轮次 15 的 41.8%(n=256)需更大样本确认。
- **判据**:reach 与 n=256 差异 <5 pp、碰撞 ≤3%。
- **配方**:dag2 best,replan='empty',gain 1.5,safety=True,safety_partial=1,commit_n=1;n=512。
- **对照**:轮次 15(n=256:41.8% / 21.5% / 2.3% / 0.259 / 967 ms)。
- **指标**:到达 **40.8%**、time_ok_1p3 **25.0%**、碰撞 **2.1%**、ratio2s **0.309**、刹停 25.3、墙钟 **852 ms/题**。
- **结论**:稳定。**历史最佳(n=512,部署口径)= 40.8% 到达 / 2.1% 碰撞 / ratio2s 0.309 / time_ok 25.0% / 852 ms/题**;从基线 0% 到达提升 40.8 pp。相对 §9.2(98%、碰撞 0、用时≤老师×1.3)仍有大差距,但机理已成立。
- **下一步**:每步规划下重扫增益(1.8/2.2/3.0),看时间效率是否继续提升;随后收尾。
## 轮次 15:每步规划(commit_n=1)(exp: `exp/dagger-deploy`,新历史最佳)
- **假设**:部分前缀的填充零步仍在消耗时间;每次只提交 1 步、每步重新规划可消除填充、提升时间效率。
- **判据**:time_ok_1p3 显著上升、到达不降、碰撞 ≤3%。
- **配方**:dag2 best,replan='empty',gain 1.5,safety=True,`safety_partial=1, commit_n=1`;n=256。
- **对照**:commit_n=3 部分前缀(轮次 14:38.3% / 13.7% / 1.6% / 0.308 / 8.6 / 770 ms)。
- **指标**:
| commit_n | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|
| 3 | 38.3% | 13.7% | 1.6% | 0.308 | 8.6 | 770 ms |
| **1** | **41.8%** | **21.5%** | 2.3% | **0.259** | 26.9 | 967 ms |
- **结论**:每步规划把到达推到 **41.8%**、时间效率 **13.7→21.5%**、精度 ratio2s **0.259**;代价:碰撞 1.6→2.3%(仍 <3%)、刹停计数回升(每步都判断)、时延 +26%。**新历史最佳(n=256,部署口径):41.8% / 2.3% / 0.259 / 21.5% / 967 ms**。
- **注**:commit_n=1 的推理频率是 3 倍,真机需核对 §9.2 的 P99 <20 ms 预算(当前桌面 12.58 ms/次)。
- **下一步**:n=512 复核;若稳定则收尾(更新最佳、监控页、分支清理)。
## 轮次 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 步、每步规划,无填充步),验证时间效率。
## 轮次 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` 验证能否在保持低碰撞的同时大幅减少刹停。
## 轮次 12:n=512 复核(稳定结论)
- **假设**:n=256 的 34.8–36.7% 需在更大样本上确认。
- **判据**:reach 与 n=256 差异 <5 pp、碰撞 ≤3%。
- **配方**:dag2 best,`replan='empty'`,safety=True,gain ∈ {1.2, 1.5},n=512。
- **对照**:轮次 11(n=256:34.8 / 36.7%)。
- **指标**:
| gain | n=256 到达 | **n=512 到达** | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|
| 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 + 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 在反馈+每步重规划下是否影响到达/刹停。
## 轮次 11:n=256 复核(发现前 128 题偏难)
- **假设**:轮次 10 的 n=128 差异含样本偏差,需扩大样本复核。
- **判据**:reach 排名与轮次 10 一致、碰撞 ≤3%;若差异大则以 n=256 为准。
- **配方**:dag2 best,`replan='empty'`,safety=True,gain ∈ {1.0, 1.2, 1.5},n=256。
- **对照**:轮次 10(n=128:22.7/26.6/28.9%)。
- **指标**:
| gain | n=128 到达 | **n=256 到达** | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|
| 1.0 | 22.7% | **31.6%** | 0.8% | 0.428 | 15.0 | 748 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 |
- **结论**:前 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 轮(部署分布采集)与监控页回写。
## 轮次 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)。
- **指标**:
| gain | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|
| 0.8 | 21.9% | 2.3% | 0.505 | 11.3 | 801 ms |
| 1.0 | 22.7% | **0.0%** | 0.489 | 15.2 | 810 ms |
| 1.2 | 26.6% | 0.8% | 0.453 | 20.6 | 845 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 |
- **结论**:到达在 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 轮(采集用新部署配置)。
## 轮次 9:replan=empty(队列空时规划、r=0)/ K=16 安全候选(exp: `exp/dagger-deploy`,新历史最佳)
- **假设**: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)。
- **指标**:
| 配置 | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|
| **gain1.2 + replan=empty** | **26.6%** | **0.8%** | **0.453** | **20.6** | **845 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 |
- **结论**:**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),找新最优。
## 轮次 8:部署分布 DAgger 第 2 轮(exp: `exp/dagger-deploy`,边际改善)
- **假设**:第 1 轮 DAgger 用纯策略轨迹采集,与部署分布(反馈 + 安全层)不一致;用部署分布采集可让模型学到刹停/反馈下的正确动作。
- **判据**:部署口径 reach > 23.4%(轮次 6)或刹停显著下降且到达不降。
- **配方**:`ml.dagger.collect --fb_gain 1.2 --safety 1`(2000 题 → 637 起点,部署分布)→ 老师重规划 → 打包 → 从 `base_dag1/best.pt` 微调 15000 步(配置 base_dag2,lr 3e-4)。
- **对照**:base_dag1/best.pt(轮次 6 历史最佳);纯策略 eval 见训练日志。
- **指标(部署口径,n=128,safety=True)**:
| 模型 | gain | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|---|
| dag2 best | 1.0 | 21.9% | 1.6% | 0.485 | **18.9** | **905 ms** |
| dag2 best | 1.2 | 22.7% | 1.6% | 0.491 | 25.9 | 922 ms |
| dag1 best | 1.0 | 20.3% | 2.3% | 0.480 | 21.9 | 944 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%)。
- **下一步**:试 replan=empty(r=0 规划,让反馈逐步生效)与安全层候选 K=16(减少全拒刹停)。
## 轮次 7:距离衰减增益 + 刹停兜底(exp: `feat/fb-mix`,负结论)
- **假设**:贴近目标减弱反馈可减少过冲/刹停;安全层全拒时用「半速朝目标一步」(单点安全认证)可减少原地停滞。
- **判据**:reach > 23.4%(轮次 6)且碰撞 ≤3%、刹停下降。
- **配方**:`fb_scale=100`(>100 mm 全增益,近目标最低 0.2 倍);`fb_fallback=0.5`(全拒时半速 DLS 一步,认证 `qe+fb1` 单点无碰)。n=128,safety=True。
- **对照**:轮次 6(gain 1.2、无衰减、无兜底:23.4% / 1.6% / 0.454 / 31.4 / 973 ms)。
- **指标**:
| 配置 | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|
| decay + fallback (gain 1.2) | 7.0% | 57.8% | 0.506 | 45.1 | 1166 ms |
| decay only (gain 1.2) | 9.4% | 1.6% | 0.454 | 29.6 | 1106 ms |
| no-decay + fallback (gain 1.2) | 21.1% | 57.8% | 0.506 | 46.8 | 1063 ms |
| gain 1.0 + decay + fallback | 4.7% | 55.5% | 0.515 | 37.2 | 939 ms |
- **结论**:两个改动都失败。① 衰减使接近目标时的收敛变慢,到达从 23.4% 掉到 9.4%;② 兜底绕过安全层的多候选选择,碰撞从 1.6% 拉到 **57.8%**(仅单点认证、且执行 3 步同向动作,危险),必须清除。保留参数但默认关闭(`fb_scale=0, fb_fallback=0`),历史最佳配置(轮次 6)可复现。
- **过程 bug(自造)**:衰减代码曾用变量名 `g` 覆盖 rollout 的随机数生成器,导致 `randn` 报错;改名 `gdec` 后正常——记录以免误判为模型问题。
- **下一步**:DAgger 第 2 轮——采集时加入 fb_gain + safety,让训练分布 = 部署分布(1 轮,15000 步微调,约 1 小时)。按目标要求先向用户确认。
## 轮次 6:反馈增益扩展扫描(exp: `feat/fb-mix`,小幅进展)
- **假设**:更大增益能把到达率继续推高。
- **判据**:reach 显著高于 20.3%(>25%)且碰撞 ≤3%、刹停不失控。
- **配方**:`rollout(fb_gain∈{1.2,1.5,2.0}, safety=True)`,模型同轮次 5,n=128。
- **对照**:轮次 5(gain 1.0:reach 20.3% / 碰撞 2.3% / ratio2s 0.480 / 刹停 21.9)。
- **指标**:
| gain | 到达 | 碰撞 | ratio2s | 刹停/题 | 墙钟/题 |
|---|---|---|---|---|---|
| 1.2 | **23.4%** | 1.6% | **0.454** | 31.4 | 973 ms |
| 1.5 | 21.9% | 2.3% | 0.498 | 43.0 | 1151 ms |
| 2.0 | 23.4% | 0.8% | 0.530 | 57.6 | 1100 ms |
- **结论**:到达率封顶在 ~23%,增益再大只增刹停(31→58 次/题)。当前瓶颈从「到不了目标」变成「安全层频繁刹停 + 近目标过冲」。
- **下一步**:距离衰减增益(远离目标全增益、贴近目标减弱)+ 刹停兜底;目标 reach > 30% 且刹停显著下降。
## 轮次 5:模型前馈 + DLS 目标反馈(exp: `feat/fb-mix`,历史最佳)
- **假设**:模型擅长开环轨迹/避撞,但不指向目标;把 DLS 朝目标的反馈叠加到候选动作上,可把「走偏」拉回目标。
- **判据**:部署口径(+安全层)reach 显著 > 0 且碰撞 < 3%(§9.2 策略单独碰撞线)、刹停可控;否则不成立。
- **配方**:`closed_loop.rollout(fb_gain=...)`:`inc += fb_gain · DLS(qe→T,R, n=1)`(加在所有候选上,安全层评估带反馈的候选)。模型 `ml/runs/base_dag1/best.pt`(DAgger 轮 4 的 best,12000 步)。n=128,val。
- **对照**:base/last.pt + 同反馈;DAgger 模型 + 无反馈。
- **指标(n=128)**:
| 配置 | 到达 | 碰撞 | 精度 ratio2s | 刹停/题 | 时延 |
|---|---|---|---|---|---|
| DAgger 模型, gain 0 | 0% | 44.5% | 0.873 | 0 | — |
| DAgger 模型, gain 1.0, 关安全层 | 29.7% | 73.4% | 0.370 | 0 | — |
| **DAgger 模型, gain 1.0, 开安全层** | **20.3%** | **2.3%** | **0.480** | **21.9** | **12.58 ms/次**(B=64,K=8,桌面 3090) |
| base 模型, gain 1.0, 关安全层 | 0% | 94.5% | 0.554 | 0 | — |
(另测 n=32 复核:gain 1.0 + safety → reach 25.0%、碰撞 6.2%、ratio2s 0.588。)
- **结论**:**反馈混合是首个把到达率从 0 拉到 20%+ 的机理**;安全层把碰撞从 73% 压到 2.3%(部署口径),代价是刹停 21.9 次/题(远超 §9.2 的 <2%/周期)。精度 ratio2s 0.48(2 s 时误差缩一半),时延 12.58 ms/次接近 §9.2 的 20 ms(推理+认证)门槛。
- **与历史最佳/基线对比**:reach 0→20.3%、碰撞 99.6→2.3%、ratio2s 0.755→0.480;开环精度仍退化(DAgger 微调导致 15.7 mm vs 基线 5.98 mm)。
- **漂移检查**:main = 70654e4(无新提交);工作区仅用户个人 html 未跟踪;数据口径 act_scale=1.0 与 ckpt 一致;`feat/abs-target` 已推送远端;本次实验在独立分支 `feat/fb-mix`。
- **下一步**:扫更大 gain(1.2–2.0)与增益衰减;目标是把刹停次数降下来、到达率再提升,并测 ONNX/浏览器时延。
## 轮次 4:真实 DAgger 1 轮(main,部分有效)
- **假设**:策略自己走到的状态(离训练集 21.5°)+ 老师重标注,可让模型学会在偏移状态上行动。
- **判据**:reach 是否从 0 抬头;同时看碰撞、开环精度、时延。
- **配方**:`ACT_SCALE=1.0 DAG_ROUNDS=1 DAG_N=2000 bash ml/dagger/run.sh base ml/runs/base/last.pt`(采集 2000 题 → 老师重规划 → 打包 → 微调 15000 步,lr 3e-4;主数据 + DAgger 混合)。
- **对照**:base/last.pt。
- **指标**:碰撞 99.6%→**40.2%**(8000 步 36.9%,10000 步 58.8%);到达 **0%**,仅微调 12000 步时出现 **0.4%(2/512)**;开环退化 6→**15.7 mm**;ratio2s 0.87–0.92;时延未测。
- **结论**:DAgger 数据确实教会避撞,但微调把主分布带偏(开环退化),到达仍近 0;单独 DAgger 不是终点。
- **下一步**:在 DAgger 模型上加目标反馈(模型前馈 + DLS 反馈)。
## 轮次 3:绝对目标参数化(exp: `feat/abs-target`,负结论)
- **假设**:模型从当前状态外推增量(判别 ρ=1.54),改为绝对目标标签可让输出指向参考位置。
- **判据**:一步采样开环误差需回到 <10 mm,闭环 success > 0;否则参数化不可行。
- **配方**:`ABS_TARGET=1` 重打主数据(201 万样本,Y=16 步绝对目标/180),train/rollout/ckpt 适配;从零训练 9950 步(p_neq 预热 5000)。过程中修复前缀噪声增广把 eps 直接加到绝对目标(≈18° 污染)的 bug。
- **对照**:base/last.pt(ol_fk 5.98 mm、reach 0%)。
- **指标**:一步采样动作误差 **31°**(4/8 段 2.3°/2.8°);ol_fk 4000/6000/8000 步 = 74.6/140.4/139.0 mm;闭环 **0% / 100%**(1 段与 4 段皆然);时延未测。
- **结论**:绝对目标的一步流匹配在 MeanFlow 头上学不动(过预热后 5000 步无下降趋势);此配方不成立。分支已推送远端 `217a70b`(本地分支可删,远端保留可复现)。
- **下一步**:回归增量口径,上真实 DAgger。
## 轮次 2:合成扰动状态微调(main,负结论)
- **假设**:偏移 1.5° 状态的老师标注可补足闭环状态覆盖。
- **判据**:若 success 仍 0 或开环精度退化,则配方不成立。
- **配方**:2000 个扰动起点(σ 混合 0.5/1/2/4°)→ 老师重规划(2000/2000 到达)→ 36k 样本 → 从 base/last.pt 微调 3000 步(lr 3e-4 / 1e-4 两轮)。
- **对照**:base/last.pt。
- **指标**:碰撞 99.6%→**48.4% / 60.4%**;到达仍 **0%**;开环 6→**15.3 / 16.4 mm**(退化 2.5×);ratio2s 0.927/0.893。
- **结论**:只学会了「保守避撞」,开环精度被带偏,success 仍 0;此配方不成立。
- **下一步**:改训练标签(绝对目标)或真实 DAgger。
## 轮次 1:数据规模能否救闭环(main,负结论)
- **假设**:训练数据不足导致分布外状态无覆盖。
- **判据**:若闭环状态到训练集最近邻距离大,且密度缩放无法在误差尺度内缩小差距,则加大同类数据无效。
- **配方**:191 万老师状态最近邻分析;6 维密度缩放 d∝N^(−1/6)。
- **对照**:起点(老师分布内)最近邻。
- **指标**:闭环状态最近邻 p50 **21.5°**(起点 13.2°);数据翻 4 倍仅降 ~21%,与 0.02° 误差尺度差三个量级。
- **结论**:加大同类老师数据无效;需要「不同的数据」(闭环状态标注)。
- **下一步**:DAgger 或状态扰动数据。
## 轮次 0:基线(2026-10-18,main)
- **假设**:模型开环已拟合老师(末端 ~6 mm),闭环失败另有原因。
- **判据**:reach = 0 且碰撞 ≈ 100% 即失败;需同时报告碰撞 / 到达 / 精度 / 时延。
- **配方**:`ml/runs/base/last.pt`(step 14000,旧配方、act_scale=1.0),n=256。
- **对照**:老师回放 reach 1.0 / 碰撞 0;H1_oracle(老师提线走 rollout)reach 1.0、碰撞 0、轨迹偏差 p99 4.6e-4°。
- **指标**:到达 **0%**;碰撞 **99.6%**;精度 ol_fk 5.98 mm / ol_best 2.92 mm / ol_div 0.655,2 s 误差比 0.755;时延未测。
- **结论**:仿真/滚动无误,失败在策略输出。闭环对每步残差极敏感(老师动作加 σ=0.05×STEP 即 reach 1.0→0.218)。
## 分支清理(2026-10-20)
- `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。
- `feat/abs-target`(217a70b):绝对目标参数化负结论分支(轮次 3);打 tag `archive/abs-target` 保留可复现实体后,删除远端分支(本地此前已删)。
- `backup/pre-deep-squash`、`backup/pre-squash`:历史备份分支,非实验方案,保留未动。
- 保留:`main`(70654e4)、`exp/dagger-deploy`(39c870c,历史最佳)。