Files
Robot_Arm/ml/mining/mine_round.py
T

79 lines
4.2 KiB
Python
Raw 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()