フィジカルAI研究室を作る Log#26 | 偏りなく学ばせるため、mechArm 270 のお手本を5か所10本に

この記事の素材(コード・ログ・データ 10ファイル・まとめてダウンロードあり)
AIで読む ChatGPT・Claude などで、この記事に質問できます

小型ロボットアーム mechArm 270 のお手本を置き場所ごとにそろえるには、位置3と位置4の教えた姿勢を作り直す必要がありました。位置3は姿勢そのものが浅く、位置4は手前の爪がブロックの真上に来る向きでした。ゲームパッドで教え直し、ブロックの上からは肩(j1)だけで下ろす形にすると、どちらも深くつかめるようになりました。Physical Intelligence の π0.5(ロボット用の AI のモデル)に学ばせるお手本が、5か所すべてで10本ずつ、合わせて50本そろいました。

この回でやったことで、次のことが変わりました。

  • 良くなること:学習データの置き場所の偏りがなくなった。前のデータ(21本)は半分以上が位置5で、位置1は0本だった。新しいデータ mecharm_redblock_box_deep_even10_v1 は、位置1〜5 がどれも10本で、全部が深くつかんだ本
  • もたらされること:教えた姿勢が悪い位置を、1本目の深さと下ろす時間で見つけて、その場で直せるようになった。位置3は9本、位置4は3本撮ったところで止めて教え直した
  • 時間:位置3と位置4の撮影は、12:07〜13:11 の約1時間(教え直し2回を含む)。ゲームパッドでの教え直しは、位置3が約4分、位置4が約3分。撮り終えてから学習データにまとめるまでは、Claude Code への依頼で7分だった(13:16〜13:23)

困ったのは、位置3の元の記録で撮った9本が、全部浅かったことです。つかめて箱にも入るのに、閉じて1秒後のはみ出しは28.7〜47.7px で、線の24px をどれも超えていました。印どおりに置いた1本目でも42.5px でした。位置4は逆に、1本目から爪がブロックの上に乗り、空で閉じました。

位置3も位置4も、置き方ではなく、教えた記録のつかむ姿勢が原因でした。 置き方を直しても変わらないので、姿勢のほうを作り直しています。位置4では、最初に下ろし方だけを変えて試し、外しています(下の表の13:00)。

位置3 の閉じて1秒後の上カメラ。左は元の教えた記録で、ブロックが爪先からはみ出している(42.5px)。右はゲームパッドで教え直した記録で、ブロックが爪の奥まで入っている(2.7px)

やったことの全体は次のとおりです。

時刻やったこと結果
12:07〜12:18位置3を元の記録で9本撮った
9本ともつかめたが、全部浅い(28.7〜47.7px)
12:21〜12:25
位置3のつかむ姿勢をゲームパッドで作り直した
肩 63.1°→68.2°・肘 13.3°→8.4°・手首 −59.8°→−66.5°。ブロックの上は肩だけ25°上げた姿勢
12:36〜12:49
新しい記録で位置3を12本撮った
使える9本・線をわずかに超えた2本・爪が乗った1本
12:52〜12:58
位置4を元の記録で撮り、下ろし方だけ変えて撮った
3本とも爪がブロックに乗り、空で閉じた
13:01〜13:03
位置4をゲームパッドで教え直した
根元の向き j0 を 3.2° 戻し、手前の爪がブロックから外れた
13:05〜13:11
新しい記録で位置4を5本撮った
5本とも深い(1.6〜10.0px)・下ろすのに約1.1秒
13:16〜13:23
今の深いデータとまとめ、位置ごとに10本にそろえた
50本・7,128コマ。データの点検6項目に合格

次は、このデータで π0.5 を Runpod で学習し直し、置き場所ごとの成功の差が縮むかを実機で確かめます。

GPU を買うか借りるかの判断は、Runpod で実際に借りて学習させた費用をもとに、別のページにまとめています。

導入比較ガイドロボットAIの学習用GPU、買うか借りるか実際に借りて学習させた費用と、損益分岐の計算機で決められます

この回の作業は、Claude(手順と数字の確かめを一緒に進めた AI)と進めました。アームとゲームパッドの操作と、目で見た判断はこちらで行い、本数を数える表の直しと学習データのまとめは Claude Code(プログラムを書く AI)に依頼箱を通して頼んでいます。ここから下は、依頼文と報告・コード・端末の出力・測定データを全量で載せた作業ログです。数字は記録から数えたもので、見込みのものは見込みと書いています。鍵(アクセストークンなど)は、どこにも載せていません。

作業ログ ― 位置3と位置4を教え直し、5か所を10本ずつにそろえるまで

この回で使う言葉

言葉意味
お手本
π0.5 に学ばせるための、人が用意した動きの記録。このシリーズでは、教えた姿勢をアームに再生させ、上カメラの映像と関節の角度を記録したものを使う
教えた記録
教えた姿勢を残した JSON のファイル(drag_v2_….json)。再生のときに使うのは、ブロックの上・つかむ・箱の上・置くの4つの姿勢だけ
位置1〜5
ブロックを置く5か所。2026-10-19(LOG352)に、それぞれ教えた記録を作った
はみ出し(px)
爪を閉じて1秒後に、ブロックが爪先の青い印からどれだけはみ出しているかを、上カメラの画で測った量(pi05/grasp_depth.py)。24px(約9mm)を超えたら浅いとして外す。マイナスはブロックが印より奥にあること
下ろす時間
ブロックの上の姿勢から、つかむ姿勢に着くまでの時間。普段は約1.1秒。爪がブロックの上に乗ると、着いたと判定されるまで6〜9秒かかる(LOG463)
爪が乗る
下ろすときに、爪の先がブロックの上の角に当たり、そのまま下りられなくなること
ゲームパッド
市販の PS4 型のコントローラー。十字キーで関節を1つずつ動かし、×で爪を閉じ、○で開く(tools/gamepad_teleop.py。LOG419・LOG446)
重ねて置く(camera_align.py)
上カメラのライブ映像に、前に撮った1コマを半透明で重ねる画面。重なったブロックに実物を合わせると、同じ場所に置ける(pi05/camera_align.py)
依頼箱
Claude Code への依頼をファイルで置くと、Mac の見張り係が順番に渡して実行させる仕組み

mechArm 270 は Elephant Robotics の小型の6軸アームで、中に Raspberry Pi が入っています。π0.5 は、カメラの画と指示の言葉から、次の関節の動きを出すロボット用のモデルです。お手本の本数を位置ごとにそろえるのは、SO-101 で π0.5 を学習した例(LOG443)で、タスクごとの本数の偏りがそのまま失敗の形になっていたためです(2026-11-07 に決めた)。

位置3の元の記録で撮った9本は、つかめたが全部浅かった

手順書(pi05/DEEP_DEMOS.md)の決まりでは、各位置の1本目を撮ったら深さを測り、24px を超えたらその位置は撮り続けずに止めます。この日は1本目を測る前に9本続けて撮ってしまい、あとからまとめて測りました。

run閉じる直前閉じて1秒後爪の値(中央)
measure からのブロックのずれ
12081867.0px42.5px8320.47mm(印どおり)
12112854.9px32.4px77727.2mm
12122465.4px39.3px8028.3mm
12132056.4px35.9px8014.9mm
12141651.5px28.7px77314.7mm
12151161.3px40.4px8095mm 未満(WARN なし)
12160561.3px39.1px78914.1mm
12165965.2px47.7px7184.8mm
12181557.6px32.0px76211.7mm

印どおりに置いた1本目(120818)でも42.5px でした。置くたびにずれは大きくなっていますが、ずれの小さい本(121320 の4.9mm で35.9px)も浅いので、どの本でも浅さは変わりません。爪の値は9本とも700〜800台で、つかめてはいます。浅くつかんだまま箱まで運べていた、ということです。下ろす時間は9本とも1.06〜1.21秒で、爪は乗っていません。

元の位置3のつかむ姿勢は、肩 63.1°・肘 13.3°・手首 −59.8° でした。

位置3のつかむ姿勢を、ゲームパッドで作り直した

この記事のコード・ログ・データは、1ファイルずつ下に置いています。全文はリンク先にあります。

ファイルの一覧(10本)

01_sec03.json ― ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_122141/log.jsonl)・138,295字

02_sec07.json ― ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_130126/log.jsonl)・95,156字

03_runs_table.csv ― runs_table.csv(全量)・4,791字

04_sec08.py.txt ― 表を作るコード(lab/Log26/log26_runs_table.py)・4,192字

05_sec08.py.txt ― 記事の画像を作るコード(lab/Log26/make_images.py)・4,730字

06_sec09.txt ― 依頼128 の報告(r2r2r-scripts/claude_code_reports/20261109_128.txt)・7,569字

07_sec09.txt ― 依頼129 の報告(r2r2r-scripts/claude_code_reports/20261109_129.txt)・6,210字

08_sec09.py.txt ― 本数を数えるコード(r2r2r-scripts/pi05/count_by_position.py・依頼130 のあとの全体)・15,603字

09_sec10.txt ― 依頼131 の報告(r2r2r-scripts/claude_code_reports/20261109_131.txt)・3,645字

10_sec11.txt ― 端末(12:04〜13:11・measure・再生と記録・ゲームパッド・camera_align)・459,039字

位置1(LOG463)と同じやり方です。ブロックを印に置き、ゲームパッドで根元を回して爪の間にブロックを入れ、肘と手首で下げて奥まで入れてから×で閉じます。閉じたまま十字キーの上で肩を上げて止め、その姿勢をブロックの上の姿勢にしました。

