フィジカルAI研究室を作る Log#19 | ゲームパッドで mechArm 270 を直す準備ができた

フィジカルAI研究室を作る Log#19 | ゲームパッドで mechArm 270 を直す準備ができた

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

3,198円の互換品のゲームパッド(PS4 型)を Mac に Bluetooth でつなぐと、ボタン16個と軸6本がすべて読め、それを使って小型ロボットアーム mechArm 270 の関節を1押し0.5°ずつ動かし、爪を開け閉めし、押した操作をすべて記録するスクリプトまで作れました。ここまでは、アームに指令を送らないドライラン(画面の中の数字だけが動く試し)で172回の操作を確かめた段階で、実物のアームではまだ動かしていません。

3,198円で買った黒い PS4 型の互換ゲームパッド。左に十字キー、右に △○×□ のボタン、真ん中に HOME ボタンと2本のスティックがある

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

  • 良くなること:AI(Physical Intelligence の π0.5)が失敗しかけたときの人の直しが、キーボードで「c で補正に入り、関節の番号を選び、+ か − を押す」から、十字キーを押すだけになる。左右(根元の回転)はいつも十字キーの左右、上下は △ で選んだ関節
  • もたらされること:押した1回ごとに、送った角度と届いた角度が1行ずつ記録に残る。 人の直しを、次の学習に使う「人が直した記録」として、そのまま集められる形になった
  • 時間・費用:コントローラーは3,198円。Claude Code の実装は66分(外部のレビュー3回を含む)、アームなしの試しは3分33秒で172回の操作。キーボードで直していたときと比べて速くなるかは、まだ測っていない。実機で、1回の直しにかかる時間を両方で測って確かめる

きっかけは、人の家事の動画でロボットを学ばせている米国の Figure 社の話でした。人の目線の動画から学べるのは「どこへ手を伸ばすか」までで、力加減やロボット自身の体のくせは、ロボットのデータで合わせる必要があります。このプロジェクトでは、AI が mechArm 270 を動かし、失敗しかけたら人がキーで関節を直し、その直した記録で学び直す、ということを続けてきました(LOG417・LOG422)。その「人が直す」部分を、もっと手早く、もっと確実に記録に残せる形にするのが、この回のねらいです。

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

段階やったこと結果
Figure の投稿を確かめる
「Figure が一般の人の家事の動画で学習させ、知らない家での成功率が9%から56%になった」という投稿の数字を、報道と公開資料で確かめた
数字はほぼ報道どおり。56% は Figure 自身の評価で、Tesla の求人の話は2024年のものだった
ゲームパッドを選ぶ
メーカーの公式品(129.99ドル)と、市販の互換品を比べた
操作を自分のスクリプトで読めて記録できる互換品(3,198円)にした。mechArm の中の Raspberry Pi(Ubuntu 18.04)では互換品がつまずく例があるので、まず Mac につなぐ
読み取りを確かめる
値を表示するだけのスクリプト gamepad_check.py を作り、ボタンとスティックを全部押した
ボタン16個すべて反応、連射機能は入っていない、スティックは離すと毎回 +0.004 に戻る。値の更新は約0.05秒おき
操作を決める
モードを2つに分ける案から、押した分だけそのまま動く1つの形にしぼった
十字キー左右 = 根元、上下 = △ で選んだ関節、R1 で2°、× と ○ で爪、HOME 長押しで始めの姿勢、L2 と R2 の同時押しで停止
スクリプトを作る
依頼箱で Claude Code に gamepad_teleop.py を頼み、codex のレビューを3回通した
1,048行とテスト46件。直すべき指摘は1回目7件・2回目2件・3回目0件
アームなしで試す
ドライランで、ボタンを一通り押した
172回の操作。範囲の守り・停止と再開・HOME が決めたとおりに動いた。前の動きが終わる前の押し25回は、ためずに捨てた
残りを頼む
ドライランは記録しない・起動時の警告を直す、を依頼69で頼んだ
この記録の時点で作業中

次は、止めてある実物のアームを動かせる状態に戻し、十字キーの左右の向きを確かめてから、AI の動きを人がゲームパッドで直す試しに入ります。

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

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

作業ログ ― 互換ゲームパッドを Mac で読み、mechArm 270 を手で直すスクリプトをアームなしで試すまで

この回の作業は 11/1 の朝から 11/2 の夜まで。実物のアームは、前の回(LOG422)で手首の関節(j4)が指令に応えなくなったまま止めてあり、この回では一度も動かしていません。記事の中の「見込める効果」は、測った値ではなく見込みです。

この回で使う言葉

言葉この記事での意味
ゲームパッド
ゲーム機のコントローラー。この回は PS4 用の形をした互換品(純正ではない安い品)を使った
テレオペレーション
人が手元の道具で、ロボットを離れた所から動かすこと。ここではゲームパッドで mechArm 270 の関節を動かすこと
ドライラン
本物のアームに指令を送らず、送るはずだった角度を画面の数字だけで動かす試し
j0〜j5
mechArm 270 の6つの関節。j0 は根元の回転、j1 は肩、j2 はひじ、j3 は腕のねじり、j4 は手首の曲げ、j5 は爪のねじり
command と reached
command はアームに送った角度、reached はアームが実際に届いた角度。2つの差が、アームの動きのずれになる
基準の姿勢
スクリプトを起動したときの姿勢。動かせる範囲(±15° や ±10°)は、この姿勢から数える
HOME
アームの始めの姿勢 [0, 0, 0, 0, 0, −30]
依頼箱
Claude が書いた依頼を Mac の見張り係が Claude Code に渡し、報告を書き戻す仕組み(iCloud のフォルダ)
codex
OpenAI のコードを読むAI。関節を動かすコードは、書いた Claude Code とは別のAIに読ませて確かめる

pygame は、Python でゲームの画面と入力を扱う道具

pygame は、Python でゲームを作るための道具の集まりです。中では SDL という、画面・音・コントローラーを扱う部品が動いています。この回では、ゲームパッドのボタンとスティックの値を読むのと、今の関節の角度を出す小さな窓を作るのに使いました。

pygame の言葉この記事での意味
ボタン(button)
押したか離したかの2通りの値。このゲームパッドでは0〜15の16個
軸(axis)
−1.0〜+1.0 の連続した値。スティックの横と縦、L2 と R2 の押し込み量で6本
hat
十字キーを1つの部品として読む形。このゲームパッドでは0個で、十字キーはボタン11〜14として読める
GUID
コントローラーの種類を表す番号。メーカーの番号と製品の番号が入っている

