#!/usr/bin/env python3 """log26_runs_table.py — 2026-11-09 の 12:00 以降に撮った位置3・位置4 のお手本を 1 本 1 行の表 runs_table.csv にする (LOG479・フィジカルAI研究室 Log#26). 読むだけ: runs/run_pick_place/run_0001_20261109_{12,13}*/run_0001_*.json (試行 JSON: 人の y/n・区間ごとの到達) runs/run_pick_place/run_0001_20261109_{12,13}*/teach/*.json (どの教えた記録で撮ったか) runs/run_pick_place/run_0001_20261109_{12,13}*/recording/ (grasp_depth.py・grip_current.py が読む) 書く: このファイルと同じフォルダの runs_table.csv だけ. 判定 (使う/外す) の決まり (lab/Log25/log25_runs_table.py と同じ): 1. PICK の区間 (ブロックの上 → つかむ姿勢) が 5 秒より長くかかった本は「爪がブロックに乗った」として外す 2. 閉じて 1.0 秒後のはみ出しが 24px を超えた本は「浅い」として外す (pi05/grasp_depth.py の線) 3. 人の判定でつかめなかった (n) 本は外す 4. 位置3 の元の記録 (drag_v2_20221013_231249.json) で撮った本は 9 本とも浅かった. 比べる相手として残すが学習には使わない 5. 位置3 の 123756 は浅くはないが, 依頼131 で位置3 を 10 本にそろえるとき (その位置でいちばん浅い本) として外した Mac で動かすとき: cd ~/r2r2r-scripts && python3 /Volumes/KIOXIA/r2r2r-data/lab/Log26/log26_runs_table.py (パスは R2R2R_DATA・R2R2R_SCRIPTS で変えられる) """ import csv, json, os, sys from pathlib import Path DATA = Path(os.environ.get("R2R2R_DATA", "/Volumes/KIOXIA/r2r2r-data")) SCRIPTS = Path(os.environ.get("R2R2R_SCRIPTS", Path.home() / "r2r2r-scripts")) sys.path.insert(0, str(SCRIPTS / "pi05")) import grasp_depth as gd # noqa: E402 import grip_current as gc # noqa: E402 RUNS = DATA / "runs/run_pick_place" HERE = Path(__file__).resolve().parent TEACH = { "drag_v2_20221013_231249.json": (3, "元の記録"), "drag_v2_20261109_123000.json": (3, "教え直し 12:30"), "drag_v2_20221013_232345.json": (4, "元の記録"), "drag_v2_20261109_130000.json": (4, "ブロックの上だけ差し替え 13:00"), "drag_v2_20261109_130300.json": (4, "教え直し 13:03"), } SNAG_SEC = 5.0 LINE_PX = 24.0 EVEN10_DROP = {"run_0001_20261109_123756"} # 依頼131 の pi05/excludes/deep_even10_v1.json def main(): rows = [] for p in sorted(x for x in RUNS.glob("run_0001_20261109_*") if x.is_dir()): if p.name < "run_0001_20261109_120000": continue tf = sorted(q.name for q in (p / "teach").glob("*.json")) if not tf or tf[0] not in TEACH: continue pos, ver = TEACH[tf[0]] tj = json.load(open(p / f"{p.name}.json")) seg = [g for g in tj["segments"] if g.get("to_name") == "PICK"][0] j = tj.get("judgments") or {} pick = (j.get("pick") or {}).get("verdict") place = (j.get("place") or {}).get("verdict") m = gd.measure_run(p, max_protrude_px=LINE_PX) g = gc.summarize(p) sec = float(seg["segment_elapsed_sec"]) after = m.get("after_px") if sec > SNAG_SEC: use, why = "外す", "爪がブロックに乗った (PICK の区間 %.2f 秒)" % sec elif pick != "yes": use, why = "外す", "つかめなかった (人の判定 n)" elif after is not None and after > LINE_PX: use, why = "外す", "浅い (閉じて 1.0 秒後 %.1fpx > 24px)" % after elif tf[0] == "drag_v2_20221013_231249.json": use, why = "外す", "元の記録 (浅い)" elif p.name in EVEN10_DROP: use, why = "外す", "位置3 を 10 本にそろえるため, いちばん浅い本を外した (依頼131)" else: use, why = "使う", "" a = seg.get("arrival_actual") or [None] * 6 rows.append({ "run": p.name, "position": pos, "teach": tf[0], "teach_version": ver, "pick": pick, "place": place, "before_px": m.get("before_px"), "after_px": after, "after_gap_px": m.get("after_gap_px"), "hand_suspect": m.get("hand_suspect"), "grip_max": g.get("max"), "grip_median": g.get("median"), "pick_segment_sec": sec, "pick_arrival_max_delta_deg": seg.get("arrival_max_delta_deg"), "j0_arrived": a[0], "j1_arrived": a[1], "j2_arrived": a[2], "j4_arrived": a[4], "use": use, "reason": why, }) out = HERE / "runs_table.csv" with open(out, "w", newline="") as f: w = csv.DictWriter(f, fieldnames=list(rows[0].keys())) w.writeheader() w.writerows(rows) print(out, len(rows), "本") for r in rows: print(r["run"][-6:], r["position"], r["teach_version"], r["pick"], r["after_px"], r["pick_segment_sec"], r["grip_median"], r["use"], r["reason"]) if __name__ == "__main__": main()