"""依頼 197: 元の記録 states.csv の実測角 (actual) の間隔を数える. 読むだけ. python3 measure_gaps.py → 同じフォルダに gaps.csv / runs.csv / by_cause.csv / by_stage.csv """ import csv, json, os, sys from collections import defaultdict import numpy as np ROOT = "/Volumes/KIOXIA/r2r2r-data" OUT = os.path.dirname(os.path.abspath(__file__)) SETS = ["teach_v1_trim2", "teach_pos1to4_deep_v1", "scripted_v1"] LONG = 1.0 def phase_of(stage): s = (stage or "").upper() if "LIFT" in s or s == "STAGE6": return "carry_lift" if "RETREAT" in s or s.startswith("STAGE10"): return "retreat" if "APPROACH_PLACE" in s or s.startswith("STAGE7"): return "carry" if s in ("PICK",) or s.startswith("STAGE4") or s.startswith("STAGE5"): return "grasp" if "PLACE" in s or s.startswith("STAGE8") or s.startswith("STAGE9"): return "release" if "APPROACH_PICK" in s or s.startswith("STAGE3"): return "approach" if "HOME" in s or s in ("STAGE1", "STAGE2") or s.startswith("STAGE11") or s.startswith("STAGE12"): return "home" return "other:" + s def load(run): p = f"{ROOT}/runs/run_pick_place/{run}/recording/states.csv" rows = list(csv.DictReader(open(p))) for r in rows: r["ts"] = float(r["ts"]) return rows def classify(rows, ds): """actual の 1 つの間ごとに, 間の中で起きたこと (原因) を決める.""" acts = [i for i, r in enumerate(rows) if r["event_type"] == "actual" and r["j0"] != ""] out = [] for a, b in zip(acts, acts[1:]): ra, rb = rows[a], rows[b] mid = rows[a + 1:b] types = [m["event_type"] for m in mid] # 直前の actual が stage 行のすぐあと (=送信前の読み) か prev = rows[a - 1]["event_type"] if a > 0 else "" if "gripper_cmd" in types or "gripper" in types: cause = "gripper" elif "commanded" in types: cause = "send" elif ds != "scripted_v1" and prev == "stage": cause = "send" # record_teach_demo: stage → 送信前の読み → 送信 (wait=1.0) elif "stage" in types: cause = "stage_change" else: cause = "poll" out.append(dict(t0=ra["ts"], t1=rb["ts"], gap=rb["ts"] - ra["ts"], stage=ra["stage"], phase=phase_of(ra["stage"]), cause=cause, between=";".join(types))) return out def q(v, p): return float(np.percentile(v, p)) if len(v) else float("nan") gap_rows, run_rows = [], [] for ds in SETS: idx = json.load(open(f"{ROOT}/datasets/mecharm_redblock_box_{ds}/index.json")) runs = [k for k, v in idx.items() if v.get("used")] for run in runs: g = classify(load(run), ds) for x in g: x.update(dataset=ds, run=run); gap_rows.append(x) v = np.array([x["gap"] for x in g]) tot = v.sum() run_rows.append(dict(dataset=ds, run=run, n_actual=len(v) + 1, span_sec=round(tot, 3), median=round(q(v, 50), 4), p90=round(q(v, 90), 4), max=round(v.max(), 3), long_gt1s_count=int((v > LONG).sum()), long_time_share=round(v[v > LONG].sum() / tot, 4), mean_hz=round(len(v) / tot, 3))) with open(f"{OUT}/gaps.csv", "w", newline="") as f: w = csv.DictWriter(f, fieldnames=["dataset", "run", "t0", "t1", "gap", "stage", "phase", "cause", "between"]) w.writeheader(); [w.writerow({**x, "gap": round(x["gap"], 4)}) for x in gap_rows] with open(f"{OUT}/runs.csv", "w", newline="") as f: w = csv.DictWriter(f, fieldnames=list(run_rows[0].keys())); w.writeheader(); w.writerows(run_rows) def agg(key, fname): rows = [] for ds in SETS: G = [x for x in gap_rows if x["dataset"] == ds] T = sum(x["gap"] for x in G) LT = sum(x["gap"] for x in G if x["gap"] > LONG) groups = defaultdict(list) for x in G: groups[x[key]].append(x["gap"]) for k, v in sorted(groups.items()): v = np.array(v); lv = v[v > LONG] rows.append(dict(dataset=ds, **{key: k}, n=len(v), median=round(q(v, 50), 4), p90=round(q(v, 90), 4), max=round(v.max(), 3), time_sec=round(v.sum(), 2), time_share=round(v.sum() / T, 4), long_n=len(lv), long_time_sec=round(lv.sum(), 2), share_of_long_time=round(lv.sum() / LT, 4) if LT else 0)) v = np.array([x["gap"] for x in G]) rows.append(dict(dataset=ds, **{key: "ALL"}, n=len(v), median=round(q(v, 50), 4), p90=round(q(v, 90), 4), max=round(v.max(), 3), time_sec=round(T, 2), time_share=1.0, long_n=int((v > LONG).sum()), long_time_sec=round(LT, 2), share_of_long_time=1.0)) with open(f"{OUT}/{fname}", "w", newline="") as f: w = csv.DictWriter(f, fieldnames=list(rows[0].keys())); w.writeheader(); w.writerows(rows) return rows for r in agg("cause", "by_cause.csv"): print(r) print() for r in agg("phase", "by_phase.csv"): print(r)