使い方は、Mac のターミナルで python3 -m pip install pygame で入れ、スクリプトの中で pygame.joystick を読むだけです。

Figure の Index は、人が家事を撮った動画をお金で集める仕組み

米国のヒューマノイド(人の形のロボット)の会社 Figure は、2026年8月に Index というスマホのアプリを公開しました。一般の人が自分の家事を撮って送ると、撮った時間に応じてお金が払われます。集めた動画で、Figure のロボットを動かす AI(Helix)を学習させています。この回の調べで使った主な報道は Forbes の記事 です。

11/1 朝:Figure の Index の投稿を確かめ、人の目線の動画で学べることを整理した

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

ファイルの一覧(9本)

01_research_figure_index_human_video_20261101.md ― 調査メモ research_figure_index_human_video_20261101.md・4,210字

02_research_gamepad_teleop_mecharm_20261101.md ― 調査メモ research_gamepad_teleop_mecharm_20261101.md・4,166字

03_gamepad_check.py.txt ― gamepad_check.py・4,889字

04_sec04.txt ― gamepad_check.py を動かしたターミナルの出力(1〜613行目)・15,002字

05_sec06.md ― 依頼文 20261102_68_ゲームパッドでアームを手で動かすスクリプトを作る.md・4,442字

06_gamepad_teleop.py.txt ― gamepad_teleop.py(commit 78bd62e)・43,822字

07_test_gamepad_teleop.py.txt ― test_gamepad_teleop.py(テスト46件)・31,288字

08_sec07.txt ― gamepad_teleop.py のドライランのターミナルの出力(614〜805行目)・21,405字

09_runs_experiments_gamepad_teleop_teleop_20261102_192657_dry_log.jsonl.json ― runs/experiments/gamepad_teleop/teleop_20261102_192657_dry/log.jsonl(172行)・98,069字

X で見た Martin(@0x_meden)の投稿のスクリーンショット3枚を Claude に見せ、事実かを確かめた。X のページは読み取りを断られたので、スクリーンショットの文字から読んだ。

結果は、数字はほぼ報道どおりだった。ただし、知らない家での成功率が9%から56%になったという数字は Figure 自身の評価で、第三者が同じ結果を出した記録はない。「ゼロショット」は、まったく初めての家事ができたという意味ではなく、家事ごとのデータは別にあって、学習に使っていない家でもできた、という意味だった。投稿の中の Tesla の求人(時給25〜48ドル)は2024年8月の話だった。

続けて「人の視点からの動画でほんとうにロボットを学習させることができるの?」と聞いた。答えは、人の動画で下地を作り、最後にロボット自身のデータで合わせる2段構え、だった。

人の目線の動画で学べること学べないこと
何をどの順でやるか(タオルを広げてから畳む、など)
力加減や、物が手の中でどう滑るか(映像に写らない)
物のどこに手を伸ばすか、手首がどう動くか
ロボット自身の体の制約(関節の範囲・重さ・指の数の違い)
見たことのない部屋や物への慣れ
2本指のグリッパーと5本指の人の手の差。mechArm 270 に移せるのは「どこへ手を伸ばすか」まで

「学べないこと」の側が、このプロジェクトで人がキーで直してきた所(根元の向きの数度のずれ、つかんだあとの持ち上げ)に当たる。直した記録はロボット自身のデータで、それを集めやすくすることが次の一手になる、という流れでゲームパッドの話に移った。調べた結果は、iCloud の research フォルダに調査メモとして残した。

調査メモ research_figure_index_human_video_20261101.md

# Figure の Index(人の家事動画を買って学習)の事実確認と、人の目線の動画でロボットが学べるか

- 調べた日:2026-11-01
- きっかけ:エイイチが共有した X の投稿のスクリーンショット3枚(Martin @0x_meden の投稿、Grok の日本語訳)。「これは事実?」と「人の視点からの動画でほんとうにロボットを学習させることができるの?」の2つの問い
- 方針:投稿の数字を、報道・公式の資料・モデルの配布ページと突き合わせた。会社の発表は「会社の発表」と書き分ける
- 換算:1ドル=150円で計算(円は目安)
- 注意:X の投稿ページはサイト側が読み取りを拒否したため開けなかった。投稿の中身はスクリーンショットから読んだ
別のタブで開く保存

11/1 朝:メーカーの公式ゲームパッドと互換品を比べ、3,198円の互換品にした

別件でmecharmの動きを補正するとき、コントローラーをゲーム用のをつけて操作したりは可能ですか?

mechArm 270 を作っている Elephant Robotics には、公式の Bluetooth ゲームパッド(129.99ドル、mechArm 270 Pi 対応)がある。ただ、押した操作を自分のスクリプトで受け取って記録できるか、今ある守り(J4_SAFE などの角度の範囲)を通るかが、商品ページのどこにも書かれていなかった。目的は「直した操作を記録に残す」ことなので、市販のゲームパッドを自分のスクリプトで読む形にした。

もう1つ調べたのは、つなぐ先。mechArm 270 の中の Raspberry Pi(Ubuntu 18.04)に直接つなぐと、互換品は「最初はつながるのに、電源を切ると再接続できない」「純正を名乗るのに Linux の PS4 用の部品に弾かれる」といった例が見つかった。Mac から使った報告があった Yommor の互換品(3,198円・USB-C・有線でも使える)を選び、まず Mac につなぐことにした。

くらべたことメーカーの公式品市販の互換品(Yommor)
値段
129.99ドル(送料・関税は別)
3,198円
操作を自分で記録できるか書かれていない
自分のスクリプトで読むので、すべて記録できる
今の守り(角度の範囲)を通るか
分からない自分のスクリプトで通す
Mac での報告—
MacBook Air・iPad Air で使えたというレビュー
気をつける点—
TURBO 連射(ボタン1回が連続入力になる)を切る。SHARE ボタンは押すと画面が開くというレビューがあり、使わない

調査メモ research_gamepad_teleop_mecharm_20261101.md

# ゲーム用コントローラーで mechArm を手で補正する:買うものと、つなぎ方の調査