姿勢j0肩 j1肘 j2j3手首 j4j5
元のブロックの上−28.1257.2119.86−3.60−71.80−27.50
元のつかむ−27.6863.1013.27−8.26−59.76−27.33
新しいつかむ(×で閉じたあと最初に読んだ角度・seq 224)
−25.4868.208.43−1.58−66.53−30.14
新しいブロックの上(肩を上げて止めた角度・seq 233)
−25.4843.068.52−1.58−66.53−30.14

新しい記録では、ブロックの上からつかむまでの間に、肩が25.1°動くだけです。元の記録は、下ろすあいだに手首が12°、肘が6.6°動いていました。

教えた記録は、元の記録の APPROACH_PICK と PICK の範囲だけを差し替えて作り、箱の側の2つの姿勢はそのまま使っています。再生は Pi から教えた記録を取ってくるので、作ったファイルは scp で Pi の drag_teaching/ に送りました。

新しい位置3の記録の作り方(mecharm_calibration/teach_poses_20261109/make_teach_pos3_20261109.py)

#!/usr/bin/env python3
"""make_teach_pos3_20261109.py — 位置3 の教えた記録 drag_v2_20221013_231249.json の「ブロックの上」と「つかむ」の姿勢だけを,
ゲームパッドで作り直した姿勢に差し替えた新しい教えた記録 drag_v2_20261109_123000.json を作る (2026-11-09 12:30).
作り方は位置1 の lab/Log25/make_teach_v2_20261109.py と同じ (再生が使う 4 つの姿勢のうち, PICK 側の 2 つの範囲を全部そろえる).
  APPROACH_PICK : phase APPROACH_PICK (idx 146〜194) を全部「ブロックの上」に
  PICK          : PICK phase (idx 195〜285. close の印は idx 214) を全部「つかむ」に
箱の側の 2 つ (APPROACH_PLACE・PLACE) と, phases・gripper_events・key_log・positions はそのまま.

姿勢は tools/gamepad_teleop.py の記録 runs/experiments/gamepad_teleop/teleop_20261109_122141/log.jsonl の実測から取った:
  つかむ      = 爪を閉じた (seq 219 の ×) あと, 最初に読んだ角度 (seq 224 の measured_before)
  ブロックの上 = つかんだまま j1 を十字キーの上で上げて止めた角度 (seq 233 の measured_after_halt)
理由: 位置3 の元の教えた PICK が浅かった (2026-11-09 の 9 本が閉じて 1 秒後 28.7〜47.7px. 印どおりに置いた 120818 でも 42.5px)
"""
import json, copy
src = "runs/run_pick_place/run_0001_20261109_120818/teach/drag_v2_20221013_231249.json"
d = json.load(open(src)); n = copy.deepcopy(d)
pick = [-25.48, 68.20, 8.43, -1.58, -66.53, -30.14]   # teleop_20261109_122141 で爪を閉じたあとの実測 (seq 224)
appr = [-25.48, 43.06, 8.52, -1.58, -66.53, -30.14]   # 同じ記録で j1 を上げて止めた実測 (seq 233)
for i in range(146, 195): n['angles'][i] = list(appr)
for i in range(195, 286): n['angles'][i] = list(pick)
n['recorded_at'] = "2026-11-09T12:30:00"
n['derived_from'] = {"teach": "drag_v2_20221013_231249.json", "replaced": {"APPROACH_PICK idx 146-194": appr, "PICK idx 195-285": pick},
 "source": "runs/experiments/gamepad_teleop/teleop_20261109_122141/log.jsonl (seq 224 の閉じたあとの実測・seq 233 の持ち上げ後の実測)",
 "reason": "位置3の教えた PICK が浅かった (2026-11-09 の 9 本が 28.7〜47.7px)。ゲームパッドで奥までつかむ姿勢を作り直した。箱側の 2 姿勢は元のまま"}
out = "mecharm_calibration/teach_poses_20261109/drag_v2_20261109_123000.json"
json.dump(n, open(out, "w"), ensure_ascii=False, indent=1); print(out)

ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_122141/meta.json)

{
  "run_stem": "teleop_20261109_122141",
  "started_at": "2026-11-09T12:21:41",
  "execute": true,
  "argv": [
    "tools/gamepad_teleop.py",
    "--execute"
  ],
  "controllers": [
    {
      "name": "PS4 Controller",
      "guid": "03008fe54c050000cc09000000016800",
      "instance_id": 0,
      "axes": 6,
      "buttons": 16,
      "hats": 0,
      "connected_at": "2026-11-09T12:21:46"
    }
  ],
  "python": "3.12.12 | packaged by conda-forge | (main, Oct 22 2025, 23:34:53) [Clang 19.1.7 ]",
  "pygame": "2.6.1",
  "sdl": "2.28.4",
  "platform": "macOS-26.6.2-arm64-arm-64bit",
  "start_pose": [
    -0.43,
    1.49,
    1.75,
    0.08,
    -0.26,
    -30.23
  ],
  "guards": {
    "J1_MAX_DEG": 90.87,
    "range": "physical_limits (no base window. 2026-11-03)",
    "J4_SAFE": [
      -90.0,
      60.0
    ],
    "JOINT_LIMITS": [
      [
        -156.0,
        161.0
      ],
      [
        -79.0,
        123.0
      ],
      [
        -177.0,
        177.0
      ],
      [
        -156.0,
        162.0
      ],
      [
        -118.0,
        118.0
      ],
      [
        -177.0,
        178.0
      ]
    ],
    "window": {
      "lo": [
        -156.0,
        -79.0,
        -177.0,
        -156.0,
        -90.0,
        -177.0
      ],
      "hi": [
        161.0,
        90.87,
        177.0,
        162.0,
        60.0,
        178.0
      ]
    },
    "home_bypasses_range_check": true
  },
  "motion": {
    "speed": 20,
    "step_fine_deg": 1.0,
    "step_coarse_deg": 2.0,
    "reach_tol_deg": 3.0,
    "reach_timeout_min_sec": 8.0,
    "poll_sec": 0.3,
    "gripper_sleep_sec": 0.8,
    "grip_close_value": 20,
    "send_wait": 0.0,
    "home": [
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      -30.0
    ],
    "home_hold_sec": 1.0,
    "trigger_stop_above": 0.0,
    "hold_start_sec": 0.4,
    "hold_interval_sec": 0.1,
    "hold_ahead_deg": 15.0,
    "hold_stall_sec": 1.5,
    "ignored_check_sec": 0.5,
    "ignored_max_resends": 3,
    "j0_left_sign": -1.0,
    "up_sign": -1.0,
    "select_order": [
      1,
      2,
      3,
      4,
      5
    ]
  },
  "button_map": {
    "0": "cross",
    "1": "circle",
    "2": "square",
    "3": "triangle",
    "4": "share",
    "5": "home",
    "6": "options",
    "7": "l3",
    "8": "r3",
    "9": "l1",
    "10": "r1",
    "11": "dpad_up",
    "12": "dpad_down",
    "13": "dpad_left",
    "14": "dpad_right",
    "15": "touchpad"
  },
  "git": {
    "commit": "23ed9141636c183562da3fe515d0fb0b374530d5",
    "script_status": "clean"
  },
  "script_copy": "gamepad_teleop.py",
  "script_sha256": "d211064093ab547bb9dac15dfaa53c49c8de77f1ce1b22cf58442fde253dd259",
  "ended_at": "2026-11-09T12:25:16",
  "end_reason": "ctrl_c",
  "n_sent": 88
}

ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_122141/log.jsonl)

