exp: 轮次33(上)——实际执行速度做成一等指标并自证;默认路径逐位复现第30轮,256题全部超VMAX
- rollout 逐题记录 max|Δq|/STEP(1.0=VMAX)与每步速度变化比;eval_cl 汇总 p50/p95/max、over_vmax_rate - 自证:用 round30_after trace 独立重算得 1.660004×VMAX、超速度占比 1.0,与新指标逐位一致 - 默认参数下 base 复现第30轮 after 的每个旧指标(0.57421875/0.390625/19.25/2.444952726/0.209975064 等) - 新开关 inc_cap(反馈叠加后再按每轴 VMAX·DT 限幅)、gap_margin(余量门限+自动抬宽相位 cap, 否则现 cap=0.5 会让 >0.5 的余量恒假);col 仍按 gap<=0 判真实碰撞,不与余量混用 - 只读核查:ik-policy.js:355 同样在 ±1·STEP 之后叠加反馈;JS 的 5 mm 余量是投影式护栏,非同机制 - 口径提醒:round30 plans['gaps'] 在 cap=0.5 截顶,不能据此算"≥1mm 占比";中位最小间隙 0.046 mm 可用 - 测试 26→30 项(限幅灵敏度、余量拒/受、cap 抬升生效)
This commit is contained in:
+8
-1
@@ -44,6 +44,11 @@ def run(model, kin, tasks, device, bs=256, **kw):
|
||||
'time_budget': ('fixed %.3f s' % kw['max_sec']) if kw.get('max_sec') is not None else
|
||||
(per[0][0]['time_budget'] if len({r['time_budget'] for r, _, _ in per}) == 1 else 'mixed'),
|
||||
}
|
||||
if 'speed_ratio' in flat: # 实际执行速度:每题时限内 max|Δq|/STEP(1.0 = VMAX),加速度同口径按每控制步的速度变化
|
||||
v, acc = flat['speed_ratio'], flat['accel_ratio']
|
||||
out.update({'speed_ratio_p50': median(v), 'speed_ratio_p95': v.quantile(0.95).item(),
|
||||
'speed_ratio_max': v.max().item(), 'over_vmax_rate': (v > 1.001).float().mean().item(),
|
||||
'accel_ratio_p95': acc.quantile(0.95).item(), 'accel_ratio_max': acc.max().item()})
|
||||
return out, per
|
||||
|
||||
|
||||
@@ -85,6 +90,8 @@ def main():
|
||||
ap.add_argument('--replan', default='r3')
|
||||
ap.add_argument('--safety', type=int, default=1, help='1=安全层+部分前缀;0=关')
|
||||
ap.add_argument('--partial', type=int, default=1)
|
||||
ap.add_argument('--inc_cap', type=int, default=0, help='1=反馈叠加后再按每轴 VMAX·DT 限幅')
|
||||
ap.add_argument('--gap_margin', type=float, default=0.0, help='安全层要求的间隙余量 mm(>0 时宽相位 cap 自动抬到 margin+0.5)')
|
||||
ap.add_argument('--max_sec', type=float, default=None)
|
||||
ap.add_argument('--seed', type=int, default=0)
|
||||
ap.add_argument('--dls', default='analytic', choices=['analytic', 'fd'])
|
||||
@@ -111,7 +118,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,
|
||||
return_traj=bool(a.trace))
|
||||
inc_cap=a.inc_cap, gap_margin=a.gap_margin, 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)
|
||||
|
||||
+25
-11
@@ -163,17 +163,21 @@ 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,
|
||||
progress=None):
|
||||
inc_cap=0, gap_margin=0.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 条)。
|
||||
oracle:TeacherOracle,代替 model 出动作(model 可为 None,须配 select='first'、use_sub=False)。
|
||||
replan:'r3'(部署:队列 ≤ 3 步就规划,稳态 r=3)/ 'empty'(队列空了才规划,每次 r=0,前缀消融用)。
|
||||
sub_rule:子目标推进规则,见 sub_advance。除默认值外均为诊断用。"""
|
||||
sub_rule:子目标推进规则,见 sub_advance。除默认值外均为诊断用。
|
||||
inc_cap=1:把「策略动作 + 目标反馈」的合计增量再按每轴 VMAX·DT 限一次幅(默认关;关时反馈是在 ±1 限幅之后叠加的,第30轮实测最大 1.66×VMAX)。
|
||||
gap_margin>0:安全层要求间隙大于该毫米余量而不是 >0;宽相位 cap 自动抬到 margin+0.5,否则 0.5 的截顶值会让余量门限恒假。
|
||||
两者默认值下行为与第32轮逐位相同。"""
|
||||
g = torch.Generator(device=device); g.manual_seed(seed)
|
||||
q0, T, R, ex, tr = tasks_to_tensors(tasks, device)
|
||||
B = q0.shape[0]
|
||||
step = torch.tensor(STEP, device=device)
|
||||
gcap = max(0.5, gap_margin + 0.5) # 余量门限必须落在截顶值之内才有意义
|
||||
act_scale = oracle.act_scale if oracle is not None else float(getattr(model, 'act_scale', 1.0))
|
||||
astep = step * act_scale # 动作反归一化:每单位 act 对应的度数
|
||||
has_ref = torch.isfinite(ex)
|
||||
@@ -190,6 +194,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)
|
||||
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)
|
||||
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
|
||||
n_start = int(round(0.5 / DT)); moved_start = None # R2 起步失败:0.5 s 内关节总行程 < 1°
|
||||
@@ -254,6 +260,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
if fb_block_gain != 1.0: # 上一次被安全层全拒:减弱目标反馈,让模型自己的绕行候选有机会通过认证
|
||||
gv = torch.where(last_brake, gv * fb_block_gain, gv)
|
||||
inc = inc + gv[:, None, None, None] * fb[:, :, None, :]
|
||||
if inc_cap:
|
||||
inc = torch.maximum(torch.minimum(inc, step), -step) # 反馈之后才限幅:限的是真正要执行的每轴增量
|
||||
if cold:
|
||||
need = sub[:, 12] > 0.5
|
||||
subs = q[:, None] + sub[:, :12].float().view(B, 2, 6) * 180
|
||||
@@ -325,8 +333,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
ck = inc[ar, order[:, kk], r:r + commit_n]
|
||||
pts, mids = execution_points(kin, qe, ck)
|
||||
allq = torch.cat([pts, mids], 1).reshape(-1, 6)
|
||||
gaps6 = kin.gaps(allq, 0.5)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
ok6 = gaps6 > 0
|
||||
gaps6 = kin.gaps(allq, gcap)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
ok6 = gaps6 > gap_margin
|
||||
okstep = ok6[:, :commit_n] & ok6[:, commit_n:]
|
||||
plen = torch.cumprod(okstep.long(), 1).sum(1)
|
||||
take = plen > maxlen
|
||||
@@ -350,8 +358,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
ck = inc[ar, order[:, kk], r:r + commit_n]
|
||||
pts, mids = execution_points(kin, qe, ck)
|
||||
allq = torch.cat([pts, mids], 1).reshape(-1, 6)
|
||||
gaps6 = kin.gaps(allq, 0.5)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
okk = (gaps6 > 0).all(-1)
|
||||
gaps6 = kin.gaps(allq, gcap)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
okk = (gaps6 > gap_margin).all(-1)
|
||||
take = okk & ~ok_any
|
||||
chosen = torch.where(take[:, None, None], ck, chosen); chosen_k = torch.where(take, order[:, kk], chosen_k); ok_any |= okk
|
||||
if return_traj:
|
||||
@@ -367,8 +375,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
fbcom[:, :1] = dls_steps(kin, qe, T, R, n=1) * fb_fallback
|
||||
pts, mids = execution_points(kin, qe, fbcom)
|
||||
allq = torch.cat([pts, mids], 1).reshape(-1, 6)
|
||||
gaps6 = kin.gaps(allq, 0.5)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
takef = braked & (gaps6 > 0).all(-1)
|
||||
gaps6 = kin.gaps(allq, gcap)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
takef = braked & (gaps6 > gap_margin).all(-1)
|
||||
commit = torch.where(takef[:, None, None], fbcom, commit)
|
||||
if return_traj:
|
||||
cert_pts = torch.where(takef[:, None, None], pts, cert_pts)
|
||||
@@ -381,8 +389,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
if dls_safety and safety: # 2026-10-20:近目标 DLS 接管也要过安全层(部署端 _certCommit 本来就会验;不过则退回候选计划)
|
||||
pts, mids = execution_points(kin, qe, ref)
|
||||
allq = torch.cat([pts, mids], 1).reshape(-1, 6)
|
||||
gaps6 = kin.gaps(allq, 0.5)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
okd = (gaps6 > 0).all(-1)
|
||||
gaps6 = kin.gaps(allq, gcap)['gm'].min(-1).values.view(B, 2 * commit_n)
|
||||
okd = (gaps6 > gap_margin).all(-1)
|
||||
take = near & okd
|
||||
if return_traj:
|
||||
cert_pts = torch.where(take[:, None, None], pts, cert_pts)
|
||||
@@ -418,6 +426,11 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
hit = ((gm[:B] <= 0) | (gm[B:] <= 0)) & ~done & (tn <= limit + 1e-6)
|
||||
col += hit.float()
|
||||
first_hit = torch.where(hit & torch.isnan(first_hit), torch.full_like(first_hit, tn), first_hit)
|
||||
act_ = ((~done) & (tn <= limit + 1e-6)).float() # 只计自己时限内的移动(与 col 同口径)
|
||||
exec_d = (q - qp).abs()
|
||||
vratio = torch.maximum(vratio, (exec_d / step).max(-1).values * act_) # 实际速度 / VMAX
|
||||
aratio = torch.maximum(aratio, ((exec_d - prev_exec) / step).max(-1).values * act_) # 每控制步的速度变化 / VMAX
|
||||
prev_exec = exec_d
|
||||
e, oe = goal_err(kin, q, T, R)
|
||||
if it + 1 == k2s: e2 = e.clone()
|
||||
newly = (~done) & (e < 1) & (oe < 0.5)
|
||||
@@ -438,13 +451,14 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
'travel_ratio_median': (travel[okr] / tr[okr].clamp(min=1)).median().item() if okr.any() else None,
|
||||
'time_mean_s': t_done[ok].mean().item() if ok.any() else None, 'brakes_mean': brakes.mean().item(),
|
||||
'start_fail': start_fail.float().mean().item(), 'goal_ratio_2s': ratio2.median().item(),
|
||||
'speed_ratio_mean': vratio.mean().item(), 'over_vmax_rate': (vratio > 1.001).float().mean().item(),
|
||||
'time_budget': 'teacher×2(≥3 s)' if bool(has_ref.all()) else ('fixed %.0f s' % (max_sec or NOREF_SEC) if not has_ref.any() else 'mixed')}
|
||||
# 绕行侧:J1 累计位移的正向/负向极值哪个更大(+1 / −1),供 R7 左右比例门禁
|
||||
# 首次方向;全程没偏离到 FIRST_DEG 的(基本不绕 J1)退回净方向
|
||||
side = torch.where(j1_first != 0, j1_first, torch.where(j1_max >= -j1_min, torch.ones_like(j1_max), -torch.ones_like(j1_max)))
|
||||
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(), 'first_hit': first_hit.cpu(), 'dls_it': dls_it.cpu(),
|
||||
'goal_ratio_2s': ratio2.cpu()}
|
||||
'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()}
|
||||
|
||||
Reference in New Issue
Block a user