- 調べた日:2026-11-01
- きっかけ:エイイチの問い「mechArm の動きを補正するとき、ゲーム用のコントローラーをつけて操作したりは可能ですか?」
- 方針:メーカーの商品ページ・掲示板・不具合の報告を開いて確かめたことだけを書く
- 換算:1ドル=150円で計算(円は目安)
- 状態:Yommor の互換コントローラーを購入済み(2026-11-02 着)。動きの確認はまだ
別のタブで開く保存

11/2 午後:gamepad_check.py で、ボタン16個と軸6本がすべて読めた

11/2 にゲームパッドが届いた。Mac の Bluetooth の設定で、SHARE を押したまま HOME を長押ししてつなぐと、「DUALSHOCK 4 Wireless Controller」と出た。Mac からは、ソニーの純正のコントローラーとして見えている。

Mac の Bluetooth の設定。自分のデバイスの一番上に DUALSHOCK 4 Wireless Controller が接続済みで並んでいる

アームにはつながず、値を表示するだけのスクリプト gamepad_check.py を Claude が書いた。ウィンドウの文字は、日本語のフォントがなくても化けないように英数字だけにしてある。

gamepad_check.py の窓。PS4 Controller の6本の軸の値が並び、スティックの4本は +0.004、L2 と R2 の2本は −1.000 で左端まで緑の棒が伸びている

ボタンを1つずつ押し、スティックを端まで倒し、L2 と R2 を押し込んだ結果は次のとおり。

確かめたこと結果
ボタン 0〜15
16個すべて、押した・離したが1回ずつ出た。押しっぱなしで繰り返す行がないので、TURBO 連射は入っていない
左スティック(軸0・1)
−1.000〜+1.000 まで届き、離すと毎回 +0.004 に戻る
右スティック(軸2・3)
軸2 は −0.984〜+0.976、軸3 は −1.000〜+1.000。離すと毎回 +0.004 に戻る
L2・R2(軸4・5)
離して −1.0、押し込んで +1.0。途中の値(−0.286、+0.404 など)も出る
十字キー
ボタン11〜14として出る(hat ではない)
値の更新の間隔
スティックを回している間、出力の時刻はおよそ0.05秒(1秒に約20回)刻み
気づいたこと
左スティックを強く回している間に、左スティックの押し込み(ボタン7)が0.03〜0.05秒だけ入ることが3回あった

ボタンの番号は、□ → × → ○ → △ の順に押して 2 → 0 → 1 → 3 と出たことで、0 = ×・1 = ○・2 = □・3 = △ と決まった。

gamepad_check.py