01_sec03.json138,295字・234行・json
{"seq": 1, "t": "2026-11-09T12:21:48.643", "t_unix": 1794194508.643, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 1, "held_sec": 0.413, "accepted": true, "measured_before": [-0.43, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-15.43, 1.49, 1.75, 0.08, -0.26, -30.23], "sent": true, "reached": null, "no_check": true, "t_send": 1794194508.6583831, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 2, "t": "2026-11-09T12:21:48.743", "t_unix": 1794194508.744, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 2, "held_sec": 0.513, "accepted": true, "measured_before": [-0.43, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 15.0, "sent": false, "why": "lead_ok", "hold_remaining_deg": 15.0, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 3, "t": "2026-11-09T12:21:48.844", "t_unix": 1794194508.844, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 3, "held_sec": 0.614, "accepted": true, "measured_before": [-1.23, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 14.2, "sent": false, "why": "lead_ok", "hold_remaining_deg": 14.2, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 4, "t": "2026-11-09T12:21:48.946", "t_unix": 1794194508.946, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 4, "held_sec": 0.716, "accepted": true, "measured_before": [-6.24, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 9.19, "sent": false, "why": "lead_ok", "hold_remaining_deg": 9.19, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 5, "t": "2026-11-09T12:21:49.059", "t_unix": 1794194509.06, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 5, "held_sec": 0.83, "accepted": true, "measured_before": [-9.93, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 5.5, "sent": false, "why": "lead_ok", "hold_remaining_deg": 5.5, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 6, "t": "2026-11-09T12:21:49.161", "t_unix": 1794194509.162, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 6, "held_sec": 0.932, "accepted": true, "released_during_job": true, "measured_before": [-14.94, 1.49, 1.75, 0.08, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 0.49, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-29.94, 1.49, 1.75, 0.08, -0.26, -30.23], "sent": false, "why": "halted_before_send", "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 7, "t": "2026-11-09T12:21:49.461", "t_unix": 1794194509.461, "button": null, "button_name": null, "kind": "halt", "selected_joint": 1, "step_deg": 1.0, "execute": true, "accepted": true, "halt_reason": "release", "hold_id": 1, "halt_joint": 0, "measured_at_request": [-9.93, 1.49, 1.75, 0.08, -0.26, -30.23], "t_halt": 1794194509.461175, "halt": "stop", "halt_result": {"jog_stop": null, "stop": null}, "measured_after_halt": [-15.64, 2.54, 2.54, 0.08, 1.23, -30.23], "moved_after_request_deg": [-5.71, 1.05, 0.79, 0.0, 1.49, 0.0], "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 8, "t": "2026-11-09T12:21:49.977", "t_unix": 1794194509.978, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -1.0, "hold_id": 2, "hold_n": 0, "accepted": true, "measured_before": [-15.64, 2.72, 2.81, 0.08, 1.49, -30.23], "start_source": "command", "others_from_measured": [], "cmd_gap_deg": 1.75, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-16.64, 1.49, 1.75, 0.08, -0.26, -30.23], "sent": true, "timeout_sec": 8.0, "t_send": 1794194510.0127418, "measured_before_send": [-15.64, 2.72, 2.81, 0.08, 1.49, -30.23], "resends": [], "reached": true, "reached_angles": [-17.05, 1.49, 1.93, 0.08, 1.49, -30.23], "reach_err": 1.75, "elapsed_sec": 0.24, "grip_state": "unknown", "stopped": false, "stop_reason": null}
別のタブで開く保存

測る仕組みが、アームの赤いケーブルをブロックと取り違えた

教え直したあと、ブロックの位置を測る measure が、ブロックではなくアームの根元の赤いケーブルを拾うようになりました(根元から約73mm。120mm 以内は WARN が出る)。

measure の確認画像。赤い丸がアームの根元のケーブルに付いていて、左下のブロックは拾われていない

この見つけ方は、画の中のいちばん広い赤を選びます。12:07 の measure ではブロックの赤が広く(約3,200画素)、ケーブル(細切れで1つ約500)より勝っていました。この日の途中からブロックの赤が小さく写るようになり、ケーブルが選ばれたようです。照明とカメラは変えておらず、マットと白い箱の色の数値も12:07 から同じでした。ブロックの赤が小さく写るようになった理由は、確かめられていません。Claude は途中で、ケーブルの通り方や、ブロックの面・照り返しのせいだと説明しましたが、どれも確かめていない推測でした。

ただ、手順書にはもともと、measure の位置は根元のケーブルを拾うことがあって当てにならない、置けたかは印と画で人が見る、と書いてありました(LOG352 で分かったこと)。教えた記録の再生は関節の角度をそのまま流すだけで、measure の位置は使いません。本数も teach/ のファイル名で数えるので、measure の値は撮影に効きません。WARN が出たまま撮影を続けています。

前に撮った本の1コマ目を重ねて、ブロックを同じ場所に置いた

置くたびにブロックの位置がずれるので、一番深かった本(124117・2.7px)の上カメラの1コマ目を、ライブ映像に半透明で重ねて置くことにしました。pi05/camera_align.py は、学習データの1本目を重ねて上カメラの写り方を合わせる画面です。この本はまだ学習データになっていないので、1コマ目だけを入れたフォルダ(align_ref/)を作って渡しました。

python3 pi05/camera_align.py --raw-dir /Volumes/KIOXIA/r2r2r-data/mecharm_calibration/teach_poses_20261109/align_ref
位置3 の最初のコマにブロックの枠を重ねた図。緑は深くつかめた本、赤は線をわずかに超えた本。赤の2本は、どちらも画面の下(手前)に4〜7mm ずれていた
  • ねらい:置く位置のずれで深さが変わるのをなくし、教えた記録の良し悪しだけを見られるようにする
  • 変わったこと:置き場所を合わせる前の位置3は8本中6本が線の内側(線をわずかに超えた2本は手前にずれていた)。深かった本の位置を画像で見て置いた124438 と、重ねて置いた124817・124920 の3本は、−2.1・−0.6・2.2px で、どれもいちばん深い側だった(この間の124530 は爪が乗った)
  • 見込める効果:置き場所ごとのお手本の位置がそろい、π0.5 が学ぶブロックの位置のばらつきが、教えた5か所のまわりに集まる(学習の結果では、まだ確かめていない)

位置4も、新しい記録の1本目(130542)ができてからは、同じように重ねて置きました。

重ねる元の画のフォルダの説明(mecharm_calibration/teach_poses_20261109/align_ref/README.md)

camera_align.py の --raw-dir に渡す、位置合わせ用の基準画像だけのフォルダ(2026-11-09)。
run_0001_20261109_124117(位置3を教え直した記録で一番深かった本・閉じて1秒後 2.7px)の上カメラ top.mp4 の1コマ目。
episode.npz は camera_align.py がフォルダを見つけるための空の印で、学習には使わない。
run_0001_20261109_130542 は、位置4をゲームパッドで教え直した記録(drag_v2_20261109_130300.json)の1本目(閉じて1秒後 5.4px・PICK の区間 1.09秒)。--episode 130542 で選ぶ。

新しい記録で撮った位置3の12本

run閉じて1秒後下ろす時間判定
12365525.9px1.07秒浅い(線をわずかに超えた)
12375621.8px1.07秒
深いが、10本にそろえるとき外した
1238489.5px1.08秒使う
12394025.6px1.07秒浅い(線をわずかに超えた)
12403220.4px1.07秒使う
1241172.7px1.07秒使う
12420718.1px1.06秒使う
1242564.5px1.10秒使う
124438−2.1px1.08秒
使う(深かった本の位置を画像で見て置いた)
124530—6.57秒
外す(爪が乗り、空で閉じた。画像で見て置いた)
124817−0.6px1.72秒使う(重ねて置いた)
1249202.2px1.07秒使う(重ねて置いた)

元の記録の9本の中央が39.1px だったのに対して、使えた9本の中央は4.5px でした。肩を25°上げた位置から弧を描いて下ろす形でしたが、下ろす時間はほぼ1.1秒にそろい、爪が乗ったのは12本中1本でした。

位置4は、手前の爪がブロックの真上に来ていた

位置4は、前に撮った5本(10/19)がどれも深かったので、元の記録のまま撮りました。ところが1本目(125209)から、下ろすのに7.2秒かかり、爪がブロックに乗って空で閉じました。

Claude は最初、元の記録の下ろし方に原因があると見ました。下ろすあいだに手首が −78.0° から −59.2° へ19°、肘が 22.7° から 13.7° へ動き、爪先が振れながら下りてくる形です。そこで、ブロックの上の姿勢だけを、つかむ姿勢から肩だけ20°上げた姿勢に差し替えました(13:00 の記録)。

姿勢j0肩 j1肘 j2j3手首 j4j5
元のブロックの上−59.9454.3122.671.49−78.04−32.34
13:00 のブロックの上(肩だけ20°上げた)
−62.0541.6913.710.87−59.15−32.34
元のつかむ(13:00 も同じ)
−62.0561.6913.710.87−59.15−32.34

この差し替えた記録で撮るつもりの1本(125607)は、撮るときに打った行が元のファイル名のままで、元の記録で撮られていました。13:00 の記録で撮った125825 も、下ろすのに6.6秒かかり、爪が乗りました。下ろし方ではなく、根元の向き(j0)がずれていて、手前の爪がブロックの真上に来ていたのが原因でした。 上カメラで下ろす区間を並べると、10/19 の本も、ブロックは2本の爪の間ではなく手前の爪の下にありました。10/19 は、爪が乗ったあとにブロックが押されて傾き、4.0秒で入っていただけでした。

位置4 の下ろす区間の上カメラ5コマを3段に並べた。上は13:00 の記録(125825)、中は10/19 の本(193825)、下は教え直した記録(130542)。上と中は手前の爪がブロックの上に重なっている。下はブロックが2本の爪の間に入っている

そこで位置4も、ゲームパッドで教え直しました。根元を 3.2° 戻してブロックを爪の間に入れ、奥まで下げて閉じています。

姿勢j0肩 j1肘 j2j3手首 j4j5
新しいつかむ(×で閉じたあと最初に読んだ角度・seq 127)
−58.8861.879.310.08−54.05−30.14
持ち上げて止めた実測(seq 135)
−58.7141.747.200.70−59.67−30.23
新しいブロックの上(つかむ姿勢の肩だけ41.74°に)
−58.8841.749.310.08−54.05−30.14

持ち上げた実測では、つかんだブロックの重さで手首が −59.7° まで下がっていました。そのまま使うと下ろすときに手首が5.6°動くので、肩以外はつかむ姿勢と同じにしています。位置3と同じく、下ろすときは肩だけが動く形です。

  • ねらい:位置4で爪がブロックに乗るのをなくし、深いお手本を撮れるようにする
  • 変わったこと:下ろす時間は、元の記録と13:00 の記録の3本が6.6〜7.2秒(3本とも爪が乗った)。教え直した記録の5本は1.07〜1.66秒で、はみ出しは1.6〜10.0px
  • 見込める効果:位置4のお手本が、ブロックを押して傾けてからつかむ動きを含まなくなる。π0.5 がその動きをまねることがなくなる見込み(学習の結果では、まだ確かめていない)

13:00 に作った位置4の記録の作り方(使わなかった版・make_teach_pos4_20261109.py)

#!/usr/bin/env python3
"""make_teach_pos4_20261109.py — 位置4 の教えた記録 drag_v2_20221013_232345.json の「ブロックの上」の姿勢だけを差し替えた
新しい教えた記録 drag_v2_20261109_130000.json を作る (2026-11-09 13:00).
つかむ姿勢 (PICK) は元のまま. 箱の側の 2 つ・phases・gripper_events・key_log・positions もそのまま.

理由: 2026-11-09 12:52 の位置4 の 1 本目 (run_0001_20261109_125209) で, 下ろす区間 (ブロックの上 → つかむ) が 7.2 秒かかり,
爪がブロックに乗って空で閉じた (n/n). 元の記録は下ろすあいだに 手首 j4 が −78.0° → −59.2° (19°)・肘 j2 が 22.7° → 13.7° 動き,
爪先が振れながら下りる. 10/19 の位置4 でも同じ区間が 4.0 秒かかっていた (ぎりぎり).
位置3 を教え直した記録 (drag_v2_20261109_123000.json) は下ろす区間が j1 だけの動きで, 11 本とも 1.07〜1.72 秒だった
(爪が乗った 124530 の 1 本を除く).
そこで位置4 も「ブロックの上」を つかむ姿勢から j1 だけ 20° 上げた姿勢にして, 下ろす区間を j1 だけの動きにする.
  ブロックの上 = PICK の close の印 (idx 223) の姿勢の j1 を 61.69 → 41.69 にしたもの (ほかの関節は PICK と同じ)
  APPROACH_PICK phase (idx 123〜197) を全部この姿勢にそろえる
"""
import json, copy
src = "runs/run_pick_place/run_0001_20261109_125209/teach/drag_v2_20221013_232345.json"
d = json.load(open(src)); n = copy.deepcopy(d)
pick = d['angles'][223]
appr = list(pick); appr[1] = round(pick[1] - 20.0, 2)
for i in range(123, 198): n['angles'][i] = list(appr)
n['recorded_at'] = "2026-11-09T13:00:00"
n['derived_from'] = {"teach": "drag_v2_20221013_232345.json", "replaced": {"APPROACH_PICK idx 123-197": appr},
 "pick_kept": pick,
 "reason": "位置4 の下ろす区間で手首と肘が振れ, 爪がブロックに乗った (run_0001_20261109_125209 で 7.2 秒・空で閉じた)。ブロックの上を PICK から j1 だけ 20° 上げた姿勢にし, 下ろす区間を j1 だけの動きにした。PICK と箱側は元のまま"}
out = "mecharm_calibration/teach_poses_20261109/drag_v2_20261109_130000.json"
json.dump(n, open(out, "w"), ensure_ascii=False, indent=1); print(out, "appr", appr, "pick", pick)

教え直した位置4の記録の作り方(make_teach_pos4_v2_20261109.py)

#!/usr/bin/env python3
"""make_teach_pos4_v2_20261109.py — 位置4 の教えた記録 drag_v2_20221013_232345.json の「ブロックの上」と「つかむ」の姿勢を,
ゲームパッドで作り直した姿勢に差し替えた新しい教えた記録 drag_v2_20261109_130300.json を作る (2026-11-09 13:03).
作り方は位置1 (lab/Log25/make_teach_v2_20261109.py)・位置3 (make_teach_pos3_20261109.py) と同じ.
  APPROACH_PICK : phase APPROACH_PICK (idx 123〜197) を全部「ブロックの上」に
  PICK          : PICK phase (idx 198〜303. close の印は idx 223) を全部「つかむ」に
箱の側の 2 つ (APPROACH_PLACE・PLACE) と, phases・gripper_events・key_log・positions はそのまま.

理由: 位置4 の元の記録は j0 = −62.05° で, ブロックが 2 本の爪の間ではなく手前の爪の真下に来ていた.
下ろす区間で爪がブロックに乗り, 今日の 125209・125607・125825 は 6.6〜7.2 秒かかって空で閉じた (10/19 の 193825 も 4.0 秒).
13:00 の差し替え (drag_v2_20261109_130000.json. ブロックの上を j1 だけ上げた形) でも直らなかったので使わない.

姿勢は tools/gamepad_teleop.py の記録 runs/experiments/gamepad_teleop/teleop_20261109_130126/log.jsonl の実測から取った:
  つかむ      = 爪を閉じた (seq 122 の ×) あと, 最初に読んだ角度 (seq 127 の measured_before). j0 は −62.05 → −58.88
  ブロックの上 = つかむ姿勢の j1 を, つかんだまま十字キーの上で上げて止めた角度 (seq 135 の measured_after_halt) の j1 = 41.74 にしたもの.
                 持ち上げた実測は手首 j4 が −59.67 (つかむときは −54.05) で, つかんだ重さで手首が下がっていた. 下ろす区間を
                 j1 だけの動きにするため (位置3 で 1.1 秒前後で下りた形), j1 以外は つかむ姿勢と同じにした
"""
import json, copy
src = "runs/run_pick_place/run_0001_20261109_125209/teach/drag_v2_20221013_232345.json"
d = json.load(open(src)); n = copy.deepcopy(d)
pick = [-58.88, 61.87, 9.31, 0.08, -54.05, -30.14]   # teleop_20261109_130126 で爪を閉じたあとの実測 (seq 127)
appr = list(pick); appr[1] = 41.74                   # 同じ記録で j1 を上げて止めた実測 (seq 135) の j1
for i in range(123, 198): n['angles'][i] = list(appr)
for i in range(198, 304): n['angles'][i] = list(pick)
n['recorded_at'] = "2026-11-09T13:03:00"
n['derived_from'] = {"teach": "drag_v2_20221013_232345.json", "replaced": {"APPROACH_PICK idx 123-197": appr, "PICK idx 198-303": pick},
 "source": "runs/experiments/gamepad_teleop/teleop_20261109_130126/log.jsonl (seq 127 の閉じたあとの実測・seq 135 の持ち上げ後の j1)",
 "reason": "位置4 の元の j0 では手前の爪がブロックの真上に来て乗った (125209・125607・125825)。ゲームパッドで j0 を合わせ直し, 奥までつかむ姿勢を作った。下ろす区間は j1 だけ。箱側の 2 姿勢は元のまま"}
out = "mecharm_calibration/teach_poses_20261109/drag_v2_20261109_130300.json"
json.dump(n, open(out, "w"), ensure_ascii=False, indent=1); print(out, appr, pick)

ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_130126/meta.json)

{
  "run_stem": "teleop_20261109_130126",
  "started_at": "2026-11-09T13:01:26",
  "execute": true,
  "argv": [
    "tools/gamepad_teleop.py",
    "--execute"
  ],
  "controllers": [
    {
      "name": "PS4 Controller",
      "guid": "03008fe54c050000cc09000000016800",
      "instance_id": 0,
      "axes": 6,
      "buttons": 16,
      "hats": 0,
      "connected_at": "2026-11-09T13:01:32"
    }
  ],
  "python": "3.12.12 | packaged by conda-forge | (main, Oct 22 2025, 23:34:53) [Clang 19.1.7 ]",
  "pygame": "2.6.1",
  "sdl": "2.28.4",
  "platform": "macOS-26.6.2-arm64-arm-64bit",
  "start_pose": [
    -0.43,
    1.58,
    2.19,
    -0.26,
    -0.26,
    -30.23
  ],
  "guards": {
    "J1_MAX_DEG": 90.87,
    "range": "physical_limits (no base window. 2026-11-03)",
    "J4_SAFE": [
      -90.0,
      60.0
    ],
    "JOINT_LIMITS": [
      [
        -156.0,
        161.0
      ],
      [
        -79.0,
        123.0
      ],
      [
        -177.0,
        177.0
      ],
      [
        -156.0,
        162.0
      ],
      [
        -118.0,
        118.0
      ],
      [
        -177.0,
        178.0
      ]
    ],
    "window": {
      "lo": [
        -156.0,
        -79.0,
        -177.0,
        -156.0,
        -90.0,
        -177.0
      ],
      "hi": [
        161.0,
        90.87,
        177.0,
        162.0,
        60.0,
        178.0
      ]
    },
    "home_bypasses_range_check": true
  },
  "motion": {
    "speed": 20,
    "step_fine_deg": 1.0,
    "step_coarse_deg": 2.0,
    "reach_tol_deg": 3.0,
    "reach_timeout_min_sec": 8.0,
    "poll_sec": 0.3,
    "gripper_sleep_sec": 0.8,
    "grip_close_value": 20,
    "send_wait": 0.0,
    "home": [
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      -30.0
    ],
    "home_hold_sec": 1.0,
    "trigger_stop_above": 0.0,
    "hold_start_sec": 0.4,
    "hold_interval_sec": 0.1,
    "hold_ahead_deg": 15.0,
    "hold_stall_sec": 1.5,
    "ignored_check_sec": 0.5,
    "ignored_max_resends": 3,
    "j0_left_sign": -1.0,
    "up_sign": -1.0,
    "select_order": [
      1,
      2,
      3,
      4,
      5
    ]
  },
  "button_map": {
    "0": "cross",
    "1": "circle",
    "2": "square",
    "3": "triangle",
    "4": "share",
    "5": "home",
    "6": "options",
    "7": "l3",
    "8": "r3",
    "9": "l1",
    "10": "r1",
    "11": "dpad_up",
    "12": "dpad_down",
    "13": "dpad_left",
    "14": "dpad_right",
    "15": "touchpad"
  },
  "git": {
    "commit": "bac9ab7650627fd5d0ca2adcd391613741710ac5",
    "script_status": "clean"
  },
  "script_copy": "gamepad_teleop.py",
  "script_sha256": "d211064093ab547bb9dac15dfaa53c49c8de77f1ce1b22cf58442fde253dd259",
  "ended_at": "2026-11-09T13:03:45",
  "end_reason": "ctrl_c",
  "n_sent": 54
}

ゲームパッドの記録(runs/experiments/gamepad_teleop/teleop_20261109_130126/log.jsonl)

02_sec07.json95,156字・170行・json
{"seq": 1, "t": "2026-11-09T13:01:34.823", "t_unix": 1794196894.823, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 1, "held_sec": 0.405, "accepted": true, "measured_before": [-0.43, 1.58, 2.19, -0.26, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-15.43, 1.58, 2.19, -0.26, -0.26, -30.23], "sent": true, "reached": null, "no_check": true, "t_send": 1794196894.8359108, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 2, "t": "2026-11-09T13:01:34.995", "t_unix": 1794196894.996, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 2, "held_sec": 0.577, "accepted": true, "measured_before": [-0.43, 1.58, 2.19, -0.26, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 15.0, "sent": false, "why": "lead_ok", "hold_remaining_deg": 15.0, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 3, "t": "2026-11-09T13:01:35.097", "t_unix": 1794196895.098, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 3, "held_sec": 0.68, "accepted": true, "measured_before": [-4.04, 1.58, 2.19, -0.26, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 11.39, "sent": false, "why": "lead_ok", "hold_remaining_deg": 11.39, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 4, "t": "2026-11-09T13:01:35.198", "t_unix": 1794196895.199, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 4, "held_sec": 0.781, "accepted": true, "measured_before": [-7.55, 1.58, 2.19, -0.26, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 7.88, "sent": false, "why": "lead_ok", "hold_remaining_deg": 7.88, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 5, "t": "2026-11-09T13:01:35.302", "t_unix": 1794196895.303, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 5, "held_sec": 0.885, "accepted": true, "measured_before": [-12.91, 1.58, 2.19, -0.26, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.0, "hold_lead_deg": 2.52, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-27.91, 1.58, 2.19, -0.26, -0.26, -30.23], "sent": true, "reached": null, "no_check": true, "t_send": 1794196895.3645818, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 6, "t": "2026-11-09T13:01:35.427", "t_unix": 1794196895.428, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 6, "held_sec": 1.01, "accepted": true, "measured_before": [-27.68, 1.58, 2.19, -0.17, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.09, "hold_lead_deg": 0.23, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-42.68, 1.58, 2.19, -0.26, -0.26, -30.23], "sent": true, "reached": null, "no_check": true, "t_send": 1794196895.934576, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 7, "t": "2026-11-09T13:01:35.984", "t_unix": 1794196895.985, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 7, "held_sec": 1.567, "accepted": true, "measured_before": [-28.82, 1.58, 2.19, -0.17, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.09, "hold_lead_deg": 13.86, "sent": false, "why": "lead_ok", "hold_remaining_deg": 13.86, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
{"seq": 8, "t": "2026-11-09T13:01:36.152", "t_unix": 1794196896.152, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 1.0, "execute": true, "joint": 0, "delta": -15.0, "hold_id": 1, "hold_n": 8, "held_sec": 1.734, "accepted": true, "measured_before": [-30.41, 1.58, 2.19, -0.17, -0.26, -30.23], "start_source": "hold", "cmd_gap_deg": 0.09, "hold_lead_deg": 12.27, "sent": false, "why": "lead_ok", "hold_remaining_deg": 12.27, "reached": null, "grip_state": "unknown", "stopped": false, "stop_reason": null}
別のタブで開く保存

位置3と位置4の29本の表(lab/Log26/runs_table.csv から)

12:00 以降に撮った本を、教えた記録の版ごとにまとめると次のとおりです。

位置教えた記録本数使う浅い爪が乗った10本にそろえるため外した
3
元の記録(drag_v2_20221013_231249)
90900
3
教え直し(drag_v2_20261109_123000)
128211
4
元の記録(drag_v2_20221013_232345)
20020
4
ブロックの上だけ差し替え(drag_v2_20261109_130000)
10010
4
教え直し(drag_v2_20261109_130300)
55000

runs_table.csv(全量)

03_runs_table.csv4,791字・30行・csv
run,position,teach,teach_version,pick,place,before_px,after_px,after_gap_px,hand_suspect,grip_max,grip_median,pick_segment_sec,pick_arrival_max_delta_deg,j0_arrived,j1_arrived,j2_arrived,j4_arrived,use,reason
run_0001_20261109_120818,3,drag_v2_20221013_231249.json,元の記録,yes,yes,67.0,42.5,99.2,False,852,832,1.088,1.93,-27.33,63.63,14.15,-61.69,外す,浅い (閉じて 1.0 秒後 42.5px > 24px)
run_0001_20261109_121128,3,drag_v2_20221013_231249.json,元の記録,yes,yes,54.9,32.4,99.4,False,1023,777,1.076,1.85,-27.33,63.72,13.97,-61.61,外す,浅い (閉じて 1.0 秒後 32.4px > 24px)
run_0001_20261109_121224,3,drag_v2_20221013_231249.json,元の記録,yes,yes,65.4,39.3,99.2,False,832,802,1.071,1.93,-28.03,63.72,13.97,-61.69,外す,浅い (閉じて 1.0 秒後 39.3px > 24px)
run_0001_20261109_121320,3,drag_v2_20221013_231249.json,元の記録,yes,yes,56.4,35.9,99.4,False,830,801,1.064,1.85,-27.59,63.72,13.97,-61.61,外す,浅い (閉じて 1.0 秒後 35.9px > 24px)
run_0001_20261109_121416,3,drag_v2_20221013_231249.json,元の記録,yes,yes,51.5,28.7,99.3,False,1022,773,1.067,1.85,-27.59,63.72,13.97,-61.61,外す,浅い (閉じて 1.0 秒後 28.7px > 24px)
run_0001_20261109_121511,3,drag_v2_20221013_231249.json,元の記録,yes,yes,61.3,40.4,99.0,False,836,809,1.097,1.85,-27.33,63.72,13.97,-61.61,外す,浅い (閉じて 1.0 秒後 40.4px > 24px)
run_0001_20261109_121605,3,drag_v2_20221013_231249.json,元の記録,yes,yes,61.3,39.1,99.2,False,815,789,1.067,1.93,-27.59,63.72,14.06,-61.69,外す,浅い (閉じて 1.0 秒後 39.1px > 24px)
別のタブで開く保存

表を作るコード(lab/Log26/log26_runs_table.py)

04_sec08.py.txt4,192字・96行・python
#!/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 が読む)
別のタブで開く保存

記事の画像を作るコード(lab/Log26/make_images.py)

05_sec08.py.txt4,730字・96行・python
#!/usr/bin/env python3
"""make_images.py — LOG479 (フィジカルAI研究室 Log#26) の記事の画像を作る. 元の記録は読むだけ.
書き出し先: 第1引数のフォルダ (既定は iCloud の フィジカルAI研究室を作る/LOG479_images/)
  LOG479_01_pos3_old_vs_new.jpg  位置3 の閉じて 1.0 秒後の上カメラ. 左: 元の記録 (120818・42.5px), 右: 教え直し (124117・2.7px)
  LOG479_03_pos3_align.jpg       位置3 の最初のコマにブロックの枠を重ねた図 (緑: 深かった本, 赤: 浅かった本)
  LOG479_04_pos4_descent.jpg     位置4 の下ろす区間の上カメラ 5 コマ × 3 段 (125825 差し替え版・193825 10/19・130542 教え直し)
  LOG479_02_measure_cable.jpg    measure の確認画像 (measure_20261109_123559.jpg) を 1280 幅に縮めたもの
"""
別のタブで開く保存

サムネイルの元データを書き出したコード(lab/Log26/extract_log479_thumb.py)

import json, csv, glob, os, sys
R = lambda v: round(float(v), 1)
o = json.load(open(sys.argv[1]))
# LOG479: 2026-11-09 12:00 以降の位置3・位置4 のお手本 29 本。下ろし始め(PICK の段階に入った時刻)から閉じたあと 1.5 秒までの手首 j4 を、時間(PICK の段階に入った時刻を0)で
#  [t, j4, つかんでいるか] の点にする。new = ゲームパッドで教え直した記録(下ろす間は j1 だけが動き、手首は動かない)
LOG479_NEW = {'drag_v2_20261109_123000.json', 'drag_v2_20261109_130300.json'}
def log479_runs(root='runs/run_pick_place'):
    out = {3: [], 4: []}
    for d in sorted(glob.glob(root + '/run_0001_20261109_1[23]*')):
        s = os.path.basename(d)
        tf = sorted(os.path.basename(x) for x in glob.glob(d + '/teach/*.json'))
        pos = {'drag_v2_20221013_231249.json': 3, 'drag_v2_20261109_123000.json': 3, 'drag_v2_20221013_232345.json': 4,
               'drag_v2_20261109_130000.json': 4, 'drag_v2_20261109_130300.json': 4}.get(tf[0] if tf else '')
        if pos is None:
            continue
        st = list(csv.DictReader(open(d + '/recording/states.csv')))
        t0 = [float(r['ts']) for r in st if r['event_type'] == 'stage' and r['stage'] == 'PICK'][0]
        tc = [float(r['ts']) for r in st if r['event_type'] == 'gripper_cmd' and r['gripper_state'] == '1'][0]
        pts = [[round(float(r['ts']) - t0, 2), R(r['j4']), 1 if float(r['ts']) >= tc else 0]
               for r in st if r['event_type'] == 'actual' and t0 <= float(r['ts']) <= tc + 1.5]
        out[pos].append({'run': s, 'snag': tf[0] in LOG479_NEW, 'p': pts})
    return out
o['LOG479'] = log479_runs()
json.dump(o, open(sys.argv[1], 'w'), separators=(',', ':'))
print({k: len(v) for k, v in o['LOG479'].items()})

依頼128〜130:教え直した記録を、本数を数える表に足した

位置ごとの本数は、pi05/count_by_position.py が試行フォルダの teach/ にある教えた記録のファイル名で決めます。教え直した記録のファイル名を表(TEACH_FILES)に足さないと、新しい本が「分からない」に数えられます。位置1・3(依頼128)、位置4の13:00 の版(依頼129)、位置4の教え直し(依頼130)を、Claude Code に順に足してもらいました。元の記録の行は、前に撮った本の数え方が変わらないように残しています。

count_by_position.py の変わったところ(df54374 → 5ae9f10)

diff --git a/pi05/count_by_position.py b/pi05/count_by_position.py
index 08c9299..6513877 100644
--- a/pi05/count_by_position.py
+++ b/pi05/count_by_position.py
@@ -64,9 +64,13 @@ DEFAULT_TARGET = 10
 # LOG352 の README の表 (runs/experiments/record_teach_demo/20261019_LOG352/README.md)
 TEACH_FILES = {
     "drag_v2_20221013_144348.json": 1,
+    "drag_v2_20261109_094222.json": 1,     # 位置 1 を教え直した記録 (DEEP_DEMOS 2-1・LOG463)
     "drag_v2_20221013_230205.json": 2,
     "drag_v2_20221013_231249.json": 3,
+    "drag_v2_20261109_123000.json": 3,     # 位置 3 を教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)
     "drag_v2_20221013_232345.json": 4,
+    "drag_v2_20261109_130000.json": 4,     # 位置 4 のブロックの上を差し替えた記録 (2026-11-09)
+    "drag_v2_20261109_130300.json": 4,     # 位置 4 をゲームパッドで教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)
     "drag_v2_20221013_233323.json": 5,
 }
 # 教えた位置のブロックの中心 (マットの上の mm). 決め方は頭の説明

依頼128〜131 の commit の一覧

8d9a4de 位置 1 (drag_v2_20261109_094222.json・LOG463) と位置 3 (drag_v2_20261109_123000.json) を教え直した記録を count_by_position.py の TEACH_FILES に足す. 同じ位置の 2 つのファイルが teach/ にあっても位置が 1 つになることのテストを足す (同じ位置が 2 つになったので既存テストの一時フォルダ名をファイル名で分ける). 2026-11-09 の 48 本で分からない 0 (依頼 128)

 pi05/count_by_position.py      |  2 ++
 pi05/test_count_by_position.py | 18 +++++++++++++++++-
 2 files changed, 19 insertions(+), 1 deletion(-)
bac9ab7 位置 4 のブロックの上を差し替えた記録 (drag_v2_20261109_130000.json・make_teach_pos4_20261109.py) を count_by_position.py の TEACH_FILES に足す. 元の記録と両方が teach/ にあっても位置 4 になることを test_same_position_two_files に足す. 2026-11-09 の 50 本で分からない 0 (依頼 129)

 pi05/count_by_position.py      | 1 +
 pi05/test_count_by_position.py | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)
5ae9f10 位置 4 をゲームパッドで教え直した記録 (drag_v2_20261109_130300.json・make_teach_pos4_v2_20261109.py) を count_by_position.py の TEACH_FILES に 130000 の次に足す (130000 は 125825 の数え方を変えないため残す). 元の記録・130000 のどちらと teach/ にあっても位置 4 になることを test_pos4_gamepad_file に足す (依頼 130)

 pi05/count_by_position.py      |  1 +
 pi05/test_count_by_position.py | 11 +++++++++++
 2 files changed, 12 insertions(+)
50f71f3 位置ごとの本数を 10 本にそろえるために外す 2 本 (位置 3 の run_0001_20261109_123756 21.8px・位置 5 の pi05_20261101_190854 21.4px. 閉じて 1.0 秒後のはみ出しがその位置でいちばん大きい本) を pi05/excludes/deep_even10_v1.json に理由と値と一緒に置く. 今の deep 20 本と 2026-11-09 の深いお手本 31 本 (mecharm_redblock_box_teach_pos1to4_deep_v1) から mecharm_redblock_box_deep_even10_v1 (50 本・位置 1〜5 が各 10 本) を作るのに使った (依頼 131)

 pi05/excludes/deep_even10_v1.json | 66 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

依頼128 の依頼文(依頼箱/済み/20261109_128_位置1を教え直した記録をTEACH_FILESに足す.md)

---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 30
---
# 依頼128:位置1と位置3を教え直した記録を count_by_position.py の TEACH_FILES に足す

LOG463(2026-11-09)で位置1のつかむ姿勢をゲームパッドで教え直し、新しい教えた記録 drag_v2_20261109_094222.json(/Volumes/KIOXIA/r2r2r-data/mecharm_calibration/teach_poses_20261109/)で位置1を10本撮った。
pi05/DEEP_DEMOS.md の 2-1 の 5(エイイチの決め 2026-11-08:教え直したら足す)のとおり、TEACH_FILES に位置1として1行足す。
同じ日の 12:30 に位置3も同じ形で教え直した(drag_v2_20261109_123000.json・同じフォルダ・作ったコードは make_teach_pos3_20261109.py)。これも位置3として1行足す。足さないと今日の位置1の本が「分からない」に数えられる。
実機は動かさない。エイイチは見ていない。

## やること
1. pi05/count_by_position.py の TEACH_FILES に `"drag_v2_20261109_094222.json": 1,     # 位置 1 を教え直した記録 (DEEP_DEMOS 2-1・LOG463)` を、`"drag_v2_20221013_144348.json": 1,` の次の行に足す。`"drag_v2_20261109_123000.json": 3,     # 位置 3 を教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)` を `"drag_v2_20221013_231249.json": 3,` の次の行に足す。元の行は消さない。ほかは変えない
2. teach_position() が同じ位置を指す2つのファイルを正しく扱うこと(集合なので1つになる)を、pi05/test_count_by_position.py に1件足して確かめ、テストを全部通す
3. 読むだけの確かめ:2026-11-09 の run(runs/run_pick_place/run_0001_20261109_*)の teach/ の中のファイル名を一覧し、count_by_position の teach_position() で位置を出す。位置ごとの本数と「分からない」の数を報告に(11:00 までの本は lab/Log25/runs_table.csv の位置と合うか。12:00 以降の本は teach/ のファイル名どおりか)
4. 変えた2ファイルだけを指定して git add・commit(git add . は使わない)

## 報告
claude_code_reports/20261109_128.txt に:git diff、テストの結果、3 の表(run・teach のファイル・出た位置・runs_table.csv の位置)、commit の番号。
**報告の最後の1行は必ず次のどれか**:「判定:合格」/「判定:止めた(決めてほしいことあり)」/「判定:失敗」

依頼128 の報告(r2r2r-scripts/claude_code_reports/20261109_128.txt)

06_sec09.txt7,569字・139行・text
依頼128 報告(2026-11-09)

■ やったこと
1. pi05/count_by_position.py の TEACH_FILES に 2 行を足した(位置 1: drag_v2_20261109_094222.json は 144348 の次、位置 3: drag_v2_20261109_123000.json は 231249 の次。元の行は残した)
2. pi05/test_count_by_position.py に test_same_position_two_files を 1 件足した(元の記録と教え直した記録が teach/ に両方あっても位置 1・3 になる。位置が違う 2 つなら「分からない」のまま)
   補足: 既存の test_teach_file_mapping は一時フォルダ名が t{位置} だったため、同じ位置が 2 つになると同じフォルダを 2 回作って落ちる。フォルダ名をファイル名で分ける 1 行だけ直した(確かめる中身は同じ)
3. 2026-11-09 の run を読むだけで確かめた(KIOXIA は書いていない。実機は動かしていない)
4. 2 ファイルを指定して git add・commit
別のタブで開く保存

依頼129 の依頼文(依頼箱/済み/20261109_129_位置4を差し替えた記録をTEACH_FILESに足す.md)

---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 30
---
# 依頼129:位置4を差し替えた記録を count_by_position.py の TEACH_FILES に足す

依頼128 で位置1・3 の教え直した記録を TEACH_FILES に足した。同じ日の 13:00 に、位置4 の教えた記録の「ブロックの上」だけを差し替えた新しい記録 drag_v2_20261109_130000.json を作った(/Volumes/KIOXIA/r2r2r-data/mecharm_calibration/teach_poses_20261109/・作ったコードは make_teach_pos4_20261109.py。理由はコードの頭)。これも位置4 として足す。
実機は動かさない。エイイチは見ていない。

## やること
1. pi05/count_by_position.py の TEACH_FILES に `"drag_v2_20261109_130000.json": 4,     # 位置 4 のブロックの上を差し替えた記録 (2026-11-09)` を `"drag_v2_20221013_232345.json": 4,` の次の行に足す。ほかは変えない
2. 依頼128 で足したテストと同じ形で位置4 の1件を足し、テストを全部通す
3. 読むだけの確かめ:2026-11-09 の run の teach/ のファイル名から位置を出し、位置ごとの本数と「分からない」の数を報告に
4. 変えた2ファイルだけを指定して git add・commit(git add . は使わない)

## 報告
claude_code_reports/20261109_129.txt に:git diff、テストの結果、3 の表、commit の番号。
**報告の最後の1行は必ず次のどれか**:「判定:合格」/「判定:止めた(決めてほしいことあり)」/「判定:失敗」

依頼129 の報告(r2r2r-scripts/claude_code_reports/20261109_129.txt)

07_sec09.txt6,210字・117行・text
依頼129:位置4を差し替えた記録を count_by_position.py の TEACH_FILES に足す

■ やったこと
1. pi05/count_by_position.py の TEACH_FILES に "drag_v2_20261109_130000.json": 4 を drag_v2_20221013_232345.json の次の行に足した(ほかは変えていない)
2. pi05/test_count_by_position.py の test_same_position_two_files(依頼128 で足したテスト)に位置4 の 1 件(元の 232345 と差し替えた 130000)を足した。コメントを「位置 1・3・4」に直した
3. 2026-11-09 の run を読むだけで確かめた(KIOXIA は書いていない。実機は動かしていない)
4. 2 ファイルを指定して git add・commit
別のタブで開く保存

依頼130 の依頼文(依頼箱/済み/20261109_130_位置4を教え直した記録をTEACH_FILESに足す.md)

---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 30
---
# 依頼130:位置4をゲームパッドで教え直した記録を count_by_position.py の TEACH_FILES に足す

依頼129 で足した drag_v2_20261109_130000.json(位置4 のブロックの上だけを差し替えた記録)は、爪がブロックに乗る件が直らなかった(run_0001_20261109_125825 の1本だけで使うのをやめた)。
13:03 に位置4 をゲームパッドで教え直した drag_v2_20261109_130300.json を作った(/Volumes/KIOXIA/r2r2r-data/mecharm_calibration/teach_poses_20261109/・作ったコードは make_teach_pos4_v2_20261109.py)。これも位置4 として足す。130000 の行は消さない(125825 の数え方が変わらないように)。
実機は動かさない。エイイチは見ていない。

## やること
1. pi05/count_by_position.py の TEACH_FILES に `"drag_v2_20261109_130300.json": 4,     # 位置 4 をゲームパッドで教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)` を、130000 の行の次に足す。ほかは変えない
2. 依頼129 と同じ形でテストを1件足し、テストを全部通す
3. 変えた2ファイルだけを指定して git add・commit(git add . は使わない)

## 報告
claude_code_reports/20261109_130.txt に:git diff、テストの結果、commit の番号。
**報告の最後の1行は必ず次のどれか**:「判定:合格」/「判定:止めた(決めてほしいことあり)」/「判定:失敗」

依頼130 の報告(r2r2r-scripts/claude_code_reports/20261109_130.txt)

依頼130 報告:位置4をゲームパッドで教え直した記録を TEACH_FILES に足す

■ git diff
diff --git a/pi05/count_by_position.py b/pi05/count_by_position.py
index 6c97123..6513877 100644
--- a/pi05/count_by_position.py
+++ b/pi05/count_by_position.py
@@ -70,6 +70,7 @@ TEACH_FILES = {
     "drag_v2_20261109_123000.json": 3,     # 位置 3 を教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)
     "drag_v2_20221013_232345.json": 4,
     "drag_v2_20261109_130000.json": 4,     # 位置 4 のブロックの上を差し替えた記録 (2026-11-09)
+    "drag_v2_20261109_130300.json": 4,     # 位置 4 をゲームパッドで教え直した記録 (DEEP_DEMOS 2-1・2026-11-09)
     "drag_v2_20221013_233323.json": 5,
 }
 # 教えた位置のブロックの中心 (マットの上の mm). 決め方は頭の説明
diff --git a/pi05/test_count_by_position.py b/pi05/test_count_by_position.py
index 3d94a0d..162f6f5 100644
--- a/pi05/test_count_by_position.py
+++ b/pi05/test_count_by_position.py
@@ -115,6 +115,17 @@ class TestPosition(unittest.TestCase):
         (d / "teach" / "drag_v2_20261109_123000.json").write_text("{}")
         self.assertEqual(cbp.teach_position(d)[0], cbp.UNKNOWN)
 
+    def test_pos4_gamepad_file(self):
+        # 位置 4 をゲームパッドで教え直した 130300 も位置 4. 元の記録・差し替えた 130000 のどちらと teach/ にあっても位置 4
+        new = "drag_v2_20261109_130300.json"
+        self.assertEqual(cbp.TEACH_FILES[new], 4)
+        self.assertEqual(cbp.TEACH_FILES["drag_v2_20261109_130000.json"], 4)
+        for old in ("drag_v2_20221013_232345.json", "drag_v2_20261109_130000.json"):
+            d = self.root / f"pos4_{old}"
+            write_teach_run(d, old)
+            (d / "teach" / new).write_text("{}")
+            self.assertEqual(cbp.teach_position(d), (4, f"teach/{old}"))
+
     def test_nearest_and_line(self):
         for pos, mm in cbp.TEACH_REF_MM.items():
             self.assertEqual(cbp.nearest_position(mm)[0], pos)

■ テストの結果 (pi05/test_*.py をファイルごとに実行)
test_count_by_position.py: Ran 9 tests in 1.305s OK  (2s)
test_diag_joint_hold.py: Ran 24 tests in 0.337s OK  (0s)
test_gap_fill.py: Ran 7 tests in 0.001s OK  (0s)
test_grasp_depth.py: Ran 12 tests in 5.365s OK  (6s)
test_hf_upload_ckpt.py: Ran 10 tests in 0.031s OK  (0s)
test_merge_and_config.py: Ran 27 tests in 2.737s OK  (3s)
test_pad_build.py: Ran 3 tests in 0.852s OK  (1s)
test_pad_correction.py: Ran 4 tests in 16.504s OK  (17s)
test_recorder_currents.py: Ran 15 tests in 37.900s OK  (39s)
test_recorder_rpc.py: Ran 4 tests in 16.055s OK  (16s)
test_rewind_correct.py: Ran 69 tests in 66.718s OK  (67s)
test_teach_currents.py: 90 秒の上限で止まったので上限 550 秒で再実行 → Ran 23 tests in 197.656s OK
test_trim_hold.py: Ran 5 tests in 0.241s OK  (1s)

合計 212 件 すべて OK (失敗 0)

■ commit
5ae9f10 (pi05/count_by_position.py・pi05/test_count_by_position.py の 2 ファイルだけを指定して add)
130000 の行は消していない。実機は動かしていない。

判定:合格

本数を数えるコード(r2r2r-scripts/pi05/count_by_position.py・依頼130 のあとの全体)

08_sec09.py.txt15,603字・382行・python
#!/usr/bin/env python3
"""count_by_position.py — 学習データの本数を, ブロックの置き場所 (LOG352 の教えた位置 1〜5) ごとに数える (2026-11-07 依頼 118).

エイイチの決め (2026-11-07): 置き場所ごとの本数をそろえる. 目安は各位置 10 本.
撮り足す本数を決めるときと, merge_raw_episodes.py でデータセットを作り終えたときの README の表に使う.

入力 (どちらでも. いくつでも):
  データセット名            datasets/{名前}/raw_episodes を数える
別のタブで開く保存

依頼131:5か所を10本ずつにそろえた学習データを作った

撮り終えたあと、本数は10本でそろえると決めました。依頼131 では、この日に撮った31本(位置1が10本・位置2が7本・位置3が9本・位置4が5本)を学習データにし、前の深いデータ(mecharm_redblock_box_teach_v1_trim2_corr_v5_deep・21本)とまとめています。10本を超えた位置3と位置5(どちらも11本)は、その位置でいちばん浅い1本を外しました。

位置本数教えた動き補正入り外した本
110100—
210100—
310100123756(21.8px)
410100—
51046
pi05_20261101_190854(21.4px・補正入り)
合計504462

できたデータは mecharm_redblock_box_deep_even10_v1(50本・7,128コマ)で、tar は193MB です。データの点検(check_dataset.py)は6項目すべて合格でした。

学習データにするときの深さの測り直しで、位置2の2本(103943・104517)に手が写っていそうという印が出ました。閉じる1.5秒前のコマだけが237.8px・237.7px と大きく、0.5秒前と閉じる直前は24.2px・22.1px で普通です。Claude Code が全体の画を見て、手は写っておらず赤いケーブルを拾った値と判断し、外さずに入れています。つかみも深い(−7.8px・−7.7px)本です。

依頼131 の依頼文(依頼箱/済み/20261109_131_今日の深いお手本をデータにし位置ごと10本にそろえてまとめる.md)

---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 120
---
# 依頼131:2026-11-09 に撮った深いお手本をデータにし、今の deep とまとめて位置1〜5 を各10本にそろえる

pi05/DEEP_DEMOS.md の 3.(撮り終えたら)を今日の本で行う。実機は動かさない。エイイチは見ていない。
**エイイチの決め(2026-11-09):本数は各位置ちょうど10本にそろえる。**
依頼130(TEACH_FILES に位置4 の 130300 を足す)が終わってから始めること(記録.log で完了を確かめる。終わっていなければ止めて報告)。

## 使う本(今日撮った本。これ以外の今日の run は入れない)
- 位置1(drag_v2_20261109_094222.json)10本:094839 095308 095810 095948 100126 100307 100444 100716 101157 103049(lab/Log25/runs_table.csv の use=使う)
- 位置2(drag_v2_20221013_230205.json)7本:102109 102248 102431 103804 103943 104110 104517(同じ表の use=使う)
- 位置3(drag_v2_20261109_123000.json)9本:123756 123848 124032 124117 124207 124256 124438 124817 124920
- 位置4(drag_v2_20261109_130300.json)5本:130542 130723 130824 130929 131103
(すべて run_0001_20261109_{時刻}。入れなかった本:位置3 の元の記録の9本は全部浅い・123655 と 123940 は浅い・124530 は爪が乗って空で閉じた・位置4 の 125209 125607 125825 は爪が乗って空で閉じた)

## やること
1. 新しい本だけのデータセット mecharm_redblock_box_teach_pos1to4_deep_v1 を作る(DEEP_DEMOS 3. のとおり --trim-mid-idle --max-protrude-px 24。上の31本を --run で渡す)。[DEPTH] の行で外れた本・測れなかった本・手が写っていそうな本を報告に。外れた本があれば、その位置が10本を割るかを報告に書いて止める(撮り足しはエイイチが決める)
2. count_by_position.py で mecharm_redblock_box_teach_v1_trim2_corr_v5_deep と 1. を合わせて数える。見込みは位置1=10・2=10・3=11・4=10・5=11。「分からない」が出たら止めて報告
3. 10本を超える位置(見込みは位置3 と位置5)は、その位置の本の中で「閉じて1.0秒後のはみ出し」(grasp_depth.py の値)がいちばん大きい本から外して10本にする。同じ値なら古い本を外す。外す本と値を merge の --exclude-json に理由と一緒に書く(pi05/excludes/ に置く。理由の文:「位置ごとの本数を10本にそろえるため、その位置でいちばん浅い本を外した(エイイチの決め 2026-11-09)」)
4. merge_raw_episodes.py で mecharm_redblock_box_teach_v1_trim2_corr_v5_deep と 1. をまとめ、mecharm_redblock_box_deep_even10_v1 を作る(--tar。元のデータセットは変えない)。README の最後の位置ごとの表が全部10本・目標まで +0 であること
5. check_dataset.py を通す
6. 変えた・足したコードの側のファイル(pi05/excludes/ の json など)だけを指定して git add・commit(git add . は使わない)

## 報告
claude_code_reports/20261109_131.txt に:1 の [DEPTH] の行、2 の表、3 で外した本と値、4 の README の位置ごとの表・エピソード数・tar の大きさと置き場所、5 の結果、commit の番号、決めてほしいこと。
**報告の最後の1行は必ず次のどれか**:「判定:合格」/「判定:止めた(決めてほしいことあり)」/「判定:失敗」

依頼131 の報告(r2r2r-scripts/claude_code_reports/20261109_131.txt)

09_sec10.txt3,645字・61行・text
依頼131 報告:2026-11-09 の深いお手本をデータにし、今の deep とまとめて位置1〜5 を各10本にそろえた

■ 始める前の確かめ
依頼箱/記録.log に「2026-11-09T13:16:19 完了 20261109_130_位置4を教え直した記録をTEACH_FILESに足す.md」がある(commit 5ae9f10)。実機は動かしていない。

■ 1. 新しい本だけのデータセット mecharm_redblock_box_teach_pos1to4_deep_v1
python3 pi05/build_demo_episodes.py --dataset mecharm_redblock_box_teach_pos1to4_deep_v1 --trim-mid-idle --max-protrude-px 24 --run(依頼の31本)
採用エピソード合計: 31  総ステップ: 4387
別のタブで開く保存

外した2本の一覧(r2r2r-scripts/pi05/excludes/deep_even10_v1.json)

{
  "run_0001_20261109_123756": {
    "reason": "位置ごとの本数を10本にそろえるため、その位置でいちばん浅い本を外した(エイイチの決め 2026-11-09)",
    "kind": "教えた動き",
    "position": 3,
    "after_px": 21.8,
    "before_px": 44.5,
    "rule": "その位置の本の中で閉じて 1.0 秒後のはみ出しがいちばん大きい本 (同じ値なら古い本) を外す. 依頼 131",
    "measured_by": "pi05/grasp_depth.py (2026-11-09 依頼 131)",
    "grasp_depth": {
      "status": "ok",
      "t_close": 1794195495.641,
      "frame_before": 562,
      "frame_after": 593,
      "before_px": 44.5,
      "after_px": 21.8,
      "after_gap_px": 99.9,
      "hand_suspect": false,
      "center_by": "wide",
      "axis_from": "body",
      "max_protrude_px": 24.0,
      "shallow": false,
      "pre_px": {
        "pre1.5s": -24.2,
        "pre0.5s": 44.4,
        "before": 44.5
      },
      "center_px": [
        714,
        642
      ]
    }
  },
  "pi05_20261101_190854": {
    "reason": "位置ごとの本数を10本にそろえるため、その位置でいちばん浅い本を外した(エイイチの決め 2026-11-09)",
    "kind": "補正入り",
    "position": 5,
    "after_px": 21.4,
    "before_px": 47.0,
    "rule": "その位置の本の中で閉じて 1.0 秒後のはみ出しがいちばん大きい本 (同じ値なら古い本) を外す. 依頼 131",
    "measured_by": "pi05/grasp_depth.py (2026-11-09 依頼 131)",
    "grasp_depth": {
      "status": "ok",
      "t_close": 1793527814.91,
      "frame_before": 696,
      "frame_after": 706,
      "before_px": 47.0,
      "after_px": 21.4,
      "after_gap_px": 100.3,
      "hand_suspect": false,
      "center_by": "E",
      "axis_from": "body",
      "max_protrude_px": 24.0,
      "shallow": false,
      "pre_px": {
        "pre1.5s": 47.0,
        "pre0.5s": 48.0,
        "before": 47.0
      },
      "center_px": [
        1147,
        688
      ]
    }
  }
}

できたデータの説明(datasets/mecharm_redblock_box_deep_even10_v1/README.md)

# mecharm_redblock_box_deep_even10_v1

いくつかの raw_episodes を 1 つにまとめたデータセット (元は読むだけ。写しを置く)。

- 作成: `python3 pi05/merge_raw_episodes.py --src mecharm_redblock_box_teach_v1_trim2_corr_v5_deep --src mecharm_redblock_box_teach_pos1to4_deep_v1 --dataset mecharm_redblock_box_deep_even10_v1 --exclude-json deep_even10_v1.json`
- LeRobot に変換するときの repo_id: `local/mecharm_redblock_box_deep_even10_v1`

合計 50 本 / 7128 コマ

| 元 | 本数 | コマ | うち補正 (correct) |
| --- | --- | --- | --- |
| mecharm_redblock_box_teach_v1_trim2_corr_v5_deep | 20 | 2873 | 224 |
| mecharm_redblock_box_teach_pos1to4_deep_v1 | 30 | 4255 | 0 |

## 外した本 (2 本. `index.json` の `excluded`)

| エピソード | 元 | コマ | 理由 |
| --- | --- | --- | --- |
| run_0001_20261109_123756 | mecharm_redblock_box_teach_pos1to4_deep_v1 | 132 | 位置ごとの本数を10本にそろえるため、その位置でいちばん浅い本を外した(エイイチの決め 2026-11-09) |
| pi05_20261101_190854 | mecharm_redblock_box_teach_v1_trim2_corr_v5_deep | 145 | 位置ごとの本数を10本にそろえるため、その位置でいちばん浅い本を外した(エイイチの決め 2026-11-09) |

## 置き場所ごとの本数 (`pi05/count_by_position.py`。依頼 118)

教えた動きは `teach/` の教えた記録のファイル名 (LOG352 の表) で、補正入りは上カメラで見つけたブロックの位置をいちばん近い教えた位置に割り当てた (30mm を超えたら「分からない」)。「浅くて外した」「手で外した」は、このデータセットの `index.json` で外したエピソードの数 (データには入っていない)。

| 位置 | 本数 | 教えた動き | 補正入り | 目標 10 本まで | 浅くて外した | 手で外した | 入っている本で手が写っていそう |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 位置 1 | 10 | 10 | 0 | +0 | 0 | 0 | — |
| 位置 2 | 10 | 10 | 0 | +0 | 0 | 0 | — |
| 位置 3 | 10 | 10 | 0 | +0 | 0 | 0 | — |
| 位置 4 | 10 | 10 | 0 | +0 | 0 | 0 | — |
| 位置 5 | 10 | 4 | 6 | +0 | 0 | 0 | — |
| 分からない | 0 | 0 | 0 | — | 0 | 0 | — |
| 合計 | 50 | 44 | 6 | +0 | 0 | 0 | — |

- 入っている本の「手が写っていそう」は測っていない (`--measure-depth` で測る)

端末の出力(全量)

Mac の端末1つで、measure・ドライラン・再生と記録・ゲームパッド・重ねて置く画面を順に動かしました。12:04 から 13:11 までの全量です。recorder を動かしていた別の端末の出力は残していません。

端末(12:04〜13:11・measure・再生と記録・ゲームパッド・camera_align)

10_sec11.txt459,039字・5,686行・text
Last login: Mon Nov  9 12:04:46 on ttys005
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % python3 dynamic_tracking_limits/drag_teaching/record_teach_demo.py measure
python3 dynamic_tracking_limits/drag_teaching/record_teach_demo.py run --teach drag_v2_20221013_231249.json
python3 dynamic_tracking_limits/drag_teaching/record_teach_demo.py run --teach drag_v2_20221013_231249.json --execute
python3: can't open file '/Users/shimazueiichi/dynamic_tracking_limits/drag_teaching/record_teach_demo.py': [Errno 2] No such file or directory
python3: can't open file '/Users/shimazueiichi/dynamic_tracking_limits/drag_teaching/record_teach_demo.py': [Errno 2] No such file or directory
python3: can't open file '/Users/shimazueiichi/dynamic_tracking_limits/drag_teaching/record_teach_demo.py': [Errno 2] No such file or directory
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % cd ~/r2r2r-scripts
別のタブで開く保存