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:
+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(),
|
||||
|
||||
Reference in New Issue
Block a user