第五轮:锁/续跑/超时/自动分支/R4 探针与 encode-once/控制台加固;阶段内断点续跑(gen 按条、prep 按 shard、训练与 APG 定时原子存档+体面停止);分歧以实现方意见为准

This commit is contained in:
Hark
2026-10-17 18:57:48 +00:00
parent e3bb78388d
commit a5e779c515
36 changed files with 1199 additions and 285 deletions
+8 -2
View File
@@ -13,6 +13,8 @@ from ml.common.features import features, STEP, H, GAP_CAP, D2R, wrap180
DT = 0.02
FIRST_DEG = 5.0 # R7 首次方向判定阈值(度):小于它的抖动不算选边
def tasks_to_tensors(tasks, device):
q0 = torch.tensor([t['q0'] for t in tasks], dtype=torch.float32, device=device)
T = torch.tensor([t['T'] for t in tasks], dtype=torch.float32, device=device)
@@ -80,6 +82,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
traj = [q.clone()] if return_traj else None
n_start = int(round(0.5 / DT)); moved_start = None # R2 起步失败:0.5 s 内关节总行程 < 1°
j1_acc = torch.zeros(B, device=device); j1_max = torch.zeros(B, device=device); j1_min = torch.zeros(B, device=device)
j1_first = torch.zeros(B, device=device) # 2026-10-18 第五轮(审查 N-3):J1 累计偏离首次超过 FIRST_DEG 时的方向(真正的"首次方向")
cold = True
for it in range(nmax):
tnow = it * DT
@@ -146,6 +149,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
qp = q; q = kin.clamp_box(q + d)
travel += d.abs().sum(-1)
j1_acc = j1_acc + d[:, 0]; j1_max = torch.maximum(j1_max, j1_acc); j1_min = torch.minimum(j1_min, j1_acc)
j1_first = torch.where((j1_first == 0) & (j1_acc.abs() >= FIRST_DEG), torch.sign(j1_acc), j1_first)
if it + 1 == n_start: moved_start = travel.clone()
if return_traj: traj.append(q.clone())
mid = qp + 0.5 * (q - qp)
@@ -169,7 +173,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
'start_fail': start_fail.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 左右比例门禁
side = torch.where(j1_max >= -j1_min, torch.ones_like(j1_max), -torch.ones_like(j1_max))
# 首次方向;全程没偏离到 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(), 't_done': t_done.cpu(), 'col': col.cpu(), 'travel': travel.cpu(), 'err': e_end.cpu(),
'side': side.cpu(), 'start_fail': start_fail.cpu(), 'brakes': brakes.cpu()}
if return_traj: per['traj'] = torch.stack(traj, 1).cpu()
@@ -217,7 +222,8 @@ def main():
k = t.get('cat', '?') + '/' + t.get('mode', '?'); b = by.setdefault(k, [0, 0, 0]); b[0] += 1; b[1] += int(d); b[2] += int(c > 0)
res['by_cat_mode'] = {k: {'n': v[0], 'success': v[1] / v[0], 'collision': v[2] / v[0]} for k, v in by.items()}
s = json.dumps(res, indent=1); print(s)
if a.out: open(a.out, 'w').write(s)
if a.out: # 先写 .tmp 再改名:中断不会留下半截结果被当成已完成(M5 续跑按文件存在跳过)
open(a.out + '.tmp', 'w').write(s); os.replace(a.out + '.tmp', a.out)
if __name__ == '__main__':