03_gamepad_check.py.txt4,889字・133行・python
#!/usr/bin/env python3
"""
gamepad_check.py — ゲーム用コントローラーの入力を読んで表示するだけの確認スクリプト

ロボットには一切つながない。ファイルにも何も書き込まない。

使い方(Mac):
    python3 -m pip install pygame
別のタブで開く保存

gamepad_check.py を動かしたターミナルの出力(1〜613行目)

pygame を入れたところから、ボタン・スティック・L2・R2 を一通り押し、番号を確かめ、つなぎ直しまで。

04_sec04.txt15,002字・613行・text
Last login: Sun Nov  1 18:58:26 on ttys001
(base) shimazueiichi@shimadzueiichinoMacBook-Pro ~ %    cd ~/r2r2r-scripts/tools 
   python3 -m pip install pygame
   python3 gamepad_check.py
Collecting pygame
  Downloading pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl.metadata (12 kB)
Downloading pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl (12.4 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.4/12.4 MB 15.2 MB/s  0:00:00
別のタブで開く保存

KIOXIA の diag/gamepad_check_20261102/README.md

# ゲームパッドの読み取り確認(2026-11-02)

- 目的:買った互換コントローラーの入力を Mac で読めるか、ロボットにつなぐ前に確かめる(ロボットにはつないでいない)
- 調査メモ:iCloud の `research/research_gamepad_teleop_mecharm_20261101.md`
- 中身
  - `terminal_output.txt`:ターミナルの出力そのまま(エイイチが貼ったもの。ESC での終了と最後の「軸の一覧」は含まない)
  - `gamepad_check.py`:使ったスクリプトの写し(原本は `~/r2r2r-scripts/tools/gamepad_check.py`)

## 条件

| 項目 | 値 |
|---|---|
| コントローラー | Yommor P4 コントローラー(Amazon、3,198円、互換品、USB-C) |
| つなぎ方 | Bluetooth(Mac の設定では「DUALSHOCK 4 Wireless Controller」と表示) |
| Mac | MacBook Pro(Apple シリコン) |
| Python / pygame / SDL | 3.12.12(miniforge)/ 2.6.1 / 2.28.4 |
| pygame が見た名前 | PS4 Controller、軸6・ボタン16・十字キー(hat)0 |
| GUID | 03008fe54c050000cc09000000016800(Sony 054C・DUALSHOCK 4 2代目 09CC と同じ番号) |

## 結果

| 確認 | 結果 |
|---|---|
| ボタン 0〜15 | 16個すべて、押した・離したが1回ずつ出た |
| TURBO 連射 | 入っていない(押しっぱなしで繰り返す行がない)。SHARE(4)を1回押しても変化なし |
| 左スティック(軸0・1) | −1.000〜+1.000 まで届く。離すと毎回 +0.004 に戻る |
| 右スティック(軸2・3) | 軸2 −0.984〜+0.976、軸3 −1.000〜+1.000。離すと毎回 +0.004 に戻る |
| L2・R2(軸4・5) | 離して −1.0、押し込んで +1.0。途中の値も出る(−0.286、+0.404 など) |
| 十字キー | ボタン 11〜14 として出る(hat ではない) |
| 値の更新の間隔 | スティックを回している間、出力の時刻はおよそ 0.05 秒(約20回/秒)刻み。この間隔がコントローラー側か読み取り側かは未確認 |

## 気づいたこと

- 左スティックを強く回している間に、左スティックの押し込み(ボタン7)が 0.03〜0.05 秒だけ入ることが3回あった(129.168s・132.848s ほか)。スティックの押し込み(7・8)には、つかむ・離すなどの大事な操作を割り当てない
- タッチパッド(15)を押している間に左スティックが少し動いた(軸0 −0.129)。親指が触れたと見られる。その後の操作では毎回 +0.004 に戻っている

## ボタン番号(SDL の PS4 の並び。押した場所との対応はエイイチの確認待ち)

| 番号 | ボタン |
|---|---|
| 0 / 1 / 2 / 3 | × / ○ / □ / △ |
| 4 / 5 / 6 | SHARE / HOME / OPTIONS |
| 7 / 8 | 左スティック押し込み / 右スティック押し込み |
| 9 / 10 | L1 / R1 |
| 11 / 12 / 13 / 14 | 十字キー 上 / 下 / 左 / 右 |
| 15 | タッチパッド押し込み |

| 軸 | 中身 |
|---|---|
| 0 / 1 | 左スティック 横 / 縦(上に倒すと −) |
| 2 / 3 | 右スティック 横 / 縦(上に倒すと −) |
| 4 / 5 | L2 / R2(離して −1.0) |

KIOXIA の diag/gamepad_check_20261102/button_map_confirmed.md

# ボタン番号の確定(2026-11-02 15:48)

README.md の「ボタン番号」の表のうち、エイイチの確認待ちだった 0〜3 を確定した。README.md は書き換えず、このファイルを足す。

- 確かめ方:エイイチが 左(□)→ 下(×)→ 右(○)→ 上(△)の順に押し、ターミナルに 2 → 0 → 1 → 3 の順で出た
- 確定:0 = ×、1 = ○、2 = □、3 = △(README.md の表のとおり)

確認したときのターミナルの出力(そのまま):

```
 188.169s  ボタン  2  押した
 188.421s  ボタン  2  離した
 188.773s  ボタン  0  押した
 188.975s  ボタン  0  離した
 189.291s  ボタン  1  押した
 189.441s  ボタン  1  離した
 189.845s  ボタン  3  押した
 189.977s  ボタン  3  離した
```

11/2 午後:操作は、押した分だけそのまま動く1つの形にしぼった

直したい動きは3つだった。

今のところあるのはPICK の位置合わせ(アームの左右)と姿勢の微調整(アームの上げ下げ) 爪の握り直しとかが多いかな HOME姿勢に戻すとか、停止ボタンも必要

Claude の最初の案は、上空の姿勢(APPROACH_PICK)と床ぎりぎりの姿勢(PICK)の間を1/10ずつ行き来する上げ下げや、「開く → 2秒待つ → 閉じる」の握り直しボタン、関節を選んで動かす別のモードを持つ形だった。話しながら、次のように変わった。

最初の案決まった形決めた理由
停止は R1
L2 と R2 の両方を半分以上押し込む
停止は片方だと間違って押しやすい。Claude は「片方でも止まるほうが、とっさのときに確実」と勧めたが、両方にした
上げ下げは2つの姿勢の間を行き来
選んだ関節をそのまま動かす
「キーパッドの動きがそのまま出ればいい」。動きの中身が見えない形は使いにくい
位置合わせのモードと関節のモードの2つ
モードは1つ。△ で上下の関節を切り替える
「迷いそうだけど」。いつ押しても同じ動きになる
握り直しボタン(開く → 待つ → 閉じる)
なし。× と ○ を自分で押す「オートの要素は無くしたい」
HOME は △ の長押しHOME ボタンの1秒長押し
「HOMEボタンでいいんじゃないの?」。長押しは、6つの関節がまとめて大きく動くので、触れただけで動かないように残した
2° の刻みは L1 を押しながらR1 を押しながら
「十字キーを操作しながらL1を押しずらいから、R1にして」
  • ねらい:AI が失敗しかけた数秒のうちに、迷わずに直せること
  • 変わったこと:押すボタンと動く関節が、いつも同じ1対1になった。自動で動く要素(握り直し・上げ下げの道筋・失敗したときの自動の HOME)はなくなった
  • 見込める効果:直した操作が「どの関節を何度」の形で残るので、記録をそのまま並べて比べられる。直すのにかかる時間が縮むかは、実機で測る

決まった操作と守り

ボタン動き
十字キー 左 / 右
j0(根元の回転)を0.5°ずつ。どの関節を選んでいても、いつも j0
十字キー 上 / 下
選んでいる関節を0.5°ずつ。上 = −、下 = +。始めは j1(肩)。PICK の上空から床へ下りるとき j1・j2・j4 はどれも角度が増えるので、下を押すと爪が下がる向きにそろう
△
上下で動かす関節を切り替える(j1 → j2 → j3 → j4 → j5 → j1)
R1 を押しながら1回2°ずつ
× / ○爪を閉じる / 開く
HOME を1秒長押しHOME の姿勢に戻る
L2 と R2 を両方半分以上停止。OPTIONS で再開

守りは数字で決めた。j1 は 90.87°(PICK の高さ。arm_kinematics.md の PICK_J1_FIXED)より下げない。j0 は基準の姿勢から ±15°、j1〜j5 は ±10° まで。j4 は J4_SAFE(−90〜+60°)の中だけ。コントローラーの接続が切れたら停止にする。既存のコードには、j1 の安全の上限として 88.0° という値(fk_lookup.J1_SAFE_MAX)もあるが、それだと PICK の高さまで下ろせないので、ここでは 90.87° にした。

j1 の止めは、PICK より下を指令しないだけで、床に当たらないことまでは保証しない。PICK の姿勢そのものが爪先が床ぎりぎりで、j2(ひじ)や j4(手首)を下げる方向に動かすと、j1 の止めとは関係なく爪先が床に近づく。最後の確かめは人の目と、L2+R2 の停止になる。

11/2 16:09〜17:15:依頼68で、Claude Code が gamepad_teleop.py を作った

決まった操作を依頼文にして、依頼箱(iCloud の 依頼箱/新規/)に置いた。Mac の見張り係が16:09に Claude Code へ渡し、17:15に報告が戻った。関節を動かすコードなので、codex のレビューを3回通している。

codex のレビュー直すべき指摘中身
1回目7件
停止と送信の取り合い、範囲の外の姿勢から大きく動く、古い目標から動く、HOME の長押しの持ち越し、同じコマでボタンを停止より先に処理する、読み取りの失敗の扱い、記録に書けなくなったあとの再開
2回目2件
送る前の待ちが10秒で切れたあとに送ってしまう穴、停止を押すと画面が止まる
3回目0件
勧めるだけの2件(終わるときに送ると決めていたかを残す・終わり方のテスト)も直した

codex のレビューの全文は、Mac の一時フォルダ(/tmp/req68/)に書かれていて、この記録には残っていない。上の表は Claude Code の作業の記録(logs/20261102_1730.md)から写した。

L2 と R2 で止めても、もう送った1回分は最後まで動く。 調べると、Raspberry Pi の上で動いている指令の受け口(mecharm_daemon.py)に、動いている途中で止める命令がなかった。ふだんの1回の動きは最大2°なので困るのは HOME のような大きい動きだけで、止める命令を足すかは、実機で使ってから決めることにした。

依頼文 20261102_68_ゲームパッドでアームを手で動かすスクリプトを作る.md

05_sec06.md4,442字・97行・markdown
---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 180
---
# 依頼68:ゲームパッドで mechArm を手で動かすスクリプト(tools/gamepad_teleop.py)を作る。実機には流さない(2026-11-02)

## ねらい
別のタブで開く保存

Claude Code の作業の記録 logs/20261102_1730.md

# 20261102_1730 依頼68:ゲームパッドで mechArm を手で動かす tools/gamepad_teleop.py

- 依頼ファイル: 20261102_68_ゲームパッドでアームを手で動かすスクリプトを作る.md
- 実機には流していない(daemon にもアームにも触っていない)。Pi への SSH(読み取りだけ)は「Operation timed out」でつながらなかった

## やったこと
- `tools/gamepad_teleop.py` を新しく作った(新規 1 本)。`tools/test_gamepad_teleop.py`(テスト 46 件)を足した
- 操作は依頼の表どおり。十字キー左右 = j0(左 = −、右 = +。依頼に向きの指定がないので `J0_LEFT_SIGN = -1.0` に置いた)、上下 = 選んでいる関節(上 = −、下 = +、始め j1)、△ = j1→j2→j3→j4→j5→j1、R1 押しながら 2°、× 閉・○ 開、HOME 1 秒長押しで HOME を 1 回、L2・R2 の両方 > 0.0 で停止・OPTIONS で再開
- 守り: j1 ≤ 90.87、j0 は基準 ±15°、j1〜j5 は ±10°、j4 は J4_SAFE、物理可動域(wr.JOINT_LIMITS)。基準 = 起動時の姿勢、HOME を送ったら HOME。動かす関節は範囲の端で切って送る(切った理由を画面・ログ・ターミナルへ)。端にいれば送らない。出発点の 6 関節のどれかが範囲の外なら送らない(HOME の長押しで戻す)
- 送り方: `daemon_send_angles`(speed 20、wait 0、timeout_override 8 秒)で 1 回だけ送り、`read_angles` で 3° 以内かを読む。送り直さない(`_send_and_wait_arrival` は届かないと 5 秒後に送り直すので、停止のあとに送ってしまう。使わなかった)。爪は `gripper_cmd` と同じ中身(閉 = `daemon_set_gripper_value(20)`、開 = `daemon_set_gripper(0, wait=0.0)`、そのあと 0.8 秒)。`install_daemon_lock` は --execute のときに呼ぶ
- 1 回の出発点: 直前の目標に届いていて、読んだ角度とも 3° 以内ならその目標、そうでなければ読んだ角度
- 停止: 送る仕事は別スレッド。仕事ごとの SendGate で「送ると決める」と「停止の取り消し」を並べる。停止のあとに送ると決める指令はない。止める前の仕事は OPTIONS で生き返らない。コントローラーの切断・読み取りの失敗・到着確認中に関節角を読めない・送信の失敗・log.jsonl に書けない(このときは再開させない)も停止
- 終わり方: Ctrl+C・ウィンドウを閉じる → 新しい指令は送らない。HOME 復帰もサーボ解放もしない
- 記録: `/Volumes/KIOXIA/r2r2r-data/runs/experiments/gamepad_teleop/teleop_YYYYMMDD_HHMMSS[_dry]/` に log.jsonl(1 操作 1 行・flush)、meta.json、スクリプトの写し。KIOXIA が未マウントなら止める
- codex レビュー 3 回(/tmp/req68/codex_r1.md・r2・r3)
  - 1 回目: 必須 7 件(停止と送信の競合、範囲の外の出発点を切ってから大きく動く、古い目標から動く、HOME 長押しの持ち越し、同じコマでボタンを停止より先に処理、読み取り失敗の扱い、記録失敗のあとの再開)→ 全部直した
  - 2 回目: 必須 2 件(鍵の待ちが切れたあとの送信、停止で画面が止まる)→ 鍵を「送ると決める」一瞬だけにし、送信に通信の上限を付けた
  - 3 回目: 必須なし。推奨 2 件(終了時に送ると決めていたかを残す、終了のテスト)→ 直した

## 停止の調べ(実機を止める方法は作っていない)
- リポジトリの写し `mecharm_daemon.py` の命令は ping / send_angles / send_angle / get_angles / set_fresh_mode / set_gripper だけ。stop はない(写しには set_gripper_value もないので、Pi で動いているものとは違う)。Pi の本物は SSH がつながらず確かめられなかった
- daemon は 1 本の接続を順に処理する(listen(1)、accept して serve_client が終わるまで次を受けない)。別のプロセスから止める命令を割り込ませることはできない。同じ接続なら send_angles は送った直後に返るので、動いている間に次の命令を送れる
- pymycobot 4.0.5(Mac)に `stop()`(ProtocolCode.STOP 0x29)・`pause()` / `resume()` / `is_paused()`・`is_moving()` がある。使うには daemon に命令を足す必要がある
- 今の角度を読んで送り直す案: 新しい命令が要らない。ただし読むのに最大 3.7 秒かかる(LOG352)ので、その間も動きが進む。送る角度は動きの途中の値なので、止まるまでに少し行き過ぎる。speed 20 で行き先を変えることになるので、急に止まるわけではない

## 変えたファイル
- tools/gamepad_teleop.py(新規)
- tools/test_gamepad_teleop.py(新規)
- logs/20261102_1730.md(この記録)

## 変えていないもの
- daemon(Pi・写し)、run_policy_client.py、visual_servo_pick.py(作業前から未コミットの差分があるが、今回は触っていない)、j0_sweep_scan.py、tools/gamepad_check.py、既存のデータ

依頼箱の報告 20261102_68_ゲームパッドでアームを手で動かすスクリプトを作る_20261102_1609.md

# 結果:20261102_68_ゲームパッドでアームを手で動かすスクリプトを作る

- 状態:完了
- 始めた時刻:2026-11-02 16:09
- 終わった時刻:2026-11-02 17:15(66分)
- 作業フォルダ:/Users/shimazueiichi/r2r2r-scripts
- この続きを Claude Code で話すとき:作業フォルダで `claude --resume 0dc0bf5d-b87f-4756-959e-0b36dbe3081e`

## Claude Code の報告

この起こしは2回目の codex レビューの確認用でしたが、その作業はもう終わっています。

## やったこと
- 2回目のレビュー結果(/tmp/req68/codex_r2.md)を見直しました。指摘は2件で、どちらも先に直してあります。
  - 停止の待ちが10秒で切れたあとに送られる穴
  - 停止を押すと画面が止まる問題
- 3回目のレビュー(/tmp/req68/codex_r3.md)は「必須の修正なし」でした。推奨2件も直しています。

## 結果
- テストは46件すべて通っています。
- commit は 78bd62e(スクリプトとテスト)と 1185747(作業の記録)で、今回足した作業はありません。
- 実機には流していません。

## 変えたファイル
なし(今回の起こしでは何も変えていません)

## 決めてほしいこと
前の報告と同じ3点です。
1. **十字キー左右の向き**:今は「右 = +、左 = −」です。アームの動く向きと合っているかを見て決めてください。
2. **動いている途中を止める方法**:daemon に stop の命令を足すかどうか。足さない場合は、今の角度を読んで送り直す案になります。こちらは少し行き過ぎてから止まります。
3. **ドライランの記録**:今はドライランでも KIOXIA に記録しています。このままでよいか決めてください。

gamepad_teleop.py(commit 78bd62e)

06_gamepad_teleop.py.txt43,822字・1,048行・python
#!/usr/bin/env python3
"""gamepad_teleop.py — ゲームパッドで mechArm 270 を手で動かす (依頼 68. 2026-11-02)

