diff --git a/docs/experiment_round36_plan_r_hist.json b/docs/experiment_round36_plan_r_hist.json new file mode 100644 index 0000000..d0cda16 --- /dev/null +++ b/docs/experiment_round36_plan_r_hist.json @@ -0,0 +1,50 @@ +{ + "empty/commit2/inc_cap0": { + "plans": 175, + "r_hist": { + "0": 175 + }, + "frac_r0": 1.0, + "save_ms_per_plan_avg": 3.631, + "success": 0.46875, + "brakes_mean": 24.0625, + "wall_s": 54.74565179999627 + }, + "r3/commit3/inc_cap0": { + "plans": 118, + "r_hist": { + "0": 1, + "2": 1, + "3": 116 + }, + "frac_r0": 0.00847457627118644, + "save_ms_per_plan_avg": 0.030771186440677965, + "success": 0.4375, + "brakes_mean": 18.03125, + "wall_s": 49.8360657000012 + }, + "r3/commit3/inc_cap1": { + "plans": 118, + "r_hist": { + "0": 1, + "2": 1, + "3": 116 + }, + "frac_r0": 0.00847457627118644, + "save_ms_per_plan_avg": 0.030771186440677965, + "success": 0.40625, + "brakes_mean": 16.9375, + "wall_s": 49.97621490000165 + }, + "empty/commit3/inc_cap1": { + "plans": 117, + "r_hist": { + "0": 117 + }, + "frac_r0": 1.0, + "save_ms_per_plan_avg": 3.631, + "success": 0.4375, + "brakes_mean": 15.0625, + "wall_s": 43.831084999997984 + } +} \ No newline at end of file diff --git a/docs/探索日志.md b/docs/探索日志.md index daf4fd2..7400303 100644 --- a/docs/探索日志.md +++ b/docs/探索日志.md @@ -738,6 +738,22 @@ - **循环内还剩的可复用点**:`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)。 +## 轮次 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']` 只增不改)。 + ## 分支清理(2026-10-20) - `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。 diff --git a/ml/sim/closed_loop.py b/ml/sim/closed_loop.py index 1d09af4..af2d0ac 100644 --- a/ml/sim/closed_loop.py +++ b/ml/sim/closed_loop.py @@ -197,7 +197,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, # 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) traj = [q.clone()] if return_traj else None - plans = {k: [] for k in ('it', 'qe', 'commit', 'points', 'midpoints', 'gaps', 'certified')} if return_traj else None + plans = {k: [] for k in ('it', 'r', 'qe', 'commit', 'points', 'midpoints', 'gaps', 'certified')} if return_traj else None n_start = int(round(0.5 / DT)); moved_start = None # R2 起步失败:0.5 s 内关节总行程 < 1° k2s = int(round(2.0 / DT)); e2 = None e0, _ = goal_err(kin, q0, T, R) @@ -422,6 +422,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, last = torch.cat([qe[:, None], fut[ar, pick]], 1); last_ok = committed; last_brake = braked if return_traj: plans['it'].append(it + r) + plans['r'].append(r) for key, value in (('qe', qe), ('commit', commit), ('points', cert_pts), ('midpoints', cert_mids), ('gaps', cert_gaps), ('certified', certified)): plans[key].append(value) queue = torch.cat([queue, commit], 1) @@ -476,7 +477,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, 'goal_ratio_2s': ratio2.cpu(), 'speed_ratio': vratio.cpu(), 'accel_ratio': aratio.cpu()} if return_traj: per['traj'] = torch.stack(traj, 1).cpu() - per['plans'] = {k: torch.tensor(v) if k == 'it' else torch.stack(v, 1).cpu() for k, v in plans.items()} + per['plans'] = {k: torch.tensor(v) if k in ('it', 'r') else torch.stack(v, 1).cpu() for k, v in plans.items()} return res, per