"""依頼110 E-6: 失敗12件それぞれに, いちばんありそうな原因を1つ付ける (e_summary / e_close / e_grasp_metrics / e_track をまとめる). 順に見て, 最初に当たったものを原因にする (線は成功4件の幅から置いた. この依頼での目安): 1 爪の間に入っていない (空振り) 閉じた直後の青い印の間 < 90px (成功は 99〜100px = ブロックの幅で止まる) 2 爪の間に入っていない (浅い) 閉じた直後, ブロックが爪先の印の線から先へはみ出した量 > 成功の最大 + 10px 3 動きながら閉じた 閉じる前 0.3 秒の関節の速さ > 5°/s か, 爪を待つ 0.8 秒に 3° 以上動いた 4 持ち上げを出していない 閉じたあと, 人の補正より前のかたまりで π0.5 の生の値が 20mm 以上上げていない 5 制限で削られた 生の値は 20mm 以上なのに, 範囲の制限のあとで 20mm 未満 6 送っても届かない 送った値は 20mm 以上なのに届いた角度で 20mm 未満, かつ成功より大きく足りない 7 分からない 上のどれでもない 出力: e_cause.csv """ import csv from pathlib import Path OUT = Path("/Volumes/KIOXIA/r2r2r-data/lab/twin/20261107_失敗の再生と置き場所/E_つかみと持ち上げ") def rd(name): return {r["run"]: r for r in csv.DictReader(open(OUT / name))} def f(v): try: return float(v) except (TypeError, ValueError): return None def main(): S, C, G, T = rd("e_summary.csv"), rd("e_close.csv"), rd("e_grasp_metrics.csv"), rd("e_track.csv") succ = [r for r in G.values() if r.get("group") == "successes"] pr_max = max(f(r["after_protrude_px"]) for r in succ) rows = [] for run, s in S.items(): if s["outcome"] != "failure": continue c, g, t = C[run], G[run], T[run] gap, pr = f(g.get("after_marker_gap_px")), f(g.get("after_protrude_px")) v, mv = f(c.get("speed_0.3s_before_close_deg_s")), f(c.get("moved_during_grip_wait_0.8s_deg")) raw, clip, sent, act = (f(s.get(k)) for k in ("model_raw_max_mm", "model_clipped_max_mm", "model_sent_max_mm", "model_actual_max_mm")) if gap is not None and gap < 90: cause = "爪の間に入っていない(空振り)" basis = f"閉じた直後の青い印の間 {gap:.0f}px(成功 99〜100px)、ブロックが印の線から {pr:.0f}px 先" elif pr is not None and pr > pr_max + 10: cause = "爪の間に入っていない(浅い)" basis = (f"閉じた直後、ブロックが爪先の印の線から {pr:.0f}px(約 {pr / 2.6:.0f}mm)先へはみ出す" f"(成功 {min(f(r['after_protrude_px']) for r in succ):.0f}〜{pr_max:.0f}px)。印の間 {gap:.0f}px") elif (v is not None and v > 5) or (mv is not None and mv >= 3): cause = "動きながら閉じた"; basis = f"閉じる前の速さ {v}°/s・爪を待つ間 {mv}°" elif raw is not None and raw < 20: cause = "持ち上げを出していない"; basis = f"π0.5 の生の値の持ち上げ最大 {raw}mm" elif raw is not None and clip is not None and clip < 20: cause = "制限で削られた"; basis = f"生の値 {raw}mm → 制限のあと {clip}mm" else: cause = "分からない" basis = f"閉じた形は成功の幅(はみ出し {pr:.0f}px・印の間 {gap:.0f}px)" lost = t.get("lost_t_s") rows.append({ "run": run, "cause": cause, "basis": basis, "after_marker_gap_px": gap, "after_protrude_px": pr, "speed_before_close_deg_s": v, "moved_during_grip_wait_deg": mv, "model_raw_lift_max_mm": raw, "model_clipped_max_mm": clip, "model_sent_max_mm": sent, "model_reached_max_mm": act, "n_model_chunks_before_human": s.get("n_model_chunks_before_correction"), "lift_by_human_correction_mm": s.get("lift_by_correction_mm"), "block_left_fingers_t_s": lost or "", "block_left_fingers_tip_rise_mm": t.get("lost_h_rise_mm") or "", "max_tip_rise_mm": t.get("max_h_rise_until_lost_or_end_mm"), "state_gap_close_deg": c.get("state_gap_close_chunk_deg"), "state_gap_next_deg": c.get("state_gap_next_chunk_deg"), }) keys = list(rows[0].keys()) with open(OUT / "e_cause.csv", "w", newline="") as fo: w = csv.DictWriter(fo, fieldnames=keys); w.writeheader(); w.writerows(rows) from collections import Counter print(Counter(r["cause"] for r in rows)) for r in rows: print(r["run"], r["cause"], "|", r["basis"], "| lost", r["block_left_fingers_t_s"], r["block_left_fingers_tip_rise_mm"]) if __name__ == "__main__": main()