Files
Hark 2a0a2e55ad ml: 数据生成 + 可微运动学对拍 + MeanFlow/WTA 策略与闭环仿真 + APG/挖错/ONNX 导出与运行手册
- M1 数据生成器:采样/老师/距离下界认证/时间律/统计/一致性门禁;确定性老师(碰撞次数计时 + resetSession)、pos 任务冻结 J6、--consensus 邻域共识过滤、Dr 类。
- 全流程代码与运行手册:数据打包/可微运动学与对拍/MeanFlow+WTA 模型/闭环仿真/APG/挖错循环/ONNX 导出;webapp 策略运行时 ik-policy.js、?policy=1 接入、tests/policy_bench.cjs;ik-solver 增 pairList/clusterExport/criticalPairs/pairGap/leverTable。
2026-10-18 06:40:23 +08:00

79 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""无老师挖错一轮(方案 §7.2):出题 → 闭环作答(关安全层)→ 验算 → 错题分类 → 错题+邻居进 APG 采样池。
python -m ml.mining.mine_round --ckpt ml/runs/apg/best.pt --seed 1001 --n 50000 --out ml/runs/mine/r1
造固定门禁集(只做一次):python -m ml.mining.mine_round --make-gate 2000 --seed 777 --out ml/runs
输出:pool.jsonl(错题与 ±5 mm/±3° 邻居,E1/E6 ×2)、e2_tasks.jsonl(卡在局部极小,可离线请老师补路径)、report.json/.md
错题类:E1 撞 / E2 没到且离目标 > 20 mm(局部极小)/ E3 用时 > 4 s / E4 没到但 < 20 mm(末端精度)/ E5 J1 换向 ≥ 3 次(摇摆)"""
import argparse, json, os
import torch
from ml.common.kin_torch import Kin
from ml.model.policy import build
from ml.sim.closed_loop import rollout, goal_err, tasks_to_tensors
from ml.mining.tasks import sample_tasks, neighbours
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--ckpt'); ap.add_argument('--seed', type=int, default=1001); ap.add_argument('--n', type=int, default=50000)
ap.add_argument('--out', required=True); ap.add_argument('--bs', type=int, default=512); ap.add_argument('--neigh', type=int, default=10)
ap.add_argument('--make-gate', type=int, default=0)
ap.add_argument('--mix', default='0.3,0.3,0.3,0.1', help='A,B,C,D 占比;贴立柱门禁集用 0,0,1,0')
ap.add_argument('--name', default='gate_tasks.jsonl')
ap.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu')
a = ap.parse_args()
os.makedirs(a.out, exist_ok=True)
kin = Kin(device=a.device)
if a.make_gate:
ts = sample_tasks(kin, a.make_gate, a.seed, mix=tuple(float(x) for x in a.mix.split(',')))
with open(os.path.join(a.out, a.name), 'w') as f:
for t in ts: f.write(json.dumps(t) + '\n')
print('gate tasks', len(ts)); return
ck = torch.load(a.ckpt, map_location=a.device)
model = build(ck['cfg']).to(a.device); model.load_state_dict(ck['model']); model.eval()
tasks = sample_tasks(kin, a.n, a.seed)
g = torch.Generator(); g.manual_seed(a.seed)
cls = {k: [] for k in ('E1', 'E2', 'E3', 'E4', 'E5', 'ok')}
for i in range(0, len(tasks), a.bs):
tb = tasks[i:i + a.bs]
_, per = rollout(model, kin, tb, a.device, K=8, safety=False, seed=a.seed + i, max_sec=6.0, return_traj=True)
q0, T, R, _, _ = tasks_to_tensors(tb, a.device)
tr = per['traj']
e, _ = goal_err(kin, tr[:, -1].to(a.device), T, R)
d1 = tr[:, 1:, 0] - tr[:, :-1, 0]
sg = torch.sign(torch.where(d1.abs() > 0.05, d1, torch.zeros_like(d1)))
sw = torch.zeros(len(tb))
for b in range(len(tb)):
s = sg[b][sg[b] != 0]
sw[b] = (s[1:] != s[:-1]).sum() if s.numel() > 1 else 0
for b, t in enumerate(tb):
if per['col'][b] > 0: cls['E1'].append(t)
elif not per['done'][b]: (cls['E2'] if e[b] > 20 else cls['E4']).append(t)
elif per['t_done'][b] > 4.0: cls['E3'].append(t)
elif sw[b] >= 3: cls['E5'].append(t)
else: cls['ok'].append(t)
pool = []
for k, lst in cls.items():
if k == 'ok': continue
rep = 2 if k == 'E1' else 1
for t in lst:
t = dict(t); t['hard'] = True; t['err'] = k
for _ in range(rep): pool.append(t); pool += neighbours(t, a.neigh, g)
with open(os.path.join(a.out, 'pool.jsonl'), 'w') as f:
for t in pool: f.write(json.dumps(t) + '\n')
with open(os.path.join(a.out, 'e2_tasks.jsonl'), 'w') as f:
for t in cls['E2'][:2000]: f.write(json.dumps(t) + '\n')
n = len(tasks)
rep = {'n': n, **{k: len(v) / n for k, v in cls.items()}, 'pool': len(pool)}
bycat = {}
for k, lst in cls.items():
for t in lst: bycat.setdefault(t['cat'], {}).setdefault(k, 0); bycat[t['cat']][k] += 1
rep['by_cat'] = bycat
json.dump(rep, open(os.path.join(a.out, 'report.json'), 'w'), indent=1)
md = ['# 挖错报告', '', f'任务 {n},错题池 {len(pool)}', '', '| 类 | 占比 |', '|---|---:|'] + [f'| {k} | {len(v) / n:.2%} |' for k, v in cls.items()]
open(os.path.join(a.out, 'report.md'), 'w').write('\n'.join(md) + '\n')
print(json.dumps(rep, indent=1))
if __name__ == '__main__':
main()