---
title: "フィジカルAI研究室を作る Log#29 | π0.5 が位置2 を外した理由として、上カメラの傾きを疑う"
slug: pi05-position-2-miss-caused-by-camera-tilt
date: 2026-11-10
day: 486
meta_description: "Physical Intelligence の π0.5 が mechArm 270 で位置2 まで下りなかった理由として、上カメラが学習データより約1.7°回っていたことが最も疑わしい。傾きを戻すと位置2へ向かったが、確定はしていない。"
tags: [フィジカルAI研究室, π0.5, Physical Intelligence, 実機推論, mechArm 270, 上カメラ, 学習データの偏り, ゲームパッド, Claude Code, 失敗の記録]
series: "フィジカルAI研究室"
---

**Physical Intelligence の [π0.5](https://www.pi.website/blog/pi05)（ロボット用の AI のモデル）が、実物の小型ロボットアーム mechArm 270 で位置2 のブロックまで下りなかった理由として、いちばん疑わしいのは、上から撮るカメラ（スマホ）が、学習データを撮ったときより約1.7°回っていたことです。** カメラの傾きを学習データにそろえ直すと、π0.5 は位置2 のつかむ姿勢（肩 j1 が約88°、ひじ j2 が約−34°）へ向かう答えを3回続けて出しました。ただ、付け直したときにカメラの上下左右の位置も数px変わっていて、比べた回数も午前4回・夜3回と少なく、傾きだけが原因だとはまだ確かめていません。最後の数mmで爪がブロックの奥側にずれ、つかむところまでも届いていません。

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

- カメラの傾きが数字で見えるようになった：位置合わせの画面と実機推論の始めに、学習データとの傾きの差が出る。これまでの確かめは上下左右のずれ（±20px）だけで、1.7°の回転は OK のまま通っていた
- お手本集めの落とし穴の候補が1つ見つかった：位置ごとに撮る時間を分けると、その間にカメラが少し動き、カメラの向きそのものが「どの位置か」の手がかりとして学習されるおそれがある。確かめる前でも、撮る前に傾きをそろえておけば避けられる
- 時間：傾きを数字で出す道具は、Claude Code への依頼から25分で使える形になった（17:16〜17:41）。原因を探すのにかかった時間は測っていない
- 失敗：位置2 を手で直そうとゲームパッドの補正を押すと、推論のプロセスが落ちて固まり、Mac の再起動が要る状態で中断した

前の回（[LOG484](https://pixelstomotors.com/record/pi05-even-demos-on-arm-misses-position-2)）で、位置ごとに10本ずつそろえたお手本50本で学習した π0.5 は、位置1 のブロックは3回とも自分でつかめたのに、位置2 では4回とも、つかむ姿勢まで下りずに途中で止まりました。ブロックの置き場所はお手本と同じなのに、なぜ位置2 だけ届かないのか。午前の記録を洗い直すと、11:20ごろから、カメラの写り方が少しだけ回っていました。その向きは、学習データ50本のうち、位置1 を撮った9本とだけ同じでした。照明の違いは画像の明るさを比べて否定できましたが、傾きが原因かどうかの決着は、録画の画像だけを回して π0.5 に渡す試しでつける予定です。

| やったこと | なぜ | 結果 |
|---|---|---|
| 午前の記録から問題点を洗い出す | 位置2 だけ届かない理由を探す | 8つの問題点。いちばん大きいのは位置2 に届かないこと |
| 位置2 のお手本と午前の画像を並べる | ブロックの置き方を疑う | ブロックの位置は同じ。違ったのはカメラの傾き |
| マットの角から傾きを測る | 学習データとの違いを数字で見る | 右下がりに写っているのは位置1 の9本だけ |
| 傾きを数字で出す道具を作る（依頼142） | 手で合わせられるようにする | 位置合わせの画面に傾きと差が出る |
| スマホを回して傾きを合わせ、位置2 を試し直す | 傾きが原因かを見る | π0.5 が位置2 のつかむ姿勢へ向かった。爪は数mm奥にずれた。カメラの位置も少し変わったので、傾きだけの影響とは言い切れない |
| ゲームパッドで直そうとする | 直した動きを学習データにする | 推論のプロセスが落ちて固まり、中断 |
| 傾きをソフトでそろえる（依頼144）・固まるのを直す（依頼145）を出す | 手で合わせる手間をなくす・補正を使えるようにする | 依頼箱で順番待ち（この回の時点では未実行） |

実機と学習の記録は、午前（LOG484）と同じ KIOXIA の `lab/Log28/` に入れています。

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

【バナー】gpu-buy-or-rent

【図解】LOG486_camera_tilt_aim.html


## 作業ログ ― 位置2 に届かない理由を探し、カメラの傾きを合わせ直して試すまで

ここから下は作業の記録です。コマンド・出力・コードは全量で載せます。効果を書いた所のうち、数字で書いたものは測った値、「見込み」と書いたものはまだ確かめていない値です。

この回で何度も出てくる言葉を先に言い換えておきます。

- 位置1〜位置5：赤いブロックを置く5つの場所。お手本はこの5か所で10本ずつ撮った（[LOG479](https://pixelstomotors.com/record/even-ten-demos-per-spot-mecharm-270-reteaching) で決めた形）
- j0〜j5：アームの6つの関節。j0 は根元の回転、j1 は肩、j2 はひじ、j4 は手首
- 上カメラ：マットを真上から撮るスマホ。π0.5 はこの画像と関節の角度を見て、次の動きを決める
- 傾き：上カメラの画像の中で、マットの手前の辺が水平からどれだけ回っているか。プラスは右下がり、マイナスは右上がり
- 依頼箱：Claude Code への依頼を、Mac の見張り係が順番に渡して報告を書き戻す仕組み。この回の依頼142・144・145 はここに入れた


## 午前の記録（lab/Log28）から洗い出した8つの問題点

午前に deep1（位置ごとに10本そろえた50本で、π0.5 を一から学習したモデル）を8回動かした記録を読み、問題を並べました。結果は、位置1 は3回中2回が人の直しなしで成功し、位置2 は4回とも自力ではつかめていません。

| # | 問題 | 記録から分かったこと |
|---|---|---|
| 1 | 位置2 の真上まで行けない | π0.5 が止まる姿勢は j1 約68°・j2 約−8°。位置2 のお手本（j1 87.9°・j2 −33.9°）より、6関節で比べると位置1 に近い（約20° 対 約35°） |
| 2 | 補正したあとも、つかむ所で失敗する | 113600 は補正が足りず浅い所で閉じた。113741 は j1 を 88.8° まで寄せて閉じたが失敗 |
| 3 | 箱の手前で補正が要る | 111034・112639 で箱の手前の直し。112639 は中断 |
| 4 | 位置3・位置4 を試していない | 位置1 が3回、位置2 が4回、位置5 付近が1回 |
| 5 | 角度の読み取りがよく失敗する | `get_angles returned empty/invalid after 12 retries` がほぼ毎回の試行で出る。ゲームパッドの長押しの起点が読み違いの値になる |
| 6 | ゲームパッドでの補正が長い | 位置2 で補正1回に 88〜92秒、送った目標は 27〜36回 |
| 7 | 補正中に Ctrl-C で抜けると成否が残らない | 113433 が中断扱い。114106 も結果を入れていない |
| 8 | 立ち上げの待ち | スクリプト送りが毎回75秒、モデルの取得が133秒。進み具合が見えない |

位置2 のお手本は j1 が 87.9° で、肩の上限 J1_SAFE_MAX（88°）の手前 0.1° しかありません。5か所のうち位置2 だけが、ほかの位置から46〜80°離れた姿勢です。はじめは、学習データの端にある姿勢なのでモデルが出しにくいのでは、と考えました。ところが位置2 は、前のモデルでは特に問題がありませんでした。そこで、学習データの中身ではなく、午前に何が違ったのかを調べることにしました。


## 位置2 のブロックは、お手本と同じ所に置かれていた

まず、ブロックの置き方を疑いました。位置2 のお手本（10/19 と 11/09）と午前の試行の、HOME のときの1枚目と閉じる直前の1枚を並べて見ます。上カメラの画像から、マットの上の mm でブロックの位置を出すスクリプト（`count_by_position.py` の見つけ方を使い回し）も回しました。

### tilt_pos2_cmp.py（位置1・位置2 のお手本と午前の試行で、ブロックの位置を mm で出す）

```python
import sys, json
sys.path.insert(0, '.')
from pathlib import Path
import count_by_position as cb
root = Path(sys.argv[1]); runs = root/'runs'
ds = root/'datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes'
rows=[]
for ep in sorted(p for p in ds.iterdir() if p.is_dir() and not p.name.startswith('.')):
    meta = {}
    for m in ('meta.json',):
        if (ep/m).exists(): meta=json.load(open(ep/m))
    r = cb.classify(ep.name, meta, runs)
    if r['position'] in (1,2):
        rd = cb.find_run_dir(ep.name, runs)
        b,why = cb.block_mm(rd) if rd else (None,'no dir')
        print('train', r['position'], ep.name, [round(v,1) for v in b['mm']] if b else why)
for s in ['pi05_20261110_112346','pi05_20261110_112639','pi05_20261110_112924','pi05_20261110_113433','pi05_20261110_113600','pi05_20261110_113741','pi05_20261110_114106']:
    b,why = cb.block_mm(runs/'pi05_policy'/s)
    print('today', s, [round(v,1) for v in b['mm']] if b else why, cb.nearest_position(b['mm']) if b else '')
```

### tilt_pos2_cmp.py の出力

```text
train 2 run_0001_20261019_191257 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261019_191541 [213.0, 239.2]
train 2 run_0001_20261019_191635 [215.8, 242.5]
train 1 run_0001_20261109_094839 [189.6, 189.0]
train 1 run_0001_20261109_095308 閉じる前のコマでブロックが見つからない
train 1 run_0001_20261109_095810 [183.7, 195.7]
train 1 run_0001_20261109_095948 [191.0, 179.8]
train 1 run_0001_20261109_100126 閉じる前のコマでブロックが見つからない
train 1 run_0001_20261109_100307 [187.4, 194.0]
train 1 run_0001_20261109_100444 閉じる前のコマでブロックが見つからない
train 1 run_0001_20261109_100716 [192.7, 190.2]
train 1 run_0001_20261109_101157 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_102109 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_102248 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_102431 閉じる前のコマでブロックが見つからない
train 1 run_0001_20261109_103049 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_103804 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_103943 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_104110 閉じる前のコマでブロックが見つからない
train 2 run_0001_20261109_104517 閉じる前のコマでブロックが見つからない
today pi05_20261110_112346 [191.8, 191.6] (1, 1, 9.5)
today pi05_20261110_112639 [190.0, 188.8] (1, 1, 7.3)
today pi05_20261110_112924 閉じる前のコマでブロックが見つからない 
today pi05_20261110_113433 閉じる前のコマでブロックが見つからない 
today pi05_20261110_113600 閉じる前のコマでブロックが見つからない 
today pi05_20261110_113741 閉じる前のコマでブロックが見つからない 
today pi05_20261110_114106 [195.9, 230.2] (2, 2, 23.0)
```

位置2 は、ブロックが爪の陰に入ってほとんど見つかりませんでした。見つかった 114106 は位置2 の印から23mmと出ましたが、この測り方は HOME の1枚目でブロックが半分隠れるので当てにならず、画像を直接見比べることにしました。

### tilt_pos2_frames.py（HOME の1枚目と、閉じる直前の1枚を横に並べた画像を作る）

```python
import sys, cv2, numpy as np
sys.path.insert(0,'.')
from pathlib import Path
import grasp_depth as gd, count_by_position as cb
root=Path(sys.argv[1]); out=root/'lab/Log28/analysis/pos2_frames'; out.mkdir(parents=True, exist_ok=True)
runs=root/'runs'
stems=['run_0001_20261019_191541','run_0001_20261109_102109','run_0001_20261109_104517','pi05_20261110_112346','pi05_20261110_113433','pi05_20261110_113600','pi05_20261110_113741','pi05_20261110_114106']
for s in stems:
    rd=cb.find_run_dir(s,runs); rec=rd/'recording'
    gt,gs=gd.load_gripper_cmds(rec/'states.csv'); tc=gd.close_time(gt,gs)
    _,fts=gd.load_frame_log(rec/'top_frame_log.csv')
    kc = next((i for i,t in enumerate(fts) if tc is not None and t>tc), len(fts)-1)
    cap=cv2.VideoCapture(str(rec/'top.mp4')); imgs={}
    for want,name in ((0,'home'),(max(kc-1,0),'close')):
        cap.set(cv2.CAP_PROP_POS_FRAMES,want); ok,f=cap.read()
        if ok: imgs[name]=f
    cap.release()
    row=np.hstack([imgs[k] for k in ('home','close') if k in imgs])
    h,w=row.shape[:2]; row=cv2.resize(row,(w*800//h if False else 1600, int(h*1600/w)))
    cv2.putText(row,s,(10,40),cv2.FONT_HERSHEY_SIMPLEX,1.2,(0,255,255),3)
    cv2.imwrite(str(out/f'{s}.jpg'),row)
    print(s, rd, 'close frame', kc, imgs['home'].shape)
```

### tilt_pos2_frames.py の出力

```text
run_0001_20261019_191541 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/run_pick_place/run_0001_20261019_191541 close frame 460 (1080, 1920, 3)
run_0001_20261109_102109 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/run_pick_place/run_0001_20261109_102109 close frame 576 (1080, 1920, 3)
run_0001_20261109_104517 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/run_pick_place/run_0001_20261109_104517 close frame 564 (1080, 1920, 3)
pi05_20261110_112346 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_112346 close frame 339 (1080, 1920, 3)
pi05_20261110_113433 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_113433 close frame 399 (1080, 1920, 3)
pi05_20261110_113600 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_113600 close frame 372 (1080, 1920, 3)
pi05_20261110_113741 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_113741 close frame 994 (1080, 1920, 3)
pi05_20261110_114106 /sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_114106 close frame 938 (1080, 1920, 3)
```

【画像】LOG486_01_pos2_demo_20261019.jpg 位置2 のお手本（10/19）。左は HOME の1枚目、右は爪を閉じる直前。ブロックはマットの左寄りの下にある

【画像】LOG486_02_pos2_demo_20261109.jpg 位置2 のお手本（11/09）。ブロックの位置は10/19とほぼ同じ

【画像】LOG486_03_pos2_pi05_morning.jpg 午前の試行（113600）。ブロックの位置はお手本と同じだが、閉じる直前の爪はブロックの手前で止まっている

ブロックの写る位置は、お手本と午前でほぼ同じでした。違ったのは画面全体で、午前の画像はアームの爪とマットの下の縁が、お手本より少し下に写っています。白い箱はほとんど動いていないのに、高い所にある爪だけが大きくずれているので、カメラが少し動いたと見ました（カメラに近い物ほど、カメラが動いたときに大きくずれて写る）。


## マットの角で測ると、午前の11:20ごろから上カメラの傾きが変わっていた

画面の動きを数字で見るため、マットの四隅を取る関数（`mat_corner_detection.detect_corners`）で、各試行の1枚目の角の位置を出しました。上の2つの角は画面の上で切れるので（y=0）、下の2つ（右下 BR・左下 BL）を見ます。

### tilt_corners.py（お手本と午前の試行の1枚目で、マットの四隅の位置を出す）

```python
import sys, cv2
sys.path.insert(0,'.')
from pathlib import Path
import count_by_position as cb
runs=Path(sys.argv[1])/'runs'
stems=['run_0001_20261019_191541','run_0001_20261019_191635','run_0001_20261109_102109','run_0001_20261109_104517','run_0001_20261109_094839','pi05_20261110_111034','pi05_20261110_112346','pi05_20261110_113433','pi05_20261110_113600','pi05_20261110_113741','pi05_20261110_114106']
for s in stems:
    rd=cb.find_run_dir(s,runs); cap=cv2.VideoCapture(str(rd/'recording/top.mp4')); ok,f=cap.read(); cap.release()
    c=cb.detect_corners(f)
    print(s, None if c is None else ' '.join(f'({x:.0f},{y:.0f})' for x,y in c))
```

### tilt_corners.py の出力（左上・右上・右下・左下の順。1920×1080 の画像の画素）

```text
run_0001_20261019_191541 (321,0) (1691,0) (1718,924) (314,937)
run_0001_20261019_191635 (320,0) (1691,0) (1717,925) (315,938)
run_0001_20261109_102109 (303,0) (1679,0) (1706,923) (308,948)
run_0001_20261109_104517 (300,0) (1674,0) (1703,915) (306,944)
run_0001_20261109_094839 (318,0) (1691,0) (1695,953) (296,941)
pi05_20261110_111034 (308,0) (1683,0) (1712,919) (311,944)
pi05_20261110_112346 (323,0) (1702,0) (1705,964) (299,947)
pi05_20261110_113433 (324,0) (1701,0) (1705,964) (299,947)
pi05_20261110_113600 (324,0) (1701,0) (1704,964) (300,948)
pi05_20261110_113741 (324,0) (1701,0) (1705,964) (299,947)
pi05_20261110_114106 (324,0) (1701,0) (1704,964) (300,948)
```

右下の角と左下の角の高さの差を見ると、10/19 と 11/09 の位置2 のお手本、それに午前1回目の 111034（成功した回）は、右下の方が上にあります（右上がり）。112346 から最後までは、右下の方が 17px ほど下にあります（右下がり）。111034 と 112346 の間、lab_start.sh を立ち上げ直した 11:17〜11:20 のあいだに、カメラの向きが変わっています。

11/09 の位置1 のお手本（094839）も、右下の方が下（1695,953 と 296,941）でした。そこで、学習データ50本の全部で同じものを測りました。


## 学習データ50本のうち、右下がりに写っているのは位置1 の9本だけだった

### tilt_corners_all.py（deep_even10_v1 の50本の1枚目で、下の2つの角と高さの差を出す）

```python
import sys, cv2, json
sys.path.insert(0,'.')
from pathlib import Path
import count_by_position as cb
root=Path(sys.argv[1]); runs=root/'runs'
ds=root/'datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes'
for ep in sorted(p for p in ds.iterdir() if p.is_dir() and not p.name.startswith('.')):
    meta=json.load(open(ep/'meta.json')) if (ep/'meta.json').exists() else {}
    r=cb.classify(ep.name,meta,runs)
    f=cv2.imread(str(ep/'frames/000000.jpg')) if (ep/'frames/000000.jpg').exists() else None
    c=cb.detect_corners(f) if f is not None else None
    s=f'BR=({c[2][0]:.0f},{c[2][1]:.0f}) BL=({c[3][0]:.0f},{c[3][1]:.0f}) tilt={c[2][1]-c[3][1]:+.0f}' if c is not None else str(None if f is None else f.shape)
    print(r['position'], ep.name, s)
```

### tilt_corners_all.py の出力（学習データの画像は 384×216。tilt は右下 − 左下の高さの差 px）

```text
1 run_0001_20261109_094839 BR=(338,190) BL=(59,187) tilt=+3
1 run_0001_20261109_095308 BR=(338,190) BL=(59,187) tilt=+3
1 run_0001_20261109_095810 BR=(338,190) BL=(59,187) tilt=+3
1 run_0001_20261109_095948 BR=(338,190) BL=(59,187) tilt=+3
1 run_0001_20261109_100126 BR=(338,190) BL=(59,188) tilt=+2
1 run_0001_20261109_100307 BR=(338,190) BL=(59,188) tilt=+2
1 run_0001_20261109_100444 BR=(338,190) BL=(59,188) tilt=+2
1 run_0001_20261109_100716 BR=(338,190) BL=(59,188) tilt=+2
1 run_0001_20261109_101157 BR=(338,190) BL=(59,188) tilt=+2
1 run_0001_20261109_103049 BR=(338,183) BL=(58,190) tilt=-7
2 run_0001_20261019_191257 BR=(342,185) BL=(63,187) tilt=-2
2 run_0001_20261019_191541 BR=(343,184) BL=(63,187) tilt=-3
2 run_0001_20261019_191635 BR=(342,185) BL=(63,187) tilt=-2
2 run_0001_20261109_102109 BR=(341,184) BL=(61,188) tilt=-4
2 run_0001_20261109_102248 BR=(341,184) BL=(61,188) tilt=-4
2 run_0001_20261109_102431 BR=(341,184) BL=(61,188) tilt=-4
2 run_0001_20261109_103804 BR=(339,183) BL=(61,188) tilt=-5
2 run_0001_20261109_103943 BR=(339,183) BL=(61,188) tilt=-5
2 run_0001_20261109_104110 BR=(339,183) BL=(61,188) tilt=-5
2 run_0001_20261109_104517 BR=(339,183) BL=(61,188) tilt=-5
3 run_0001_20261019_192438 BR=(343,184) BL=(63,187) tilt=-3
3 run_0001_20261019_192539 BR=(343,184) BL=(63,187) tilt=-3
3 run_0001_20261109_123848 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124032 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124117 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124207 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124256 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124438 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124817 BR=(340,184) BL=(61,189) tilt=-5
3 run_0001_20261109_124920 BR=(340,184) BL=(61,189) tilt=-5
4 run_0001_20261019_193404 BR=(343,184) BL=(62,187) tilt=-3
4 run_0001_20261019_193459 BR=(343,184) BL=(62,186) tilt=-2
4 run_0001_20261019_193617 BR=(343,184) BL=(62,186) tilt=-2
4 run_0001_20261019_193825 BR=(342,185) BL=(62,187) tilt=-2
4 run_0001_20261019_193932 BR=(342,185) BL=(62,187) tilt=-2
4 run_0001_20261109_130542 BR=(341,181) BL=(62,186) tilt=-5
4 run_0001_20261109_130723 BR=(341,181) BL=(62,186) tilt=-5
4 run_0001_20261109_130824 BR=(341,181) BL=(62,186) tilt=-5
4 run_0001_20261109_130929 BR=(341,181) BL=(62,186) tilt=-5
4 run_0001_20261109_131103 BR=(341,181) BL=(62,186) tilt=-5
5 pi05_20261031_175527 BR=(342,184) BL=(62,184) tilt=+0
5 pi05_20261031_180435 BR=(341,185) BL=(62,185) tilt=+0
5 pi05_20261031_184016 BR=(341,185) BL=(62,185) tilt=+0
5 pi05_20261101_190347 BR=(339,186) BL=(60,185) tilt=+1
5 pi05_20261106_183926 BR=(343,182) BL=(64,190) tilt=-8
5 pi05_20261106_184650 BR=(343,182) BL=(64,190) tilt=-8
5 run_0001_20261019_194818 BR=(342,185) BL=(62,187) tilt=-2
5 run_0001_20261019_194913 BR=(342,185) BL=(62,187) tilt=-2
5 run_0001_20261019_195023 BR=(342,185) BL=(62,187) tilt=-2
5 run_0001_20261019_195120 BR=(342,185) BL=(63,188) tilt=-3
```

**50本のうち、高さの差がプラス（右下がり）なのは、11/09 の午前に撮った位置1 の9本だけでした。** 位置2〜位置5 はすべてマイナス（右上がり）か0です。11/09 は位置ごとに撮る時間を分けていて、位置1 を撮り終えた 10:12 と、位置2 を撮り始めた 10:21 の間に、カメラが少し動いています。

午前の 112346 以降の画像を学習データと同じ大きさに縮めると、右下 (341,193)・左下 (60,189) になり、位置1 の9本（338,190・59,187）とほぼ同じ写り方でした。

ここから、π0.5 がブロックの場所だけでなく、画面全体の傾きも手がかりにして位置を見分けている可能性がある、と考えました。午前の結果とは食い違いません。

- 位置1 は3回中2回成功した
- 位置2 では4回とも、位置2 のつかむ姿勢まで下りなかった（止まった姿勢は、6関節で比べると位置1 のつかむ姿勢の方に近いが、位置1 の姿勢そのものでもない）
- 前のモデル（11/09 のお手本を足す前）は、位置2 で問題がなかった

この時点ではまだ推定で、カメラを戻して位置2 で試すまでは確かめられていません。


## 写り方の確かめ（phaseCorrelate・±20px）は平行移動しか見ていなかった

実機推論の始めと位置合わせの画面には、写り方の確かめがあります。午前もずっと OK でした。なぜ見逃したのかを `camera_align.py` で確かめました。

読んだ所（`pi05/camera_align.py` の `measure_framing` と `framing_verdict`。依頼142 より前の版）:

```python
def measure_framing(ref_bgr, cur_bgr):
    """写り方のずれを測る. 戻り値: (dx, dy, response). cv2.phaseCorrelate + ハニング窓.

    2026-10-21 実測 (基準 = teach_v1 の run_0001_20261019_183454 の 1 枚目):
      お手本 191257   dy= +9.1  / お手本 195120 dy= -6.7   ← ブロックの位置で揺れる範囲
      ドライラン 082512 dy= -7.8                            ← お手本と同じ写り方. 通す
      実機 082645       dy=+37.5                            ← 下にずれた写り方. 止める
    dx はブロックの位置で ±6 ほど動く (お手本どうしで +5.0 / +5.8) ので判定には使わず, 表示だけ.
    """
    a = cv2.cvtColor(ref_bgr, cv2.COLOR_BGR2GRAY).astype(np.float32)
    b = cv2.cvtColor(cur_bgr, cv2.COLOR_BGR2GRAY).astype(np.float32)
    if a.shape != b.shape:
        b = cv2.resize(b, (a.shape[1], a.shape[0]), interpolation=cv2.INTER_AREA)
    win = cv2.createHanningWindow((a.shape[1], a.shape[0]), cv2.CV_32F)
    (dx, dy), response = cv2.phaseCorrelate(a, b, win)
    return float(dx), float(dy), float(response)


def framing_verdict(dx, dy, response):
    """写り方の判定. 戻り値: (verdict, 理由). verdict は "OK" / "ずれ" / "測れない".

    相関が弱い (response が低い) ときは dx/dy 自体が当てにならないので,
    「ずれ」ではなく「測れない」として扱う (既定ではどちらも止める).
    """
    if not all(np.isfinite(v) for v in (dx, dy, response)):
        return "測れない", "dx / dy / response に非有限値"
    if response < FRAMING_MIN_RESPONSE:
        return "測れない", (f"相関が弱く dx/dy が当てにならない "
                            f"(response={response:.3f} < {FRAMING_MIN_RESPONSE:.2f})")
    bad = []
    if abs(dy) > FRAMING_MAX_DY:
        bad.append(f"|dy|={abs(dy):.1f} > {FRAMING_MAX_DY:.0f}")
    if abs(dx) > FRAMING_MAX_DX:
        bad.append(f"|dx|={abs(dx):.1f} > {FRAMING_MAX_DX:.0f}")
    if bad:
        return "ずれ", " / ".join(bad)
    return "OK", (f"|dy|<={FRAMING_MAX_DY:.0f} / |dx|<={FRAMING_MAX_DX:.0f} / "
                  f"response>={FRAMING_MIN_RESPONSE:.2f}")
```

phaseCorrelate（OpenCV の関数。2枚の画像を重ねて、上下左右に何 px ずらすと一番よく重なるかを出す）は、平行移動だけを測ります。±20px の線は、10/21 にカメラが下に37px ずれた失敗を止めるために決めた値で、わずかな回転は最初から対象に入っていません。

回すと画面の中心はほとんど動かず端だけが動くので、平行移動として測るとほぼ 0px になります。午前も dy +2.0・dx +4.2 で、OK の範囲に十分収まっていました。重ねた画像（透かし）を目で見比べても、1.7°の回転はまず気づけない大きさです。人の目やこの確かめでは誤差に見える違いが、学習データの中では位置を見分ける手がかりになっていた可能性があります。


## 依頼142：上カメラの傾きを数字で出す camera_tilt.py を作った

傾きを数字で見ながら合わせられるように、Claude Code に依頼しました。カメラを読むだけで、実機は動かしません。

### 依頼文（依頼箱 20261110_142）

```markdown
---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 60
---
# 依頼142：上カメラの傾き（回転）を数字で出し、位置合わせの画面に足す

実機は動かさない（カメラを読むだけ）。エイイチは見ていない。Log28。

## なぜ
2026-11-10 の deep1 の実機推論（Log28）で、位置2が4回ともつかめず、AI は位置1寄りの姿勢へ行った。
上カメラのマットの下の辺の傾きを測ると、

- 学習データ deep_even10_v1 の位置1（11/09 午前の9本）だけが 右下がり 約 +0.5°
- 位置2〜5 と 11/10 の1回目（pi05_20261110_111034・成功）は 右上がり 約 −0.5〜−1.6°
- 11/10 の 112346 以降は 右下がり 約 +0.7°（iPhone を付け直したあと）

だった。camera_align.py の写り方の確かめ（phaseCorrelate の dx/dy・±20px）は平行移動しか見ないので、1.7° の回転は OK のまま通った。
カメラの傾きを数字で見ながら合わせられるようにしたい。

測り方の下書き（Claude が KIOXIA の lab/Log28/analysis で試したもの。/tmp/corners_all.py 相当）：
pi05/count_by_position.py の detect_corners() でマットの四隅を取り、上の2つは画面の上で切れる（y=0）ので使わず、
下の2つ（左下 BL・右下 BR）を結ぶ線の角度を傾きとした。1920×1080 の生の画像でも、学習データの小さい画像でも四隅は取れた。
傾きの向き：度で、プラス＝右下がり（画像の y は下向きなので atan2(BR.y − BL.y, BR.x − BL.x)）。

## やること
1. 傾きを測る関数を1つ作る（置き場所は pi05/ の中で、camera_align.py から使える所。detect_corners を使い回す）。四隅が取れないときは「測れない」を返す
2. 学習データの傾きを一覧にする小さな道具 pi05/camera_tilt.py：
   - `--dataset <名前>`：各エピソードの1枚目の傾きを、位置（count_by_position.classify）ごとに 中央値・最小〜最大 で表にする。全体の中央値も出す
   - `--live`：上カメラ（camera_resolver）から1枚取り、今の傾きと、--dataset の全体の中央値との差を出す
   - 何も書き出さない（datasets/ には書かない）
3. camera_align.py の位置合わせの画面（透かし）と [ALIGN ] の行に、今の傾き・お手本の中央値・差 を出す。お手本は今の基準画像と同じく lab_start.sh から渡る raw_episodes（deep1 なら deep_even10_v1 を見るのがよい。どちらの raw_episodes を見るか迷ったら、安全範囲の --raw-dir の方を使い、報告に書く）
   - 差が ±0.5° を超えたら、画面の数字を目立つ色にして「傾きがお手本と違う」と出す。止めはしない（エイイチは止める条件が増えるのを嫌う）
4. run_policy_client.py の [CAMERA] の行にも傾きと差を1行足す（表示だけ。判定・停止は変えない）
5. 2 を deep_even10_v1 で回し、表を報告に貼る（上の数字と合うか）。Log28 の 11/10 の試行（pi05_20261110_111034・112346・113600・114106）の top.mp4 の1コマ目の傾きも出して報告に貼る
6. テストを足して通す（傾きの向きと値：四隅を回した合成画像で ±1° が正しく出る・測れないとき落ちない）。既存のテストも通す
7. codex で差分を見てもらう。指摘を直して通るまで
8. 変えたファイルだけを指定して git add・commit（git add . は使わない）

## やらないこと
- 写り方の OK／ずれ の判定の線（±20px・response）は変えない
- lab_train.sh・学習の設定・datasets/ は触らない

## 報告
claude_code_reports/20261110_142.txt に：変えた所、5 の表、テストの結果、codex の回数と指摘、commit の番号、`python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1` の打ち方（エイイチが試す前に使う）、決めてほしいこと。
**報告の最後の1行は必ず次のどれか**：「判定：合格」／「判定：止めた（決めてほしいことあり）」／「判定：失敗」
```

### Claude Code の報告（claude_code_reports/20261110_142.txt）

```text
依頼 142：上カメラの傾き（回転）を数字で出し、位置合わせの画面に足す（Log28・実機は動かしていない）

■ 1. 変えた所
- pi05/camera_tilt.py（新規）
  - tilt_deg(frame)：count_by_position.detect_corners でマットの四隅を取り、下の 2 つ（左下 BL・右下 BR）を結ぶ線の角度を返す。
    度で、プラス＝右下がり（atan2(BR.y − BL.y, BR.x − BL.x)）。四隅が取れない・見つけ方が落ちる・左右が逆のときは None（＝測れない）。
  - reference_tilt(raw_dir)：各エピソードの 1 枚目の傾きの中央値（測れた本数・全本数も返す）。50 本で 0.1 秒。
  - tilt_text(今, お手本)：端末用の 1 行。差が ±0.5°（TILT_WARN_DEG）を超えたら「傾きがお手本と違う」。
  - 道具として：--dataset（位置ごとの表）／--live（上カメラ 1 枚と全体の中央値との差）／--video（動画の 1 コマ目. 記録の見直し用. 足した）。何も書き出さない。
- pi05/camera_align.py
  - 画面の dy/resp の下に「tilt=… ref=… diff=… deg」を出す。差が ±0.5° を超えたら橙色にして「TILT DIFFERS (> 0.5 deg)」を大きく出す。四隅が取れなければ「tilt: NO CORNERS」。止めはしない。
  - [ALIGN ] の行の最後に傾き・お手本の中央値・差を足した（傾きが 0.15° 以上動いたときも行を出す）。始めに [TILT  ] でお手本のフォルダと中央値を出す。
  - --tilt-raw-dir を足した（傾きのお手本を取る raw_episodes。未指定なら --raw-dir）。写り方の判定（±20px・response）は変えていない。
  - 傾きは画面と同じ縮めた画像（長辺 384. 学習画像と同じ大きさ）で測る。
- lab_start.sh：camera_align.py に --tilt-raw-dir "$RAW_RANGE"（安全範囲と同じ raw_episodes。deep1・deep1b なら deep_even10_v1、ほかは trim2）を渡す。起動時に [TILT  ] の行で取り先を出す。頭の説明に 1 項足した。
  （位置合わせの重ねる画像は今までどおり trim2。どちらを見るか迷ったので、依頼どおり安全範囲の --raw-dir の方にした）
- pi05/run_policy_client.py：[CAMERA] 基準: の次に [CAMERA] 傾き … の 1 行（お手本は --raw-dir＝安全範囲と同じ）。表示だけで、log.jsonl・判定・停止は変えていない。測り方が落ちても「測れない」の行を出して続く。
- pi05/test_camera_tilt.py（新規）・test_lab_start.sh（2 項足した）

■ 2. deep_even10_v1 の表（python3 pi05/camera_tilt.py --dataset mecharm_redblock_box_deep_even10_v1）
| 位置 | 本数 | 測れた | 中央値 (°) | 最小〜最大 (°) |
| --- | --- | --- | --- | --- |
| 位置 1 | 10 | 10 | +0.41 | -1.43 〜 +0.62 |
| 位置 2 | 10 | 10 | -0.82 | -1.03 〜 -0.41 |
| 位置 3 | 10 | 10 | -1.03 | -1.03 〜 -0.61 |
| 位置 4 | 10 | 10 | -0.82 | -1.03 〜 -0.41 |
| 位置 5 | 10 | 10 | -0.41 | -1.64 〜 +0.21 |
| 全体 | 50 | 50 | -0.61 | -1.64 〜 +0.62 |

依頼の数字と合う：
- 位置 1 の 11/09 午前の 9 本（094839〜101157）は +0.41〜+0.62（右下がり）。位置 1 の残り 1 本 103049 は -1.43。
- 位置 2〜5 はほぼ -0.41〜-1.03（右上がり）。位置 5 の 10/31 の 3 本は 0.00、11/01 は +0.21、11/06 の 2 本は -1.64。
- 学習画像は長辺 384 なので 1 本の値は約 0.2° きざみ（-0.41／-0.61／-0.82／-1.03 が並ぶのはそのため）。
（trim2 の 19 本の全体の中央値は -0.41°。trim・corr 系で lab_start を使うとこちらがお手本になる）

Log28 の 11/10 の試行（top.mp4 の 1 コマ目. 1920×1080 のまま／384 に縮めて）：
| 試行 | 傾き (生) | 傾き (縮めて) | お手本 -0.61° との差 |
| --- | --- | --- | --- |
| pi05_20261110_111034（成功） | -1.02 | -1.02 | -0.41（±0.5 以内） |
| pi05_20261110_112346 | +0.69 | +0.61 | +1.31 → 傾きがお手本と違う |
| pi05_20261110_113600 | +0.65 | +0.61 | +1.27 → 傾きがお手本と違う |
| pi05_20261110_114106 | +0.65 | +0.61 | +1.27 → 傾きがお手本と違う |
（差は生の値で計算。112346 以降の iPhone の付け直しで約 1.7° 回ったことが、この表示なら位置合わせの画面で TILT DIFFERS として出る）

■ 3. テスト
- pi05/test_camera_tilt.py：8 件 OK
  合成画像（黒いマット・上は画面の外）を +1／-1／0／+1.7／-0.5° 回して、生の大きさで ±0.1° 以内（+1° → +0.98、-1° → -0.98、+1.7° → +1.73）、
  384×216 で ±0.35° 以内（+1° → +0.94、-1° → -0.94）。向き（右下がり＝プラス）も確かめた。
  測れないとき（真っ白・画像なし・見つけ方が落ちる・左右が逆・角が 4 つでない）は None で落ちない。
  reference_tilt（空のフォルダ・画像のない本を含む）、±0.5° の線、camera_align の画面（傾きあり・なし・差が大きい・今までの呼び方）、
  camera_align.main のループ（偽物のカメラで 数値→測れない→数値 のとき [ALIGN ] 行に傾きが出る）、run_policy_client.camera_tilt_line（フォルダなし・測り方が落ちる）。
- 既存：test_lab_start.sh 178 OK / NG 0（176 → 178。足したのは deep1 の傾きのお手本が deep_even10_v1・camera_align.py に --tilt-raw-dir "$RAW_RANGE" を渡す の 2 項）。
  pi05/test_*.py はすべて OK（count_by_position・diag_joint_hold・framing_raw_dir・gap_fill・grasp_depth・hf_upload_ckpt・merge_and_config 34・pad_build・pad_correction 4・recorder_currents 15・recorder_rpc・rewind_correct 69・teach_currents 23・trim_hold）。

■ 4. codex
2 回（codex exec -s read-only）。
- 1 回目：必須なし。推奨 2 件 ①camera_align.main のループ（初回・数値→None→数値・[ALIGN ] の出力）をテストする ②reference_tilt／tilt_deg が例外を投げても camera_tilt_line が「測れない」を返すことをテストする → 両方テストに足した。
- 2 回目：必須なし・推奨なし。

■ 5. commit
0531717（変えた 6 ファイルだけを指定して add）

■ 6. エイイチが試す前に使う打ち方
  cd ~/r2r2r-scripts
  python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
上カメラ（iPhone 連係）が見えている状態で、アームは HOME。1 枚だけ取って次のように出る（何も書き出さない）：
  [REF ] …/mecharm_redblock_box_deep_even10_v1/raw_episodes  全体の中央値 -0.61°  (測れた 50/50 本)
  [LIVE] 上カメラ (1920×1080) 傾き +0.65° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.26° → 傾きがお手本と違う (±0.5° を超える)
差がプラスなら右下がりが強い（iPhone の右側を少し上げる向きに回す）、マイナスなら逆。
lab_start.sh の 7 の位置合わせの画面にも同じ数字が出る（実機での画面の見え方はまだ確かめていない。カメラを開いた確かめもしていない）。

■ 7. 決めてほしいこと（作業は止めていない。次に使う前に見てほしい）
1. 傾きのお手本：lab_start.sh は安全範囲と同じ raw_episodes を渡すようにした（deep1・deep1b は deep_even10_v1 の中央値 -0.61°、trim・corr 系は trim2 の -0.41°）。位置合わせで重ねる画像（trim2）とはお手本が違う。これでよいか。
2. どの傾きに合わせるか：学習データ自体が位置 1 だけ +0.41°、位置 3 は -1.03° とばらついている（全体 -1.64〜+0.62）。全体の中央値 -0.61° に合わせると、位置 1 の写り方とは約 1° 違う。全体の中央値に合わせるか、位置ごとの中央値（表の値）に合わせるか。今は全体の中央値だけを画面に出している。
3. 線 ±0.5°：依頼どおりにした。学習画像の 1 本の値は 0.2° きざみで揺れるので、中央値の近くでは表示がちらつくことがある（止めないので動きには影響しない）。

判定：合格
```

報告の表は、私が先に測った値と合いました。位置1 の11/09午前の9本は +0.41〜+0.62°、残りの1本（103049）だけが −1.43° です。午前の試行の1コマ目は、成功した 111034 が −1.02°、112346・113600・114106 は +0.65〜+0.69° でした。

- ねらい：1.7°の回転のような、目では気づけない写り方の違いを、合わせる段階で数字で見えるようにする
- 変わったこと：位置合わせの画面と [ALIGN ] の行に、今の傾き・お手本の中央値（−0.61°）・差が出る。差が ±0.5° を超えると色が変わり TILT DIFFERS と出る（止めない）。実機推論の始めの [CAMERA] にも1行出る
- 見込める効果：位置2 のような、カメラの向きで行き先が変わる失敗を、動かす前に気づける。合成画像の試験では +1° を +0.98° と測れた（報告の値）。実機で何回の失敗を防げるかは、これから数える

### commit 0531717 の差分（camera_tilt.py・test_camera_tilt.py は全文。camera_align.py・run_policy_client.py・lab_start.sh・test_lab_start.sh は変えた所）

```diff
commit 05317174b9bd7eec825671d72ebf99b6f90e5231
Author: Eiichi Shimazu <studio@adaptdesign.jp>
Date:   Tue Nov 10 17:40:03 2026 +0900

    上カメラの傾き (マットの下の辺 左下→右下 の角度. プラス=右下がり) を数字で出す pi05/camera_tilt.py を足す (tilt_deg は count_by_position.detect_corners の下の 2 つの角を使い, 取れなければ None. --dataset で各エピソードの 1 枚目を位置ごとに 中央値・最小〜最大 の表に, --live で上カメラ 1 枚の傾きとお手本の全体の中央値との差, --video で動画の 1 コマ目. 何も書き出さない). camera_align.py の画面と [ALIGN ] の行に 今の傾き・お手本の中央値・差 を出し, 差が ±0.5° を超えたら色を変えて TILT DIFFERS と出す (止めない. お手本は --tilt-raw-dir, 未指定なら --raw-dir). lab_start.sh は --tilt-raw-dir に安全範囲と同じ raw_episodes (deep1 なら deep_even10_v1) を渡す. run_policy_client.py の [CAMERA] に傾きと差を 1 行 (表示だけ・判定と停止は変えない). テストを足す (test_camera_tilt 8・lab_start 178) (依頼 142. codex 2 回)
    
    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/lab_start.sh b/lab_start.sh
index 8c18032..bd1ce6a 100755
--- a/lab_start.sh
+++ b/lab_start.sh
@@ -25,6 +25,8 @@
 #              raw_episodes (corr_v3・corr_v4 と合わせても範囲はほぼ同じで, trim2 の方が狭い. corr_v5 は 11-06 の 184650 の j2 が
 #              trim2 の上限 +31.4° を最大 11° 超える). 位置合わせ (camera_align.py) のお手本と, 実機推論の写り方の確かめの基準画像
 #              (run_policy_client.py --framing-raw-dir. 依頼 137) はどれも trim2 の raw_episodes
+#              上カメラの傾きのお手本 (camera_align.py --tilt-raw-dir・run_policy_client.py の [CAMERA] の傾き. 依頼 142) は
+#              安全範囲と同じ raw_episodes (deep1・deep1b なら deep_even10_v1). 表示だけで止めない
 #              つなぎ直しで, 残っている Pod のサーバーが別のモデルで動いていれば, そのことを出して止まる
 #
 # 1. Pod を作る (A40×1・Runpod Pytorch 2.8.0・コンテナのディスク 200GB・22/tcp を外へ出す)
@@ -115,6 +117,7 @@ RAW_RANGE=/Volumes/KIOXIA/r2r2r-data/datasets/$MODEL_RANGE_DATA/raw_episodes
 [ -d "$RAW_RANGE" ] || { echo "[ERROR] 安全範囲を作る学習データがありません: $RAW_RANGE" >&2; exit 1; }
 echo "[RANGE ] 安全範囲 (学習データ ±10°) は $RAW_RANGE から (--model $MODEL)"
 echo "[FRAME ] 写り方の確かめの基準画像は $RAW の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)"
+echo "[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は $RAW_RANGE から (安全範囲と同じ. 表示だけ. 依頼 142)"
 lab_ssh_opts
 
 HF_REPO=$MODEL_REPO                                                        # --model で選んだモデル (lab_lib.sh の lab_model_def)
@@ -448,7 +451,7 @@ while :; do
         python3 dynamic_tracking_limits/place/place_modules/08_home_from_anywhere.py || fail "HOME に戻せない"
         python3 -c "from visual_servo_pick import get_daemon_socket, daemon_set_gripper, close_daemon_socket; get_daemon_socket(); daemon_set_gripper(0); close_daemon_socket()" \
             || fail "爪を開けない"
-        python3 pi05/camera_align.py --raw-dir "$RAW" --episode "$EPISODE" --scale 5 --alpha 0.7
+        python3 pi05/camera_align.py --raw-dir "$RAW" --episode "$EPISODE" --scale 5 --alpha 0.7 --tilt-raw-dir "$RAW_RANGE"
         rc=$?
         [ $rc = 0 ] || [ $rc = 130 ] || fail "camera_align.py が失敗 (終了コード $rc)"
         stage_done
diff --git a/pi05/camera_align.py b/pi05/camera_align.py
index 93c785e..4e3c4af 100644
--- a/pi05/camera_align.py
+++ b/pi05/camera_align.py
@@ -18,6 +18,10 @@ iPhone を付けたあと, この画面で写り方を合わせてから実機
     OK       しきい値以内
     ZURE     dx か dy がしきい値の外 (= ずれ)
     NO MATCH 相関が弱く dx/dy が当てにならない (= 測れない)
+  その下に上カメラの傾き (マットの下の辺. プラス=右下がり・camera_tilt.py) と, お手本の傾きの中央値・差を出す
+  (依頼 142). 差が ±0.5° を超えたら色を変えて「TILT DIFFERS」(傾きがお手本と違う) と出す. 止めはしない.
+  傾きのお手本は --tilt-raw-dir (未指定なら --raw-dir) の各エピソードの 1 枚目の中央値.
+  lab_start.sh は安全範囲と同じ raw_episodes (deep1 なら deep_even10_v1) を渡す.
   q または Esc で終了. **表示だけで, ファイルには何も書かない.**
 
 run_policy_client.py は開始時に この measure_framing / framing_verdict で同じ検査をして,
@@ -32,6 +36,7 @@ Usage:
   python3 pi05/camera_align.py --raw-dir $RAW --episode run_0001_20261019_192343  # 位置を変える
   python3 pi05/camera_align.py --raw-dir $RAW --episode 192343                    # 部分一致でもよい
   python3 pi05/camera_align.py --raw-dir $RAW --alpha 0.7 --scale 2   # 基準を濃く / 画面を小さく
+  python3 pi05/camera_align.py --raw-dir $RAW --tilt-raw-dir $DEEP   # 傾きのお手本を別のデータにする
 """
 import argparse
 import sys
@@ -48,6 +53,7 @@ for p in (_HERE, _ROOT, _PLACE):
     sys.path.insert(0, str(p))
 
 import camera_resolver  # noqa: E402
+from camera_tilt import TILT_WARN_DEG, reference_tilt, tilt_deg, tilt_text  # noqa: E402
 
 IMG_LONG = 384            # build_demo_episodes.py / run_policy_client.py と同じ (学習画像の長辺)
 FRAMING_MAX_DY = 20.0     # |dy| がこれを超えたら「ずれ」. 下の実測値の間を取った
@@ -182,7 +188,7 @@ def _put(img, text, org, scale, color, thick):
     cv2.putText(img, text, org, cv2.FONT_HERSHEY_SIMPLEX, scale, color, thick, cv2.LINE_AA)
 
 
-def draw_overlay(small_bgr, ref_bgr, dx, dy, resp, alpha, scale, ref_label=""):
+def draw_overlay(small_bgr, ref_bgr, dx, dy, resp, alpha, scale, ref_label="", tilt=None, tilt_ref=None):
     ref = ref_bgr
     if ref.shape[:2] != small_bgr.shape[:2]:
         ref = cv2.resize(ref, (small_bgr.shape[1], small_bgr.shape[0]), interpolation=cv2.INTER_AREA)
@@ -196,6 +202,18 @@ def draw_overlay(small_bgr, ref_bgr, dx, dy, resp, alpha, scale, ref_label=""):
     _put(big, f"dy={dy:+6.1f}  dx={dx:+6.1f}  (OK: both <= {FRAMING_MAX_DY:.0f})", (16, 92), 0.6, color, 2)
     _put(big, f"resp={resp:.3f}  (OK: >= {FRAMING_MIN_RESPONSE:.2f})   q: quit", (16, 116),
          0.6, (230, 230, 230), 1)
+    # 傾き (依頼 142. 表示だけ. 写り方の判定には入れない)
+    if tilt is None:
+        _put(big, "tilt: NO CORNERS (mat corners not found)", (16, 140), 0.6, (0, 200, 255), 2)
+    elif tilt_ref is None:
+        _put(big, f"tilt={tilt:+.2f} deg  (+ = right side down)  ref: n/a", (16, 140), 0.6, (230, 230, 230), 1)
+    else:
+        d = tilt - tilt_ref
+        warn = abs(d) > TILT_WARN_DEG
+        _put(big, f"tilt={tilt:+.2f}  ref={tilt_ref:+.2f}  diff={d:+.2f} deg  (+ = right side down)", (16, 140),
+             0.6, (0, 200, 255) if warn else (0, 200, 0), 2)
+        if warn:
+            _put(big, f"TILT DIFFERS (> {TILT_WARN_DEG:g} deg)", (16, 176), 1.0, (0, 200, 255), 3)
     if ref_label:        # どのお手本を重ねているか (ブロックの位置を合わせるときの目印)
         _put(big, f"ref: {ref_label}", (16, big.shape[0] - 14), 0.6, (230, 230, 230), 1)
     return big
@@ -209,6 +227,9 @@ def main():
                     help="基準にするエピソード名 (例 run_0001_20261019_192343. "
                          "1 本だけに当てはまる部分一致 192343 でもよい). "
                          "未指定なら最初のエピソード")
+    ap.add_argument("--tilt-raw-dir", default=None,
+                    help="傾きのお手本 (各エピソードの 1 枚目の傾きの中央値) を取る学習エピソード. "
+                         "未指定なら --raw-dir (依頼 142)")
     ap.add_argument("--alpha", type=float, default=0.5, help="基準画像の重ね具合 (0=ライブのみ / 1=基準のみ)")
     ap.add_argument("--scale", type=float, default=3.0, help="画面の拡大倍率")
     args = ap.parse_args()
@@ -222,10 +243,16 @@ def main():
     print(f"[REF   ] お手本 {ep_dir.name} ({pos})"
           + ("" if args.episode else "  ← --episode 未指定なので最初のエピソード"))
     print("[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける")
+    tilt_dir = Path(args.tilt_raw_dir or args.raw_dir)
+    tilt_ref, n_ok, n_all = reference_tilt(tilt_dir)
+    print(f"[TILT  ] 傾きのお手本: {tilt_dir}  中央値 "
+          + ("測れない" if tilt_ref is None else f"{tilt_ref:+.2f}°") + f" (測れた {n_ok}/{n_all} 本. プラス=右下がり)")
+    print(f"[TILT  ] 傾きの差が ±{TILT_WARN_DEG:g}° を超えたら画面の色を変える (表示だけ. 止めない)")
     print(f"[ALIGN ] |dy| <= {FRAMING_MAX_DY:.0f} なら OK. アームは HOME に置いた状態で合わせる. "
           "q または Esc で終了 (ファイルには何も書かない)")
     cap = open_top_camera()
     last = None
+    last_tilt = None
     try:
         while True:
             for _ in range(2):            # バッファに溜まった古いフレームを捨てる
@@ -237,13 +264,16 @@ def main():
                 continue
             small = to_small_bgr(frame)
             dx, dy, resp = measure_framing(ref_bgr, small)
+            tilt = tilt_deg(small)          # 学習画像と同じ大きさで測る (お手本の中央値と同じ条件)
             cv2.imshow(WINDOW, draw_overlay(small, ref_bgr, dx, dy, resp,
-                                            args.alpha, args.scale, ref_label))
-            if last is None or abs(dy - last) > 1.0:      # 端末は値が動いたときだけ出す
+                                            args.alpha, args.scale, ref_label, tilt, tilt_ref))
+            tilt_moved = (tilt is None) != (last_tilt is None) or (
+                tilt is not None and abs(tilt - last_tilt) > 0.15)
+            if last is None or abs(dy - last) > 1.0 or tilt_moved:      # 端末は値が動いたときだけ出す
                 verdict, why = framing_verdict(dx, dy, resp)
                 print(f"[ALIGN ] dy={dy:+6.1f}  dx={dx:+6.1f}  response={resp:.3f}  "
-                      f"→ {verdict} ({why})")
-                last = dy
+                      f"→ {verdict} ({why})  {tilt_text(tilt, tilt_ref)[0]}")
+                last, last_tilt = dy, tilt
             if cv2.waitKey(30) & 0xFF in (ord("q"), 27):
                 break
     except KeyboardInterrupt:
diff --git a/pi05/camera_tilt.py b/pi05/camera_tilt.py
new file mode 100644
index 0000000..a7483e0
--- /dev/null
+++ b/pi05/camera_tilt.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+"""camera_tilt.py — 上カメラの傾き (回転) を数字で出す (2026-11-10 依頼 142・Log28).
+
+2026-11-10 の deep1 の実機推論 (Log28) で位置 2 が 4 回ともつかめなかった. 上カメラのマットの下の辺の傾きは
+学習データ deep_even10_v1 の位置 1 (11/09 午前の 9 本) だけ 右下がり 約 +0.5°, 位置 2〜5 と 11/10 の 1 回目
+(111034・成功) は 右上がり 約 −0.5〜−1.6°, 11/10 の 112346 以降 (iPhone を付け直したあと) は 右下がり 約 +0.7° だった.
+camera_align.py の写り方の確かめ (phaseCorrelate の dx/dy) は平行移動しか見ないので, 1.7° の回転は OK のまま通る.
+
+傾きの測り方 (tilt_deg):
+  count_by_position.detect_corners (mat_corner_detection) でマットの四隅を取る (左上・右上・右下・左下の順).
+  上の 2 つは画面の上 (y=0) で切れるので使わず, 下の 2 つ (左下 BL・右下 BR) を結ぶ線の角度を傾きとする.
+  度で, プラス = 右下がり (画像の y は下向きなので atan2(BR.y − BL.y, BR.x − BL.x)).
+  四隅が取れないときは None (= 測れない). 1920×1080 の生の画像でも学習データの小さい画像 (長辺 384) でも測れる.
+  小さい画像では角が 1px 単位なので, 1 枚の値は ±0.2° ほど揺れる.
+
+使い方 (読むだけ. 何も書き出さない. datasets/ にも書かない):
+  python3 pi05/camera_tilt.py --dataset mecharm_redblock_box_deep_even10_v1
+      各エピソードの 1 枚目の傾きを, 位置 (count_by_position.classify) ごとに 中央値・最小〜最大 で表にする
+  python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
+      上カメラから 1 枚取り, 今の傾きと --dataset の全体の中央値との差を出す
+  python3 pi05/camera_tilt.py --video runs/pi05_policy/pi05_20261110_111034/recording/top.mp4 ...
+      動画の 1 コマ目の傾き (記録の見直し用. いくつでも)
+
+camera_align.py (位置合わせの画面) と run_policy_client.py ([CAMERA] の行) も reference_tilt / tilt_deg で
+今の傾きとお手本の中央値との差を出す (表示だけ. 止めない).
+"""
+import argparse
+import math
+import statistics
+import sys
+from pathlib import Path
+
+import cv2
+
+_HERE = Path(__file__).resolve()
+sys.path.insert(0, str(_HERE.parent))
+sys.path.insert(0, str(_HERE.parent.parent))
+
+TILT_WARN_DEG = 0.5      # 今の傾きとお手本の中央値の差がこれを超えたら目立たせる (表示だけ. 止めない)
+
+
+def tilt_deg(frame, corners_fn=None):
+    """マットの下の辺 (左下 → 右下) の傾き [度]. プラス = 右下がり. 四隅が取れなければ None."""
+    if frame is None:
+        return None
+    if corners_fn is None:
+        from count_by_position import detect_corners as corners_fn
+    try:
+        c = corners_fn(frame)
+    except Exception:          # 見つけ方で落ちても「測れない」にする
+        return None
+    if c is None or len(c) != 4:
+        return None
+    (blx, bly), (brx, bry) = c[3], c[2]
+    dx, dy = float(brx) - float(blx), float(bry) - float(bly)
+    if dx <= 0:                # 左下が右下より右 = 四隅の取り違え
+        return None
+    return math.degrees(math.atan2(dy, dx))
+
+
+def first_frame(ep_dir):
+    """エピソードの 1 枚目 (frames/ の最初の jpg). 読めなければ None."""
+    fs = sorted((Path(ep_dir) / "frames").glob("*.jpg"))
+    return cv2.imread(str(fs[0])) if fs else None
+
+
+def video_first_frame(path):
+    cap = cv2.VideoCapture(str(path))
+    try:
+        ok, f = cap.read()
+    finally:
+        cap.release()
+    return f if ok else None
+
+
+def episode_tilts(raw_dir, corners_fn=None):
+    """raw_episodes の各エピソードの 1 枚目の傾き. 戻り値 [(エピソードのフォルダ, 傾き or None)]."""
+    eps = sorted(p.parent for p in Path(raw_dir).glob("[!.]*/episode.npz"))
+    return [(ep, tilt_deg(first_frame(ep), corners_fn)) for ep in eps]
+
+
+def reference_tilt(raw_dir, corners_fn=None):
+    """お手本の傾きの中央値 (各エピソードの 1 枚目). 戻り値 (中央値 or None, 測れた本数, 全本数)."""
+    ts = [t for _ep, t in episode_tilts(raw_dir, corners_fn)]
+    ok = [t for t in ts if t is not None]
+    return (statistics.median(ok) if ok else None), len(ok), len(ts)
+
+
+def tilt_text(cur, ref):
+    """端末用の 1 行の中身 (日本語). 戻り値 (文字, 差が線を超えたか)."""
+    if cur is None:
+        return "傾き 測れない (マットの四隅が取れない)", False
+    s = f"傾き {cur:+.2f}° (プラス=右下がり)"
+    if ref is None:
+        return s + " / お手本の中央値 測れない", False
+    d = cur - ref
+    warn = abs(d) > TILT_WARN_DEG
+    s += f" / お手本の中央値 {ref:+.2f}° / 差 {d:+.2f}°"
+    s += f" → 傾きがお手本と違う (±{TILT_WARN_DEG:g}° を超える)" if warn else f" (±{TILT_WARN_DEG:g}° 以内)"
+    return s, warn
+
+
+def _fmt(t):
+    return "測れない" if t is None else f"{t:+.2f}"
+
+
+def dataset_table(raw_dir, data_root, corners_fn=None, log=print):
+    """位置ごとの 中央値・最小〜最大 の表 (Markdown の行) と全体の中央値."""
+    import json
+
+    import count_by_position as cbp
+    runs_root = Path(data_root) / "runs"
+    by_pos = {}
+    for ep, t in episode_tilts(raw_dir, corners_fn):
+        meta = json.loads((ep / "meta.json").read_text()) if (ep / "meta.json").exists() else {}
+        stem = meta.get("run_stem", ep.name)
+        pos = cbp.classify(stem, meta, runs_root)["position"]
+        by_pos.setdefault(pos, []).append(t)
+        log(f"[TILT] {ep.name}  位置 {pos}  {_fmt(t)}°")
+    lines = ["| 位置 | 本数 | 測れた | 中央値 (°) | 最小〜最大 (°) |", "| --- | --- | --- | --- | --- |"]
+    keys = [k for k in list(cbp.POSITIONS) + [cbp.UNKNOWN] if k in by_pos]
+    alls = []
+    for k in keys:
+        ok = [t for t in by_pos[k] if t is not None]
+        alls += ok
+        name = f"位置 {k}" if k != cbp.UNKNOWN else "分からない"
+        if ok:
+            lines.append(f"| {name} | {len(by_pos[k])} | {len(ok)} | {statistics.median(ok):+.2f} | "
+                         f"{min(ok):+.2f} 〜 {max(ok):+.2f} |")
+        else:
+            lines.append(f"| {name} | {len(by_pos[k])} | 0 | — | — |")
+    n = sum(len(v) for v in by_pos.values())
+    med = statistics.median(alls) if alls else None
+    if alls:
+        lines.append(f"| 全体 | {n} | {len(alls)} | {med:+.2f} | {min(alls):+.2f} 〜 {max(alls):+.2f} |")
+    else:
+        lines.append(f"| 全体 | {n} | 0 | — | — |")
+    return lines, med
+
+
+def live_tilt():
+    """上カメラ (camera_resolver) から 1 枚取って傾きを測る (生の大きさのまま)."""
+    import camera_align
+    cap = camera_align.open_top_camera()
+    try:
+        for _ in range(3):         # バッファに溜まった古いフレームを捨てる
+            cap.grab()
+        ok, f = cap.read()
+    finally:
+        cap.release()
+    if not ok or f is None:
+        sys.exit("[ERROR] 上カメラの 1 枚を撮れない")
+    return tilt_deg(f), f.shape
+
+
+def main():
+    import count_by_position as cbp
+    ap = argparse.ArgumentParser(description="上カメラの傾き (マットの下の辺. プラス=右下がり) を出す. 読むだけ")
+    ap.add_argument("--dataset", help="データセット名 (datasets/ の下) か raw_episodes のフォルダ")
+    ap.add_argument("--live", action="store_true", help="上カメラから 1 枚取って今の傾きを出す (--dataset があれば差も)")
+    ap.add_argument("--video", nargs="*", default=[], help="動画 (top.mp4) の 1 コマ目の傾きを出す")
+    ap.add_argument("--data-root", default=str(cbp.DATA_ROOT), help=argparse.SUPPRESS)
+    args = ap.parse_args()
+    if not (args.dataset or args.live or args.video):
+        ap.error("--dataset / --live / --video のどれかを付ける")
+
+    ref = None
+    if args.dataset:
+        raw, _ds = cbp.resolve_input(args.dataset, args.data_root)
+        if not raw.is_dir():
+            sys.exit(f"[ABORT] raw_episodes がない: {raw}")
+        if args.live:             # 今の傾きとの差だけなら位置分けは要らない (速い)
+            ref, n_ok, n = reference_tilt(raw)
+            print(f"[REF ] {raw}  全体の中央値 {_fmt(ref)}°  (測れた {n_ok}/{n} 本)")
+        else:
+            lines, ref = dataset_table(raw, args.data_root)
+            print()
+            print("\n".join(lines))
+    for v in args.video:
+        t = tilt_deg(video_first_frame(v))
+        s, _w = tilt_text(t, ref) if ref is not None else (f"傾き {_fmt(t)}°", False)
+        print(f"[VIDEO] {v}  {s}")
+    if args.live:
+        t, shape = live_tilt()
+        s, warn = tilt_text(t, ref)
+        print(f"[LIVE] 上カメラ ({shape[1]}×{shape[0]}) {s}")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/pi05/run_policy_client.py b/pi05/run_policy_client.py
index 8ac31ec..2b879ca 100644
--- a/pi05/run_policy_client.py
+++ b/pi05/run_policy_client.py
@@ -135,6 +135,7 @@ import waypoint_replay as wr  # noqa: E402
 from camera_align import (  # noqa: E402
     FRAMING_MAX_DX, FRAMING_MAX_DY, FRAMING_MIN_RESPONSE, framing_verdict, load_reference, measure_framing,
 )
+from camera_tilt import reference_tilt, tilt_deg, tilt_text  # noqa: E402  依頼 142
 from fk_lookup import J1_SAFE_MAX  # noqa: E402  88.0. 本線と二重定義しない
 from j0_sweep_scan import SEND_WAIT_SEC, SPEED, _send_and_wait_arrival  # noqa: E402  本線と同じ 20 / 1.0 (待ちは下の SEND_WAIT で上書き)
 from policy_recorder import PolicyRecorder, daemon_read_servo, install_daemon_lock  # noqa: E402
@@ -1082,6 +1083,15 @@ def apply_wait_args(args):
           f"{GRIP_OPEN_DAEMON_WAIT:.1f}s) / 止まるのを待つ目安 {SETTLE_WAIT_SEC:.1f}s")
 
 
+def camera_tilt_line(bgr, raw_dir: Path) -> str:
+    """上カメラの傾きと, 安全範囲と同じお手本 (--raw-dir) の傾きの中央値との差 (依頼 142. 表示だけ. 落ちない)."""
+    try:
+        ref, _n_ok, _n = reference_tilt(raw_dir)
+        return tilt_text(tilt_deg(bgr), ref)[0] + f" (お手本: {raw_dir})"
+    except Exception as e:
+        return f"傾き 測れない ({type(e).__name__}: {e})"
+
+
 def framing_raw_dir(args) -> Path:
     """写り方の確かめの基準画像を取るお手本. --framing-raw-dir がなければ --raw-dir (依頼 137)."""
     return Path(args.framing_raw_dir or args.raw_dir)
@@ -1205,6 +1215,7 @@ def main():
         verdict, why = framing_verdict(dx, dy, resp)
         print(f"[CAMERA] 写り方 dy={dy:+.1f} dx={dx:+.1f} response={resp:.3f} → {verdict} ({why})")
         print(f"[CAMERA] 基準: {ref_path}")
+        print(f"[CAMERA] {camera_tilt_line(bgr0, Path(args.raw_dir))}")   # 依頼 142. 表示だけ (判定・停止は変えない)
         log_f.write(json.dumps({"type": "framing", "dy": round(dy, 2), "dx": round(dx, 2),
                                 "response": round(resp, 3), "verdict": verdict, "why": why,
                                 "max_dy": FRAMING_MAX_DY, "max_dx": FRAMING_MAX_DX,
diff --git a/pi05/test_camera_tilt.py b/pi05/test_camera_tilt.py
new file mode 100644
index 0000000..6882772
--- /dev/null
+++ b/pi05/test_camera_tilt.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python3
+"""test_camera_tilt.py — 上カメラの傾き (camera_tilt.py・依頼 142).
+
+  - マット (黒い四角. 上は画面の上で切れる) を ±1° 回した合成画像で, 向き (プラス=右下がり) と値が正しく出る
+    (1920×1080 の生の大きさと, 学習画像の大きさ 384×216 の両方)
+  - 四隅が取れない (真っ白・画像なし・見つけ方が落ちる・左右の取り違え) ときは None で, 落ちない
+  - お手本の中央値 (reference_tilt) と差の文字 (tilt_text) の ±0.5° の線
+  - camera_align.draw_overlay が傾きあり・なし・差が大きいときに落ちない
+  - camera_align.main のループで 数値 → 測れない → 数値 のとき [ALIGN ] 行に傾きが出る (カメラは偽物)
+  - run_policy_client.camera_tilt_line がお手本のフォルダが無くても・測り方が落ちても 1 行を返す
+
+アーム・カメラには触らない.
+
+    python3 pi05/test_camera_tilt.py
+"""
+import math
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+
+import cv2
+import numpy as np
+
+_HERE = Path(__file__).resolve().parent
+sys.path.insert(0, str(_HERE))
+
+import camera_tilt as ct  # noqa: E402
+
+
+def mat_image(deg, w=1920, h=1080):
+    """白い机に黒いマット. マットの上は画面の上で切れる. 画面の中心で deg 度回す (プラス=右下がり)."""
+    img = np.full((h, w, 3), 230, np.uint8)
+    cx, cy = w / 2, h * 0.45
+    hw, hh = w * 0.32, h * 0.75                       # 上は画面の外まで伸ばす
+    pts = np.array([[-hw, -hh], [hw, -hh], [hw, hh * 0.55], [-hw, hh * 0.55]])
+    r = math.radians(deg)
+    rot = np.array([[math.cos(r), -math.sin(r)], [math.sin(r), math.cos(r)]])   # y は下向き
+    p = (pts @ rot.T) + [cx, cy]
+    cv2.fillPoly(img, [np.round(p).astype(np.int32)], (20, 20, 20), cv2.LINE_AA)
+    return img
+
+
+def small(img):
+    return cv2.resize(img, (384, 216), interpolation=cv2.INTER_AREA)
+
+
+class TestTilt(unittest.TestCase):
+    def test_sign_and_value_full(self):
+        for deg in (+1.0, -1.0, 0.0, +1.7, -0.5):
+            t = ct.tilt_deg(mat_image(deg))
+            self.assertIsNotNone(t, deg)
+            self.assertAlmostEqual(t, deg, delta=0.1, msg=f"{deg}: {t}")
+
+    def test_sign_and_value_small(self):
+        for deg in (+1.0, -1.0, 0.0):
+            t = ct.tilt_deg(small(mat_image(deg)))
+            self.assertIsNotNone(t, deg)
+            self.assertAlmostEqual(t, deg, delta=0.35, msg=f"{deg}: {t}")
+        self.assertGreater(ct.tilt_deg(small(mat_image(+1.0))), 0.5)    # 右下がり = プラス
+        self.assertLess(ct.tilt_deg(small(mat_image(-1.0))), -0.5)      # 右上がり = マイナス
+
+    def test_unmeasurable(self):
+        self.assertIsNone(ct.tilt_deg(np.full((216, 384, 3), 255, np.uint8)))   # マットが無い
+        self.assertIsNone(ct.tilt_deg(None))
+        self.assertIsNone(ct.tilt_deg(mat_image(1.0), corners_fn=lambda f: None))
+
+        def boom(_f):
+            raise RuntimeError("x")
+        self.assertIsNone(ct.tilt_deg(mat_image(1.0), corners_fn=boom))
+        swapped = lambda f: [(0, 0), (10, 0), (5, 10), (50, 10)]      # 左下が右下より右
+        self.assertIsNone(ct.tilt_deg(mat_image(1.0), corners_fn=swapped))
+        self.assertIsNone(ct.tilt_deg(mat_image(1.0), corners_fn=lambda f: [(0, 0), (1, 1)]))
+
+    def test_reference_and_text(self):
+        with tempfile.TemporaryDirectory() as d:
+            self.assertEqual(ct.reference_tilt(d), (None, 0, 0))            # 空のフォルダ
+            for i, deg in enumerate((-1.0, -0.5, 0.0)):
+                ep = Path(d) / f"ep{i}"
+                (ep / "frames").mkdir(parents=True)
+                np.savez(ep / "episode.npz", state=np.zeros((2, 7)))
+                cv2.imwrite(str(ep / "frames" / "000000.jpg"), small(mat_image(deg)))
+            bad = Path(d) / "ep9"                                           # 画像が無い本は測れない
+            (bad / "frames").mkdir(parents=True)
+            np.savez(bad / "episode.npz", state=np.zeros((2, 7)))
+            ref, n_ok, n = ct.reference_tilt(d)
+            self.assertEqual((n_ok, n), (3, 4))
+            self.assertAlmostEqual(ref, -0.5, delta=0.35)
+        s, warn = ct.tilt_text(0.7, -0.6)
+        self.assertTrue(warn)
+        self.assertIn("差 +1.30°", s)
+        self.assertIn("傾きがお手本と違う", s)
+        self.assertFalse(ct.tilt_text(-0.2, -0.6)[1])                       # 0.4° は線の内
+        self.assertFalse(ct.tilt_text(None, -0.6)[1])
+        self.assertIn("測れない", ct.tilt_text(None, -0.6)[0])
+        self.assertIn("お手本の中央値 測れない", ct.tilt_text(0.1, None)[0])
+
+    def test_camera_align_overlay(self):
+        import camera_align as ca
+        img = small(mat_image(1.0))
+        for tilt, ref in ((None, None), (0.9, None), (0.9, 0.8), (0.9, -0.6)):
+            big = ca.draw_overlay(img, img, 0.0, 0.0, 1.0, 0.5, 2.0, "ep", tilt, ref)
+            self.assertEqual(big.shape[:2], (432, 768))
+        ca.draw_overlay(img, img, 0.0, 0.0, 1.0, 0.5, 2.0, "ep")          # 今までの呼び方も通る
+
+    def test_camera_align_loop(self):
+        """位置合わせのループ: 数値 → 測れない → 数値 のとき [ALIGN ] 行に傾きが出て, 落ちない (codex R1)."""
+        import contextlib
+        import io
+        from unittest import mock
+
+        import camera_align as ca
+        frames = [mat_image(1.0), np.full((1080, 1920, 3), 255, np.uint8), mat_image(-1.0)]
+
+        class Cap:
+            def __init__(self):
+                self.i = 0
+
+            def grab(self):
+                return True
+
+            def read(self):
+                if self.i >= len(frames):
+                    return False, None
+                self.i += 1
+                return True, frames[self.i - 1]
+
+            def release(self):
+                pass
+
+        keys = iter([0, 0, ord("q")])
+        ref = small(mat_image(0.0))
+        out = io.StringIO()
+        with mock.patch.object(ca, "open_top_camera", return_value=Cap()), \
+                mock.patch.object(ca, "load_reference", return_value=(ref, Path("/x/ep/frames/000000.jpg"))), \
+                mock.patch.object(ca, "reference_tilt", return_value=(-0.6, 50, 50)), \
+                mock.patch.object(ca, "grasp_j0", return_value=None), \
+                mock.patch.object(ca.cv2, "imshow"), mock.patch.object(ca.cv2, "destroyAllWindows"), \
+                mock.patch.object(ca.cv2, "waitKey", side_effect=lambda _t: next(keys)), \
+                mock.patch.object(sys, "argv", ["camera_align.py", "--raw-dir", "/x"]), \
+                contextlib.redirect_stdout(out):
+            ca.main()
+        al = [l for l in out.getvalue().splitlines() if l.startswith("[ALIGN ] dy=")]
+        self.assertEqual(len(al), 3, out.getvalue())
+        self.assertIn("差 +1.5", al[0])
+        self.assertIn("傾きがお手本と違う", al[0])
+        self.assertIn("傾き 測れない", al[1])
+        self.assertIn("差 -0.3", al[2])
+        self.assertIn("お手本の中央値 -0.60°", out.getvalue())
+
+    def test_run_policy_client_line_exception(self):
+        """傾きの測り方が落ちても 1 行の文字を返し, 推論は止めない (codex R1)."""
+        from unittest import mock
+
+        import run_policy_client as rpc
+        with mock.patch.object(rpc, "reference_tilt", side_effect=OSError("disk")):
+            self.assertIn("測れない", rpc.camera_tilt_line(small(mat_image(1.0)), Path("/x")))
+        with mock.patch.object(rpc, "tilt_deg", side_effect=ValueError("x")), \
+                mock.patch.object(rpc, "reference_tilt", return_value=(0.0, 1, 1)):
+            self.assertIn("測れない", rpc.camera_tilt_line(small(mat_image(1.0)), Path("/x")))
+
+    def test_run_policy_client_line(self):
+        import run_policy_client as rpc
+        s = rpc.camera_tilt_line(small(mat_image(1.0)), Path("/nonexistent/raw_episodes"))
+        self.assertIn("お手本の中央値 測れない", s)
+        self.assertIn("傾き +", s)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/test_lab_start.sh b/test_lab_start.sh
index c98ed67..d64080c 100755
--- a/test_lab_start.sh
+++ b/test_lab_start.sh
@@ -424,6 +424,7 @@ check "deep1 の設定とフォルダでサーバーを起動" 'has ssh.log "--p
 check "クライアントに --model deep1 を渡す" 'has py.log "--model deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)"'
 check "deep1 の安全範囲 (run_policy_client.py --raw-dir) は deep_even10_v1 の raw_episodes・trim2 は使わない (依頼 135)" 'has py.log "run_policy_client.py --raw-dir $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes --steps-per-chunk" && ! has py.log "--raw-dir $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes" && has out.txt "[RANGE ] 安全範囲 (学習データ ±10°) は $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)"'
 check "deep1 の写り方の基準画像 (--framing-raw-dir) は trim2・deep_even10_v1 ではない (依頼 137)" 'has py.log "--ignore-framing --model deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1) --framing-raw-dir $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes" && ! has py.log "--framing-raw-dir $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes" && has out.txt "[FRAME ] 写り方の確かめの基準画像は $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから" && has out.txt "[RANGE ] 安全範囲 (学習データ ±10°) は $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)"'
+check "deep1 の傾きのお手本は安全範囲と同じ deep_even10_v1 (依頼 142)" 'has out.txt "[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から"'
 check "記録に model=deep1" '[ "$(jq -r .model "$T/data/lab/LogT/pod/lab_pod.json")" = deep1 ]'
 rm -rf "$T"
 setup; rmdir "$T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes"; run --model deep1
@@ -562,6 +563,9 @@ rm -rf "$d/params" "$d/_CHECKPOINT_METADATA"; mkdir -p "$d/assets/local"
 check "済んだ印と assets だけ (前の確かめなら ckpt=1 だった形) → ckpt=0" '[[ "$(pr)" == *" ckpt=0 mark=1 "* ]]'
 rm -rf "$d"
 
+# 位置合わせ (camera_align.py) はドライランでは流れないので, 書き方を lab_start.sh から確かめる (依頼 142)
+check "camera_align.py に傾きのお手本 --tilt-raw-dir \"\$RAW_RANGE\" を渡す (依頼 142)" 'grep -qF "python3 pi05/camera_align.py --raw-dir \"\$RAW\" --episode \"\$EPISODE\" --scale 5 --alpha 0.7 --tilt-raw-dir \"\$RAW_RANGE\"" "$HERE/lab_start.sh"'
+
 echo
 echo "OK $OK / NG $NG"
 [ $NG = 0 ]
```

Claude Code からは決めてほしいことが3つ来ていて、次のように答えました（依頼144 の頭に書いて渡した）。

1. 傾きのお手本は、安全範囲と同じデータ（deep_even10_v1 の −0.61°）のままでよい。今回問題になったのは deep_even10_v1 の中の偏りなので、そことそろえる
2. 合わせる先は全体の中央値。位置ごとの中央値に合わせると、カメラの向きで位置が分かる状態が続く
3. ±0.5°の線と、表示が 0.2°きざみで揺れることは、止めないのでこのままでよい


## スマホを手で回して傾きを合わせるのは難しかった

道具ができたので、スマホを回して合わせました。最初の打ち方は、r2r2r-scripts のフォルダに入らずに打ったので、ファイルが見つかりませんでした。

### 端末：camera_tilt.py --live で今の傾きを測る

```text
Last login: Tue Nov 10 14:42:07 on ttys005
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
python3: can't open file '/Users/shimazueiichi/pi05/camera_tilt.py': [Errno 2] No such file or directory
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % cd ~/r2r2r-scripts && python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
[REF ] /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  全体の中央値 -0.61°  (測れた 50/50 本)
[LIVE] 上カメラ (1920×1080) 傾き +1.69° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.30° → 傾きがお手本と違う (±0.5° を超える)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts % ccd ~/r2r2r-scripts && python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
zsh: command not found: ccd
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts % cd ~/r2r2r-scripts && python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
[REF ] /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  全体の中央値 -0.61°  (測れた 50/50 本)
[LIVE] 上カメラ (1920×1080) 傾き +1.77° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.38° → 傾きがお手本と違う (±0.5° を超える)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts % cd ~/r2r2r-scripts && python3 pi05/camera_tilt.py --live --dataset mecharm_redblock_box_deep_even10_v1
[REF ] /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  全体の中央値 -0.61°  (測れた 50/50 本)
[LIVE] 上カメラ (1920×1080) 傾き +1.65° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.26° → 傾きがお手本と違う (±0.5° を超える)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts %
```

3回とも +1.65〜+1.77° で、午前（+0.7°）からさらに回っていました。1回ずつ打つのは手間なので、位置合わせの画面（透かし）を単独で開き、傾きを見ながらスマホを回しました。画面は、基準の画像（お手本の1枚目）と今のカメラを重ねて表示し、上に平行移動のずれと傾きを出します。

【画像】LOG486_04_overlay_no_match.png 位置合わせの画面。上下に約31、左右に約17ずれて NO MATCH、傾きは +1.24°。マット・白い箱・爪が2重に写っている

【画像】LOG486_05_overlay_tilt_minus123.png 平行移動は OK になったが、傾きは −1.23°で、逆向きに回しすぎた

【画像】LOG486_06_overlay_tilt_plus082.png 重なりはほぼぴったり（dy −1.0・dx +1.3・相関 0.64）なのに、傾きは +0.82°

3枚目の画面は、重なりがほぼぴったりなのに傾きが合っていません。原因は2つありました。

1つ目は、重ねている基準の画像と、傾きの比べる相手が違うことです。重ねる画像（trim2 の 183454・10/19 の位置1 のお手本）は、それ自体が +0.2〜+0.4°傾いています。傾きの数字は学習データ50本の中央値 −0.61° と比べるので、重なりをぴったり合わせるほど傾きの数字は合わなくなります。測る画像の大きさで値が変わっていないかも、同時に確かめました。

### tilt_res_cmp.py（同じ1枚を、学習データの大きさ 384×216 と 1920×1080 で測り比べる）

```python
import sys, cv2
sys.path.insert(0,'pi05'); sys.path.insert(0,'.')
from pathlib import Path
import camera_tilt as ct, count_by_position as cb
root=Path(sys.argv[1])
for ds,stem in [('mecharm_redblock_box_teach_v1_trim2','run_0001_20261019_183454'),('mecharm_redblock_box_deep_even10_v1','run_0001_20261019_191541'),('mecharm_redblock_box_deep_even10_v1','run_0001_20261109_094839'),('mecharm_redblock_box_deep_even10_v1','run_0001_20261109_102109')]:
    small=cv2.imread(str(root/'datasets'/ds/'raw_episodes'/stem/'frames/000000.jpg'))
    rd=cb.find_run_dir(stem, root/'runs'); cap=cv2.VideoCapture(str(rd/'recording/top.mp4')); ok,big=cap.read(); cap.release()
    f=[n for n in dir(ct) if 'tilt' in n.lower()]
    print(stem, small.shape, big.shape, f)
    print('   small', round(ct.tilt_deg(small),2) if ct.tilt_deg(small) is not None else None, ' big', ct.tilt_deg(big), ' big->small', ct.tilt_deg(cv2.resize(big,(384,216),interpolation=cv2.INTER_AREA)))
```

### tilt_res_cmp.py の出力

```text
run_0001_20261019_183454 (216, 384, 3) (1080, 1920, 3) ['TILT_WARN_DEG', 'episode_tilts', 'live_tilt', 'reference_tilt', 'tilt_deg', 'tilt_text']
   small 0.2  big 0.36675318121020006  big->small 0.2046269139658639
run_0001_20261019_191541 (216, 384, 3) (1080, 1920, 3) ['TILT_WARN_DEG', 'episode_tilts', 'live_tilt', 'reference_tilt', 'tilt_deg', 'tilt_text']
   small -0.61  big -0.5305013166738104  big->small -0.613859863109328
run_0001_20261109_094839 (216, 384, 3) (1080, 1920, 3) ['TILT_WARN_DEG', 'episode_tilts', 'live_tilt', 'reference_tilt', 'tilt_deg', 'tilt_text']
   small 0.62  big 0.4914456703916715  big->small 0.6160599083992302
run_0001_20261109_102109 (216, 384, 3) (1080, 1920, 3) ['TILT_WARN_DEG', 'episode_tilts', 'live_tilt', 'reference_tilt', 'tilt_deg', 'tilt_text']
   small -0.82  big -1.0244934394563543  big->small -0.8184554616886145
```

大きさによる違いは 0.2°ほどで、183454 は小さい画像で +0.20°、大きい画像で +0.37° でした。

2つ目は、±0.5°が手で回すには細かすぎることです。0.5°はスマホの端がほんの1mmほど動く差で、数字も1回ごとに約0.2°揺れます。

位置2 のお手本は −1.03〜−0.41°の範囲で撮られているので、この試しには −0.4〜−1.6°のどこかに入れば足りる、と線をゆるめて合わせ直しました。

【画像】LOG486_07_overlay_tilt_minus102.png 傾き −1.02°・差 −0.41°で、すべて OK。薄く写る左のブロックが今の位置2、爪の右下に隠れているのが基準の画像（位置5 のお手本）のブロック

重ねている基準の画像は位置5 のお手本（194818）なので、その赤いブロックは爪の右下に隠れていて、位置2 に置いたブロックは重なりません。ここではカメラを合わせるためだけに使っています。


## 依頼144：上カメラの傾きをソフトでそろえてから π0.5 に渡す

手で ±0.5°に入れるのは毎回の手間になるので、カメラの画像を π0.5 に渡す前にソフトの側で回し、学習データの傾きにそろえる形を依頼しました。スマホはだいたいの位置に置くだけで済む見込みです。この回の時点では依頼箱で順番待ちで、まだ動いていません。

### 依頼文（依頼箱 20261110_144）

```markdown
---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 90
---
# 依頼144：上カメラの傾きをソフトで直してから AI に渡す

実機は動かさない（実装・テスト・レビューまで）。エイイチは見ていない。Log28。

## 依頼142 の「決めてほしいこと」への答え（エイイチ 2026-11-10）
1. 傾きのお手本は安全範囲と同じデータ（deep 系は deep_even10_v1 の −0.61°）のままでよい
2. 合わせる先は全体の中央値。位置ごとの中央値は使わない
3. ±0.5° の線と表示のちらつきはこのままでよい

## なぜ
依頼142 の道具で iPhone を手で回して合わせてみたが、±0.5° に入れるのが難しい（0.5° は iPhone の端で約1mm、測るたびに約0.2°揺れる）。
さらに、位置合わせで重ねる基準画像 trim2 の run_0001_20261019_183454 は、それ自体の傾きが +0.2〜+0.4° あり、
重なりをぴったり合わせると傾きの数字が合わない（重なり OK・傾き +0.82° になった）。
iPhone はだいたいの位置に置くだけにして、傾きはソフトでそろえたい。

## やること
1. run_policy_client.py：実機推論の始め（HOME にいる間）に上カメラの傾きを数コマ測り（中央値）、
   お手本の中央値（--tilt-raw-dir。deep 系は deep_even10_v1）との差だけ画像を回してから policy.infer の observation/image に渡す。
   - 回すのは画像の中心まわり（cv2.warpAffine。縁は BORDER_REPLICATE などで黒い三角が出ないように。どれにしたかを報告に）
   - 角度は始めに1回決めて、その試行の間は同じ角度を使う（腕でマットの角が隠れるので、動いている間は測り直さない）
   - 測れなかったときは回さずに続け、そう1行出す（止めない）
   - 差が大きくても止めない（3° を超えたら目立つ表示だけ）
   - [CAMERA] の行に「傾き +0.82° → −0.61° にそろえて渡す（−1.43° 回す）」のように出す
   - log.jsonl の頭に、測った傾き・お手本の値・回した角度を残す
   - `--no-tilt-fix` で今までどおり回さない形にできるようにする。lab_start.sh は回す方を既定にする
2. 写り方の確かめ（phaseCorrelate・±20px）は、回したあとの画像で測る（判定の線は変えない）。どちらで測ったかを [CAMERA] に出す
3. camera_align.py の位置合わせの画面：ライブの画像を 1 と同じ角度で回して重ねる（AI に渡る画像と同じ見え方にする）。
   傾きの行に「ソフトでそろえる」と出し、TILT DIFFERS は 3° を超えたときだけにする
4. 記録（recording/top.mp4 など）と学習データづくり（build_correction_episodes.py など）：
   - 録画は今までどおり回していない元の画像のまま残す。回した角度は記録に残す（1 の log.jsonl）
   - 補正入りの試行から学習データを作るとき、どの画像をデータに入れるべきか（元の画像か、回した画像か）を調べ、案と理由を報告に書く。今回は build_* は変えない
5. テスト：合成画像でマットを ±1°・±2° 回したものを直すと、直したあとの傾きがお手本の値から 0.2° 以内になること。測れないときに落ちないこと。--no-tilt-fix で今までどおりになること。既存のテスト（test_lab_start.sh ほか pi05/ のもの）も通す
6. 実データでの確かめ：Log28 の pi05_20261110_112346・113600 の top.mp4 の1コマ目を 1 と同じ処理で回し、回したあとの傾きと、
   trim2 の 183454 の1枚目との phaseCorrelate（dx・dy・response）を、回す前と後で並べて報告に貼る。回す前と後の画像を
   KIOXIA の lab/Log28/analysis/tilt_fix/ に jpg で置く（ここだけに書く）
7. codex で差分を見てもらう。指摘を直して通るまで
8. 変えたファイルだけを指定して git add・commit（git add . は使わない）

## やらないこと
- 関節・安全範囲・送る速さ・爪の扱いは変えない
- lab_train.sh・学習の設定・datasets/ は触らない
- 平行移動（dx・dy）はソフトで直さない（必要そうなら報告の「決めてほしいこと」へ）

## 報告
claude_code_reports/20261110_144.txt に：変えた所、5・6 の結果、4 の案、codex の回数と指摘、commit の番号、エイイチが次に実機で試すときの打ち方（lab_start.sh の打ち方が変わらないならそう書く）、決めてほしいこと。
**報告の最後の1行は必ず次のどれか**：「判定：合格」／「判定：止めた（決めてほしいことあり）」／「判定：失敗」
```

- ねらい：スマホを付け直すたびに変わるカメラの向きを、学習データの向きに自動でそろえる
- 変わること：実機推論の始め（HOME の間）に傾きを測り、学習データの中央値との差だけ画像を回してから π0.5 に渡す。録画は元の画像のまま残し、回した角度を記録する
- 見込める効果：位置合わせで傾きを手で合わせる時間がなくなる見込み。ただし、回して渡した画像で π0.5 が位置2 まで届くかは、まだ確かめていない


## 傾きを直すと、π0.5 は位置2 のつかむ姿勢まで向かった

カメラを合わせ直し（傾き −0.82〜−1.03°）、新しい Pod を借りて、位置2 にブロックを置いて試しました。夜の試行は4回で、どれも途中で止めています。

| 試行 | 始めの傾き | どこまで進めたか | 終わり方 |
|---|---|---|---|
| 191618 | −1.03° | 推論5（j1 が上限 88° で切られる所）のあと p | ゲームパッドの補正で推論のプロセスが落ちた（Segmentation fault） |
| 191905 | −1.03° | 推論の前 | Ctrl-C で止めた |
| 192201 | −0.82° | 推論4（j1 88°・j2 約−31°）のあと p | ゲームパッドの補正で固まった |
| 193431 | −0.82° | 推論3 を送ったあと、推論4（閉じる指令）の前に h | 爪がブロックの奥側にずれていたので HOME へ戻した |
| 194015 | +4.59°（測り違いと見ている） | 推論3 のあと p | ゲームパッドの補正で固まった |

π0.5 がどこへ行こうとしたかを、午前と夜で比べました。log.jsonl に残る、π0.5 が出した10ステップ先までの行き先を、最初に人が操作する手前までつなぎ、肩 j1 の最大とひじ j2 の最小を出しています（最後の1チャンクは送らなかった分も含む）。

### extract_LOG486.py（位置2 の試行で π0.5 が出した行き先を、午前と夜で書き出す）

```python
"""LOG486 のサムネイル用: 2026-11-10 の位置2 の試行で、π0.5 が出した行き先（log.jsonl の actions を、最初の人の操作の手前まで順につなぐ。
最後の1チャンクは送らなかったものも含める）。before = 午前（カメラが右下がり約 +0.7°）、after = 夜（右上がり約 -0.8〜-1.0°）。点は [j0, j1, j2, 閉じる指令か, 'm']"""
import json, sys
R = lambda v: round(float(v), 1)
def aims(s):
    pts = []
    for l in open(f'runs/pi05_policy/pi05_20261110_{s}/log.jsonl'):
        x = json.loads(l)
        if 'actions' in x:
            pts += [[R(a[0]), R(a[1]), R(a[2]), 1 if a[6] > 0.5 else 0, 'm'] for a in x['actions']]
        elif x.get('type') not in ('model', 'framing'):
            break
    return pts
o = {'LOG486': {'before': [aims(s) for s in ('113433', '113600', '113741', '114106')],
                'after': [aims(s) for s in ('191618', '192201', '193431', '194015')]}}
json.dump(o, open(sys.argv[1], 'w'), separators=(',', ':'))
for k in ('before', 'after'):
    for r in o['LOG486'][k]:
        print(k, len(r), 'j1 max', max(p[1] for p in r), 'j2 min', min(p[2] for p in r))
```

### extract_LOG486.py の出力（上の4行が午前の 113433・113600・113741・114106、下の4行が夜の 191618・192201・193431・194015）

```text
before 50 j1 max 79.7 j2 min -18.5
before 40 j1 max 72.4 j2 min -11.4
before 40 j1 max 70.6 j2 min -9.9
before 40 j1 max 73.3 j2 min -13.7
after 60 j1 max 93.7 j2 min -34.8
after 50 j1 max 88.4 j2 min -30.4
after 50 j1 max 88.5 j2 min -33.6
after 40 j1 max 76.2 j2 min -17.4
```

午前の4回は j1 が 70〜80°、j2 が −10〜−18°までしか行こうとしていません。夜は、途中で止めた 194015 を除く3回が、j1 88〜94°、j2 −30〜−35°まで向かっています。位置2 のお手本のつかむ姿勢は j1 87.9°・j2 −33.9°です。同じ位置2 のブロックで、変えたつもりだったのはカメラの傾きです。ただ、付け直したので、上下左右の位置も数px変わっています（実機推論の始めの写り方の確かめの値は、午前の位置1・位置2 の7回が dy +1.8〜+2.7・dx +3.7〜+4.9、夜の5回が dy −2.0〜−2.7・dx +6.2〜+7.2）。

**上カメラの傾きを学習データにそろえ直すと、π0.5 は位置2 のつかむ姿勢へ向かう答えを出すようになりました。** ただし、これで傾きが原因だと確定したわけではありません。何が確かめられていないかは、下の「傾きだけが原因とは、まだ言い切れない」の節にまとめました。

191618 では、π0.5 が j1 を 93.7°まで出していて、肩の上限 88°で切られています。

```
[005] 推論 871ms  10ステップ先  Δ= +1.5  +9.7 -16.7  -0.4  +8.2  -1.6
       [LIMIT] j1 安全範囲でクランプ +93.7→+88.0
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       [LIMIT] step 5: j1 安全範囲でクランプ +88.2→+88.0
       [LIMIT] step 8: j1 安全範囲でクランプ +92.8→+88.0
       [LIMIT] step 9: j1 安全範囲でクランプ +93.7→+88.0
```

位置2 のお手本の j1 は 87.9°で、上限の 88°とほぼ同じです。π0.5 の答えが少し上へぶれると、上限で切られて届かなくなる位置でもあります。


## 傾きだけが原因とは、まだ言い切れない

午前と夜の違いは、カメラの傾きのほかにもあり得ます。照明は画像の明るさと色味で比べ、ほぼ同じでした。

### tilt_brightness.py（学習データ・午前・夜の1枚目で、明るさと青と赤の比を比べる）

```python
# 学習データ・午前・夜の1枚目で、画像全体とマットの明るさ、青と赤の比を比べる（照明の違いを疑ったため。2026-11-10 夜）
# cd /Volumes/KIOXIA/r2r2r-data && python3 <このファイル>
import cv2, numpy as np, glob
def first(d):
    fr=sorted(glob.glob(d+'/frames/*.jpg'))
    if fr: return cv2.imread(fr[0])
    cap=cv2.VideoCapture(d+'/recording/top.mp4'); ok,f=cap.read(); return f
for d in ['runs/run_pick_place/run_0001_20261019_191541','runs/run_pick_place/run_0001_20261109_094839','runs/run_pick_place/run_0001_20261109_102109',
          'runs/pi05_policy/pi05_20261110_111034','runs/pi05_policy/pi05_20261110_113600','runs/pi05_policy/pi05_20261110_191618','runs/pi05_policy/pi05_20261110_193431']:
    f=first(d); 
    if f is None: print(d,'none'); continue
    f=cv2.resize(f,(384,216)); g=cv2.cvtColor(f,cv2.COLOR_BGR2GRAY); hsv=cv2.cvtColor(f,cv2.COLOR_BGR2HSV)
    mat=g[60:200,80:300]
    print(d.split('/')[-1], 'mean',round(g.mean(),1),'mat',round(mat.mean(),1),'B/R',round(f[...,0].mean()/f[...,2].mean(),3))
```

### tilt_brightness.py の出力（mean は画像全体、mat はマットのあたりの明るさ。B/R は青と赤の比）

```text
run_0001_20261019_191541 mean 130.0 mat 102.3 B/R 0.965
run_0001_20261109_094839 mean 129.2 mat 101.8 B/R 0.975
run_0001_20261109_102109 mean 129.4 mat 102.4 B/R 0.962
pi05_20261110_111034 mean 130.2 mat 105.6 B/R 0.968
pi05_20261110_113600 mean 128.4 mat 102.7 B/R 0.968
pi05_20261110_191618 mean 129.6 mat 103.4 B/R 0.96
pi05_20261110_193431 mean 129.4 mat 103.7 B/R 0.96
```

明るさは 128〜130、マットのあたりは 102〜106、青と赤の比は 0.96〜0.98 で、学習データ・午前・夜でほぼ同じでした。照明が違ったから、という説明は弱いと見ています。

まだ消せていない疑いは次の4つです。

| 疑い | 今わかっていること |
|---|---|
| 変えたのは傾きだけではない | 付け直したので、上下左右の位置も数px変わった。傾きと位置のどちらが効いたかは分けられていない |
| 回数が少ない | 比べたのは午前4回と夜3回。π0.5 は同じ画像でも毎回少しずつ違う答えを出すので、偶然の範囲かもしれない |
| 逆の向きを試していない | 夜に、わざと右下がりへ戻して、また届かなくなるかは確かめていない |
| 「位置1 だと思った」とまでは言えない | 午前に止まった姿勢は、位置1 のつかむ姿勢（ひじ j2 約 0°）そのものではない。言えるのは「位置2 まで下りなかった」まで |

決着は、実機を動かさずに、録画済みの画像だけでつけられます。

1. 午前に届かなかった 113600 の1枚目を、ソフトで約1.5°右上がりに回して π0.5 に渡し、行き先が位置2 へ変わるかを見る
2. 逆に、夜の 193431 の1枚目を右下がりに回して、位置2 へ行かなくなるかを見る
3. 関節の角度は元のまま、回す角度だけを変えて、それぞれ10回ずつ答えを出させ、ばらつきも見る

画像の回転以外は何も変えないので、傾きが原因なら行き先がはっきり入れ替わり、変わらなければ別の原因を探すことになります。


## 最後の数mmで、爪はブロックの奥側にずれた

193431 は、推論2 のあとの姿勢を、位置2 のお手本が通る途中の姿勢と比べました。

### demo_traj.py（位置2 のお手本2本の関節角を、閉じる前まで8コマおきに出す）

```python
# 位置2 のお手本2本（deep_even10_v1）の関節角を、閉じる前まで8コマおきに出す（2026-11-10 夜に Mac 連携の shell で実行）
# cd /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes; python3 -c "<下>"
import numpy as np
for ep in ['run_0001_20261109_102109','run_0001_20261019_191541']:
    s=np.load(ep+'/episode.npz')['state']
    ch=np.flatnonzero(s[1:,6]>s[:-1,6])+1
    print(ep,'close idx',ch[:1],len(s))
    for i in range(0,(ch[0] if len(ch) else len(s))+1,8): print(i, np.round(s[i,:7],1))
```

### demo_traj.py の出力（j0〜j5 と爪。最後の列が 0 の間は開いている）

```text
run_0001_20261109_102109 close idx [47] 141
0 [ -0.4   1.6   1.9   0.1  -0.3 -30.2   0. ]
8 [ -8.   16.2  -2.2   1.7 -12.6 -31.6   0. ]
16 [-20.9  39.   -8.6   5.  -32.1 -32.9   0. ]
24 [-32.9  62.6 -13.9   6.2 -52.3 -33.    0. ]
32 [-35.8  68.  -14.1   6.9 -57.  -33.1   0. ]
40 [-35.2  81.  -26.9   6.8 -50.3 -34.    0. ]
run_0001_20261019_191541 close idx [45] 139
0 [ -0.4   1.5   0.8  -0.2  -0.3 -30.2   0. ]
8 [ -8.   16.4  -2.6   1.5 -12.9 -31.4   0. ]
16 [-21.1  39.2  -8.1   4.7 -32.6 -32.8   0. ]
24 [-32.9  62.9 -13.7   6.5 -52.7 -32.7   0. ]
32 [-35.6  70.  -16.3   6.6 -56.1 -33.1   0. ]
40 [-35.7  85.1 -31.2   6.4 -47.7 -34.3   0. ]
```

| | j0 | j1 | j2 | j4 |
|---|---|---|---|---|
| 193431 の推論2 のあと | −37.4 | +67.4 | −12.7 | −58.2 |
| お手本 102109（つかむ15コマ前） | −35.8 | +68.0 | −14.1 | −57.0 |
| お手本 191541（同じ所） | −35.6 | +70.0 | −16.3 | −56.1 |
| お手本でつかむ直前 | −35.2〜−35.7 | +81〜+85 | −27〜−31 | −48〜−50 |

位置2 のお手本は、いったんこのブロックの上の姿勢を通り、そこから j1 を足し j2 を引きながら手前へ下りてつかみます。193431 はこの姿勢で、j0 が 1.6〜1.8°多い（横に約6mm）ほかは、お手本とほぼ同じ所にいました。

推論3 を送ったあと、π0.5 は推論4 で j1 を 88°（上限で切った値）、j2 を約 −34°まで下げて爪を閉じようとしました。上から見ると爪はブロックの奥側にずれていて、このまま下りると爪の先がブロックに当たると判断して、h で HOME へ戻しました。

ブロックの置き場所がお手本とずれていないかも、画像の赤の位置で測りました。

### tilt_red.py（HOME の1枚目で、ブロックの赤の中心を画素で出す）

```python
import sys,cv2,numpy as np
from pathlib import Path
def first(p):
    p=Path(p)
    fr=sorted((p/'frames').glob('*.jpg')) if (p/'frames').is_dir() else []
    if fr: return cv2.imread(str(fr[0]))
    cap=cv2.VideoCapture(str(p/'recording/top.mp4')); ok,f=cap.read(); cap.release(); return f if ok else None
for p in sys.argv[1:]:
    f=first(p)
    if f is None: print(p,'none'); continue
    f=cv2.resize(f,(1920,1080))
    hsv=cv2.cvtColor(f,cv2.COLOR_BGR2HSV)
    m=((hsv[...,0]<10)|(hsv[...,0]>170))&(hsv[...,1]>90)&(hsv[...,2]>70)
    m[:, :300]=0; m[:, 900:]=0; m[:400]=0; m[800:]=0
    ys,xs=np.nonzero(m)
    print(Path(p).name, (int(xs.mean()),int(ys.mean()),len(xs)) if len(xs) else 'no red')
```

### tilt_red.py の出力

```text
pi05_policy/pi05_20261110_193431/:
frames
log.jsonl
recording

pi05_policy/pi05_20261110_193431/frames:
0000.jpg
0001.jpg
0002.jpg
0003.jpg
run_0001_20261019_191541 (552, 673, 4002)
run_0001_20261019_191635 (546, 672, 4266)
run_0001_20261109_102109 (598, 649, 1762)
run_0001_20261109_104517 (611, 640, 1481)
pi05_policy/pi05_20261110_113600 none
pi05_policy/pi05_20261110_114106 none
pi05_policy/pi05_20261110_192201 none
pi05_policy/pi05_20261110_193431 none
```

夜の試行の画像では、赤を見つける範囲に入らず、測れませんでした（範囲と色の線をお手本の画像に合わせて決めたため）。置いた本人の目では、ブロックは位置2 の印に合っていて、ずれていませんでした。爪が奥側にずれた理由は、まだ分かっていません。π0.5 の答えの j0 が 1.6〜1.8°多いことと、j1 が上限で切られて手前へ届ききらないことの、どちらか（または両方）を疑っています。

ここで直す方針を決めました。π0.5 がずれる所をゲームパッドで手で直し、その直した動きを補正つきのお手本として集め、学習し直します（前から使っている、巻き戻しと補正の流れ）。


## ゲームパッドの補正 p で、推論のプロセスが落ち、固まった

直そうとして p（ゲームパッドで補正）を押すと、3回とも止まりました。

| 試行 | p を押したあと |
|---|---|
| 191618 | 「コントローラーを待っています」のあと Segmentation fault: 11 で推論のプロセスが落ちた（終了コード 139） |
| 192201 | 「[PAD   ] 補正 1 …」の行のあと何も出ず、Ctrl-C も効かない |
| 194015 | 192201 と同じ |

192201 のプロセスは、pkill・kill -9 でも消えず、「終わりかけ（E）」の状態で残りました。

### 端末：192201 の推論のプロセス（24051）を止める

```text
Last login: Tue Nov 10 14:47:32 on ttys002
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % pkill -f run_policy_client.py
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % pkill -9 -f run_policy_client.py
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % pgrep -fl python3
1426 python3 /Users/shimazueiichi/r2r2r-scripts/inbox/inbox_runner.py
24051 python3 pi05/run_policy_client.py --raw-dir /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes --steps-per-chunk 10 --via-stride 3 --settle-retries 0 --state-from-target --execute --ignore-framing --model deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1) --framing-raw-dir /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % kill -9 24051; sleep 1; ps -o pid,stat,command -p 24051
  PID STAT COMMAND
24051 ?E+  (python3.12)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % ps -o pid,stat,command -p 24051
  PID STAT COMMAND
24051 ?E+  (python3.12)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ %
```

E は、プロセスが終わろうとしていて、OS の中の後片付けが済んでいない状態です。ゲームパッドかカメラを Mac が手放すのを待って止まっていると見ています。このプロセスが残っている間は、Pi のアームを動かす係（daemon。一度に1つのつながりしか受けない）も返事をしなくなり、立ち上げ直しが HOME の所で止まりました（記録の全量の lab_start_20261110_192719.txt）。daemon を起動し直して先へ進めましたが、194015 で p を押すと、また同じ所で固まりました。

### 端末：19:39 に 24051 がまだ残っているか確かめた

```text
Last login: Tue Nov 10 19:26:57 on ttys002
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ %    ps -p 24051
  PID TTY           TIME CMD
24051 ??         0:00.00 (python3.12)
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ %
```

### pygame と SDL2 は、ゲームパッドを読むための部品

p を押すと、推論のプロセスはゲームパッドを読むために [pygame](https://www.pygame.org/)（Python でゲームの入力や画面を扱う部品）を読み込みます。pygame は中で [SDL2](https://www.libsdl.org/)（画面・音・コントローラーを扱う C の部品）を使います。カメラの画像を扱う OpenCV も、別の SDL2 を抱えています。

| 言葉 | ここでの意味 |
|---|---|
| pygame | ゲームパッドのボタンとスティックを読むのに使っている部品（tools/gamepad_teleop.py） |
| SDL2 | pygame の下でコントローラーを扱う部品。OpenCV も同じ名前の部品を別に持っている |
| objc の警告 | Mac で、同じ名前の部品が2つ読み込まれたときに出る警告。「動作がおかしくなったり、謎の落ち方をすることがある」と書かれている |

p を押すたびに、次の警告が出ていました（全文は記録の全量にある）。

```
objc[24773]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc890) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328592c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
```

午前は同じ警告が出ても p が何度も使えていたので、この警告だけが原因とは言い切れません。夜は1回目（191618）で落ちたあと、2回目からは固まっています。1回目で落ちたときに何かが残ったのか、残った 24051 がゲームパッドを握ったままなのか、まだ分かっていません。24051 を消せるのは Mac の再起動だけで、この場では再起動できなかったため、中断しました。

固まらない形に直すことを、依頼145 として依頼箱に入れました。

### 依頼文（依頼箱 20261110_145）

```markdown
---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 90
---
# 依頼145：実機推論の p（ゲームパッドで補正）で固まるのを直す

実機は動かさない（実装・テスト・レビューまで）。エイイチは見ていない。Log28。

## 何が起きたか（2026-11-10 夜・KIOXIA lab/Log28/lab_start_20261110_193100.txt ほか）
- 11/10 午前は p が何度も使えた（「コントローラーを待っています」→「[接続] PS4 Controller」が出た）
- 19:22 の試行（pi05_20261110_192201）で p を押すと「[PAD   ] 補正 1 (推論 4 のあと)…」の行のあと何も出ず、Ctrl-C も効かず固まった。
  pkill・kill -9 のあとも、プロセス（24051）が ps で STAT「?E+」のまま消えない（終わりかけで止まっている）。Mac の再起動まで残る見込み
- そのため Pi の daemon（1本ずつしか受けない）が ping に答えなくなり、tools/daemon_ctl.sh restart が要った
- 19:40 の試行（pi05_20261110_194015）で p を押すと、また同じ所で固まった（24051 が残ったまま）
- p のたびに objc の警告が出る：cv2 と pygame が libSDL2 をそれぞれ別に持っていて「Class SDL… is implemented in both … This may cause spurious casting failures and mysterious crashes」

## やること
1. 原因を調べて報告に書く（SDL2 の二重読み込み・すでに死にかけのプロセスがパッドを握っている・pygame の初期化がパッド待ちで止まる、など。分かる範囲で）
2. 固まっても推論ごと止まらない形にする。案：ゲームパッドの読み取りを別のプロセスに分ける（cv2 と同じプロセスで pygame を読まない）。
   別プロセスが一定時間（例 5 秒）で接続の返事をしなければ、「ゲームパッドがつながりません。c (キー) で補正してください」と出して、その補正を抜け、推論の入力待ちに戻る。
   daemon のつながりと鍵の扱い（1本ずつ）は今のまま守る
3. 補正中に Ctrl-C を押したら、補正を抜けて推論の入力待ちに戻れるようにする（今は効かない）
4. tools/gamepad_teleop.py を単独で動かす形も、今までどおり使えるようにする
5. 操作と送り方（1関節ずつ・長押し・L2+R2 で止める・□ で戻る）は変えない
6. テストを足して通す（パッドがつながらない・返事がないときに固まらず戻る、を偽物のパッドで）。既存のテスト（test_gamepad_teleop.py・pi05/ のもの・test_lab_start.sh）も通す
7. codex で差分を見てもらう。指摘を直して通るまで
8. 変えたファイルだけを指定して git add・commit（git add . は使わない）

## やらないこと
- 関節・安全範囲・送る速さ・爪の扱いは変えない
- 依頼144（傾きをソフトで直す）と同じファイル（run_policy_client.py）を触るので、144 の commit の上に重ねる。食い違ったら止めて報告

## 報告
claude_code_reports/20261110_145.txt に：原因の見立て、変えた所、テストの結果、codex の回数と指摘、commit の番号、Mac の再起動のあとエイイチが確かめる手順（p を押して何が出れば直っているか）、決めてほしいこと。
**報告の最後の1行は必ず次のどれか**：「判定：合格」／「判定：止めた（決めてほしいことあり）」／「判定：失敗」
```

- ねらい：補正（直した動きを学習データにするための入り口）を、固まらずに使えるようにする
- 変わること：ゲームパッドの読み取りを推論とは別のプロセスに分け、5秒つながらなければ補正を抜けて推論の入力待ちに戻る。補正中の Ctrl-C でも戻れる
- 見込める効果：p で固まって Mac の再起動まで止まる状態がなくなる見込み。直ったかは、Mac を再起動したあと p を押して「[接続] PS4 Controller」が出るかで確かめる


## アームを HOME に戻し、Pod を消した（38分・約59円）

推論のプロセスが固まったまま、アームは位置2 のブロックの上で止まっていました。daemon を起動し直してから HOME に戻し、Pod を消しました。

### 端末：daemon を起動し直して HOME に戻し、Pod を消す

```text
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ % cd ~/r2r2r-scripts && tools/daemon_ctl.sh restart && sleep 3 && python3 dynamic_tracking_limits/place/place_modules/08_home_from_anywhere.py
--- process
21910 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [-37.35, 70.48, -10.01, 6.85, -54.14, -32.69]
[SEND] HOME へ移動指令
[WAIT] t=0.6s  max_err=57.39°
[WAIT] t=1.1s  max_err=41.74°
[WAIT] t=1.6s  max_err=25.83°
[WAIT] t=2.1s  max_err=9.93°
[WAIT] t=2.6s  max_err=1.40°
[DONE] HOME 到達 max_err=1.40°
[POST] 最終姿勢: [-0.35, 1.40, 0.70, -0.61, 0.79, -30.84]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts % ./lab_stop.sh --log Log28 
[STOP  ] Log28 の Pod: id 9qvy252fd9qgb4  GPU=NVIDIA A40  作った時刻 2026-11-10 19:09:20
[LOG   ] serve.log を持ち帰った → /Volumes/KIOXIA/r2r2r-data/lab/Log28/serve_20261110_194719.log (33 行)
[STOP  ] Terminate を送った (19:47:27)
[STOP  ] Pod (id 9qvy252fd9qgb4) が消えたことを API で確かめた
[COST  ] 借りていた時間 38 分 7 秒 (0.64 時間) × 1時間 0.59 ドル = 約 0.37 ドル (約 59 円. 1ドル=158円)
[TUNNEL] 8000 番のトンネルは残っていません
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts %
```

夜に借りた Pod は NVIDIA A40 で、38分7秒・約0.37ドル（約59円）でした。午前の分（51分・約80円）は LOG484 にあります。


## 位置2 の補正つきのお手本を集め、deep1b で学習し直す

この回で分かったことと、次に進める順番です。

- 位置2 に届かなかった理由として、いちばん疑わしいのは上カメラの傾き。学習データの中で、位置1 の9本だけがほかと違う向きで写っていた。ただし確定はしていない
- 傾きをそろえ直した夜は、π0.5 が位置2 のつかむ姿勢へ向かう答えを3回続けて出した。ただし最後の数mmで、爪がブロックの奥側にずれる
- お手本を撮るときは、撮る前にカメラの傾きをそろえる（位置ごとに時間を分けて撮ると、向きの違いが位置の手がかりになるおそれがある）

次は、まず録画の画像だけを回して π0.5 に渡す試し（上の節の3つ）で、傾きが原因かを確かめます。そのうえで、Mac を再起動して 24051 を消し、依頼145 の報告で p が固まらなくなったことを確かめてから、位置2 で π0.5 がずれる所をゲームパッドで直し、補正つきのお手本を5本ほど集めます。それを足して、学習率の下げきりをステップ数に合わせた deep1b（[LOG480](https://pixelstomotors.com/record/retrain-pi05-from-base-on-50-even-demos) のあとに用意した設定）で学習し直します。位置1 の9本だけ写り方が違う件は、そのあとで、撮り直すか、依頼144 のソフトでそろえる形で吸収できるかを見て決めます。


## 記録の全量 ― 端末の出力・各回の記録・Pod の記録

端末の出力は、lab_start.sh が KIOXIA の `lab/Log28/` に残したものをそのまま載せます。serve_20261110_194719.log の Pod の中の IP アドレス（192.168 で始まる、Pod の中だけの番地）は `<POD_INTERNAL_IP>` に置き換えました。色の付いた文字の制御コード（ESC）は、表示が崩れるので外しています。

### lab_start_20261110_190916.txt（新しい Pod を借り、1回目 191618 で p を押して落ち、2回目 191905 を Ctrl-C で止めた）

```text
[RANGE ] 安全範囲 (学習データ ±10°) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)
[FRAME ] 写り方の確かめの基準画像は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)
[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (安全範囲と同じ. 表示だけ. 依頼 142)
[LAB   ] 2026-11-10 19:09:16  GPU=NVIDIA A40  episode=run_0001_20261019_194818  log=/Volumes/KIOXIA/r2r2r-data/lab/Log28  
[MODEL ] --model deep1: π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133. decay_steps 5000)
         PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 → /workspace/ckpt/deep_v1_2999  設定 pi05_mecharm_deep_even10_lora  assets local/mecharm_redblock_box_deep_even10_v1

=== 1. Pod を作る
[POD   ] 記録の Pod (id prm86xv997s945) は API で見ると残っていない. 新しく作ります
         古い記録は /Volumes/KIOXIA/r2r2r-data/lab/Log28/pod/lab_pod_20261110_190918.json に残しました
[POD   ] 作った: id 9qvy252fd9qgb4  GPU=NVIDIA A40  1時間 0.59 ドル  (記録 /Volumes/KIOXIA/r2r2r-data/lab/Log28/pod/lab_pod.json)
    (4 秒)

=== 2. Pod が立ち上がるまで待つ
[POD   ] SSH の IP とポートを API から受け取った (<POD_IP>:<SSH_PORT>)
[POD   ] 入れた: 0064877cc448
NVIDIA A40, 46068 MiB
    (41 秒)

=== 3a. スクリプトを送る
    (127 秒)

=== 3b. huggingface_hub を入れ, 鍵を渡す
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Hint: The `hf-cli` skill is not installed. Run `hf skills add -g` to teach your AI agents how to use the `hf` CLI.
✓ Logged in
  user: PixelstoMotors
    (13 秒)

=== 3c. モデルを取る (hf download PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)

Fetching 34 files:   0%|          | 0/34 [00:00<?, ?it/s]
Fetching 34 files:   3%|▎         | 1/34 [00:00<00:09,  3.56it/s]
Fetching 34 files:  26%|██▋       | 9/34 [00:00<00:01, 18.70it/s]
Fetching 34 files:  35%|███▌      | 12/34 [00:00<00:01, 14.91it/s]
Fetching 34 files:  41%|████      | 14/34 [00:04<00:10,  1.92it/s]
Fetching 34 files:  44%|████▍     | 15/34 [00:05<00:09,  2.06it/s]
Fetching 34 files:  47%|████▋     | 16/34 [00:07<00:15,  1.19it/s]
Fetching 34 files:  50%|█████     | 17/34 [00:07<00:12,  1.37it/s]
Fetching 34 files:  53%|█████▎    | 18/34 [00:09<00:13,  1.20it/s]
Fetching 34 files:  56%|█████▌    | 19/34 [00:09<00:10,  1.43it/s]
Fetching 34 files:  59%|█████▉    | 20/34 [00:09<00:08,  1.57it/s]
Fetching 34 files:  62%|██████▏   | 21/34 [00:10<00:06,  1.91it/s]
Fetching 34 files:  65%|██████▍   | 22/34 [00:10<00:05,  2.21it/s]
Fetching 34 files:  68%|██████▊   | 23/34 [00:10<00:05,  2.18it/s]
Fetching 34 files:  74%|███████▎  | 25/34 [00:11<00:02,  3.01it/s]
Fetching 34 files:  79%|███████▉  | 27/34 [00:11<00:01,  3.93it/s]
Fetching 34 files:  85%|████████▌ | 29/34 [00:12<00:01,  3.43it/s]
Fetching 34 files:  88%|████████▊ | 30/34 [00:12<00:01,  3.38it/s]
Fetching 34 files:  94%|█████████▍| 32/34 [00:12<00:00,  3.94it/s]
Fetching 34 files:  97%|█████████▋| 33/34 [00:13<00:00,  2.40it/s]
Fetching 34 files: 100%|██████████| 34/34 [00:14<00:00,  2.03it/s]✓ Downloaded
  path: /workspace/ckpt/deep_v1_2999

Fetching 34 files: 100%|██████████| 34/34 [00:14<00:00,  2.34it/s]
6.0G	/workspace/ckpt/deep_v1_2999
    (19 秒)

=== 3d. openpi を取る (215abfb)
215abfb docs(droid): fix config search instruction (#1023)
    (23 秒)

=== 3e. uv sync
downloading uv 0.13.7 x86_64-unknown-linux-gnu
WARN: The following commands are shadowed by other commands in your PATH: uv uvx
Using CPython 3.11.13 interpreter at: /usr/bin/python3.11
Creating virtual environment at: .venv
Resolved 279 packages in 4ms
   Building openpi-client @ file:///workspace/openpi/packages/openpi-client
   Building openpi @ file:///workspace/openpi
   Updating https://github.com/huggingface/lerobot (0cf864870cf29f4738d3ade893e6fd13fbd7cdb5)
Downloading sympy (6.0MiB)
Downloading diffusers (3.4MiB)
Downloading virtualenv (5.8MiB)
Downloading labmaze (4.7MiB)
Downloading pyarrow (40.3MiB)
Downloading fonttools (4.7MiB)
Downloading nvidia-cuda-nvcc-cu12 (38.7MiB)
Downloading numpy (17.4MiB)
Downloading matplotlib (8.2MiB)
Downloading nvidia-cusparselt-cu12 (149.5MiB)
Downloading torchvision (7.1MiB)
Downloading ruff (11.0MiB)
Downloading pillow (4.4MiB)
Downloading numcodecs (8.4MiB)
Downloading jax-cuda12-plugin (15.9MiB)
Downloading jax-cuda12-pjrt (99.8MiB)
Downloading wandb (20.4MiB)
Downloading lxml (4.7MiB)
Downloading scipy (35.9MiB)
Downloading dm-control (37.5MiB)
Downloading pyopengl (3.0MiB)
Downloading llvmlite (40.4MiB)
Downloading polars (34.6MiB)
Downloading av (34.7MiB)
Downloading mujoco (4.4MiB)
Downloading transformers (10.3MiB)
Downloading nvidia-curand-cu12 (53.7MiB)
Downloading hf-xet (4.9MiB)
Downloading nvidia-cufft-cu12 (190.9MiB)
Downloading cmake (26.6MiB)
Downloading opencv-python-headless (47.7MiB)
Downloading nvidia-cublas-cu12 (374.9MiB)
Downloading nvidia-cusolver-cu12 (150.9MiB)
Downloading nvidia-nccl-cu12 (192.0MiB)
Downloading nvidia-nvjitlink-cu12 (18.8MiB)
Downloading nvidia-cuda-cupti-cu12 (8.5MiB)
Downloading numba (3.7MiB)
Downloading opencv-python (60.1MiB)
Downloading nvidia-cuda-nvrtc-cu12 (22.6MiB)
Downloading nvidia-cudnn-cu12 (544.5MiB)
Downloading pandas (12.5MiB)
Downloading tensorstore (17.5MiB)
Downloading h5py (4.3MiB)
Downloading torch (783.1MiB)
Downloading rerun-sdk (68.1MiB)
Downloading jaxlib (100.2MiB)
Downloading triton (148.5MiB)
Downloading hf-transfer (3.4MiB)
Downloading imageio-ffmpeg (28.1MiB)
Downloading nvidia-cusparse-cu12 (206.5MiB)
 Downloaded pyopengl
Downloading debugpy (3.0MiB)
 Downloaded diffusers
 Downloaded hf-transfer
Downloading jax (2.3MiB)
Downloading tokenizers (2.9MiB)
 Downloaded numba
Downloading widgetsnbextension (2.1MiB)
 Downloaded pillow
 Downloaded h5py
 Downloaded mujoco
Downloading networkx (1.9MiB)
Downloading ml-dtypes (2.1MiB)
Downloading pydantic-core (1.9MiB)
 Downloaded fonttools
 Downloaded labmaze
 Downloaded lxml
Downloading kiwisolver (1.4MiB)
Downloading jedi (1.5MiB)
Downloading aiohttp (1.6MiB)
 Downloaded hf-xet
Downloading torchcodec (1.3MiB)
 Downloaded virtualenv
 Downloaded sympy
 Downloaded widgetsnbextension
Downloading sentencepiece (1.2MiB)
Downloading pygments (1.2MiB)
 Downloaded jax
Downloading setuptools (1.1MiB)
Downloading nvidia-cufile-cu12 (1.1MiB)
 Downloaded kiwisolver
 Downloaded debugpy
 Downloaded torchcodec
Downloading pymunk (1.0MiB)
 Downloaded tokenizers
 Downloaded jedi
 Downloaded pydantic-core
 Downloaded networkx
 Downloaded aiohttp
 Downloaded ml-dtypes
 Downloaded pygments
 Downloaded torchvision
 Downloaded sentencepiece
 Downloaded nvidia-cufile-cu12
 Downloaded setuptools
 Downloaded pymunk
 Downloaded matplotlib
 Downloaded numcodecs
 Downloaded nvidia-cuda-cupti-cu12
   Building evdev==1.9.2
 Downloaded transformers
 Downloaded ruff
   Building antlr4-python3-runtime==4.9.3
 Downloaded pandas
 Downloaded jax-cuda12-plugin
 Downloaded numpy
 Downloaded tensorstore
 Downloaded nvidia-nvjitlink-cu12
      Built openpi-client @ file:///workspace/openpi/packages/openpi-client
      Built openpi @ file:///workspace/openpi
 Downloaded wandb
 Downloaded nvidia-cuda-nvrtc-cu12
      Built antlr4-python3-runtime==4.9.3
 Downloaded imageio-ffmpeg
 Downloaded cmake
 Downloaded polars
 Downloaded av
 Downloaded scipy
 Downloaded dm-control
 Downloaded nvidia-cuda-nvcc-cu12
 Downloaded llvmlite
 Downloaded pyarrow
 Downloaded opencv-python-headless
 Downloaded nvidia-curand-cu12
 Downloaded opencv-python
      Built evdev==1.9.2
 Downloaded rerun-sdk
 Downloaded jax-cuda12-pjrt
 Downloaded jaxlib
    Updated https://github.com/huggingface/lerobot (0cf864870cf29f4738d3ade893e6fd13fbd7cdb5)
   Building lerobot @ git+https://github.com/huggingface/lerobot@0cf864870cf29f4738d3ade893e6fd13fbd7cdb5
 Downloaded nvidia-cusparselt-cu12
 Downloaded nvidia-cusolver-cu12
 Downloaded triton
      Built lerobot @ git+https://github.com/huggingface/lerobot@0cf864870cf29f4738d3ade893e6fd13fbd7cdb5
 Downloaded nvidia-cufft-cu12
 Downloaded nvidia-nccl-cu12
 Downloaded nvidia-cusparse-cu12
 Downloaded nvidia-cublas-cu12
 Downloaded nvidia-cudnn-cu12
 Downloaded torch
Prepared 240 packages in 13.72s
Installed 240 packages in 2.66s
 + absl-py==2.3.0
 + aiohappyeyeballs==2.6.1
 + aiohttp==3.12.4
 + aiosignal==1.3.2
 + annotated-types==0.7.0
 + antlr4-python3-runtime==4.9.3
 + asttokens==3.0.0
 + attrs==25.3.0
 + augmax==0.4.1
 + av==17.0.0
 + beartype==0.19.0
 + beautifulsoup4==4.13.4
 + blinker==1.9.0
 + cachetools==5.5.2
 + certifi==2025.4.26
 + cffi==1.17.1
 + cfgv==3.4.0
 + charset-normalizer==3.4.2
 + chex==0.1.90
 + click==8.2.1
 + cloudpickle==3.1.1
 + cmake==4.0.2
 + comm==0.2.2
 + contourpy==1.3.2
 + crc32c==2.7.1
 + cycler==0.12.1
 + datasets==3.6.0
 + debugpy==1.8.14
 + decorator==5.2.1
 + deepdiff==8.5.0
 + diffusers==0.33.1
 + dill==0.3.8
 + distlib==0.3.9
 + dm-control==1.0.14
 + dm-env==1.6
 + dm-tree==0.1.9
 + docker-pycreds==0.4.0
 + docstring-parser==0.16
 + donfig==0.8.1.post1
 + draccus==0.10.0
 + einops==0.8.1
 + equinox==0.12.2
 + etils==1.12.2
 + evdev==1.9.2
 + executing==2.2.0
 + farama-notifications==0.0.4
 + filelock==3.18.0
 + flask==3.1.1
 + flatbuffers==25.2.10
 + flax==0.10.2
 + fonttools==4.58.1
 + frozenlist==1.6.0
 + fsspec==2025.3.0
 + gcsfs==2025.3.0
 + gdown==5.2.0
 + gitdb==4.0.12
 + gitpython==3.1.44
 + glfw==2.9.0
 + google-api-core==2.24.2
 + google-auth==2.40.2
 + google-auth-oauthlib==1.2.2
 + google-cloud-core==2.4.3
 + google-cloud-storage==3.1.0
 + google-crc32c==1.7.1
 + google-resumable-media==2.7.2
 + googleapis-common-protos==1.70.0
 + gym-aloha==0.1.1
 + gymnasium==0.29.1
 + h5py==3.13.0
 + hf-transfer==0.1.9
 + hf-xet==1.1.2
 + huggingface-hub==0.32.3
 + humanize==4.12.3
 + identify==2.6.12
 + idna==3.10
 + imageio==2.37.0
 + imageio-ffmpeg==0.6.0
 + importlib-metadata==8.7.0
 + importlib-resources==6.5.2
 + iniconfig==2.1.0
 + inquirerpy==0.3.4
 + ipykernel==6.29.5
 + ipython==9.2.0
 + ipython-pygments-lexers==1.1.1
 + ipywidgets==8.1.7
 + itsdangerous==2.2.0
 + jax==0.5.3
 + jax-cuda12-pjrt==0.5.3
 + jax-cuda12-plugin==0.5.3
 + jaxlib==0.5.3
 + jaxtyping==0.2.36
 + jedi==0.19.2
 + jinja2==3.1.6
 + jsonlines==4.0.0
 + jupyter-client==8.6.3
 + jupyter-core==5.8.1
 + jupyterlab-widgets==3.0.15
 + kiwisolver==1.4.8
 + labmaze==1.0.6
 + lerobot==0.1.0 (from git+https://github.com/huggingface/lerobot@0cf864870cf29f4738d3ade893e6fd13fbd7cdb5)
 + llvmlite==0.44.0
 + lxml==5.4.0
 + markdown-it-py==3.0.0
 + markupsafe==3.0.2
 + matplotlib==3.10.3
 + matplotlib-inline==0.1.7
 + mdurl==0.1.2
 + mergedeep==1.3.4
 + ml-collections==1.0.0
 + ml-dtypes==0.4.1
 + mpmath==1.3.0
 + msgpack==1.1.0
 + mujoco==2.3.7
 + multidict==6.4.4
 + multiprocess==0.70.16
 + mypy-extensions==1.1.0
 + nest-asyncio==1.6.0
 + networkx==3.5
 + nodeenv==1.9.1
 + numba==0.61.2
 + numcodecs==0.16.1
 + numpy==1.26.4
 + numpydantic==1.6.9
 + nvidia-cublas-cu12==12.6.4.1
 + nvidia-cuda-cupti-cu12==12.6.80
 + nvidia-cuda-nvcc-cu12==12.9.41
 + nvidia-cuda-nvrtc-cu12==12.6.77
 + nvidia-cuda-runtime-cu12==12.6.77
 + nvidia-cudnn-cu12==9.5.1.17
 + nvidia-cufft-cu12==11.3.0.4
 + nvidia-cufile-cu12==1.11.1.6
 + nvidia-curand-cu12==10.3.7.77
 + nvidia-cusolver-cu12==11.7.1.2
 + nvidia-cusparse-cu12==12.5.4.2
 + nvidia-cusparselt-cu12==0.6.3
 + nvidia-ml-py==12.575.51
 + nvidia-nccl-cu12==2.26.2
 + nvidia-nvjitlink-cu12==12.6.85
 + nvidia-nvtx-cu12==12.6.77
 + oauthlib==3.2.2
 + omegaconf==2.3.0
 + opencv-python==4.11.0.86
 + opencv-python-headless==4.11.0.86
 + openpi==0.1.0 (from file:///workspace/openpi)
 + openpi-client==0.1.0 (from file:///workspace/openpi/packages/openpi-client)
 + opt-einsum==3.4.0
 + optax==0.2.4
 + orbax-checkpoint==0.11.13
 + orderly-set==5.4.1
 + packaging==25.0
 + pandas==2.2.3
 + parso==0.8.4
 + pexpect==4.9.0
 + pfzy==0.3.4
 + pillow==11.2.1
 + platformdirs==4.3.8
 + pluggy==1.6.0
 + polars==1.30.0
 + pre-commit==4.2.0
 + prompt-toolkit==3.0.51
 + propcache==0.3.1
 + proto-plus==1.26.1
 + protobuf==4.25.8
 + psutil==7.0.0
 + ptyprocess==0.7.0
 + pure-eval==0.2.3
 + pyarrow==20.0.0
 + pyasn1==0.6.1
 + pyasn1-modules==0.4.2
 + pycparser==2.22
 + pydantic==2.11.5
 + pydantic-core==2.33.2
 + pygments==2.19.1
 + pymunk==7.0.0
 + pynput==1.8.1
 + pynvml==12.0.0
 + pyopengl==3.1.9
 + pyparsing==3.2.3
 + pysocks==1.7.1
 + pytest==8.3.5
 + python-dateutil==2.9.0.post0
 + python-xlib==0.33
 + pytz==2025.2
 + pyyaml==6.0.2
 + pyyaml-include==1.4.1
 + pyzmq==26.4.0
 + regex==2024.11.6
 + requests==2.32.3
 + requests-oauthlib==2.0.0
 + rerun-sdk==0.23.1
 + rich==14.0.0
 + rsa==4.9.1
 + ruff==0.11.12
 + safetensors==0.5.3
 + scipy==1.15.3
 + sentencepiece==0.2.0
 + sentry-sdk==2.29.1
 + setproctitle==1.3.6
 + setuptools==80.9.0
 + shtab==1.7.2
 + simplejson==3.20.1
 + six==1.17.0
 + smmap==5.0.2
 + soupsieve==2.7
 + stack-data==0.6.3
 + sympy==1.14.0
 + tensorstore==0.1.74
 + termcolor==3.1.0
 + tokenizers==0.21.1
 + toml==0.10.2
 + toolz==1.0.0
 + torch==2.7.1
 + torchcodec==0.4.0
 + torchvision==0.22.1
 + tornado==6.5.1
 + tqdm==4.67.1
 + tqdm-loggable==0.2
 + traitlets==5.14.3
 + transformers==4.53.2
 + treescope==0.1.9
 + triton==3.3.1
 + typeguard==4.4.2
 + typing-extensions==4.13.2
 + typing-inspect==0.9.0
 + typing-inspection==0.4.1
 + tyro==0.9.22
 + tzdata==2025.2
 + urllib3==2.4.0
 + virtualenv==20.31.2
 + wadler-lindig==0.1.6
 + wandb==0.19.11
 + wcwidth==0.2.13
 + websockets==15.0.1
 + werkzeug==3.1.3
 + widgetsnbextension==4.0.14
 + wrapt==1.14.1
 + xxhash==3.5.0
 + yarl==1.20.0
 + zarr==3.0.8
 + zipp==3.22.0
    (21 秒)

=== 3f. addon・設定・assets (pi05_mecharm_deep_even10_lora)
installed: policies/mecharm_policy.py + config.py (pi05_mecharm_lora)
installed: pi05_mecharm_trim_lora (repo_id=local/mecharm_redblock_box_teach_v1_trim2, 4,000 steps)
installed: pi05_mecharm_deep_even10_lora (repo_id=local/mecharm_redblock_box_deep_even10_v1, 3000 steps, decay_steps 5000, init=π0.5 base)
    (4 秒)

=== 4. サーバーの起動
[SERVE ] --model deep1: --policy.config=pi05_mecharm_deep_even10_lora --policy.dir=/workspace/ckpt/deep_v1_2999
INFO:websockets.server:server listening on 0.0.0.0:8000
    (53 秒)

=== 5. Mac の 8000 番へのトンネル
[TUNNEL] localhost:8000 → Pod の 8000 番 (pid 23643. このスクリプトが終わると閉じる)
    (3 秒)

=== 6. デーモンの状態
--- process
512 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
    (3 秒)

=== 7. HOME → 爪を開く → 位置合わせ (1 回目)
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [0.00, 1.23, 1.05, 0.26, -0.26, -29.70]
[SEND] HOME へ移動指令
[WAIT] t=0.6s  max_err=1.23°
[DONE] HOME 到達 max_err=1.23°
[POST] 最終姿勢: [0.00, 1.23, 1.05, 0.26, -0.26, -29.70]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
  [Pi] GRIPPER: OPEN result= None
[REF   ] 基準画像: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818/frames/000000.jpg
[REF   ] お手本 run_0001_20261019_194818 (つかんだ瞬間の j0 = +13.2°)
[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける
[TILT  ] 傾きのお手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  中央値 -0.61° (測れた 50/50 本. プラス=右下がり)
[TILT  ] 傾きの差が ±0.5° を超えたら画面の色を変える (表示だけ. 止めない)
[ALIGN ] |dy| <= 20 なら OK. アームは HOME に置いた状態で合わせる. q または Esc で終了 (ファイルには何も書かない)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.676  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.679  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.675  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.3  dx=  -5.8  response=0.704  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.7  response=0.698  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.3  dx=  -5.8  response=0.703  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.7  response=0.692  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.670  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.678  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.672  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.679  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.1  dx=  -5.9  response=0.663  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.4  dx=  -6.0  response=0.713  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.3  dx=  -6.0  response=0.702  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.5  dx=  -5.2  response=0.679  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.2  dx=  -5.8  response=0.665  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.5  dx=  -6.0  response=0.716  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.4  dx=  -6.1  response=0.698  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.1  dx=  -6.7  response=0.687  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.1  dx=  -7.9  response=0.709  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.02° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.1  dx=  -6.8  response=0.671  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.23° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.7  dx=  -4.7  response=0.777  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.7  dx=  -3.8  response=0.720  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.64° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.3  dx=  -4.8  response=0.658  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.2  dx=  -4.7  response=0.654  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.64° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +6.6  dx=  -4.8  response=0.738  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.43° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.9  dx=  -7.8  response=0.824  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.02° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.3  dx= -11.3  response=0.706  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.00° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +5.4  dx= -10.6  response=0.803  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.82° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx= -10.8  response=0.845  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.41° (±0.5° 以内)
[ALIGN ] dy=  +2.7  dx= -12.9  response=0.847  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.20° (±0.5° 以内)
[ALIGN ] dy=  +2.6  dx= -11.7  response=0.838  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.41° (±0.5° 以内)
[ALIGN ] dy=  +2.8  dx= -12.7  response=0.859  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.3  dx= -14.0  response=0.750  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.41° (±0.5° 以内)
[ALIGN ] dy=  +4.6  dx= -17.6  response=0.806  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.00° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.6  dx= -17.0  response=0.795  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.82° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.5  dx= -17.9  response=0.772  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.00° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +2.8  dx= -16.3  response=0.787  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.82° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +2.0  dx= -15.5  response=0.865  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.00° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +2.4  dx= -12.6  response=0.811  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.20° (±0.5° 以内)
[ALIGN ] dy=  +1.6  dx=  -6.1  response=0.825  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.00° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +0.4  dx=  -7.0  response=0.826  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.21° (±0.5° 以内)
[ALIGN ] dy=  -1.6  dx=  -2.0  response=0.779  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.41° (±0.5° 以内)
[ALIGN ] dy=  -1.2  dx=  -0.6  response=0.838  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.21° (±0.5° 以内)
[ALIGN ] dy=  +1.2  dx=  -0.2  response=0.746  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.61° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +0.00° (±0.5° 以内)
[ALIGN ] dy=  +4.1  dx=  +2.4  response=0.817  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.5  dx=  -0.0  response=0.889  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.22° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.0  dx=  +0.4  response=0.822  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.3  dx=  +0.3  response=0.869  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.22° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.4  dx=  +0.3  response=0.884  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.5  dx=  +0.4  response=0.724  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.7  dx=  +0.3  response=0.749  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -6.20° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -5.59° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.7  dx=  +0.4  response=0.725  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.704  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.25° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.86° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.711  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.27° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.88° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.7  dx=  +0.4  response=0.728  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.6  dx=  +0.5  response=0.786  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.7  dx=  +0.4  response=0.817  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.9  dx=  +0.9  response=0.875  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.8  dx=  +0.4  response=0.822  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.9  dx=  +0.9  response=0.876  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.795  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.9  dx=  +1.1  response=0.823  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.7  dx=  +0.5  response=0.735  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +3.7  dx=  +0.5  response=0.740  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.05° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.66° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.5  response=0.760  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.64° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.5  response=0.741  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.25° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.86° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.5  response=0.753  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.05° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.66° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.5  response=0.755  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.23° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.84° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.5  response=0.762  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.64° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.25° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.5  response=0.738  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.25° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.86° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.5  response=0.750  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.66° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.27° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.5  response=0.756  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.86° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.48° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.5  response=0.741  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.66° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.27° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.7  dx=  +0.5  response=0.699  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +0.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +1.02° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.732  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -4.43° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -3.82° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.751  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -5.37° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -4.76° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.6  dx=  +0.4  response=0.752  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
2026-11-10 19:16:13.121 python3[23671:332823] error messaging the mach port for IMKCFRunLoopWakeUpReliable
[DONE  ] 終了 (何も書いていません)
    (110 秒)

=== 8. 実機推論 (1 回目)
[WAIT  ] 送ったあと 0.0s / 爪のあと 0.8s (開くときの daemon 0.0s) / 止まるのを待つ目安 0.8s
[SAFE  ] 安全範囲 (学習 50 本 ±10°): j0[-72,+55]  j1[-9,+88]  j2[-45,+52]  j3[-18,+27]  j4[-87,+10]  j5[-51,-15]
[SAFE  ] 肩 j1 の上限は J1_SAFE_MAX=88° で切った (これを超える指令はアームが黙って捨てる / LOG352)  元データ: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes
[MODE  ] EXECUTE  steps/chunk=10  max_move=20.0°  log=/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191618
[MODEL ] deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
[CAMERA] 写り方 dy=-2.7 dx=+7.2 response=0.587 → OK (|dy|<=20 / |dx|<=20 / response>=0.20)
[CAMERA] 基準: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_183454/frames/000000.jpg
[CAMERA] 傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内) (お手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes)
[POLICY] 接続 ws://localhost:8000  metadata={}
[RECORD] 上カメラと実測角を 1 秒 10 コマで記録します → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191618/recording
[GRIP  ] 開始時のグリッパー開放は, 最初の目標が検査を通ってから行います (爪が閉じていたら手で開けておく)
[STATE ] 実測を渡す (直前に送った目標がない)
[000] 推論 61602ms  10ステップ先  Δ=-12.7 +23.9  -4.7  +2.7 -21.7  -1.5
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[GRIP  ] 開始時にグリッパーを開く
  [Pi] GRIPPER: OPEN result= None
[SEND] pi05_chunk000_via02: send_angles (single) [  -2.55,   +6.57,   -0.06,   +0.82,   -5.00,  -30.05]  speed=20 wait=0.0s  (pre max|Δ|=5.34°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.93° (tol 3.0°)
       [VIA   ] step  2  Δ= -2.5  +5.3  -1.1  +0.6  -4.7  -0.4  着いた
[SEND] pi05_chunk000_via05: send_angles (single) [  -6.64,  +14.40,   -1.64,   +1.87,  -12.20,  -30.58]  speed=20 wait=0.0s  (pre max|Δ|=9.13°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.81° (tol 3.0°)
       [VIA   ] step  5  Δ= -4.0  +6.9  -2.9  +1.6  -9.1  -0.9  着いた
[SEND] pi05_chunk000_via08: send_angles (single) [ -11.00,  +22.67,   -3.13,   +2.71,  -19.50,  -31.01]  speed=20 wait=0.0s  (pre max|Δ|=8.87°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.19° (tol 3.0°)
       [VIA   ] step  8  Δ= -4.4  +7.4  -3.3  +1.8  -8.9  -0.7  着いた
[SEND] pi05_chunk000_via09: send_angles (single) [ -12.66,  +25.13,   -3.69,   +2.98,  -21.93,  -31.15]  speed=20 wait=0.0s  (pre max|Δ|=3.92°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.03° (tol 3.0°)
       [VIA   ] step  9  Δ= -1.9  +1.5  -2.5  +1.0  -3.9  -0.4  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.94°（関節 j2）
[001] 推論 811ms  10ステップ先  Δ=-15.9 +29.6  -8.4  +4.2 -27.7  -0.8
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk001_via02: send_angles (single) [ -17.95,  +33.72,   -5.40,   +4.75,  -29.65,  -31.46]  speed=20 wait=0.0s  (pre max|Δ|=8.91°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.71° (tol 3.0°)
       [VIA   ] step  2  Δ= -5.0  +7.5  -3.6  +2.5  -8.9  -0.5  着いた
[SEND] pi05_chunk001_via05: send_angles (single) [ -23.18,  +43.59,   -7.45,   +6.13,  -38.55,  -31.56]  speed=20 wait=0.0s  (pre max|Δ|=9.20°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.65° (tol 3.0°)
       [VIA   ] step  5  Δ= -5.7  +8.7  -3.8  +1.0  -9.2  -0.4  着いた
[SEND] pi05_chunk001_via08: send_angles (single) [ -27.28,  +52.83,   -9.50,   +6.45,  -46.27,  -31.69]  speed=20 wait=0.0s  (pre max|Δ|=8.31°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.59° (tol 3.0°)
       [VIA   ] step  8  Δ= -4.4  +8.0  -3.7  +0.0  -8.3  -0.3  着いた
[SEND] pi05_chunk001_via09: send_angles (single) [ -28.28,  +55.75,  -10.17,   +6.52,  -48.48,  -31.70]  speed=20 wait=0.0s  (pre max|Δ|=2.69°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.65° (tol 3.0°)
       [VIA   ] step  9  Δ= -1.5  +1.7  -2.2  -0.5  -2.7  -0.3  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.56°（関節 j2）
[002] 推論 916ms  10ステップ先  Δ= -6.9 +10.3  -2.5  -0.5  -8.9  -0.6
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk002_via02: send_angles (single) [ -33.20,  +63.82,  -10.83,   +6.80,  -54.84,  -31.79]  speed=20 wait=0.0s  (pre max|Δ|=6.94°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.34° (tol 3.0°)
       [VIA   ] step  2  Δ= -5.2  +6.8  -2.2  -0.4  -6.9  -0.4  着いた
[SEND] pi05_chunk002_via05: send_angles (single) [ -35.34,  +68.12,  -11.19,   +6.77,  -57.89,  -31.91]  speed=20 wait=0.0s  (pre max|Δ|=3.40°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.70° (tol 3.0°)
       [VIA   ] step  5  Δ= -2.6  +3.0  -1.7  -0.5  -3.4  -0.4  着いた
[SEND] pi05_chunk002_via08: send_angles (single) [ -35.08,  +67.78,  -11.11,   +6.66,  -57.12,  -31.97]  speed=20 wait=0.0s  (pre max|Δ|=1.62°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.62° (tol 3.0°)
       [VIA   ] step  8  Δ= -0.1  -1.5  -1.6  -0.6  +0.4  -0.4  着いた
[SEND] pi05_chunk002_via09: send_angles (single) [ -34.81,  +67.38,  -11.15,   +6.62,  -56.79,  -31.98]  speed=20 wait=0.0s  (pre max|Δ|=1.87°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.87° (tol 3.0°)
       [VIA   ] step  9  Δ= +0.2  -1.9  -1.7  -0.6  +0.8  -0.3  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.87°（関節 j1）
[003] 推論 2349ms  10ステップ先  Δ= +0.1  +0.5  -2.5  -0.7  -1.3  -0.5
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk003_via02: send_angles (single) [ -35.25,  +68.37,  -11.13,   +6.64,  -58.01,  -32.06]  speed=20 wait=0.0s  (pre max|Δ|=1.64°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.64° (tol 3.0°)
       [VIA   ] step  2  Δ= -0.2  -0.9  -1.6  -0.4  -0.5  -0.4  着いた
[SEND] pi05_chunk003_via05: send_angles (single) [ -35.10,  +68.18,  -11.11,   +6.62,  -58.18,  -32.08]  speed=20 wait=0.0s  (pre max|Δ|=1.62°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.62° (tol 3.0°)
       [VIA   ] step  5  Δ= -0.0  -1.1  -1.6  -0.4  -0.6  -0.4  着いた
[SEND] pi05_chunk003_via08: send_angles (single) [ -35.03,  +68.98,  -11.60,   +6.48,  -58.50,  -32.12]  speed=20 wait=0.0s  (pre max|Δ|=2.11°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.41° (tol 3.0°)
       [VIA   ] step  8  Δ= +0.0  -0.3  -2.1  -0.6  -0.9  -0.4  着いた
[SEND] pi05_chunk003_via09: send_angles (single) [ -35.01,  +69.75,  -11.96,   +6.44,  -58.84,  -32.15]  speed=20 wait=0.0s  (pre max|Δ|=1.77°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.42° (tol 3.0°)
       [VIA   ] step  9  Δ= +0.1  -0.3  -1.8  -0.3  -0.9  -0.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.42°（関節 j2）
[004] 推論 1472ms  10ステップ先  Δ= +0.5  +6.2  -8.8  -0.9  +0.4  -0.9
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk004_via02: send_angles (single) [ -35.09,  +70.75,  -12.06,   +6.47,  -59.90,  -32.19]  speed=20 wait=0.0s  (pre max|Δ|=1.55°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.52° (tol 3.0°)
       [VIA   ] step  2  Δ= -0.0  -0.2  -1.5  -0.3  -1.6  -0.3  着いた
[SEND] pi05_chunk004_via05: send_angles (single) [ -34.85,  +71.93,  -13.08,   +6.30,  -60.31,  -32.32]  speed=20 wait=0.0s  (pre max|Δ|=2.54°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.37° (tol 3.0°)
       [VIA   ] step  5  Δ= +0.2  +0.0  -2.5  -0.5  -0.7  -0.4  着いた
[SEND] pi05_chunk004_via08: send_angles (single) [ -34.74,  +76.03,  -18.14,   +5.94,  -58.65,  -32.68]  speed=20 wait=0.0s  (pre max|Δ|=6.37°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.37° (tol 3.0°)
       [VIA   ] step  8  Δ= +0.3  +2.7  -6.4  -0.7  +1.4  -0.6  着いた
[SEND] pi05_chunk004_via09: send_angles (single) [ -34.52,  +77.13,  -19.30,   +5.87,  -57.98,  -32.80]  speed=20 wait=0.0s  (pre max|Δ|=2.17°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.78° (tol 3.0°)
       [VIA   ] step  9  Δ= +0.5  -0.0  -2.2  -0.4  +2.0  -0.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.69°（関節 j4）
[005] 推論 871ms  10ステップ先  Δ= +1.5  +9.7 -16.7  -0.4  +8.2  -1.6
       [LIMIT] j1 安全範囲でクランプ +93.7→+88.0
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       [LIMIT] step 5: j1 安全範囲でクランプ +88.2→+88.0
       [LIMIT] step 8: j1 安全範囲でクランプ +92.8→+88.0
       [LIMIT] step 9: j1 安全範囲でクランプ +93.7→+88.0
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > p
objc[23749]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7c890) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed2c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLAppDelegate is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7c8e0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed318). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLTranslatorResponder is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7c958) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed390). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLMessageBoxPresenter is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7c980) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed3b8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_cocoametalview is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7c9d0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed408). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLOpenGLContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7ca20) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed458). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_ShapeData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7ca98) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed4d0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_CocoaClosure is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cae8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed520). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_VideoData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cb38) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed570). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_WindowData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cb88) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed5c0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLWindow is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cbb0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed5e8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class Cocoa_WindowListener is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cbd8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed610). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDLView is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cc50) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed688). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class METAL_RenderData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7ccc8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed700). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class METAL_TextureData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cd18) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed750). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_RumbleMotor is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cd40) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed778). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[23749]: Class SDL_RumbleContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x116b7cd90) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137bed7c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
/opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/pkgdata.py:25: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  from pkg_resources import resource_stream, resource_exists
[PAD   ] 補正 1 (推論 5 のあと). ゲームパッドで直して, □ で推論に戻ります
コントローラーを待っています（つないでいなければ、今つないでください）。□ で推論に戻ります
./lab_start.sh: line 340: 23749 Segmentation fault: 11  python3 pi05/run_policy_client.py --raw-dir "$RAW_RANGE" --steps-per-chunk 10 --via-stride 3 --settle-retries 0 --state-from-target --execute --ignore-framing --model "$MODEL_TAG" --framing-raw-dir "$RAW"
[CLIENT] run_policy_client.py が終わった (終了コード 139. モデル deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1))
    (147 秒)
もう1回やりますか? y=やる / 他=終わる > y

=== 7. HOME → 爪を開く → 位置合わせ (2 回目)
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [-35.06, 78.31, -18.19, 6.06, -58.27, -32.69]
[SEND] HOME へ移動指令
[WAIT] t=0.6s  max_err=62.13°
[WAIT] t=1.2s  max_err=46.75°
[WAIT] t=1.7s  max_err=31.02°
[WAIT] t=2.2s  max_err=15.11°
[WAIT] t=2.7s  max_err=1.49°
[DONE] HOME 到達 max_err=1.49°
[POST] 最終姿勢: [0.35, 1.31, -0.08, -0.87, 1.58, -30.84]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
  [Pi] GRIPPER: OPEN result= None
[REF   ] 基準画像: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818/frames/000000.jpg
[REF   ] お手本 run_0001_20261019_194818 (つかんだ瞬間の j0 = +13.2°)
[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける
[TILT  ] 傾きのお手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  中央値 -0.61° (測れた 50/50 本. プラス=右下がり)
[TILT  ] 傾きの差が ±0.5° を超えたら画面の色を変える (表示だけ. 止めない)
[ALIGN ] |dy| <= 20 なら OK. アームは HOME に置いた状態で合わせる. q または Esc で終了 (ファイルには何も書かない)
[ALIGN ] dy=  +3.7  dx=  -0.3  response=0.926  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[DONE  ] 終了 (何も書いていません)
    (16 秒)

=== 8. 実機推論 (2 回目)
[WAIT  ] 送ったあと 0.0s / 爪のあと 0.8s (開くときの daemon 0.0s) / 止まるのを待つ目安 0.8s
[SAFE  ] 安全範囲 (学習 50 本 ±10°): j0[-72,+55]  j1[-9,+88]  j2[-45,+52]  j3[-18,+27]  j4[-87,+10]  j5[-51,-15]
[SAFE  ] 肩 j1 の上限は J1_SAFE_MAX=88° で切った (これを超える指令はアームが黙って捨てる / LOG352)  元データ: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes
[MODE  ] EXECUTE  steps/chunk=10  max_move=20.0°  log=/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191905
[MODEL ] deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
[CAMERA] 写り方 dy=-2.6 dx=+6.7 response=0.591 → OK (|dy|<=20 / |dx|<=20 / response>=0.20)
[CAMERA] 基準: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_183454/frames/000000.jpg
[CAMERA] 傾き -1.03° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内) (お手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes)
[POLICY] 接続 ws://localhost:8000  metadata={}
[RECORD] 上カメラと実測角を 1 秒 10 コマで記録します → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191905/recording
[GRIP  ] 開始時のグリッパー開放は, 最初の目標が検査を通ってから行います (爪が閉じていたら手で開けておく)
[STATE ] 実測を渡す (直前に送った目標がない)
^C
[STOP  ] Ctrl-C
[RECORD] 記録終了 コマ 10 (8.67 fps) 実測角 11 回 (9.54 Hz)  電流 11 行 (read_servo)  → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191905/recording
[DONE  ] 記録: /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191905
[CLIENT] run_policy_client.py が終わった (終了コード 0. モデル deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1))
    (4 秒)
もう1回やりますか? y=やる / 他=終わる > ^C

=== かかった時間  モデル deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
  1. Pod を作る                      4 秒
  2. Pod が立ち上がるまで待つ     41 秒
  3a. スクリプトを送る        127 秒
  3b. huggingface_hub を入れ, 鍵を渡す     13 秒
  3c. モデルを取る (hf download PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)     19 秒
  3d. openpi を取る (215abfb)       23 秒
  3e. uv sync                          21 秒
  3f. addon・設定・assets (pi05_mecharm_deep_even10_lora)      4 秒
  4. サーバーの起動             53 秒
  5. Mac の 8000 番へのトンネル      3 秒
  6. デーモンの状態              3 秒
  7. HOME → 爪を開く → 位置合わせ (1 回目)    110 秒
  8. 実機推論 (1 回目)          147 秒
  7. HOME → 爪を開く → 位置合わせ (2 回目)     16 秒
  8. 実機推論 (2 回目)            4 秒

[LAB   ] Pod は残しています (id 9qvy252fd9qgb4). 片付けは ./lab_stop.sh --log Log28
[TUNNEL] 8000 番のトンネルを閉じました
```

### lab_start_20261110_191918.txt（同じ Pod につなぎ直し、192201 で推論4 のあと p を押して固まった）

```text
[RANGE ] 安全範囲 (学習データ ±10°) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)
[FRAME ] 写り方の確かめの基準画像は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)
[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (安全範囲と同じ. 表示だけ. 依頼 142)
[LAB   ] 2026-11-10 19:19:18  GPU=NVIDIA A40  episode=run_0001_20261019_191541  log=/Volumes/KIOXIA/r2r2r-data/lab/Log28  
[MODEL ] --model deep1: π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133. decay_steps 5000)
         PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 → /workspace/ckpt/deep_v1_2999  設定 pi05_mecharm_deep_even10_lora  assets local/mecharm_redblock_box_deep_even10_v1

=== 1. Pod を作る
[POD   ] Log28 の Pod (id 9qvy252fd9qgb4) につなぎ直します (状態 RUNNING  GPU=NVIDIA A40  1時間 0.59 ドル  作った時刻 2026-11-10 19:09:20)
[POD   ] 記録の最後のモデル: deep1  今回: --model deep1
    (1 秒)

=== 2. Pod が立ち上がるまで待つ
[POD   ] SSH の IP とポートを API から取り直した (<POD_IP>:<SSH_PORT>)
[POD   ] 入れた: 0064877cc448
NVIDIA A40, 46068 MiB
    (8 秒)

=== 3. Pod の中を確かめる (つなぎ直し)
[POD   ] serve_policy.py のプロセス: ある  8000 番の LISTEN: ある
[POD   ] モデル (/workspace/ckpt/deep_v1_2999): ある  openpi (215abfb): ある  .venv: ある
[POD   ] 動いているサーバーのモデル: --policy.dir=/workspace/ckpt/deep_v1_2999 (--model deep1 と同じ)
[POD   ] サーバーは 8000 番で待っています. 3b〜4 を飛ばします
INFO:websockets.server:server listening on 0.0.0.0:8000
    (10 秒)

=== 3a. スクリプトを送る
    (119 秒)

=== 3b〜4. 準備とサーバーの起動 → 飛ばす (サーバーが 8000 番で待っている)

=== 5. Mac の 8000 番へのトンネル
[TUNNEL] localhost:8000 → Pod の 8000 番 (pid 24014. このスクリプトが終わると閉じる)
    (3 秒)

=== 6. デーモンの状態
--- process
512 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
    (2 秒)

=== 7. HOME → 爪を開く → 位置合わせ (1 回目)
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [-0.26, 1.31, 0.87, -0.26, -0.26, -30.23]
[SEND] HOME へ移動指令
[WAIT] t=0.7s  max_err=1.31°
[DONE] HOME 到達 max_err=1.31°
[POST] 最終姿勢: [-0.26, 1.31, 0.87, -0.26, -0.26, -30.23]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
  [Pi] GRIPPER: OPEN result= None
[REF   ] 基準画像: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_191541/frames/000000.jpg
[REF   ] お手本 run_0001_20261019_191541 (つかんだ瞬間の j0 = -35.7°)
[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける
[TILT  ] 傾きのお手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  中央値 -0.61° (測れた 50/50 本. プラス=右下がり)
[TILT  ] 傾きの差が ±0.5° を超えたら画面の色を変える (表示だけ. 止めない)
[ALIGN ] |dy| <= 20 なら OK. アームは HOME に置いた状態で合わせる. q または Esc で終了 (ファイルには何も書かない)
[ALIGN ] dy=  +4.7  dx=  +1.4  response=0.765  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[ALIGN ] dy=  +4.4  dx=  +1.4  response=0.832  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -5.69° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -5.08° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.4  dx=  +1.4  response=0.820  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -6.41° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -5.80° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.4  dx=  +1.3  response=0.797  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.25° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.86° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.5  dx=  +1.4  response=0.792  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.88° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +4.50° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.6  dx=  +1.6  response=0.786  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.68° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +4.29° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.5  dx=  +1.4  response=0.780  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.47° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +4.09° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.6  dx=  +1.6  response=0.785  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.27° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.88° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.6  dx=  +1.7  response=0.783  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.07° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.68° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.7  dx=  +1.6  response=0.785  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.86° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.48° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.7  dx=  +1.6  response=0.785  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.66° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.27° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.7  dx=  +1.6  response=0.784  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.86° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.48° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.7  dx=  +1.6  response=0.735  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +1.64° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +2.25° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +4.4  dx=  +1.5  response=0.767  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[DONE  ] 終了 (何も書いていません)
    (20 秒)

=== 8. 実機推論 (1 回目)
[WAIT  ] 送ったあと 0.0s / 爪のあと 0.8s (開くときの daemon 0.0s) / 止まるのを待つ目安 0.8s
[SAFE  ] 安全範囲 (学習 50 本 ±10°): j0[-72,+55]  j1[-9,+88]  j2[-45,+52]  j3[-18,+27]  j4[-87,+10]  j5[-51,-15]
[SAFE  ] 肩 j1 の上限は J1_SAFE_MAX=88° で切った (これを超える指令はアームが黙って捨てる / LOG352)  元データ: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes
[MODE  ] EXECUTE  steps/chunk=10  max_move=20.0°  log=/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_192201
[MODEL ] deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
[CAMERA] 写り方 dy=-2.6 dx=+6.7 response=0.624 → OK (|dy|<=20 / |dx|<=20 / response>=0.20)
[CAMERA] 基準: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_183454/frames/000000.jpg
[CAMERA] 傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内) (お手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes)
[POLICY] 接続 ws://localhost:8000  metadata={}
[RECORD] 上カメラと実測角を 1 秒 10 コマで記録します → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_192201/recording
[GRIP  ] 開始時のグリッパー開放は, 最初の目標が検査を通ってから行います (爪が閉じていたら手で開けておく)
[STATE ] 実測を渡す (直前に送った目標がない)
[000] 推論 1519ms  10ステップ先  Δ=-12.9 +22.6  -5.0  +3.2 -19.7  -1.6
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[GRIP  ] 開始時にグリッパーを開く
  [Pi] GRIPPER: OPEN result= None
[SEND] pi05_chunk000_via02: send_angles (single) [  -2.09,   +4.64,   +0.06,   +0.24,   -3.08,  -30.51]  speed=20 wait=0.0s  (pre max|Δ|=3.33°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.29° (tol 3.0°)
       [VIA   ] step  2  Δ= -1.8  +3.3  -0.8  +0.5  -2.8  -0.3  着いた
[SEND] pi05_chunk000_via05: send_angles (single) [  -6.87,  +13.67,   -1.79,   +1.57,  -10.81,  -31.21]  speed=20 wait=0.0s  (pre max|Δ|=9.67°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.87° (tol 3.0°)
       [VIA   ] step  5  Δ= -5.1  +8.1  -2.7  +1.6  -9.7  -1.0  着いた
[SEND] pi05_chunk000_via08: send_angles (single) [ -11.41,  +21.41,   -3.50,   +2.57,  -17.93,  -31.73]  speed=20 wait=0.0s  (pre max|Δ|=8.97°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.84° (tol 3.0°)
       [VIA   ] step  8  Δ= -5.0  +6.7  -3.6  +1.2  -9.0  -1.4  着いた
[SEND] pi05_chunk000_via09: send_angles (single) [ -13.11,  +23.93,   -4.17,   +2.96,  -19.93,  -31.87]  speed=20 wait=0.0s  (pre max|Δ|=3.32°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.98° (tol 3.0°)
       [VIA   ] step  9  Δ= -2.0  +1.4  -2.5  +0.2  -3.3  -1.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.89°（関節 j2）
[001] 推論 799ms  10ステップ先  Δ=-16.9 +28.9  -9.0  +3.4 -29.2  -1.4
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk001_via02: send_angles (single) [ -18.26,  +32.69,   -6.23,   +4.58,  -28.00,  -32.23]  speed=20 wait=0.0s  (pre max|Δ|=9.20°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.75° (tol 3.0°)
       [VIA   ] step  2  Δ= -4.7  +7.7  -4.0  +1.2  -9.2  -1.2  着いた
[SEND] pi05_chunk001_via05: send_angles (single) [ -23.70,  +41.77,   -8.46,   +5.94,  -36.67,  -32.30]  speed=20 wait=0.0s  (pre max|Δ|=9.34°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.61° (tol 3.0°)
       [VIA   ] step  5  Δ= -5.9  +7.9  -4.0  +0.4  -9.3  -0.7  着いた
[SEND] pi05_chunk001_via08: send_angles (single) [ -28.26,  +50.88,  -10.58,   +6.54,  -45.10,  -32.34]  speed=20 wait=0.0s  (pre max|Δ|=8.89°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.53° (tol 3.0°)
       [VIA   ] step  8  Δ= -4.8  +7.8  -3.7  +0.1  -8.9  -0.6  着いた
[SEND] pi05_chunk001_via09: send_angles (single) [ -30.04,  +53.95,  -11.26,   +6.77,  -48.04,  -32.35]  speed=20 wait=0.0s  (pre max|Δ|=3.31°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.04° (tol 3.0°)
       [VIA   ] step  9  Δ= -2.4  +1.9  -2.2  -0.6  -3.3  -0.5  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.77°（関節 j2）
[002] 推論 547ms  10ステップ先  Δ= -7.1 +12.2  -2.6  -0.3 -11.0  -0.8
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk002_via02: send_angles (single) [ -34.52,  +62.10,  -11.93,   +7.06,  -54.45,  -32.42]  speed=20 wait=0.0s  (pre max|Δ|=7.17°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.39° (tol 3.0°)
       [VIA   ] step  2  Δ= -4.4  +7.0  -2.4  -0.7  -7.2  -0.5  着いた
[SEND] pi05_chunk002_via05: send_angles (single) [ -36.41,  +66.47,  -12.10,   +7.16,  -57.95,  -32.51]  speed=20 wait=0.0s  (pre max|Δ|=3.73°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.56° (tol 3.0°)
       [VIA   ] step  5  Δ= -1.9  +3.1  -1.6  -0.7  -3.7  -0.5  着いた
[SEND] pi05_chunk002_via08: send_angles (single) [ -36.73,  +67.37,  -12.08,   +7.20,  -58.23,  -32.61]  speed=20 wait=0.0s  (pre max|Δ|=1.54°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.54° (tol 3.0°)
       [VIA   ] step  8  Δ= -0.5  -0.3  -1.5  -0.6  -0.8  -0.5  着いた
[SEND] pi05_chunk002_via09: send_angles (single) [ -36.71,  +67.29,  -12.08,   +7.19,  -58.29,  -32.63]  speed=20 wait=0.0s  (pre max|Δ|=1.54°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.54° (tol 3.0°)
       [VIA   ] step  9  Δ= -0.4  -1.2  -1.5  -0.6  -0.4  -0.5  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.54°（関節 j2）
[003] 推論 534ms  10ステップ先  Δ= -0.4  +3.3  -4.8  -0.9  -1.3  -0.8
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk003_via02: send_angles (single) [ -36.96,  +67.61,  -12.00,   +7.17,  -59.35,  -32.66]  speed=20 wait=0.0s  (pre max|Δ|=1.46°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.46° (tol 3.0°)
       [VIA   ] step  2  Δ= -0.7  -0.8  -1.5  -0.6  -1.4  -0.4  着いた
[SEND] pi05_chunk003_via05: send_angles (single) [ -37.00,  +67.73,  -12.11,   +7.07,  -59.65,  -32.70]  speed=20 wait=0.0s  (pre max|Δ|=1.57°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.57° (tol 3.0°)
       [VIA   ] step  5  Δ= +0.2  -0.8  -1.6  -0.6  -0.5  -0.4  着いた
[SEND] pi05_chunk003_via08: send_angles (single) [ -36.76,  +70.52,  -14.25,   +6.92,  -59.37,  -32.92]  speed=20 wait=0.0s  (pre max|Δ|=3.71°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.34° (tol 3.0°)
       [VIA   ] step  8  Δ= +0.4  +1.8  -3.7  -0.6  -0.2  -0.6  着いた
[SEND] pi05_chunk003_via09: send_angles (single) [ -36.74,  +71.78,  -15.31,   +6.90,  -59.20,  -33.01]  speed=20 wait=0.0s  (pre max|Δ|=2.31°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.43° (tol 3.0°)
       [VIA   ] step  9  Δ= +0.4  -0.0  -2.3  -0.5  -0.0  -0.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.34°（関節 j2）
[004] 推論 891ms  10ステップ先  Δ= +1.7 +14.9 -16.4  -0.4  +8.9  -1.8
       [LIMIT] j1 安全範囲でクランプ +88.4→+88.0
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       [LIMIT] step 9: j1 安全範囲でクランプ +88.4→+88.0
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > p
objc[24051]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4890) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e312c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLAppDelegate is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b48e0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31318). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLTranslatorResponder is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4958) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31390). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLMessageBoxPresenter is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4980) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e313b8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_cocoametalview is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b49d0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31408). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLOpenGLContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4a20) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31458). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_ShapeData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4a98) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e314d0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_CocoaClosure is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4ae8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31520). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_VideoData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4b38) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31570). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_WindowData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4b88) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e315c0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLWindow is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4bb0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e315e8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class Cocoa_WindowListener is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4bd8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31610). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDLView is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4c50) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31688). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class METAL_RenderData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4cc8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31700). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class METAL_TextureData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4d18) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31750). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_RumbleMotor is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4d40) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e31778). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24051]: Class SDL_RumbleContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1168b4d90) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x137e317c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
/opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/pkgdata.py:25: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  from pkg_resources import resource_stream, resource_exists
[PAD   ] 補正 1 (推論 4 のあと). ゲームパッドで直して, □ で推論に戻ります
^C^C
^C^C
^C^C
```

### lab_start_20261110_192618.txt（古い lab_start.sh が残っていて、二重に起動しないよう止まった）

```text
[RANGE ] 安全範囲 (学習データ ±10°) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)
[FRAME ] 写り方の確かめの基準画像は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)
[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (安全範囲と同じ. 表示だけ. 依頼 142)
[ERROR] Log28 の lab_start.sh がもう動いています (pid 23845). 二重に借りないよう止まります
```

### lab_start_20261110_192719.txt（daemon が ping に答えず、HOME に戻せずに止まった）

```text
[RANGE ] 安全範囲 (学習データ ±10°) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)
[FRAME ] 写り方の確かめの基準画像は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)
[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (安全範囲と同じ. 表示だけ. 依頼 142)
[LAB   ] 2026-11-10 19:27:19  GPU=NVIDIA A40  episode=run_0001_20261019_194818  log=/Volumes/KIOXIA/r2r2r-data/lab/Log28  
[MODEL ] --model deep1: π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133. decay_steps 5000)
         PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 → /workspace/ckpt/deep_v1_2999  設定 pi05_mecharm_deep_even10_lora  assets local/mecharm_redblock_box_deep_even10_v1

=== 1. Pod を作る
[POD   ] Log28 の Pod (id 9qvy252fd9qgb4) につなぎ直します (状態 RUNNING  GPU=NVIDIA A40  1時間 0.59 ドル  作った時刻 2026-11-10 19:09:20)
[POD   ] 記録の最後のモデル: deep1  今回: --model deep1
    (2 秒)

=== 2. Pod が立ち上がるまで待つ
[POD   ] SSH の IP とポートを API から取り直した (<POD_IP>:<SSH_PORT>)
[POD   ] 入れた: 0064877cc448
NVIDIA A40, 46068 MiB
    (9 秒)

=== 3. Pod の中を確かめる (つなぎ直し)
[POD   ] serve_policy.py のプロセス: ある  8000 番の LISTEN: ある
[POD   ] モデル (/workspace/ckpt/deep_v1_2999): ある  openpi (215abfb): ある  .venv: ある
[POD   ] 動いているサーバーのモデル: --policy.dir=/workspace/ckpt/deep_v1_2999 (--model deep1 と同じ)
[POD   ] サーバーは 8000 番で待っています. 3b〜4 を飛ばします
INFO:websockets.server:server listening on 0.0.0.0:8000
    (11 秒)

=== 3a. スクリプトを送る
    (122 秒)

=== 3b〜4. 準備とサーバーの起動 → 飛ばす (サーバーが 8000 番で待っている)

=== 5. Mac の 8000 番へのトンネル
[TUNNEL] localhost:8000 → Pod の 8000 番 (pid 24375. このスクリプトが終わると閉じる)
    (3 秒)

=== 6. デーモンの状態
--- process
512 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
    (2 秒)

=== 7. HOME → 爪を開く → 位置合わせ (1 回目)
[INIT] daemon 接続中...
  [WARN] _daemon_request timeout (timeout=35.0s, cmd=ping). socket closed.
Traceback (most recent call last):
  File "/Users/shimazueiichi/r2r2r-scripts/visual_servo_pick.py", line 670, in _daemon_request
    chunk = sock.recv(4096)
            ^^^^^^^^^^^^^^^
TimeoutError: timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/shimazueiichi/r2r2r-scripts/visual_servo_pick.py", line 539, in get_daemon_socket
    _daemon_request({"cmd": "ping"})
  File "/Users/shimazueiichi/r2r2r-scripts/visual_servo_pick.py", line 683, in _daemon_request
    raise RuntimeError(f"daemon timeout after {timeout:.1f}s (cmd={cmd_obj.get('cmd')})")
RuntimeError: daemon timeout after 35.0s (cmd=ping)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/shimazueiichi/r2r2r-scripts/dynamic_tracking_limits/place/place_modules/08_home_from_anywhere.py", line 97, in <module>
    main()
  File "/Users/shimazueiichi/r2r2r-scripts/dynamic_tracking_limits/place/place_modules/08_home_from_anywhere.py", line 37, in main
    get_daemon_socket()
  File "/Users/shimazueiichi/r2r2r-scripts/visual_servo_pick.py", line 555, in get_daemon_socket
    raise RuntimeError(f"daemon ping failed: {e}")
RuntimeError: daemon ping failed: daemon timeout after 35.0s (cmd=ping)

[STOP  ] 「7. HOME → 爪を開く → 位置合わせ (1 回目)」で止まりました: HOME に戻せない
[STOP  ] Pod は残っています (id 9qvy252fd9qgb4). 消すなら ./lab_stop.sh --log Log28

=== かかった時間 (Log28 の Pod (id 9qvy252fd9qgb4) につなぎ直し)  モデル deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
  1. Pod につなぎ直す             2 秒
  2. Pod が立ち上がるまで待つ      9 秒
  3. Pod の中を確かめる (つなぎ直し)     11 秒
  3a. スクリプトを送る        122 秒
  3b〜4. 準備とサーバーの起動 (飛ばした)      0 秒
  5. Mac の 8000 番へのトンネル      3 秒
  6. デーモンの状態              2 秒
[TUNNEL] 8000 番のトンネルを閉じました
```

### 端末：daemon を起動し直してから lab_start.sh を打った所

```text
(base) shimazueiichi@shimadzueiichinoMacBook-Pro r2r2r-scripts % tools/daemon_ctl.sh restart && sleep 3 && ./lab_start.sh --model deep1 --log Log28
--- process
9155 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
（このあとは lab_start_20261110_193100.txt）
```

### lab_start_20261110_193100.txt（193431 で位置2 のつかむ姿勢へ向かい h で戻した、194015 で p を押して固まった）

```text
[RANGE ] 安全範囲 (学習データ ±10°) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)
[FRAME ] 写り方の確かめの基準画像は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes の最初のエピソードから (位置合わせと同じ trim2. 依頼 137)
[TILT  ] 上カメラの傾きのお手本 (各エピソードの 1 枚目の中央値) は /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (安全範囲と同じ. 表示だけ. 依頼 142)
[LAB   ] 2026-11-10 19:31:00  GPU=NVIDIA A40  episode=run_0001_20261019_194818  log=/Volumes/KIOXIA/r2r2r-data/lab/Log28  
[MODEL ] --model deep1: π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133. decay_steps 5000)
         PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 → /workspace/ckpt/deep_v1_2999  設定 pi05_mecharm_deep_even10_lora  assets local/mecharm_redblock_box_deep_even10_v1

=== 1. Pod を作る
[POD   ] Log28 の Pod (id 9qvy252fd9qgb4) につなぎ直します (状態 RUNNING  GPU=NVIDIA A40  1時間 0.59 ドル  作った時刻 2026-11-10 19:09:20)
[POD   ] 記録の最後のモデル: deep1  今回: --model deep1
    (1 秒)

=== 2. Pod が立ち上がるまで待つ
[POD   ] SSH の IP とポートを API から取り直した (<POD_IP>:<SSH_PORT>)
[POD   ] 入れた: 0064877cc448
NVIDIA A40, 46068 MiB
    (8 秒)

=== 3. Pod の中を確かめる (つなぎ直し)
[POD   ] serve_policy.py のプロセス: ある  8000 番の LISTEN: ある
[POD   ] モデル (/workspace/ckpt/deep_v1_2999): ある  openpi (215abfb): ある  .venv: ある
[POD   ] 動いているサーバーのモデル: --policy.dir=/workspace/ckpt/deep_v1_2999 (--model deep1 と同じ)
[POD   ] サーバーは 8000 番で待っています. 3b〜4 を飛ばします
INFO:websockets.server:server listening on 0.0.0.0:8000
    (12 秒)

=== 3a. スクリプトを送る
    (121 秒)

=== 3b〜4. 準備とサーバーの起動 → 飛ばす (サーバーが 8000 番で待っている)

=== 5. Mac の 8000 番へのトンネル
[TUNNEL] localhost:8000 → Pod の 8000 番 (pid 24612. このスクリプトが終わると閉じる)
    (3 秒)

=== 6. デーモンの状態
--- process
9155 /usr/bin/python3 -u /home/ubuntu/mecharm_daemon.py
--- 5555
LISTEN0      1                                 0.0.0.0:5555        0.0.0.0:*    
--- systemd
enabled: enabled
active:  active
    (2 秒)

=== 7. HOME → 爪を開く → 位置合わせ (1 回目)
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [-37.17, 73.12, -14.06, 7.20, -54.05, -32.95]
[SEND] HOME へ移動指令
[WAIT] t=0.6s  max_err=56.95°
[WAIT] t=1.2s  max_err=41.22°
[WAIT] t=1.7s  max_err=25.57°
[WAIT] t=2.2s  max_err=9.66°
[WAIT] t=2.7s  max_err=1.31°
[DONE] HOME 到達 max_err=1.31°
[POST] 最終姿勢: [0.35, 1.31, 0.79, -0.70, 0.61, -30.84]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
  [Pi] GRIPPER: OPEN result= None
[REF   ] 基準画像: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818/frames/000000.jpg
[REF   ] お手本 run_0001_20261019_194818 (つかんだ瞬間の j0 = +13.2°)
[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける
[TILT  ] 傾きのお手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  中央値 -0.61° (測れた 50/50 本. プラス=右下がり)
[TILT  ] 傾きの差が ±0.5° を超えたら画面の色を変える (表示だけ. 止めない)
[ALIGN ] |dy| <= 20 なら OK. アームは HOME に置いた状態で合わせる. q または Esc で終了 (ファイルには何も書かない)
[ALIGN ] dy=  +3.9  dx=  +0.1  response=0.818  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.868  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -6.23° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -5.61° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.842  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.45° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.07° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.866  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.47° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +4.09° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.852  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.88° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +4.50° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.847  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.07° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.68° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.851  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.45° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.07° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.776  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.66° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.27° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.2  response=0.782  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.27° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.88° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.2  response=0.781  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.07° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.68° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.2  response=0.778  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +2.86° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.48° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.2  response=0.774  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.07° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.68° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.9  dx=  +0.0  response=0.847  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き +3.27° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +3.88° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.0  response=0.852  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -4.07° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -3.46° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.0  response=0.883  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -5.62° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -5.00° → 傾きがお手本と違う (±0.5° を超える)
[ALIGN ] dy=  +3.8  dx=  +0.1  response=0.886  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -1.02° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.41° (±0.5° 以内)
[DONE  ] 終了 (何も書いていません)
    (63 秒)

=== 8. 実機推論 (1 回目)
[WAIT  ] 送ったあと 0.0s / 爪のあと 0.8s (開くときの daemon 0.0s) / 止まるのを待つ目安 0.8s
[SAFE  ] 安全範囲 (学習 50 本 ±10°): j0[-72,+55]  j1[-9,+88]  j2[-45,+52]  j3[-18,+27]  j4[-87,+10]  j5[-51,-15]
[SAFE  ] 肩 j1 の上限は J1_SAFE_MAX=88° で切った (これを超える指令はアームが黙って捨てる / LOG352)  元データ: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes
[MODE  ] EXECUTE  steps/chunk=10  max_move=20.0°  log=/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_193431
[MODEL ] deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
[CAMERA] 写り方 dy=-2.5 dx=+6.9 response=0.602 → OK (|dy|<=20 / |dx|<=20 / response>=0.20)
[CAMERA] 基準: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_183454/frames/000000.jpg
[CAMERA] 傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内) (お手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes)
[POLICY] 接続 ws://localhost:8000  metadata={}
[RECORD] 上カメラと実測角を 1 秒 10 コマで記録します → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_193431/recording
[GRIP  ] 開始時のグリッパー開放は, 最初の目標が検査を通ってから行います (爪が閉じていたら手で開けておく)
[STATE ] 実測を渡す (直前に送った目標がない)
[000] 推論 1367ms  10ステップ先  Δ=-13.3 +21.7  -5.0  +3.1 -21.0  -1.7
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[GRIP  ] 開始時にグリッパーを開く
  [Pi] GRIPPER: OPEN result= None
[SEND] pi05_chunk000_via02: send_angles (single) [  -2.68,   +5.93,   -0.25,   +0.40,   -4.77,  -30.57]  speed=20 wait=0.0s  (pre max|Δ|=4.62°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.31° (tol 3.0°)
       [VIA   ] step  2  Δ= -2.6  +4.6  -1.0  +0.7  -4.5  -0.3  着いた
[SEND] pi05_chunk000_via05: send_angles (single) [  -7.28,  +13.41,   -1.91,   +1.49,  -11.98,  -31.17]  speed=20 wait=0.0s  (pre max|Δ|=9.52°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.79° (tol 3.0°)
       [VIA   ] step  5  Δ= -4.5  +6.6  -2.7  +1.3  -9.5  -0.9  着いた
[SEND] pi05_chunk000_via08: send_angles (single) [ -12.20,  +21.20,   -3.80,   +2.57,  -19.09,  -31.78]  speed=20 wait=0.0s  (pre max|Δ|=8.90°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.78° (tol 3.0°)
       [VIA   ] step  8  Δ= -5.3  +6.9  -3.6  +0.9  -8.9  -1.5  着いた
[SEND] pi05_chunk000_via09: send_angles (single) [ -13.39,  +23.05,   -4.26,   +2.82,  -21.23,  -31.91]  speed=20 wait=0.0s  (pre max|Δ|=3.57°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.24° (tol 3.0°)
       [VIA   ] step  9  Δ= -1.5  +0.6  -2.2  -0.2  -3.6  -1.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 2.24°（関節 j2）
[001] 推論 811ms  10ステップ先  Δ=-17.5 +30.3  -9.6  +3.3 -28.6  -1.5
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk001_via02: send_angles (single) [ -18.75,  +32.11,   -6.28,   +4.50,  -29.33,  -32.31]  speed=20 wait=0.0s  (pre max|Δ|=8.94°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.89° (tol 3.0°)
       [VIA   ] step  2  Δ= -5.0  +8.0  -4.3  +1.2  -8.9  -1.2  着いた
[SEND] pi05_chunk001_via05: send_angles (single) [ -24.35,  +41.76,   -8.57,   +5.88,  -38.24,  -32.41]  speed=20 wait=0.0s  (pre max|Δ|=9.68°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.81° (tol 3.0°)
       [VIA   ] step  5  Δ= -6.2  +8.5  -4.1  +0.5  -9.7  -0.7  着いた
[SEND] pi05_chunk001_via08: send_angles (single) [ -28.99,  +50.85,  -10.74,   +6.65,  -46.27,  -32.48]  speed=20 wait=0.0s  (pre max|Δ|=8.48°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.69° (tol 3.0°)
       [VIA   ] step  8  Δ= -4.7  +7.8  -4.0  +0.2  -8.6  -0.7  着いた
[SEND] pi05_chunk001_via09: send_angles (single) [ -30.61,  +54.38,  -11.67,   +6.93,  -49.01,  -32.51]  speed=20 wait=0.0s  (pre max|Δ|=3.22°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.66° (tol 3.0°)
       [VIA   ] step  9  Δ= -2.0  +2.4  -2.6  -0.3  -3.2  -0.5  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.57°（関節 j2）
[002] 推論 923ms  10ステップ先  Δ= -7.1 +11.8  -2.6  -0.1 -10.0  -0.8
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk002_via02: send_angles (single) [ -35.00,  +62.46,  -12.42,   +7.23,  -54.82,  -32.57]  speed=20 wait=0.0s  (pre max|Δ|=6.92°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.32° (tol 3.0°)
       [VIA   ] step  2  Δ= -4.2  +6.9  -2.3  -0.5  -6.7  -0.6  着いた
[SEND] pi05_chunk002_via05: send_angles (single) [ -37.24,  +67.04,  -12.65,   +7.32,  -58.24,  -32.65]  speed=20 wait=0.0s  (pre max|Δ|=3.75°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.58° (tol 3.0°)
       [VIA   ] step  5  Δ= -2.5  +3.3  -1.7  -0.8  -3.8  -0.5  着いた
[SEND] pi05_chunk002_via08: send_angles (single) [ -37.48,  +67.60,  -12.67,   +7.27,  -58.24,  -32.77]  speed=20 wait=0.0s  (pre max|Δ|=1.60°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.60° (tol 3.0°)
       [VIA   ] step  8  Δ= -0.7  -0.5  -1.6  -0.5  -0.6  -0.4  着いた
[SEND] pi05_chunk002_via09: send_angles (single) [ -37.39,  +67.38,  -12.71,   +7.25,  -58.19,  -32.78]  speed=20 wait=0.0s  (pre max|Δ|=1.64°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.64° (tol 3.0°)
       [VIA   ] step  9  Δ= -0.3  -1.4  -1.6  -0.7  -0.4  -0.4  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.64°（関節 j2）
[003] 推論 2258ms  10ステップ先  Δ= +1.0  +7.5 -11.3  -1.2  +4.1  -1.4
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk003_via02: send_angles (single) [ -37.51,  +67.70,  -12.76,   +7.12,  -59.01,  -32.81]  speed=20 wait=0.0s  (pre max|Δ|=10.32°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.16° (tol 3.0°)
       [VIA   ] step  2  Δ= +0.0  -1.0  -1.2  -0.4 -10.3  -0.4  着いた
[SEND] pi05_chunk003_via05: send_angles (single) [ -37.32,  +69.79,  -15.01,   +6.95,  -58.18,  -33.01]  speed=20 wait=0.0s  (pre max|Δ|=3.41°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.40° (tol 3.0°)
       [VIA   ] step  5  Δ= +0.2  +1.1  -3.4  -0.3  +0.4  -0.4  着いた
[SEND] pi05_chunk003_via08: send_angles (single) [ -36.30,  +74.93,  -20.96,   +6.85,  -54.46,  -33.56]  speed=20 wait=0.0s  (pre max|Δ|=7.17°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.45° (tol 3.0°)
       [VIA   ] step  8  Δ= +1.2  +3.7  -7.2  -0.3  +4.2  -0.9  着いた
[SEND] pi05_chunk003_via09: send_angles (single) [ -36.05,  +76.21,  -22.35,   +6.82,  -53.72,  -33.70]  speed=20 wait=0.0s  (pre max|Δ|=2.49°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.30° (tol 3.0°)
       [VIA   ] step  9  Δ= -0.2  +0.2  -2.5  +0.3  +1.7  -0.4  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.30°（関節 j1）
[004] 推論 867ms  10ステップ先  Δ= +0.9 +10.5 -12.5  +0.3  +7.1  -1.1  グリッパー→閉
       [LIMIT] j1 安全範囲でクランプ +88.4→+88.0
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       [LIMIT] step 8: j1 安全範囲でクランプ +88.4→+88.0
       [LIMIT] step 9: j1 安全範囲でクランプ +88.4→+88.0
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > h
[SEND] pi05_home: send_angles (single) [  +0.00,   +0.00,   +0.00,   +0.00,   +0.00,  -30.00]  speed=20 wait=0.0s  (pre max|Δ|=77.51°, post-send timeout 13.6s)
[SEND] arrival OK: max|Δ|=1.49° (tol 2.5°)
[RECORD] 記録終了 コマ 830 (8.795 fps) 実測角 884 回 (9.37 Hz)  電流 890 行 (read_servo)  → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_193431/recording
       結果: y=成功 / n=失敗 / Enter=記録しない > n
[DONE  ] 記録: /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_193431
[CLIENT] run_policy_client.py が終わった (終了コード 0. モデル deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1))
    (328 秒)
もう1回やりますか? y=やる / 他=終わる > y

=== 7. HOME → 爪を開く → 位置合わせ (2 回目)
[INIT] daemon 接続中...
  [Pi] fresh_mode=1
[INIT] fresh_mode=1
[PRE]  現在姿勢: [-0.26, 1.49, 0.70, 0.26, -0.26, -30.23]
[SEND] HOME へ移動指令
[WAIT] t=0.7s  max_err=1.49°
[DONE] HOME 到達 max_err=1.49°
[POST] 最終姿勢: [-0.26, 1.49, 0.70, 0.26, -0.26, -30.23]
  [Pi] fresh_mode=0
[EXIT] fresh_mode=0, socket close
  [Pi] GRIPPER: OPEN result= None
[REF   ] 基準画像: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818/frames/000000.jpg
[REF   ] お手本 run_0001_20261019_194818 (つかんだ瞬間の j0 = +13.2°)
[REF   ] 重なった画像のブロックに実物のブロックを合わせると, お手本と同じ位置に置ける
[TILT  ] 傾きのお手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes  中央値 -0.61° (測れた 50/50 本. プラス=右下がり)
[TILT  ] 傾きの差が ±0.5° を超えたら画面の色を変える (表示だけ. 止めない)
[ALIGN ] |dy| <= 20 なら OK. アームは HOME に置いた状態で合わせる. q または Esc で終了 (ファイルには何も書かない)
[ALIGN ] dy=  +4.2  dx=  -0.4  response=0.810  → OK (|dy|<=20 / |dx|<=20 / response>=0.20)  傾き -0.82° (プラス=右下がり) / お手本の中央値 -0.61° / 差 -0.20° (±0.5° 以内)
[DONE  ] 終了 (何も書いていません)
    (14 秒)

=== 8. 実機推論 (2 回目)
[WAIT  ] 送ったあと 0.0s / 爪のあと 0.8s (開くときの daemon 0.0s) / 止まるのを待つ目安 0.8s
[SAFE  ] 安全範囲 (学習 50 本 ±10°): j0[-72,+55]  j1[-9,+88]  j2[-45,+52]  j3[-18,+27]  j4[-87,+10]  j5[-51,-15]
[SAFE  ] 肩 j1 の上限は J1_SAFE_MAX=88° で切った (これを超える指令はアームが黙って捨てる / LOG352)  元データ: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes
[MODE  ] EXECUTE  steps/chunk=10  max_move=20.0°  log=/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_194015
[MODEL ] deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)
[CAMERA] 写り方 dy=-2.0 dx=+6.2 response=0.499 → OK (|dy|<=20 / |dx|<=20 / response>=0.20)
[CAMERA] 基準: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_183454/frames/000000.jpg
[CAMERA] 傾き +4.59° (プラス=右下がり) / お手本の中央値 -0.61° / 差 +5.20° → 傾きがお手本と違う (±0.5° を超える) (お手本: /Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes)
[POLICY] 接続 ws://localhost:8000  metadata={}
[RECORD] 上カメラと実測角を 1 秒 10 コマで記録します → /Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_194015/recording
[GRIP  ] 開始時のグリッパー開放は, 最初の目標が検査を通ってから行います (爪が閉じていたら手で開けておく)
[STATE ] 実測を渡す (直前に送った目標がない)
[000] 推論 870ms  10ステップ先  Δ=-10.8 +20.7  -4.1  +1.5 -18.7  -1.3
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[GRIP  ] 開始時にグリッパーを開く
  [Pi] GRIPPER: OPEN result= None
[SEND] pi05_chunk000_via02: send_angles (single) [  -0.65,   +1.97,   +0.54,   +0.35,   -0.84,  -30.22]  speed=20 wait=0.0s  (pre max|Δ|=0.58°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.10° (tol 3.0°)
       [VIA   ] step  2  Δ= -0.4  +0.5  -0.2  +0.1  -0.6  +0.0  着いた
[SEND] pi05_chunk000_via05: send_angles (single) [  -5.28,  +11.62,   -1.18,   +0.83,   -9.32,  -30.77]  speed=20 wait=0.0s  (pre max|Δ|=9.06°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.94° (tol 3.0°)
       [VIA   ] step  5  Δ= -5.0  +8.6  -2.6  +0.7  -9.1  -0.5  着いた
[SEND] pi05_chunk000_via08: send_angles (single) [  -9.50,  +19.67,   -2.82,   +1.57,  -16.70,  -31.33]  speed=20 wait=0.0s  (pre max|Δ|=9.32°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.86° (tol 3.0°)
       [VIA   ] step  8  Δ= -4.3  +6.9  -3.5  +1.4  -9.3  -1.0  着いた
[SEND] pi05_chunk000_via09: send_angles (single) [ -11.08,  +22.14,   -3.44,   +1.78,  -19.00,  -31.50]  speed=20 wait=0.0s  (pre max|Δ|=3.80°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.30° (tol 3.0°)
       [VIA   ] step  9  Δ= -1.9  +1.3  -2.3  +1.3  -3.8  -1.2  着いた
[STATE ] 目標を渡す  目標と実測の最大差 2.30°（関節 j2）
[001] 推論 855ms  10ステップ先  Δ=-17.0 +28.5  -9.0  +4.9 -28.9  -1.5
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk001_via02: send_angles (single) [ -16.38,  +30.30,   -5.18,   +2.84,  -26.08,  -31.91]  speed=20 wait=0.0s  (pre max|Δ|=8.42°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.93° (tol 3.0°)
       [VIA   ] step  2  Δ= -5.1  +7.2  -4.0  +1.8  -8.4  -1.2  着いた
[SEND] pi05_chunk001_via05: send_angles (single) [ -21.80,  +39.03,   -7.32,   +4.27,  -34.75,  -32.04]  speed=20 wait=0.0s  (pre max|Δ|=9.44°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.79° (tol 3.0°)
       [VIA   ] step  5  Δ= -5.7  +7.6  -4.1  +1.3  -9.4  -0.8  着いた
[SEND] pi05_chunk001_via08: send_angles (single) [ -27.17,  +48.61,   -9.51,   +5.51,  -43.93,  -32.12]  speed=20 wait=0.0s  (pre max|Δ|=9.75°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.69° (tol 3.0°)
       [VIA   ] step  8  Δ= -5.9  +8.4  -4.0  +1.0  -9.7  -0.7  着いた
[SEND] pi05_chunk001_via09: send_angles (single) [ -28.24,  +51.64,  -10.13,   +5.86,  -46.56,  -32.11]  speed=20 wait=0.0s  (pre max|Δ|=3.41°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.61° (tol 3.0°)
       [VIA   ] step  9  Δ= -1.1  +1.9  -2.3  +0.1  -3.5  -0.6  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.61°（関節 j2）
[002] 推論 2367ms  10ステップ先  Δ= -8.7 +16.3  -2.7  +0.4 -14.6  -1.0
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > 
[SEND] pi05_chunk002_via02: send_angles (single) [ -33.31,  +61.16,  -11.28,   +6.21,  -54.42,  -32.22]  speed=20 wait=0.0s  (pre max|Δ|=8.55°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.35° (tol 3.0°)
       [VIA   ] step  2  Δ= -5.1  +8.3  -2.8  -0.0  -8.6  -0.5  着いた
[SEND] pi05_chunk002_via05: send_angles (single) [ -36.45,  +67.45,  -11.51,   +6.49,  -59.71,  -32.33]  speed=20 wait=0.0s  (pre max|Δ|=5.13°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=2.35° (tol 3.0°)
       [VIA   ] step  5  Δ= -3.1  +5.0  -1.6  -0.3  -5.1  -0.4  着いた
[SEND] pi05_chunk002_via08: send_angles (single) [ -37.10,  +69.18,  -11.34,   +6.58,  -60.62,  -32.46]  speed=20 wait=0.0s  (pre max|Δ|=1.56°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.33° (tol 3.0°)
       [VIA   ] step  8  Δ= -0.6  +0.6  -1.4  -0.7  -1.6  -0.4  着いた
[SEND] pi05_chunk002_via09: send_angles (single) [ -36.92,  +69.12,  -11.26,   +6.62,  -60.43,  -32.52]  speed=20 wait=0.0s  (pre max|Δ|=1.36°, post-send timeout 8.0s)
[SEND] arrival OK: max|Δ|=1.36° (tol 3.0°)
       [VIA   ] step  9  Δ= +0.6  -1.4  -1.2  -0.7  +0.3  -0.4  着いた
[STATE ] 目標を渡す  目標と実測の最大差 1.36°（関節 j1）
[003] 推論 871ms  10ステップ先  Δ= +0.2  +5.8  -7.4  -1.1  +1.2  -0.7
       [VIA   ] 中継点 4 点: step 2, 5, 8, 9
       Enter=実行 / s=送らず次へ / b=巻き戻し / p=ゲームパッドで補正 / c=キーで補正 / r=爪の締め直し / h=HOMEに戻して終了 / q=終了 > p
objc[24773]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc890) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328592c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLAppDelegate is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc8e0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859318). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLTranslatorResponder is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc958) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859390). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLMessageBoxPresenter is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc980) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328593b8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_cocoametalview is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffc9d0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859408). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLOpenGLContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffca20) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859458). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_ShapeData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffca98) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328594d0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_CocoaClosure is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcae8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859520). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_VideoData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcb38) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859570). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_WindowData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcb88) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328595c0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLWindow is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcbb0) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328595e8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class Cocoa_WindowListener is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcbd8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859610). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDLView is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcc50) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859688). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class METAL_RenderData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffccc8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859700). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class METAL_TextureData is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcd18) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859750). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_RumbleMotor is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcd40) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x132859778). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[24773]: Class SDL_RumbleContext is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x120ffcd90) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1328597c8). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
/opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/pkgdata.py:25: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  from pkg_resources import resource_stream, resource_exists
[PAD   ] 補正 1 (推論 3 のあと). ゲームパッドで直して, □ で推論に戻ります
^C
^C
^C
^C
```

### lab_stop_20261110_194717.txt

```text
[STOP  ] Log28 の Pod: id 9qvy252fd9qgb4  GPU=NVIDIA A40  作った時刻 2026-11-10 19:09:20
[LOG   ] serve.log を持ち帰った → /Volumes/KIOXIA/r2r2r-data/lab/Log28/serve_20261110_194719.log (33 行)
[STOP  ] Terminate を送った (19:47:27)
[STOP  ] Pod (id 9qvy252fd9qgb4) が消えたことを API で確かめた
[COST  ] 借りていた時間 38 分 7 秒 (0.64 時間) × 1時間 0.59 ドル = 約 0.37 ドル (約 59 円. 1ドル=158円)
[TUNNEL] 8000 番のトンネルは残っていません
```

### serve_20261110_194719.log（Pod の推論サーバーの記録）

```text
INFO:root:Loading model...
INFO:2026-11-10 10:13:57,743:jax._src.xla_bridge:925: Unable to initialize backend 'rocm': module 'jaxlib.xla_extension' has no attribute 'GpuAllocatorConfig'
INFO:jax._src.xla_bridge:Unable to initialize backend 'rocm': module 'jaxlib.xla_extension' has no attribute 'GpuAllocatorConfig'
INFO:2026-11-10 10:13:57,747:jax._src.xla_bridge:925: Unable to initialize backend 'tpu': INTERNAL: Failed to open libtpu.so: libtpu.so: cannot open shared object file: No such file or directory
INFO:jax._src.xla_bridge:Unable to initialize backend 'tpu': INTERNAL: Failed to open libtpu.so: libtpu.so: cannot open shared object file: No such file or directory
INFO:absl:orbax-checkpoint version: 0.11.13
INFO:absl:Created BasePyTreeCheckpointHandler: use_ocdbt=True, use_zarr3=False, pytree_metadata_options=PyTreeMetadataOptions(support_rich_types=False), array_metadata_store=<orbax.checkpoint._src.metadata.array_metadata_store.Store object at 0x7c9647275390>
INFO:absl:Restoring checkpoint from /workspace/ckpt/deep_v1_2999/params.
INFO:absl:[thread=MainThread] Failed to get flag value for EXPERIMENTAL_ORBAX_USE_DISTRIBUTED_PROCESS_ID.
INFO:absl:[process=0] /jax/checkpoint/read/bytes_per_sec: 1.1 GiB/s (total bytes: 6.3 GiB) (time elapsed: 5 seconds) (per-host)
INFO:absl:Finished restoring checkpoint in 5.96 seconds from /workspace/ckpt/deep_v1_2999/params.
INFO:openpi.shared.download:Downloading gs://big_vision/paligemma_tokenizer.model to /root/.cache/openpi/big_vision/paligemma_tokenizer.model
INFO:tqdm_loggable.tqdm_logging:Progress on: -/4264023 rate:- remaining:? elapsed:00:00 postfix:-
INFO:tqdm_loggable.tqdm_logging:Progress on: 4.07MiB/4.07MiB rate:4,259,989.8iB/s remaining:00:00 elapsed:00:01 postfix:-
INFO:root:Loaded norm stats from /workspace/openpi/assets/pi05_mecharm_deep_even10_lora/local/mecharm_redblock_box_deep_even10_v1
INFO:root:Loaded norm stats from /workspace/ckpt/deep_v1_2999/assets/local/mecharm_redblock_box_deep_even10_v1
INFO:root:Creating server (host: 0064877cc448, ip: <POD_INTERNAL_IP>)
INFO:websockets.server:server listening on 0.0.0.0:8000
INFO:websockets.server:connection open
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 48044) opened
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 48044) closed
INFO:websockets.server:connection open
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 52508) opened
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 52508) closed
INFO:websockets.server:connection open
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 55724) opened
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 55724) closed
INFO:websockets.server:connection open
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 52668) opened
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 52668) closed
INFO:websockets.server:connection open
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 54700) opened
INFO:openpi.serving.websocket_policy_server:Connection from ('127.0.0.1', 54700) closed
```

### pi05_20261110_191905.json（2回目。推論の前に Ctrl-C）

```json
{
 "script": "run_policy_client.py",
 "source": "pi05_policy",
 "run_stem": "pi05_20261110_191905",
 "run_dir": "/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_191905",
 "recording_enabled": true,
 "end_reason": "ctrl_c",
 "result": "interrupted",
 "exit_code": 0,
 "outcome": null,
 "rewinds": 0,
 "corrections": 0,
 "history_left": [],
 "args": {
  "host": "localhost",
  "port": 8000,
  "execute": true,
  "auto": false,
  "steps_per_chunk": 10,
  "max_chunks": 60,
  "max_move_deg": 20.0,
  "raw_dir": "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes",
  "framing_raw_dir": "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes",
  "prompt": "put the red block in the white box",
  "model": "deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)",
  "record": false,
  "ignore_framing": true,
  "state_from_target": true,
  "via_stride": 3,
  "settle_tol": 1.0,
  "settle_retries": 0,
  "dry_keys": false,
  "send_wait": 0.0,
  "grip_wait": 0.8,
  "grip_open_wait": 0.0,
  "settle_wait": 0.8
 },
 "ended_at": "2026-11-10T19:19:08.564102"
}
```

### pi05_20261110_193431.json（爪が奥側にずれていたので h で HOME へ。失敗として記録）

```json
{
 "script": "run_policy_client.py",
 "source": "pi05_policy",
 "run_stem": "pi05_20261110_193431",
 "run_dir": "/Volumes/KIOXIA/r2r2r-data/runs/pi05_policy/pi05_20261110_193431",
 "recording_enabled": true,
 "end_reason": "h",
 "result": "completed",
 "exit_code": 0,
 "outcome": "failure",
 "rewinds": 0,
 "corrections": 0,
 "history_left": [
  {
   "kind": "chunk",
   "chunk": 0,
   "correction_id": null,
   "grip_changed": false
  },
  {
   "kind": "chunk",
   "chunk": 1,
   "correction_id": null,
   "grip_changed": false
  },
  {
   "kind": "chunk",
   "chunk": 2,
   "correction_id": null,
   "grip_changed": false
  },
  {
   "kind": "chunk",
   "chunk": 3,
   "correction_id": null,
   "grip_changed": false
  }
 ],
 "args": {
  "host": "localhost",
  "port": 8000,
  "execute": true,
  "auto": false,
  "steps_per_chunk": 10,
  "max_chunks": 60,
  "max_move_deg": 20.0,
  "raw_dir": "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes",
  "framing_raw_dir": "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes",
  "prompt": "put the red block in the white box",
  "model": "deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)",
  "record": false,
  "ignore_framing": true,
  "state_from_target": true,
  "via_stride": 3,
  "settle_tol": 1.0,
  "settle_retries": 0,
  "dry_keys": false,
  "send_wait": 0.0,
  "grip_wait": 0.8,
  "grip_open_wait": 0.0,
  "settle_wait": 0.8
 },
 "ended_at": "2026-11-10T19:39:57.652312",
 "judgments": {
  "pick": {
   "human_verdict": "no",
   "verdict": "no",
   "source": "human"
  },
  "place": {
   "human_verdict": "no",
   "verdict": "no",
   "source": "human"
  }
 }
}
```

191618・192201・194015 は、推論のプロセスが落ちたか固まったため、各回の json が書かれていません（log.jsonl と recording/ は残っている）。


## 調べるのに使ったスクリプト（KIOXIA の lab/Log28/analysis/）

上の節で使ったもののほか、ブロックの位置を mm で出そうとして、夜の画像では赤が見つからなかったスクリプトも残しています。

### tilt_bm.py（マットの上の mm で赤の候補を出す。お手本・夜の試行とも候補が見つからなかった）

```python
import sys, cv2
sys.path.insert(0,'.')
from pathlib import Path
import count_by_position as cb
runs=Path(sys.argv[1])/'runs'
for s in sys.argv[2:]:
    rd=cb.find_run_dir(s,runs)
    cap=cv2.VideoCapture(str(rd/'recording/top.mp4')); ok,f=cap.read(); cap.release()
    c=cb.detect_corners(f)
    import numpy as np
    co=[[float(x),float(y)] for x,y in c]
    H=cv2.getPerspectiveTransform(np.array(co,np.float32),np.array(cb.MAT_CORNERS_MM,np.float32))
    bbox=(int(min(co[0][0],co[3][0])),int(max(co[1][0],co[2][0])),int(min(co[0][1],co[1][1])),int(max(co[2][1],co[3][1])))
    cs=cb.red_candidates(f,bbox,H)
    print(s, [ (round(x['mm'][0],1),round(x['mm'][1],1),x['area']) for x in cs])
```

tilt_bm.py の出力（193431 は、この時点では録画のファイルがまだ書き終わっておらず、読めなかった）:

```
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xc93e4ceff5f0] moov atom not found
run_0001_20261019_191541 []
run_0001_20261019_191635 []
run_0001_20261109_102109 []
run_0001_20261109_104517 []
pi05_20261110_113600 []
pi05_20261110_114106 []
Traceback (most recent call last):
  File "/tmp/bm.py", line 9, in <module>
    c=cb.detect_corners(f)
  File "/sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/r2r2r-scripts/pi05/./count_by_position.py", line 141, in detect_corners
    return mcd.detect_corners(frame)
  File "/sessions/rcw-01levexubzsy9ycdbwdhrthd/mnt/r2r2r-scripts/mat_corner_detection.py", line 47, in detect_corners
    h, w = frame.shape[:2]
AttributeError: 'NoneType' object has no attribute 'shape'
```


## サムネイルのために足したコード（p2m-site の tools/lab_thumbs/）

表紙は、位置2 で π0.5 が出した行き先のひじ j2 を、午前の4回（薄い線）と夜の4回（青）で重ねたものです。夜の線だけが、位置2 のつかむ姿勢の −34°まで下りていきます。

### lab_thumb.py に足した LOG486 の分（git diff）

```diff
diff --git a/tools/lab_thumbs/lab_thumb.py b/tools/lab_thumbs/lab_thumb.py
index 9c20cd5..ce418e8 100644
--- a/tools/lab_thumbs/lab_thumb.py
+++ b/tools/lab_thumbs/lab_thumb.py
@@ -339,7 +339,7 @@ def both(*fs):
 # ---------- 記事ごとの指定 ----------
 # 一覧の並び（LOG番号の大きい順）と構図。隣どうしで同じ構図にしない
 ORDER = """
-484 split   480 dark    479 split   463 dark    461 paper   460 split   450 dark    446 paper   436 dark    422 photo_l 419 photo_r 417 dark
+486 paper   484 split   480 dark    479 split   463 dark    461 paper   460 split   450 dark    446 paper   436 dark    422 photo_l 419 photo_r 417 dark
 407 split   403 dark    401 paper   363 split   361 paper   358 dark    355 paper   352 split
 349 dark    348 paper   345 split   341 dark    337 split   327 paper   326 dark    277 paper
 234 dark    225 paper
@@ -349,6 +349,14 @@ ORDER = """
 def build(log, d, img_dir):
     n = int(log[3:])
     # GPU ラックの写真（public/site/gbr/reverse.webp）はここでは使わない（2026-11-08 エイイチ「GPUラックの写真は作っちゃダメ」）
+    if n == 486:
+        # 位置2 で π0.5 が出した行き先（log.jsonl の actions を、最初の人の操作の手前までつないだもの）のひじ j2。
+        # 薄い線は午前の4回（上カメラが右下がり約 +0.7°。位置1 寄りの -10° 前後で平らになる）、青は夜の4回（右上がり約 -0.8〜-1.0° に直したあと。
+        # 位置2 のつかむ姿勢の -34° まで下りていく）。赤は閉じる指令の所
+        d6 = d['LOG486']
+        runs = d6['before'] + d6['after']
+        role = lambda ri, p: 'q' if ri < len(d6['before']) else None
+        return paper('CAMERA TILT', 'POSITION 2', runs_draw(runs, 2, w=6, kind_of=role, scale=(-38, 4)))
     if n == 484:
         # 上は位置1 の3回、下は位置2 の4回。肩 j1 の動き（横は1回の中の順番、縦は同じ目盛り）。青＝π0.5 が動かした所、赤＝つかんでいる間、
         # 墨・紙色＝ゲームパッドで直した所。位置1 は3回とも下りきってつかみ、位置2 は途中で人の直しに替わる
```