PICK で爪の位置がずれたときに, エイイチがゲームパッドで左右・上げ下げ・爪の開閉を直す.
押した分だけそのまま動く. 自動で動く要素は入れない (自動の握り直し・失敗時の自動 HOME などはない).
押した操作と, 送った角度・届いた角度をすべて log.jsonl に残す (あとで「人が直した記録」として使う).

操作 (エイイチが決めた. 変えない):
別のタブで開く保存

test_gamepad_teleop.py(テスト46件)

07_test_gamepad_teleop.py.txt31,288字・765行・python
#!/usr/bin/env python3
"""test_gamepad_teleop.py — gamepad_teleop.py の確かめ (アームなし・コントローラーなし. 依頼 68).

  python3 tools/test_gamepad_teleop.py -v

中身:
  - ボタン番号と操作の対応 (依頼 68 の表) / 十字キー上下の向き / △ で j1 → j5 → j1
  - 守り: j1 は 90.87 を超えない / j0 ±15°・j1〜j5 ±10° / J4_SAFE / HOME のあと基準が HOME
別のタブで開く保存

11/2 19:26〜19:34:アームなしのドライランで、172回の操作が決めたとおりに動いた

Mac で python3 ~/r2r2r-scripts/tools/gamepad_teleop.py を、何も付けずに起動した。何も付けないとドライランになり、HOME から始まる仮の姿勢の数字だけが動く。mechArm 270 と Raspberry Pi の電源は入れていない。

gamepad_teleop.py の窓。DRY RUN、動かす関節 j1、刻み 0.5 deg、状態 IDLE が大きく出て、その下に6関節の今の角度・送った角度・基準の角度と、ボタンの割り当ての一覧が並ぶ

19:27:17 から 19:30:50 までの3分33秒で、172回の操作が記録された。

操作の種類回数中身
関節を動かす(十字キー)137回
受け付けて送ったのは107回。j0 が35回、j3 が34回、j5 が23回、j4 が8回、j2 が5回、j1 が2回
関節の切り替え(△)10回
j1 → j2 → j3 → j4 → j5 → j1 と回った
爪を開く / 閉じる(○ / ×)7回 / 6回
1回ごとに0.8秒の待ちが入る
R1(2°の刻み)3回
2° ずつ動いた(j0 +2.0° が6回、j5 +2.0° が10回など)
HOME の長押し2回
19:29:42 の1回は送られ、角度が HOME に戻った。もう1回は停止中で受け付けなかった
停止 / 再開1回 / 1回
L2+R2 で停止し、停止中の押しはすべて受け付けず、OPTIONS で戻った
割り当てのないボタン(□)5回何もしなかった

受け付けなかった34回の内わけは、前の動きや爪の待ちが終わる前に押した分が25回、割り当てのない □ が5回、停止中が4回。範囲の守りは j5 で3回効いた。j5 は始めが −30° なので、動かせるのは ±10° の [−40, −20]。−41° と −42° を送ろうとした所で −40° に切られ、−18° を送ろうとした所で送らなかった。j1 の 90.87° の止めは、HOME から始めると j1 が ±10° までしか動かないので、この試しでは効く場面がなかった。

ドライランで気づいたこと次にどうするか
前の動きが終わる前の押しを、ためずに捨てた分が25回あった。実機では1回の動きに約1秒かかるので、もっと増える
実機で押した感触を見てから、そのままでよいか決める
起動したとき、画面の部品(SDL)が2つ読み込まれているという警告が十数行出た。gamepad_check.py では出なかった
依頼69で、どこで2つ目が読み込まれているかを調べて直す
ドライランでも KIOXIA に記録を書き、KIOXIA がつながっていないと止まる
依頼69で、ドライランは記録しない形にする
十字キーの右で j0 が+になるが、+がアームのどちら向きかは実機で動かさないと分からない
実機で0.5°動かして確かめる

gamepad_teleop.py のドライランのターミナルの出力(614〜805行目)

08_sec07.txt21,405字・191行・text
(base) shimazueiichi@shimadzueiichinoMacBook-Pro tools % python3 ~/r2r2r-scripts/tools/gamepad_teleop.py
/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
objc[86737]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1050612c8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1275a8890). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[86737]: Class SDLAppDelegate is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x105061318) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1275a88e0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[86737]: Class SDLTranslatorResponder is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x105061390) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1275a8958). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[86737]: Class SDLMessageBoxPresenter is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x1050613b8) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1275a8980). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[86737]: Class SDL_cocoametalview is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (0x105061408) and /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/cv2/.dylibs/libSDL2-2.0.0.dylib (0x1275a89d0). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
別のタブで開く保存

runs/experiments/gamepad_teleop/teleop_20261102_192657_dry/meta.json

{
  "run_stem": "teleop_20261102_192657_dry",
  "started_at": "2026-11-02T19:26:57",
  "execute": false,
  "argv": [
    "/Users/shimazueiichi/r2r2r-scripts/tools/gamepad_teleop.py"
  ],
  "controllers": [
    {
      "name": "PS4 Controller",
      "guid": "03008fe54c050000cc09000000016800",
      "instance_id": 0,
      "axes": 6,
      "buttons": 16,
      "hats": 0,
      "connected_at": "2026-11-02T19:27:01"
    }
  ],
  "python": "3.12.12 | packaged by conda-forge | (main, Oct 22 2025, 23:34:53) [Clang 19.1.7 ]",
  "pygame": "2.6.1",
  "sdl": "2.28.4",
  "platform": "macOS-26.6.2-arm64-arm-64bit",
  "start_pose": [
    0.0,
    0.0,
    0.0,
    0.0,
    0.0,
    -30.0
  ],
  "guards": {
    "J1_MAX_DEG": 90.87,
    "J0_RANGE_DEG": 15.0,
    "JOINT_RANGE_DEG": 10.0,
    "J4_SAFE": [
      -90.0,
      60.0
    ],
    "JOINT_LIMITS": [
      [
        -156.0,
        161.0
      ],
      [
        -79.0,
        123.0
      ],
      [
        -177.0,
        177.0
      ],
      [
        -156.0,
        162.0
      ],
      [
        -118.0,
        118.0
      ],
      [
        -177.0,
        178.0
      ]
    ],
    "window_at_start": {
      "lo": [
        -15.0,
        -10.0,
        -10.0,
        -10.0,
        -10.0,
        -40.0
      ],
      "hi": [
        15.0,
        10.0,
        10.0,
        10.0,
        10.0,
        -20.0
      ]
    },
    "home_bypasses_range_check": true
  },
  "motion": {
    "speed": 20,
    "step_fine_deg": 0.5,
    "step_coarse_deg": 2.0,
    "reach_tol_deg": 3.0,
    "reach_timeout_min_sec": 8.0,
    "poll_sec": 0.3,
    "gripper_sleep_sec": 0.8,
    "grip_close_value": 20,
    "send_wait": 0.0,
    "home": [
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      -30.0
    ],
    "home_hold_sec": 1.0,
    "trigger_stop_above": 0.0,
    "j0_left_sign": -1.0,
    "up_sign": -1.0,
    "select_order": [
      1,
      2,
      3,
      4,
      5
    ]
  },
  "button_map": {
    "0": "cross",
    "1": "circle",
    "2": "square",
    "3": "triangle",
    "4": "share",
    "5": "home",
    "6": "options",
    "7": "l3",
    "8": "r3",
    "9": "l1",
    "10": "r1",
    "11": "dpad_up",
    "12": "dpad_down",
    "13": "dpad_left",
    "14": "dpad_right",
    "15": "touchpad"
  },
  "git": {
    "commit": "1185747a39e3442acffa89a8afd537f02ed14116",
    "script_status": "clean"
  },
  "script_copy": "gamepad_teleop.py",
  "script_sha256": "630228e45c596001c331612bbf6fa00838cfb5129844070aa9eeeeed45e99086",
  "ended_at": "2026-11-02T19:34:24",
  "end_reason": "ctrl_c",
  "n_sent": 108
}

runs/experiments/gamepad_teleop/teleop_20261102_192657_dry/log.jsonl(172行)

{"seq": 1, "t": "2026-11-02T19:27:17.144", "t_unix": 1793615237.144, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 0, "delta": -0.5, "accepted": true, "measured_before": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615237.14743, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 2, "t": "2026-11-02T19:27:17.962", "t_unix": 1793615237.962, "button": 13, "button_name": "dpad_left", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 0, "delta": -0.5, "accepted": true, "measured_before": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-1.0, 0.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [-1.0, 0.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615237.963496, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 3, "t": "2026-11-02T19:27:18.584", "t_unix": 1793615238.585, "button": 14, "button_name": "dpad_right", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 0, "delta": 0.5, "accepted": true, "measured_before": [-1.0, 0.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615238.586001, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 4, "t": "2026-11-02T19:27:19.102", "t_unix": 1793615239.102, "button": 14, "button_name": "dpad_right", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 0, "delta": 0.5, "accepted": true, "measured_before": [-0.5, 0.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615239.1033611, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 5, "t": "2026-11-02T19:27:19.899", "t_unix": 1793615239.899, "button": 12, "button_name": "dpad_down", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 1, "delta": 0.5, "accepted": true, "measured_before": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [0.0, 0.5, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [0.0, 0.5, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615239.900151, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 6, "t": "2026-11-02T19:27:20.349", "t_unix": 1793615240.35, "button": 12, "button_name": "dpad_down", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 1, "delta": 0.5, "accepted": true, "measured_before": [0.0, 0.5, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [0.0, 1.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [0.0, 1.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615240.350843, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 7, "t": "2026-11-02T19:27:24.208", "t_unix": 1793615244.209, "button": 2, "button_name": "square", "kind": "ignored", "selected_joint": 1, "step_deg": 0.5, "execute": false, "accepted": false, "reason": "unassigned", "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
{"seq": 8, "t": "2026-11-02T19:27:25.294", "t_unix": 1793615245.294, "button": 14, "button_name": "dpad_right", "kind": "move", "selected_joint": 1, "step_deg": 0.5, "execute": false, "joint": 0, "delta": 0.5, "accepted": true, "measured_before": [0.0, 1.0, 0.0, 0.0, 0.0, -30.0], "start_source": "command", "cmd_gap_deg": 0.0, "clip": [], "refused": [], "limit_ja": "", "limit_ascii": "", "command": [0.5, 1.0, 0.0, 0.0, 0.0, -30.0], "sent": true, "reached": true, "reached_angles": [0.5, 1.0, 0.0, 0.0, 0.0, -30.0], "reach_err": 0.0, "t_send": 1793615245.295143, "dry": true, "grip_state": "unknown", "stopped": false, "stop_reason": null, "base_pose": [0.0, 0.0, 0.0, 0.0, 0.0, -30.0]}
別のタブで開く保存

ゲームパッドの HOME ボタンを押すと、Mac の Apple Games が開いた

ゲームパッドの電源を入れるには HOME ボタンを押す。そのたびに、Mac で Apple Games のアプリが開いた。HOME ボタンは、アームを HOME の姿勢に戻すボタンにも使うので、止めておきたい。

ゲームパッドの HOME ボタンを押したときに Mac に出た「Apple Games へようこそ」の画面

Mac(macOS 26.6.2)では、システム設定 →「ゲームコントローラ」→「コントローラのショートカット」に、HOME ボタンの設定があった。「コントローラショートカットを有効にする」が入っていて、「ホームボタンを押して開く」が「ゲーム」になっている。コントローラーを選んだ先の、ボタンごとの割り当ての画面にはなく、1つ前の画面にあった。

システム設定のゲームコントローラの中の「コントローラのショートカット」。コントローラショートカットを有効にするが入っていて、ホームボタンを押して開く先がゲームになっている

「コントローラショートカットを有効にする」を切ると、HOME ボタンを押しても Games は開かなくなる。切ったあとも、スクリプトからは HOME ボタンをボタン5として読める見込みで、次に gamepad_check.py を動かしたときに確かめる。

同じ設定の中の、コントローラーを選んだ先の画面の一番下には「ジェスチャ共有」があり、SHARE ボタンの2回押しが「収録を開始/停止」になっていた。スクリプトでは SHARE を使わないが、誤って押すと画面の収録が始まるので、こちらも「オフ」にしておく。

依頼69:ドライランは記録しない・起動時の SDL の警告を直す

残っていた3つを決めて、依頼69を置いた。19:39 に作業が始まり、この記録の時点では作業中。

残っていたこと決めたこと
ドライランの記録
記録しない。KIOXIA がつながっていなくても動くようにする。11/2 のドライランの記録は消さない
動いている途中で止める方法
今は足さない。スクリプトの説明に「送った1回分は最後まで動く」と書く
十字キーの左右の向き実機で確かめてから決める
SDL が2つ読み込まれる警告
どこで読み込まれているかを調べ、gamepad_teleop.py の側で避ける。Python の環境は変えない

依頼文 20261102_69_ゲームパッドのドライランは記録しない・SDLの重複を直す.md

---
作業フォルダ: /Users/shimazueiichi/r2r2r-scripts
上限時間: 120
---
# 依頼69:tools/gamepad_teleop.py のドライランは記録しない・SDL が2つ読み込まれる警告を直す。実機には流さない(2026-11-02)

依頼68(commit 78bd62e)の「決めてほしいこと」に、エイイチが決めた(2026-11-02 19:36)。
**操作(ボタンの割り当て・刻み・守りの値)は変えない。**

## エイイチが決めたこと

1. **ドライランは記録しない**:`--execute` がないときは、KIOXIA に何も書かない(フォルダも作らない)。KIOXIA がつながっていなくてもドライランは動くようにする。ターミナルと画面の表示は今のまま
   - 今ある `runs/experiments/gamepad_teleop/teleop_20261102_192657_dry/` は測定データなので、消さない・動かさない
2. **動いている途中で止める方法は、今は足さない**:daemon も送り方も変えない。スクリプトの先頭の説明に「L2+R2 は新しい指令を送らないだけで、送った1回分(HOME を含む)は最後まで動く」と書く
3. **十字キー左右の向き(今は右 = +)**:明日の実機で確かめてから決める。この依頼では変えない

## 直すこと:SDL が2つ読み込まれる警告

2026-11-02 19:26 にドライランを起動すると、次の警告が十数行出た(動きには影響なし)。

```
objc[86737]: Class SDLApplication is implemented in both /opt/homebrew/Caskroom/miniforge/base/lib/python3.12/site-packages/pygame/.dylibs/libSDL2-2.0.0.dylib (...) and ...
objc[86737]: Class SDLAppDelegate is implemented in both ...
(SDLTranslatorResponder・SDLMessageBoxPresenter・SDL_cocoametalview・SDLOpenGLContext・SDL_ShapeData・SDL_CocoaClosure・SDL_VideoData・SDL_WindowData・SDLWindow・Cocoa_WindowListener・SDLView・METAL_RenderData・METAL_TextureData・SDL_RumbleMotor・SDL_RumbleContext も同じ)
```

`tools/gamepad_check.py`(pygame だけを読む)では出なかった。gamepad_teleop.py が読み込む別のモジュール(visual_servo_pick など)の先で、2つ目の SDL が読み込まれていると見られる。

- どのモジュールが2つ目の SDL を読み込んでいるか(どのファイル・どのパッケージか)を突き止めて、報告に書く
- 直し方:gamepad_teleop.py の側で、その読み込みを避ける(例:要る関数だけを読む・読み込みを `--execute` のときだけにする・読み込む順番を変える)。**ほかのスクリプトと、Python の環境(パッケージの入れ替え・削除)は変えない。** 環境を変えないと直らない場合は、直さずに「決めてほしいこと」に書く
- 直したあと、ドライランで警告が出ないことを確かめる

## 確かめ(実機なし)

- 今のテスト46件が通ること。ドライランで KIOXIA に書かないことのテストを足す(`--execute` のときは今までどおり書く)
- ドライランを起動して、警告が出ないことと、画面とターミナルの表示が今までと同じことを確かめる

## 変えないもの

- 操作の割り当て・刻み・守りの値・送り方・停止の作り(上の1〜3以外)
- daemon、run_policy_client.py・visual_servo_pick.py などの既存のスクリプト、tools/gamepad_check.py
- 既存のデータ

## 終わったら

- 関節を動かすスクリプトの変更なので、codex のレビューを通してから報告する(code_review_request_template.md の形)。指摘は直すか、直さない理由を書く
- 実機には流さない
- 個別のファイルを指定して add・commit する(`git add .` は使わない)

次に実機で確かめること

順番確かめること見る数字
1
手首(j4)が指令に応えるかを点検する(LOG422 で止まったまま)
指令と実測の差
2
アームを HOME に置いて --execute で起動し、j0 を0.5°ずつ数回
十字キーの右で、根元がどちらに回るか
3
L2+R2 の停止と OPTIONS での再開、爪の開け閉め、HOME の長押し
停止のあとに指令が出ていないこと
4
AI(π0.5)の動きを、キーボードの代わりにゲームパッドで直す
1回の直しにかかる時間(キーボードのときと比べる)・command と reached の差