exp: 轮次45——tie(等长安全前缀按沿路最小间隙定夺)判掉
两臂 n=256 同 seed 背靠背(配方=轮次42 K8+cert_batch=1+inc_cap=1):到达 0.5234→0.5039 (-1.95pp,逐题配对丢6赢1净-5,McNemar 精确 p=0.125 与零不可分),时限内 +1.17pp、 刹停 -9%、行程比 -0.076、执行碰撞仍为 0(判据未放松)。丢的 6 题 5 题在 A 类(开阔区) =买余量卖进度的签名。 仪表与离线数交叉验证:改选率 5.58% 对上轮次44 独立测得的"执行点间隙落在 0.2–0.5mm 占 5.6%",并列率 79.0% 对上"满前缀 79.4%" ⇒ 分辨率上界被宽相位截顶 gcap=max(0.5,margin+0.5) 锁死,5.6% 是这个家族在 cap=0.5 下的天花板,不是实现没调好。 结构性发现:#24 作用在错的人群——tie 只在 maxlen>0 触发,而失败主人群是全拒(maxlen=0, 未到达题 22% 的规划);全拒那条已由 backoff(轮次43)判掉。前置条件记为 #28(gcap 与 gap_margin 解耦)。 实现账:新增 tie 与触发计数器(生产端 per 数组+dump 键、消费端汇总 tie_* 同批改完, tie=0 时 22/22 老键逐位复现);修掉 tie 分支局部名 nmax 顶掉主循环总步数的遮蔽 bug, 守卫单测做过变异检查;计数器正对照可手算(tie_opp=tie_changed=n_plans)。47 项测试绿。
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"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/round42_K8cb1_rows.json",
|
||||
"out": "docs/experiment_round42_K8cb1_dump.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,
|
||||
"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.5234375,
|
||||
"collision_free_success": 0.5234375,
|
||||
"time_ok_1p3": 0.265625,
|
||||
"collision_rate": 0.0,
|
||||
"collision_steps_mean": 0.0,
|
||||
"travel_ratio_median": 2.615795850753784,
|
||||
"time_mean_s": 2.486567258834839,
|
||||
"brakes_mean": 19.5546875,
|
||||
"start_fail": 0.0,
|
||||
"goal_ratio_2s": 0.27920001745224,
|
||||
"err_fail_med": 204.71360778808594,
|
||||
"err_success_med": 0.21751396358013153,
|
||||
"dls_share": 0.61328125,
|
||||
"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.24741201102733612,
|
||||
"reverse_mean": 0.25991496443748474,
|
||||
"reverse_moving_p50": 0.32581019401550293,
|
||||
"moving_share_p50": 0.9587156176567078,
|
||||
"brake_rate_mean": 0.10028044879436493,
|
||||
"brake_rate_p50": 0.0051282052882015705,
|
||||
"brake_rate_p90": 0.3153846263885498,
|
||||
"stuck_share": 0.234375,
|
||||
"n_plans_mean": 195.0,
|
||||
"success_reachable": 0.5514403581619263,
|
||||
"by_cat": {
|
||||
"A": [
|
||||
128,
|
||||
96
|
||||
],
|
||||
"B": [
|
||||
74,
|
||||
20
|
||||
],
|
||||
"C": [
|
||||
32,
|
||||
11
|
||||
],
|
||||
"D": [
|
||||
13,
|
||||
0
|
||||
],
|
||||
"Dr": [
|
||||
9,
|
||||
7
|
||||
]
|
||||
}
|
||||
},
|
||||
"simulation_wall_s": 397.44336470001144
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"config": {
|
||||
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
|
||||
"samples": "ml/data/samples",
|
||||
"tasks": null,
|
||||
"split": "val",
|
||||
"n": 64,
|
||||
"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/round44_tieprobe64_rows.json",
|
||||
"out": "docs/experiment_round44_tieprobe64.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": 1,
|
||||
"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": "6185a312dbae3bf5b361c4b9ff3aa11b7329d9723cff2be9b9157563d6b6ec1c",
|
||||
"kin_fp": "2ebcbf17",
|
||||
"act_scale": 1.0,
|
||||
"metrics": {
|
||||
"n": 64,
|
||||
"n_ref": 64,
|
||||
"success": 0.40625,
|
||||
"collision_free_success": 0.40625,
|
||||
"time_ok_1p3": 0.234375,
|
||||
"collision_rate": 0.0,
|
||||
"collision_steps_mean": 0.0,
|
||||
"travel_ratio_median": 2.358931303024292,
|
||||
"time_mean_s": 2.3323075771331787,
|
||||
"brakes_mean": 18.71875,
|
||||
"start_fail": 0.0,
|
||||
"goal_ratio_2s": 0.33309799432754517,
|
||||
"err_fail_med": 229.02410888671875,
|
||||
"err_success_med": 0.22094391286373138,
|
||||
"dls_share": 0.515625,
|
||||
"time_budget": "teacher×2(≥3 s)",
|
||||
"speed_ratio_p50": 1.0000016689300537,
|
||||
"speed_ratio_p95": 1.0000091791152954,
|
||||
"speed_ratio_max": 1.0000101327896118,
|
||||
"over_vmax_rate": 0.0,
|
||||
"accel_ratio_p95": 1.0000016689300537,
|
||||
"accel_ratio_max": 1.0000101327896118,
|
||||
"reverse_p50": 0.25150150060653687,
|
||||
"reverse_mean": 0.25861242413520813,
|
||||
"reverse_moving_p50": 0.318452388048172,
|
||||
"moving_share_p50": 0.9090909361839294,
|
||||
"brake_rate_mean": 0.10696429014205933,
|
||||
"brake_rate_p50": 0.017142856493592262,
|
||||
"brake_rate_p90": 0.33142855763435364,
|
||||
"stuck_share": 0.21875,
|
||||
"n_plans_mean": 175.0,
|
||||
"success_reachable": 0.4262295067310333,
|
||||
"by_cat": {
|
||||
"A": [
|
||||
28,
|
||||
14
|
||||
],
|
||||
"B": [
|
||||
21,
|
||||
6
|
||||
],
|
||||
"C": [
|
||||
9,
|
||||
4
|
||||
],
|
||||
"D": [
|
||||
3,
|
||||
0
|
||||
],
|
||||
"Dr": [
|
||||
3,
|
||||
2
|
||||
]
|
||||
},
|
||||
"tie_opp_rate_mean": 0.7484821081161499,
|
||||
"tie_changed_rate_mean": 0.07437500357627869,
|
||||
"tie_opp_task_share": 1.0
|
||||
},
|
||||
"simulation_wall_s": 78.19308080000337
|
||||
}
|
||||
@@ -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/round45_tie0_rows.json",
|
||||
"out": "docs/experiment_round45_tie0.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": "249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772",
|
||||
"kin_fp": "2ebcbf17",
|
||||
"act_scale": 1.0,
|
||||
"metrics": {
|
||||
"n": 256,
|
||||
"n_ref": 256,
|
||||
"success": 0.5234375,
|
||||
"collision_free_success": 0.5234375,
|
||||
"time_ok_1p3": 0.265625,
|
||||
"collision_rate": 0.0,
|
||||
"collision_steps_mean": 0.0,
|
||||
"travel_ratio_median": 2.615795850753784,
|
||||
"time_mean_s": 2.486567258834839,
|
||||
"brakes_mean": 19.5546875,
|
||||
"start_fail": 0.0,
|
||||
"goal_ratio_2s": 0.27920001745224,
|
||||
"err_fail_med": 204.71360778808594,
|
||||
"err_success_med": 0.21751396358013153,
|
||||
"dls_share": 0.61328125,
|
||||
"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.24741201102733612,
|
||||
"reverse_mean": 0.25991496443748474,
|
||||
"reverse_moving_p50": 0.32581019401550293,
|
||||
"moving_share_p50": 0.9587156176567078,
|
||||
"brake_rate_mean": 0.10028044879436493,
|
||||
"brake_rate_p50": 0.0051282052882015705,
|
||||
"brake_rate_p90": 0.3153846263885498,
|
||||
"stuck_share": 0.234375,
|
||||
"n_plans_mean": 195.0,
|
||||
"success_reachable": 0.5514403581619263,
|
||||
"by_cat": {
|
||||
"A": [
|
||||
128,
|
||||
96
|
||||
],
|
||||
"B": [
|
||||
74,
|
||||
20
|
||||
],
|
||||
"C": [
|
||||
32,
|
||||
11
|
||||
],
|
||||
"D": [
|
||||
13,
|
||||
0
|
||||
],
|
||||
"Dr": [
|
||||
9,
|
||||
7
|
||||
]
|
||||
}
|
||||
},
|
||||
"simulation_wall_s": 317.56223549999413
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"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/round45_tie1_rows.json",
|
||||
"out": "docs/experiment_round45_tie1.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": 1,
|
||||
"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.50390625,
|
||||
"collision_free_success": 0.50390625,
|
||||
"time_ok_1p3": 0.27734375,
|
||||
"collision_rate": 0.0,
|
||||
"collision_steps_mean": 0.0,
|
||||
"travel_ratio_median": 2.53989315032959,
|
||||
"time_mean_s": 2.3586044311523438,
|
||||
"brakes_mean": 17.7890625,
|
||||
"start_fail": 0.0,
|
||||
"goal_ratio_2s": 0.27920001745224,
|
||||
"err_fail_med": 213.06358337402344,
|
||||
"err_success_med": 0.22968679666519165,
|
||||
"dls_share": 0.62109375,
|
||||
"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.25315314531326294,
|
||||
"reverse_mean": 0.26444095373153687,
|
||||
"reverse_moving_p50": 0.3198198080062866,
|
||||
"moving_share_p50": 0.9772727489471436,
|
||||
"brake_rate_mean": 0.09122596681118011,
|
||||
"brake_rate_p50": 0.0,
|
||||
"brake_rate_p90": 0.3282051384449005,
|
||||
"stuck_share": 0.203125,
|
||||
"n_plans_mean": 195.0,
|
||||
"success_reachable": 0.5308641791343689,
|
||||
"by_cat": {
|
||||
"A": [
|
||||
128,
|
||||
91
|
||||
],
|
||||
"B": [
|
||||
74,
|
||||
19
|
||||
],
|
||||
"C": [
|
||||
32,
|
||||
12
|
||||
],
|
||||
"D": [
|
||||
13,
|
||||
0
|
||||
],
|
||||
"Dr": [
|
||||
9,
|
||||
7
|
||||
]
|
||||
},
|
||||
"tie_opp_rate_mean": 0.7898838520050049,
|
||||
"tie_changed_rate_mean": 0.055829327553510666,
|
||||
"tie_opp_task_share": 1.0
|
||||
},
|
||||
"simulation_wall_s": 283.46215640001174
|
||||
}
|
||||
+25
-1
@@ -31,7 +31,8 @@
|
||||
|
||||
| 序 | 候选 | 支撑证据 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 0 | **把"沿路最小间隙"接进候选打分**(#24) | 安全层的拒绝是真实几何(端点/中点穿模),而 `cert_batch=1` 已经在同一次 gaps 调用里算了 K 条候选各 `2·commit_n` 个点的间隙——**这些信息目前只用来判通过/拒绝,没进打分**。做法:`score = −e_end − w·relu(gap_ref − min_gap_path)`,让"会穿模"的候选在排序阶段就出局,而不是等安全层全拒。代价为零(同一批数),判据完全不放松 | 待实现(链跑完后动库) |
|
||||
| 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);三臂排队 |
|
||||
@@ -108,6 +109,29 @@
|
||||
- **对 `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)要读的第一个数。
|
||||
|
||||
## 轮次 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)逐题对齐后按类别:
|
||||
|
||||
+8
-2
@@ -68,6 +68,10 @@ def run(model, kin, tasks, device, bs=256, **kw):
|
||||
if 'backoff' in flat and flat['backoff'].any(): # backoff=缩步顶上的次数;brakes 现在只记连最小档都不安全的
|
||||
out['backoff_mean'] = flat['backoff'].mean().item() # ⇒ 原来的"全拒"事件数 = 两者之和
|
||||
out['reject_all_mean'] = (flat['backoff'] + flat['brakes']).mean().item()
|
||||
if 'tie_opp' in flat and flat['tie_opp'].any(): # tie=1 的自检:钩子可用率/真的改选率,分母与 brake_rate 同为规划次数
|
||||
out.update({'tie_opp_rate_mean': flat['tie_opp_rate'].mean().item(),
|
||||
'tie_changed_rate_mean': flat['tie_changed_rate'].mean().item(),
|
||||
'tie_opp_task_share': (flat['tie_opp'] > 0).float().mean().item()})
|
||||
return out, per
|
||||
|
||||
|
||||
@@ -115,6 +119,7 @@ def main():
|
||||
ap.add_argument('--steps', type=int, default=1, help='MeanFlow 采样段数;1=线上一口径(历史发布数字全是 1)')
|
||||
ap.add_argument('--z_scale', type=float, default=1.0, help='候选噪声缩放:>1 让 K 条候选更分散(测"全拒是没有备选"还是"真的没路")')
|
||||
ap.add_argument('--len_w', type=float, default=0.0, help="ep_len=罚累计归一行程;ep_waste=罚(1−净/总)×当前误差;0 与 endpoint 逐位相同")
|
||||
ap.add_argument('--tie', type=int, default=0, help='1=等长安全前缀时按沿路最小间隙定夺(需 --cert_batch 1)')
|
||||
ap.add_argument('--backoff', type=int, default=0, help='全拒时把首选候选按 1/2、1/4…缩步重试的档数(需 --cert_batch 1)')
|
||||
ap.add_argument('--max_sec', type=float, default=None)
|
||||
ap.add_argument('--seed', type=int, default=0)
|
||||
@@ -142,7 +147,7 @@ def main():
|
||||
safety_partial=(a.commit if (a.safety and a.partial) else 0), commit_n=a.commit,
|
||||
select=a.select, replan=a.replan, fb_gain=a.fb, fb_scale=a.fb_scale, fb_near=a.fb_near, fb_fallback=a.fb_fallback, fb_ki=a.ki, fb_decay=a.kdecay, fb_imax=a.kimax,
|
||||
fb_every=a.fb_every, safety_try=a.safety_try, sel_w=a.sel_w, dls_safety=a.dls_safety, fb_clear=a.fb_clear, fb_cmin=a.fb_cmin, sub_refresh=a.sub_refresh, fb_block_gain=a.fb_block_gain,
|
||||
inc_cap=a.inc_cap, gap_margin=a.gap_margin, cert_batch=a.cert_batch, backoff=a.backoff, len_w=a.len_w, return_traj=bool(a.trace))
|
||||
inc_cap=a.inc_cap, gap_margin=a.gap_margin, cert_batch=a.cert_batch, backoff=a.backoff, len_w=a.len_w, tie=a.tie, return_traj=bool(a.trace))
|
||||
wall = time.perf_counter() - t0
|
||||
result = {**meta, 'metrics': r, 'simulation_wall_s': wall}
|
||||
print(json.dumps(json_finite(result), ensure_ascii=False, allow_nan=False), flush=True)
|
||||
@@ -150,7 +155,8 @@ def main():
|
||||
Path(a.out).write_text(json.dumps(json_finite(result), indent=2, ensure_ascii=False, allow_nan=False) + '\n', encoding='utf-8')
|
||||
if a.dump:
|
||||
rows = []
|
||||
keys = ['done', 'ok', 't_done', 'limit', 'col', 'brakes', 'err', 'first_hit', 'dls_it', 'goal_ratio_2s', 'brake_rate', 'n_plans']
|
||||
keys = ['done', 'ok', 't_done', 'limit', 'col', 'brakes', 'err', 'first_hit', 'dls_it', 'goal_ratio_2s', 'brake_rate', 'n_plans',
|
||||
'tie_opp', 'tie_changed', 'tie_opp_rate', 'tie_changed_rate']
|
||||
flat = {k: torch.cat([p[k] for _, p, _ in pers]).tolist() for k in keys}
|
||||
for i, t in enumerate(tasks):
|
||||
rows.append({**t, 'i': i, **{k: flat[k][i] for k in keys}})
|
||||
|
||||
+17
-2
@@ -163,7 +163,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
qp0=None, steps=1, z_scale=1.0, select='consist', oracle=None, replan='r3', sub_rule='cross',
|
||||
fb_gain=0.0, fb_scale=0.0, fb_near=0.2, fb_fallback=0.0, safety_partial=0, commit_n=3, collect=None,
|
||||
fb_exec=0.0, exec_safety=0, fb_ki=0.0, fb_decay=0.9, fb_imax=0.5, fb_every=1, safety_try=0, sel_w=0.1, dls_safety=0, fb_clear=0.0, fb_cmin=0.3, sub_refresh=0, fb_block_gain=1.0,
|
||||
inc_cap=0, gap_margin=0.0, cert_batch=0, backoff=0, len_w=0.0, progress=None):
|
||||
inc_cap=0, gap_margin=0.0, cert_batch=0, backoff=0, len_w=0.0, tie=0, progress=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 条)。
|
||||
@@ -174,10 +174,11 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
gap_margin>0:安全层要求间隙大于该毫米余量而不是 >0;宽相位 cap 自动抬到 margin+0.5,否则 0.5 的截顶值会让余量门限恒假。
|
||||
backoff>0:全拒(连第 1 步都不安全)时不再站住,把首选候选按 1/2、1/4…(共 backoff 档)缩步一起送进同一次认证,
|
||||
取能通过的第 1 档执行;缩步候选不参与"前缀更长者优先"的比较,只在原候选全被拒时顶上。需 cert_batch=1。
|
||||
tie=1:等长安全前缀之间改用「沿路最小间隙更大」定夺(默认 0=沿用选择器分数顺序)。需 cert_batch=1。
|
||||
select='ep_len' + len_w>0:在末态误差打分上再减 len_w·Σ|Δ|/STEP(每轴归一的候选累计行程)。§9.2 要行程≤规划器×1.10,
|
||||
而实测行程比 2.4~2.6 没有任何机制在管;len_w=0 与 select='endpoint' 逐位相同(有回归断言)。
|
||||
两者默认值下行为与第32轮逐位相同。"""
|
||||
if backoff and not cert_batch: raise ValueError('backoff 目前只实现在 cert_batch=1 口径')
|
||||
if (backoff or tie) and not cert_batch: raise ValueError('backoff/tie 目前只实现在 cert_batch=1 口径')
|
||||
g = torch.Generator(device=device); g.manual_seed(seed)
|
||||
q0, T, R, ex, tr = tasks_to_tensors(tasks, device)
|
||||
B = q0.shape[0]
|
||||
@@ -199,6 +200,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
done = torch.zeros(B, dtype=torch.bool, device=device); t_done = torch.full((B,), float('nan'), device=device)
|
||||
col = torch.zeros(B, device=device); travel = torch.zeros(B, device=device); brakes = torch.zeros(B, device=device)
|
||||
bo = torch.zeros(B, device=device) # 缩步顶上的次数(backoff>0 才会计)
|
||||
to = torch.zeros(B, device=device) # tie=1 才计:出现「等长安全前缀并列」的规划次数(钩子可用)
|
||||
tx = torch.zeros(B, device=device) # tie=1 才计:并列里真的由余量改选了那条的次数(钩子有效)
|
||||
first_hit = torch.full((B,), float('nan'), device=device); dls_it = torch.full((B,), -1, dtype=torch.long, device=device)
|
||||
# 2026-10-20(轮次33):速度/加速度按「实际执行位移」算,不看指令——第30轮证明反馈是在限幅之后叠加的
|
||||
vratio = torch.zeros(B, device=device); aratio = torch.zeros(B, device=device); prev_exec = torch.zeros(B, 6, device=device)
|
||||
@@ -363,6 +366,15 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
plen = pl_all[:, :ntry]
|
||||
maxlen = plen.max(-1).values
|
||||
best = plen.argmax(-1) # 与串行同口径:严格更大者胜 ⇒ 首个最大值
|
||||
if tie: # 等长前缀时不再只看选择器分数:取"沿路最小间隙更大"的那条(零额外几何调用,判据仍是 gap>0)
|
||||
ci = torch.arange(2 * commit_n, device=device)
|
||||
keep = (ci < maxlen[:, None]) | ((ci >= commit_n) & (ci < commit_n + maxlen[:, None])) # [B,2cn] 只看会被执行的那些点
|
||||
clr = gaps6[:, :ntry].where(keep[:, None, :], torch.full((), float('inf'), device=device)).amin(-1)
|
||||
cand = torch.where(plen == maxlen[:, None], clr, torch.full_like(clr, -float('inf'))).argmax(-1)
|
||||
ntile = (plen == maxlen[:, None]).sum(-1) # 并列最长的候选条数(勿用 nmax:那是主循环的总步数)
|
||||
to += (ntile > 1) & (maxlen > 0)
|
||||
tx += (cand != best)
|
||||
best = cand
|
||||
if backoff: # 缩步候选只在全拒时顶上:不参与「前缀更长者优先」的比较,所以正常路径逐位不变
|
||||
full0 = maxlen == 0
|
||||
bb = pl_all[:, ntry:].argmax(-1) # 平手取缩得最少的(档 0 = 1/2)
|
||||
@@ -516,6 +528,9 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
per = {'done': done.cpu(), 'ok': ok.cpu(), 't_done': t_done.cpu(), 'limit': limit.cpu(), 'col': col.cpu(), 'travel': travel.cpu(), 'err': e_end.cpu(),
|
||||
'side': side.cpu(), 'start_fail': start_fail.cpu(), 'brakes': brakes.cpu(), 'backoff': bo.cpu(), 'first_hit': first_hit.cpu(), 'dls_it': dls_it.cpu(),
|
||||
'n_plans': nplan.cpu(), 'brake_rate': (brakes / nplan.clamp_min(1.0)).cpu(),
|
||||
'tie_opp': to.cpu(), 'tie_changed': tx.cpu(), # tie=1 才有意义:并列次数/改选次数(每题)
|
||||
'tie_opp_rate': (to / nplan.clamp_min(1.0)).cpu(), # 分母与 brake_rate 同口径 = 规划次数
|
||||
'tie_changed_rate': (tx / nplan.clamp_min(1.0)).cpu(),
|
||||
'goal_ratio_2s': ratio2.cpu(), 'speed_ratio': vratio.cpu(), 'accel_ratio': aratio.cpu(),
|
||||
# 口径与离线分解一致:换向的 (步,轴) 对数 / 活动的 (步,轴) 对数 ∈ [0,1](除以 6 个轴)
|
||||
'reverse_rate': (rev / (6.0 * nact.clamp_min(1.0))).cpu(),
|
||||
|
||||
@@ -889,5 +889,82 @@ class TravelPenaltySelectTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(float(per['moving_share'][0]), 2.0 / 3.0, places=5)
|
||||
|
||||
|
||||
class TieByClearanceTest(unittest.TestCase):
|
||||
"""#24:两条候选的安全前缀一样长时,现在按"沿路最小间隙更大"定夺(零额外几何、判据不变)。"""
|
||||
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 ClearKin:
|
||||
"""间隙 = 0.4 − 0.2·q[0](恒为正 ⇒ 两条候选都全安全;走得越远间隙越小)。全部低于 0.5 的宽相位截顶值,
|
||||
否则一切都被 clamp 成 0.5 就分不出高低了。"""
|
||||
def clamp_box(self, q):
|
||||
return q
|
||||
|
||||
def gaps(self, q, cap):
|
||||
gm = (0.4 - 0.2 * q[:, 0]).clamp(min=0.0)
|
||||
return {'gm': torch.stack([gm] * 49, 1)}
|
||||
|
||||
def run1(self, tie, cert_batch=1):
|
||||
big = [[0.2, 0, 0, 0, 0, 0]] * 16 # 走得远:间隙小,但分数并列第一
|
||||
small = [[0.1, 0, 0, 0, 0, 0]] * 16 # 走得近:间隙大
|
||||
acts = torch.tensor([big, small], dtype=torch.float32)
|
||||
with patch('ml.sim.closed_loop.features', return_value=torch.zeros(1, 83)), \
|
||||
patch('ml.sim.closed_loop.goal_err',
|
||||
side_effect=lambda kin, q, T, R: (torch.full((q.shape[0],), 10.0), torch.zeros(q.shape[0]))):
|
||||
_, per = rollout(self.StubModel(acts), self.ClearKin(), [{'q0': [0] * 6, 'T': [10, 0, 0]}], 'cpu',
|
||||
K=2, safety=True, max_sec=0.04, return_traj=True, use_sub=False,
|
||||
select='endpoint', replan='empty', commit_n=2, safety_partial=2,
|
||||
cert_batch=cert_batch, tie=tie)
|
||||
return per
|
||||
|
||||
def test_tie_prefers_the_wider_clearance(self):
|
||||
p0 = self.run1(0) # 分数并列 ⇒ 稳定排序取第 0 条(走得远、余量小)
|
||||
self.assertAlmostEqual(float(p0['traj'][0, 1, 0]), 0.2 * 1.2, places=5)
|
||||
p1 = self.run1(1) # 改由余量定夺 ⇒ 取间隙更大的那条(0.1×STEP=0.12°)
|
||||
self.assertAlmostEqual(float(p1['traj'][0, 1, 0]), 0.1 * 1.2, places=5)
|
||||
torch.testing.assert_close(p0['brakes'], p1['brakes'], rtol=0, atol=0) # 安全性判据没动
|
||||
torch.testing.assert_close(p0['plans']['certified'], p1['plans']['certified'], rtol=0, atol=0)
|
||||
|
||||
def test_tie_requires_batched_certification(self):
|
||||
with self.assertRaisesRegex(ValueError, 'cert_batch'):
|
||||
self.run1(1, cert_batch=0)
|
||||
|
||||
def test_tie_counters_are_exact(self):
|
||||
"""钩子自检的期望值可手算:夹具里两条候选永远全安全 ⇒ 每次规划都是并列,且每次都改选 ⇒
|
||||
tie_opp = tie_changed = n_plans,两个比率恒 1。tie=0 时两个计数恒 0。"""
|
||||
p0 = self.run1(0)
|
||||
self.assertGreater(float(p0['n_plans'][0]), 0) # 夹具得真的跑过规划,否则下面的相等是 0==0 的假通过
|
||||
torch.testing.assert_close(p0['tie_opp'], torch.zeros_like(p0['tie_opp']), rtol=0, atol=0)
|
||||
torch.testing.assert_close(p0['tie_changed'], torch.zeros_like(p0['tie_changed']), rtol=0, atol=0)
|
||||
p1 = self.run1(1)
|
||||
torch.testing.assert_close(p1['tie_opp'], p1['n_plans'], rtol=0, atol=0)
|
||||
torch.testing.assert_close(p1['tie_changed'], p1['n_plans'], rtol=0, atol=0)
|
||||
self.assertAlmostEqual(float(p1['tie_opp_rate'][0]), 1.0, places=6)
|
||||
self.assertAlmostEqual(float(p1['tie_changed_rate'][0]), 1.0, places=6)
|
||||
|
||||
def test_tie_block_does_not_shadow_loop_state(self):
|
||||
"""tie 分支里新增的局部名一旦顶掉主循环状态(真实踩过 nmax=总步数被改成张量),
|
||||
进度回调会立刻炸;所以 tie=1 必须带 progress 跑通,且 total 保持标量。"""
|
||||
seen = []
|
||||
acts = torch.tensor([[[0.2, 0, 0, 0, 0, 0]] * 16, [[0.1, 0, 0, 0, 0, 0]] * 16], dtype=torch.float32)
|
||||
with patch('ml.sim.closed_loop.features', return_value=torch.zeros(1, 83)), \
|
||||
patch('ml.sim.closed_loop.goal_err',
|
||||
side_effect=lambda kin, q, T, R: (torch.full((q.shape[0],), 10.0), torch.zeros(q.shape[0]))):
|
||||
rollout(self.StubModel(acts), self.ClearKin(), [{'q0': [0] * 6, 'T': [10, 0, 0]}], 'cpu',
|
||||
K=2, safety=True, max_sec=0.04, use_sub=False, select='endpoint', replan='empty',
|
||||
commit_n=2, safety_partial=2, cert_batch=1, tie=1,
|
||||
progress=lambda step, total: seen.append((step, total)))
|
||||
self.assertTrue(seen)
|
||||
self.assertTrue(all(isinstance(s, int) and isinstance(t, int) for s, t in seen))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user