exp: 轮次46-47——select=pre(打分地平线=被执行前缀终点)正向,两 seed 复现

2 s 误差比逐题符号检验 s0 192好/64差 p=4.9e-16、s1 191/65 p=1.5e-15;时限内(≤1.3×老师)
0.2656→0.3320(+6.64pp)、0.2969→0.3398(+4.30pp);行程比 −0.171/−0.171;碰撞仍 0、超 VMAX 仍 0。
到达只 +5/+3 题(McNemar p=0.42/0.68 ⇒ 噪声),故不改默认口径、作可选口径入库。
代价:到达题末态误差 0.22→0.42 mm(判据壳 1 mm 内,余量 4.6×→2.4×),两 seed 都涨。

对照可比性先证:控制臂 tie0 与轮次42 round42_K8cb1 的 22/22 键逐位相同 ⇒ 该配方跨窗口确定性
复现,本轮起新杠杆可用单臂对已落盘控制。

实现:select='pre' 用 fut[:,:,commit_n-1] 的终点误差(同一次 goal_err 只换点集);两条可手算正对照
(前缀好/末端差 vs 末端好/前缀差 ⇒ 两口径必须选相反候选) + 不变式(commit_n=16 时与 endpoint 同选);
49 项测试绿;n=16 冒烟证非惰性。候选表同步修准:backoff/ep_waste 两行状态改判掉,#29/#30 登记。
This commit is contained in:
cbb
2026-10-21 00:57:08 +08:00
parent 72c8835229
commit 3c250c7df3
8 changed files with 602 additions and 2 deletions
+96
View File
@@ -0,0 +1,96 @@
{
"config": {
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
"samples": "ml/data/samples",
"tasks": null,
"split": "val",
"n": 16,
"K": 8,
"commit": 2,
"fb": 2.2,
"ki": 0.0,
"fb_scale": 0.0,
"fb_near": 0.2,
"fb_fallback": 0.0,
"kdecay": 0.9,
"kimax": 0.5,
"fb_every": 1,
"safety_try": 0,
"sel_w": 0.1,
"dls_safety": 1,
"fb_clear": 0.0,
"fb_cmin": 0.3,
"sub_refresh": 0,
"fb_block_gain": 0.5,
"dump": null,
"out": "docs/experiment_round46_ep16_smoke.json",
"trace": null,
"select": "endpoint",
"replan": "empty",
"safety": 1,
"partial": 1,
"inc_cap": 1,
"gap_margin": 0.0,
"cert_batch": 1,
"steps": 1,
"z_scale": 1.0,
"len_w": 0.0,
"tie": 0,
"backoff": 0,
"max_sec": null,
"seed": 0,
"dls": "analytic",
"device": "cuda"
},
"batch_size": 256,
"batch_seed_rule": "same seed per batch",
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
"tasks_sha256": "7b7c219c70d15b280682fe0626192b267f0e4dfd843b1e70fb279e148eb6eaa3",
"kin_fp": "2ebcbf17",
"act_scale": 1.0,
"metrics": {
"n": 16,
"n_ref": 16,
"success": 0.375,
"collision_free_success": 0.375,
"time_ok_1p3": 0.25,
"collision_rate": 0.0,
"collision_steps_mean": 0.0,
"travel_ratio_median": 2.426271438598633,
"time_mean_s": 2.680000066757202,
"brakes_mean": 24.9375,
"start_fail": 0.0,
"goal_ratio_2s": 0.43800246715545654,
"err_fail_med": 231.5127410888672,
"err_success_med": 0.2731371223926544,
"dls_share": 0.5,
"time_budget": "teacher×2(≥3 s)",
"speed_ratio_p50": 1.0000016689300537,
"speed_ratio_p95": 1.0000053644180298,
"speed_ratio_max": 1.0000101327896118,
"over_vmax_rate": 0.0,
"accel_ratio_p95": 1.0000016689300537,
"accel_ratio_max": 1.0000016689300537,
"reverse_p50": 0.22442243993282318,
"reverse_mean": 0.2425607144832611,
"reverse_moving_p50": 0.30572596192359924,
"moving_share_p50": 0.7567567825317383,
"brake_rate_mean": 0.14249999821186066,
"brake_rate_p50": 0.07428571581840515,
"brake_rate_p90": 0.3571428656578064,
"stuck_share": 0.3125,
"n_plans_mean": 175.0,
"success_reachable": 0.375,
"by_cat": {
"A": [
8,
4
],
"B": [
8,
2
]
}
},
"simulation_wall_s": 35.48499080000329
}
+96
View File
@@ -0,0 +1,96 @@
{
"config": {
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
"samples": "ml/data/samples",
"tasks": null,
"split": "val",
"n": 16,
"K": 8,
"commit": 2,
"fb": 2.2,
"ki": 0.0,
"fb_scale": 0.0,
"fb_near": 0.2,
"fb_fallback": 0.0,
"kdecay": 0.9,
"kimax": 0.5,
"fb_every": 1,
"safety_try": 0,
"sel_w": 0.1,
"dls_safety": 1,
"fb_clear": 0.0,
"fb_cmin": 0.3,
"sub_refresh": 0,
"fb_block_gain": 0.5,
"dump": null,
"out": "docs/experiment_round46_pre16_smoke.json",
"trace": null,
"select": "pre",
"replan": "empty",
"safety": 1,
"partial": 1,
"inc_cap": 1,
"gap_margin": 0.0,
"cert_batch": 1,
"steps": 1,
"z_scale": 1.0,
"len_w": 0.0,
"tie": 0,
"backoff": 0,
"max_sec": null,
"seed": 0,
"dls": "analytic",
"device": "cuda"
},
"batch_size": 256,
"batch_seed_rule": "same seed per batch",
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
"tasks_sha256": "7b7c219c70d15b280682fe0626192b267f0e4dfd843b1e70fb279e148eb6eaa3",
"kin_fp": "2ebcbf17",
"act_scale": 1.0,
"metrics": {
"n": 16,
"n_ref": 16,
"success": 0.375,
"collision_free_success": 0.375,
"time_ok_1p3": 0.25,
"collision_rate": 0.0,
"collision_steps_mean": 0.0,
"travel_ratio_median": 2.6735622882843018,
"time_mean_s": 2.763333320617676,
"brakes_mean": 21.9375,
"start_fail": 0.0,
"goal_ratio_2s": 0.4253818392753601,
"err_fail_med": 223.724365234375,
"err_success_med": 0.7986489534378052,
"dls_share": 0.5625,
"time_budget": "teacher×2(≥3 s)",
"speed_ratio_p50": 1.0000016689300537,
"speed_ratio_p95": 1.0000053644180298,
"speed_ratio_max": 1.0000101327896118,
"over_vmax_rate": 0.0,
"accel_ratio_p95": 1.0000016689300537,
"accel_ratio_max": 1.0000016689300537,
"reverse_p50": 0.23239436745643616,
"reverse_mean": 0.22883012890815735,
"reverse_moving_p50": 0.29054054617881775,
"moving_share_p50": 0.9583333134651184,
"brake_rate_mean": 0.12535713613033295,
"brake_rate_p50": 0.0057142856530845165,
"brake_rate_p90": 0.3257142901420593,
"stuck_share": 0.3125,
"n_plans_mean": 175.0,
"success_reachable": 0.375,
"by_cat": {
"A": [
8,
4
],
"B": [
8,
2
]
}
},
"simulation_wall_s": 31.1417752999987
}
+108
View File
@@ -0,0 +1,108 @@
{
"config": {
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
"samples": "ml/data/samples",
"tasks": null,
"split": "val",
"n": 256,
"K": 8,
"commit": 2,
"fb": 2.2,
"ki": 0.0,
"fb_scale": 0.0,
"fb_near": 0.2,
"fb_fallback": 0.0,
"kdecay": 0.9,
"kimax": 0.5,
"fb_every": 1,
"safety_try": 0,
"sel_w": 0.1,
"dls_safety": 1,
"fb_clear": 0.0,
"fb_cmin": 0.3,
"sub_refresh": 0,
"fb_block_gain": 0.5,
"dump": "ml/runs/round46_pre256_rows.json",
"out": "docs/experiment_round46_pre256.json",
"trace": null,
"select": "pre",
"replan": "empty",
"safety": 1,
"partial": 1,
"inc_cap": 1,
"gap_margin": 0.0,
"cert_batch": 1,
"steps": 1,
"z_scale": 1.0,
"len_w": 0.0,
"tie": 0,
"backoff": 0,
"max_sec": null,
"seed": 0,
"dls": "analytic",
"device": "cuda"
},
"batch_size": 256,
"batch_seed_rule": "same seed per batch",
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
"tasks_sha256": "249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772",
"kin_fp": "2ebcbf17",
"act_scale": 1.0,
"metrics": {
"n": 256,
"n_ref": 256,
"success": 0.54296875,
"collision_free_success": 0.54296875,
"time_ok_1p3": 0.33203125,
"collision_rate": 0.0,
"collision_steps_mean": 0.0,
"travel_ratio_median": 2.4448752403259277,
"time_mean_s": 2.342589855194092,
"brakes_mean": 19.83984375,
"start_fail": 0.0,
"goal_ratio_2s": 0.17014972865581512,
"err_fail_med": 185.13986206054688,
"err_success_med": 0.4150978922843933,
"dls_share": 0.63671875,
"time_budget": "teacher×2(≥3 s)",
"speed_ratio_p50": 1.0000016689300537,
"speed_ratio_p95": 1.0000101327896118,
"speed_ratio_max": 1.0000101327896118,
"over_vmax_rate": 0.0,
"accel_ratio_p95": 1.0000016689300537,
"accel_ratio_max": 1.0000101327896118,
"reverse_p50": 0.22891566157341003,
"reverse_mean": 0.23401273787021637,
"reverse_moving_p50": 0.28184282779693604,
"moving_share_p50": 0.9583333134651184,
"brake_rate_mean": 0.10174279659986496,
"brake_rate_p50": 0.0051282052882015705,
"brake_rate_p90": 0.35384616255760193,
"stuck_share": 0.24609375,
"n_plans_mean": 195.0,
"success_reachable": 0.5720164775848389,
"by_cat": {
"A": [
128,
95
],
"B": [
74,
22
],
"C": [
32,
14
],
"D": [
13,
0
],
"Dr": [
9,
8
]
}
},
"simulation_wall_s": 319.2378991000005
}
+108
View File
@@ -0,0 +1,108 @@
{
"config": {
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
"samples": "ml/data/samples",
"tasks": null,
"split": "val",
"n": 256,
"K": 8,
"commit": 2,
"fb": 2.2,
"ki": 0.0,
"fb_scale": 0.0,
"fb_near": 0.2,
"fb_fallback": 0.0,
"kdecay": 0.9,
"kimax": 0.5,
"fb_every": 1,
"safety_try": 0,
"sel_w": 0.1,
"dls_safety": 1,
"fb_clear": 0.0,
"fb_cmin": 0.3,
"sub_refresh": 0,
"fb_block_gain": 0.5,
"dump": "ml/runs/round47_ep_s1_rows.json",
"out": "docs/experiment_round47_ep_s1.json",
"trace": null,
"select": "endpoint",
"replan": "empty",
"safety": 1,
"partial": 1,
"inc_cap": 1,
"gap_margin": 0.0,
"cert_batch": 1,
"steps": 1,
"z_scale": 1.0,
"len_w": 0.0,
"tie": 0,
"backoff": 0,
"max_sec": null,
"seed": 1,
"dls": "analytic",
"device": "cuda"
},
"batch_size": 256,
"batch_seed_rule": "same seed per batch",
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
"tasks_sha256": "249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772",
"kin_fp": "2ebcbf17",
"act_scale": 1.0,
"metrics": {
"n": 256,
"n_ref": 256,
"success": 0.53125,
"collision_free_success": 0.53125,
"time_ok_1p3": 0.296875,
"collision_rate": 0.0,
"collision_steps_mean": 0.0,
"travel_ratio_median": 2.636941432952881,
"time_mean_s": 2.4600000381469727,
"brakes_mean": 19.265625,
"start_fail": 0.0,
"goal_ratio_2s": 0.2750342786312103,
"err_fail_med": 214.6193084716797,
"err_success_med": 0.22276456654071808,
"dls_share": 0.62890625,
"time_budget": "teacher×2(≥3 s)",
"speed_ratio_p50": 1.0000016689300537,
"speed_ratio_p95": 1.0000101327896118,
"speed_ratio_max": 1.0000101327896118,
"over_vmax_rate": 0.0,
"accel_ratio_p95": 1.0000016689300537,
"accel_ratio_max": 1.0000101327896118,
"reverse_p50": 0.25339367985725403,
"reverse_mean": 0.2575649917125702,
"reverse_moving_p50": 0.3117443919181824,
"moving_share_p50": 0.9636363387107849,
"brake_rate_mean": 0.09879808127880096,
"brake_rate_p50": 0.0051282052882015705,
"brake_rate_p90": 0.3384615480899811,
"stuck_share": 0.25390625,
"n_plans_mean": 195.0,
"success_reachable": 0.5596708059310913,
"by_cat": {
"A": [
128,
96
],
"B": [
74,
21
],
"C": [
32,
12
],
"D": [
13,
0
],
"Dr": [
9,
7
]
}
},
"simulation_wall_s": 315.10028919999604
}
+108
View File
@@ -0,0 +1,108 @@
{
"config": {
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
"samples": "ml/data/samples",
"tasks": null,
"split": "val",
"n": 256,
"K": 8,
"commit": 2,
"fb": 2.2,
"ki": 0.0,
"fb_scale": 0.0,
"fb_near": 0.2,
"fb_fallback": 0.0,
"kdecay": 0.9,
"kimax": 0.5,
"fb_every": 1,
"safety_try": 0,
"sel_w": 0.1,
"dls_safety": 1,
"fb_clear": 0.0,
"fb_cmin": 0.3,
"sub_refresh": 0,
"fb_block_gain": 0.5,
"dump": "ml/runs/round47_pre_s1_rows.json",
"out": "docs/experiment_round47_pre_s1.json",
"trace": null,
"select": "pre",
"replan": "empty",
"safety": 1,
"partial": 1,
"inc_cap": 1,
"gap_margin": 0.0,
"cert_batch": 1,
"steps": 1,
"z_scale": 1.0,
"len_w": 0.0,
"tie": 0,
"backoff": 0,
"max_sec": null,
"seed": 1,
"dls": "analytic",
"device": "cuda"
},
"batch_size": 256,
"batch_seed_rule": "same seed per batch",
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
"tasks_sha256": "249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772",
"kin_fp": "2ebcbf17",
"act_scale": 1.0,
"metrics": {
"n": 256,
"n_ref": 256,
"success": 0.54296875,
"collision_free_success": 0.54296875,
"time_ok_1p3": 0.33984375,
"collision_rate": 0.0,
"collision_steps_mean": 0.0,
"travel_ratio_median": 2.4660637378692627,
"time_mean_s": 2.4476258754730225,
"brakes_mean": 19.703125,
"start_fail": 0.0,
"goal_ratio_2s": 0.20109058916568756,
"err_fail_med": 187.68463134765625,
"err_success_med": 0.3424937129020691,
"dls_share": 0.6484375,
"time_budget": "teacher×2(≥3 s)",
"speed_ratio_p50": 1.0000016689300537,
"speed_ratio_p95": 1.0000101327896118,
"speed_ratio_max": 1.0000101327896118,
"over_vmax_rate": 0.0,
"accel_ratio_p95": 1.0000016689300537,
"accel_ratio_max": 1.0000101327896118,
"reverse_p50": 0.22884614765644073,
"reverse_mean": 0.23487445712089539,
"reverse_moving_p50": 0.2777777910232544,
"moving_share_p50": 0.9679999947547913,
"brake_rate_mean": 0.10104167461395264,
"brake_rate_p50": 0.0051282052882015705,
"brake_rate_p90": 0.35641026496887207,
"stuck_share": 0.2421875,
"n_plans_mean": 195.0,
"success_reachable": 0.5720164775848389,
"by_cat": {
"A": [
128,
95
],
"B": [
74,
23
],
"C": [
32,
14
],
"D": [
13,
0
],
"Dr": [
9,
7
]
}
},
"simulation_wall_s": 314.3736673000094
}
+30 -2
View File
@@ -20,6 +20,7 @@
- **当前最好(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 重跑。
- **合法限速口径下的新最好时限读数(轮次 46–47)**:同配方把打分地平线换成被执行前缀 `select=pre` → **到达 54.3%、时限内 33.2%(控制 26.6%)、2 s 误差比 0.170(控制 0.279)、行程比 2.445、碰撞 0、超 VMAX 0**;seed=1 复现 54.3%/34.0%。到达相对控制只 +5/+3 题(McNemar p=0.42/0.68 ⇒ 噪声),**显著的是时限内与进度**,代价是到达题末态误差 0.22→0.42 mm(判据壳 1 mm 内,余量 4.6×→2.4×)。所以它刷新的是"时限内"这一栏,不是"到达"这一栏。
- **实时性(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% 的步是零运动。
@@ -34,8 +35,10 @@
| 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`:首选候选按 1/2、1/4、1/8 并进同一次认证,只在原候选连第 1 步都不安全时启用) | 失败分类 63/109 是"被冻住"、未到组 37% 步为零运动;但 4 题 CPU 冒烟提示转化率只 ~13% | 代码与单测已落,256 题四臂待补跑(判读规则已预注册) |
| 3 | **白费比例惩罚**(`select=ep_waste --len_w`,罚 `1−同轴净/总` × 当前误差) | 换向是**全局**现象(~1/4 的(步,轴)对在反向,到达/未到达两组差不多);净位移只占总行程 0.445 | 已实现+正对照单测(可手算 2/18);三臂排队 |
| 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,16=现 endpoint) | 若时限收益随 h 增大而塌、精度随 h 增大而恢复 ⇒ 形态应是"前瞻长度随剩余距离收缩",而不是二选一 | 待实现(`pre` 已落,只差把 `commit_n` 换成参数) |
| 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 | 未解,是验收硬门槛 |
@@ -109,6 +112,31 @@
- **对 `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)要读的第一个数。
## 轮次 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 上都只是噪声**。诚实的写法是"时限内到达显著变多、总到达未见显著变化",即它把路上的时间买回来了,没有把通路买回来。
- 末态误差逐题: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`)。
+6
View File
@@ -167,6 +167,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
"""use_sub=False:不用子目标(sf=0);sub_override=(subs [B,M,6] 绝对关节角, n [B] 个数 或 need [B] bool):冷启动时用给定途经点代替子目标头;
qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放;
select:consist(部署口径:冷启动价值前瞻、之后与上一条已提交计划的一致性)/ value(每次都价值前瞻)/ first(不选,取第 0 条)。
select='pre':把打分地平线换成「这一轮真会执行到的位置」(fut 的第 commit_n 步);'endpoint' 用的是整条地平线末端。
oracle:TeacherOracle,代替 model 出动作(model 可为 None,须配 select='first'、use_sub=False)。
replan:'r3'(部署:队列 ≤ 3 步就规划,稳态 r=3)/ 'empty'(队列空了才规划,每次 r=0,前缀消融用)。
sub_rule:子目标推进规则,见 sub_advance。除默认值外均为诊断用。
@@ -291,6 +292,11 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
fut = qe[:, None, None] + torch.cumsum(inc[:, :, r:], 2) # [B,K,H-r,6]
if select == 'first':
score = torch.zeros(B, K, device=device); score[:, 0] = 1
elif select == 'pre': # #29:按「这一轮真会执行到的位置」打分,而不是 16 步开环外推的末端。
# 诊断来自 ep_len/ep_waste 的失败:打分地平线远长于提交步数,被安全层截断的候选那个终点根本到不了。
qa_pre = fut[:, :, min(commit_n, fut.shape[2]) - 1].reshape(B * K, 6)
e_pre, _ = goal_err(kin, qa_pre, T.repeat_interleave(K, 0), R.repeat_interleave(K, 0))
score = -e_pre.view(B, K)
elif select in ('endpoint', 'adapt', 'ep_len', 'ep_waste'):
qa = fut[:, :, -1].reshape(B * K, 6)
e_end, _ = goal_err(kin, qa, T.repeat_interleave(K, 0), R.repeat_interleave(K, 0))
+50
View File
@@ -966,5 +966,55 @@ class TieByClearanceTest(unittest.TestCase):
self.assertTrue(all(isinstance(s, int) and isinstance(t, int) for s, t in seen))
class PreHorizonTest(unittest.TestCase):
"""#29:`select='pre'` 按「这一轮真会执行到的位置」打分;`endpoint` 按整条地平线末端打分。
夹具是两条刻意做成「前缀好但末端差」/「末端好但前缀差」的候选,期望值可手算。"""
class StubModel:
act_scale = 1.0
def __init__(self, act):
self.act = act
def sample_k(self, feat, K, z, steps=1):
B = feat.shape[0]
return (self.act.expand(B, K, 16, 6).clone(), torch.zeros(B, device=feat.device),
torch.zeros(B, 13, device=feat.device))
class NearKin:
def clamp_box(self, q):
return q
def gaps(self, q, cap):
return {'gm': torch.ones(q.shape[0], 49)}
def run1(self, select, commit_n=2):
# 候选0:前 2 步各 +0.2(前缀 0.4),随后 14 步 −0.4/14 ⇒ 地平线末端回到 0
# 候选1:16 步各 +0.05 ⇒ 前缀只有 0.1,但地平线末端 0.8
c0 = [0.2, 0.2] + [-(0.4 / 14.0)] * 14
c1 = [0.05] * 16
acts = torch.stack([torch.tensor([[v, 0, 0, 0, 0, 0] for v in c0], dtype=torch.float32),
torch.tensor([[v, 0, 0, 0, 0, 0] for v in c1], dtype=torch.float32)])
self.assertEqual(tuple(acts.shape), (2, 16, 6)) # 夹具自证:第 0 轴才是唯一有内容的轴
err = lambda kin, q, T, R: (((0.9 - q[:, 0]).clamp(min=0.0)) * 1000.0, torch.zeros(q.shape[0]))
with patch('ml.sim.closed_loop.features', return_value=torch.zeros(1, 83)), \
patch('ml.sim.closed_loop.goal_err', side_effect=err):
_, per = rollout(self.StubModel(acts), self.NearKin(), [{'q0': [0] * 6, 'T': [1, 0, 0]}], 'cpu',
K=2, safety=False, max_sec=0.04, return_traj=True, use_sub=False,
select=select, replan='empty', commit_n=commit_n)
return per
def test_pre_and_endpoint_pick_opposite(self):
p_end = self.run1('endpoint')
self.assertAlmostEqual(float(p_end['traj'][0, 1, 0]), 0.05 * 1.2, places=5) # 末端 0.8 ⇒ 选候选1
p_pre = self.run1('pre')
self.assertAlmostEqual(float(p_pre['traj'][0, 1, 0]), 0.2 * 1.2, places=5) # 前缀 0.4 ⇒ 选候选0
self.assertNotAlmostEqual(float(p_end['traj'][0, 1, 0]), float(p_pre['traj'][0, 1, 0]), places=3)
def test_pre_equals_endpoint_when_commit_is_the_whole_horizon(self):
"""不变式:commit_n=16 = 整条地平线 ⇒ pre 的索引落到末端,必须与 endpoint 选同一条。"""
p = self.run1('pre', commit_n=16)
self.assertAlmostEqual(float(p['traj'][0, 1, 0]), 0.05 * 1.2, places=5)
if __name__ == '__main__':
unittest.main()