exp: 轮次29——全拒后减半反馈到达55.1%,补齐无碰撞指标与首撞回放
512题无碰撞到达54.7%、碰撞1.8%;9次首撞均在J5限位且双精度复现。保留小样本消融,撤回未经实际调度验证的50Hz达标结论,附六项回归测试及完整配置结果。
This commit is contained in:
@@ -25,9 +25,58 @@ def pair_names():
|
||||
return {a * 7 + b: f'{nm(a)}-{nm(b)}' for a in range(7) for b in range(a + 1, 7)}
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def replay_first_hits(path, out=None):
|
||||
saved = torch.load(path, map_location='cpu', weights_only=True)
|
||||
k32, k64 = Kin(), Kin(dtype=torch.float64)
|
||||
rows, offset = [], 0
|
||||
for _, per, count in saved['batches']:
|
||||
for b in (per['col'] > 0).nonzero().flatten().tolist():
|
||||
it = int(round(float(per['first_hit'][b]) / DT))
|
||||
prev, q = per['traj'][b, it - 1:it + 1]
|
||||
mid = prev + 0.5 * (q - prev)
|
||||
points = torch.stack([per['traj'][b, 0], prev, mid, q])
|
||||
gm32 = k32.gaps(points, 0.5)['gm']
|
||||
gm64 = k64.gaps(points.double(), 0.5)['gm']
|
||||
gap32, gp32 = gm32.min(-1)
|
||||
gap64, gp64 = gm64.min(-1)
|
||||
nseg = max(1, math.ceil(float((q - prev).abs().max()) / 0.01))
|
||||
dense = prev.double() + torch.linspace(0, 1, nseg + 1, dtype=torch.float64)[:, None] * (q.double() - prev.double())
|
||||
dense_gap = torch.cat([k64.gaps(chunk, 0.5)['gm'].min(-1).values for chunk in dense.split(32)])
|
||||
boundary = (~k32.cont) & ((q - k32.lo).abs().lt(1e-5) | (q - k32.hi).abs().lt(1e-5))
|
||||
row = {
|
||||
'i': offset + b, 'task': saved['tasks'][offset + b],
|
||||
'first_hit_s': float(per['first_hit'][b]), 'dls_it': int(per['dls_it'][b]),
|
||||
'point_order': ['q0', 'previous', 'midpoint', 'endpoint'],
|
||||
'gap32_mm': gap32.tolist(), 'gap64_mm': gap64.tolist(),
|
||||
'pair32': gp32.tolist(), 'pair64': gp64.tolist(),
|
||||
'boundary_joints': (boundary.nonzero().flatten() + 1).tolist(),
|
||||
'previous': prev.tolist(), 'endpoint': q.tolist(),
|
||||
'dense_nsegments': nseg, 'dense_gap64_min_mm': float(dense_gap.min()),
|
||||
}
|
||||
rows.append(row)
|
||||
print('task %d first_hit %.2fs gap32 %s gap64 %s boundary %s' %
|
||||
(row['i'], row['first_hit_s'], row['gap32_mm'], row['gap64_mm'], row['boundary_joints']), flush=True)
|
||||
offset += count
|
||||
result = {
|
||||
'trace': path, 'n': offset, 'reported_collision_tasks': len(rows),
|
||||
'replayed_first_hits32': sum(min(r['gap32_mm'][2:]) <= 0 for r in rows),
|
||||
'replayed_first_hits64': sum(min(r['gap64_mm'][2:]) <= 0 for r in rows),
|
||||
'scope': 'Replay reported first hits only; same geometry kernel at float32/float64, not a full safety audit.',
|
||||
'rows': rows,
|
||||
}
|
||||
if out:
|
||||
with open(out, 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, indent=2, allow_nan=False)
|
||||
print('replayed first hits: %d/%d float32, %d/%d float64' %
|
||||
(result['replayed_first_hits32'], len(rows), result['replayed_first_hits64'], len(rows)), flush=True)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--ckpt', required=True)
|
||||
ap.add_argument('--ckpt')
|
||||
ap.add_argument('--trace')
|
||||
ap.add_argument('--samples', default='ml/data/samples')
|
||||
ap.add_argument('--split', default='val')
|
||||
ap.add_argument('--n', type=int, default=512)
|
||||
@@ -38,6 +87,11 @@ def main():
|
||||
ap.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu')
|
||||
ap.add_argument('--out', default=None)
|
||||
a = ap.parse_args()
|
||||
if a.trace:
|
||||
replay_first_hits(a.trace, a.out)
|
||||
return
|
||||
if not a.ckpt:
|
||||
ap.error('--ckpt or --trace is required')
|
||||
|
||||
ck = torch.load(a.ckpt, map_location=a.device)
|
||||
model = build(ck['cfg']).to(a.device)
|
||||
|
||||
Reference in New Issue
Block a user