LOG294R2R2R2026/7/21

mechArm 270 Pi 受け渡し動作の成立を探る Log#5 | AI任せで全部作ってから実機に出したら、動きは前と変わらなかった

受け渡しシリーズの制御コードが、設計上の全ブロック(D-1 から E-6)まで完成した。実機の前に書くコードはもう無い、という状態で実機に出した。結果、腕は受け渡しの一連を最後まで通した。当たらずに動いた。VLM も「取られた」と判定した。通ったこと自体は事実だ。

それなのに、実機を動かした本人(アームの前にいる私=島津)から出た言葉は「これをやるだけのためにあんなに時間をかけたのか」「動きを見てるだけだと前とほぼ変わらない」だった。この回は、その違和感が何だったのかを掘る記録になる。成功した通しの話ではなく、成功したのに割に合わなかった、その構造の話だ。

コードは全量では載せない。この回で残す価値があるのは、コードの中身ではなく、進め方の判断がどこで間違ったかだからだ。やりとりと経緯をそのまま置く。

実機で起きたことは「前とほぼ同じ動き」だった

実機で走らせた受け渡しの通しは、ログの上ではこう流れた。HOME 復帰、カメラで赤ブロックと白箱を検出、approach、掴む、持ち上げる、そこから PRESENT_MID → PRESENT へ差し出し、数秒待ってグリッパーを開き、退避して HOME へ戻る。到達精度は tolerance 内で、いちばん確認したかった PRESENT_MID → PRESENT の経路も机やマットに当たらず通った。

問題は、この動きが目で見て新しく見えなかったことだ。理由は単純で、受け渡しの動きの中身が、既存の PICK と同じ「send_angles で姿勢に順番に動かす」でしかないからだ。HOME に戻す、approach する、掴む、上げる——全部、前のシリーズ(動的追従の限界を探る・56本)でやってきた動作。今回新しく足した PRESENT_MID → PRESENT → 退避も、やっていることは「別の4つの姿勢に順番に動かす」だけ。座標が違うだけで、動きの種類は同じだった。

だから「実装にあれだけ時間をかけたのに、動きは前と変わらない」という観察は錯覚ではない。実際に動きは変わっていない。

時間をかけた先は、動きではなく「記録・安全・検証の層」だった

では D-1 から E-6 の実装で何を積んでいたのか。動きではないものばかりだった。試行を台帳へ記録する層(E-4)、SSD が動作中に外れたときの退避処理(emergency journal)と復旧処理(recovery)、撮影、VLM 判定(E-5)、検証テスト53件(E-6)。これらは「動きを変える」ものではなく、「動いた結果を記録する・安全に止める・後で検証する」ための周辺だ。見た目の動きには一切現れない部分に、実装時間の大半がかかっていた。

ここで問うべきは、その目に見えない周辺層が、この段階で本当に必要だったのか、だ。

台帳・emergency journal・recovery・VLM・テスト53件は、「人を入れて何十試行も回してデータを貯めて分析する」段階では要る。だが「動きが実機で通るか初めて見る」段階では要らなかった。動きが通るかは、4姿勢を送るだけの小さなスクリプトで確かめられた。周辺層は、動きが固まってから足せばよかった。順序が逆だった。

台帳の実物がこの逆転を一番はっきり示している。受け渡し専用の台帳 handover_ledger_v1.csv は44列の器として完成している。だが実機を動かした後、その中身は次の8行だけだった。

timestamp,run_stem,trial_no,trial_status,...,outcome,outcome_source,...
2026-07-20T21:05:45...,run_handover_...001,1,pick_failed,...,daemon 接続失敗: [Errno 61] Connection refused,...,not_applicable,unknown,...
2026-07-20T21:05:53...,run_handover_...002,2,pick_failed,...,daemon 接続失敗: [Errno 61] Connection refused,...,not_applicable,unknown,...
(以下、同じ daemon 接続失敗が計8行)

8行すべて、daemon が立っていなかった最初の実行で「腕が一度も動かなかった失敗」の記録だ。中身のある測定値は一つも入っていない。しかも、daemon を立て直した後に唯一通った成功試行は、記帳の入力途中で抜けたためこの台帳に残っていない。器が先にできて、意味のある中身がまだ入っていない——記録層を先に作りすぎたことの帰結が、そのまま台帳に出ている。

daemon が立っていなかった件で、切り分けられずに時間を溶かした

もう一つ、この回で大きく時間を失ったのは実装の中身ではなく、実機に出す前の環境確認だった。最初の実行は全試行が daemon 接続失敗: [Errno 61] Connection refused で落ちた。Pi 側の daemon プロセスが 5555 番を待ち受けていなかった、それだけの話だ。

ところがここで、状態を確認するのに pgrep -f mecharm_daemon を使った。これがコマンドライン文字列に mecharm_daemon を含むプロセスを拾う仕様のため、確認しているコマンド自身の SSH プロセスにマッチしてしまった。返ってきた PID は実体の daemon ではなく、確認コマンド自身だった。それを実プロセスと思い込んで、「daemon が湧き直している」「systemd か cron が再起動をかけているのでは」と、存在しない現象を何ターンも追った。

正体は、アームの前にいる本人からの一言で割れた。「Pi じゃなくて、ただターミナルの1でやってるだけじゃない?」。ps -eo pid,cmd | grep '[m]echarm_daemon.py' と、grep 自身にマッチしない書き方で見たら、実プロセスは最初から存在していなかった。daemon は一度も立っていなかった。listen を一度見れば1手で終わる話に、遠回りをした。

さらに悪いことに、常用していた起動確認スクリプト daemon_check.sh も同じ pgrep 判定で組まれていて、実体が無くても「daemon alive」を返していた。確認の道具自体が、プロセスの存在と待ち受けの成立を区別していなかった。

何を確認したか

使った手段

返した答え

実態

daemon が居るか

pgrep -f mecharm_daemon

PID を返す(居る)

自分のコマンドを拾っただけ・daemon は不在

daemon が居るか

daemon_check.sh

[OK] daemon alive

同上・誤検出で OK

daemon が居るか

ps ... grep '[m]echarm...'

何も返らない

正しい・不在

待ち受けているか

ss -ltn | grep 5555

no-listen

正しい・listen していない

プロセスの存在と、ポートを待ち受けているかは別物だ。確認は listen で見るべきだった。この一件で、pgrep によるプロセス名判定を捨て、listen 判定に一本化する起動手順に作り替えた。

この回の失敗は、過去に機能していた進め方を手放したこと

ここまでを一つにまとめると、失敗は明確だ。動きを固めた後に必要になる記録・安全・検証の層を、動きを一度も実機で通さないまま先に全部作った。だから実装は重くなり、実機で見た動きは前と変わらず、問題が出たときにどこが原因かを切り出せなかった。

なぜ先に作ったのか。設計書が実装ブロックを D-1 から E-6 まで最初から並べていて、それを上から順に潰したからだ。「動きが通るか先に見る」という一手を、計画の頭に置いていなかった。コードとテストが緑になれば動く、という前提で確認を最後に回した。このプロジェクトの原則は「Data より実機」なのに、コードの緑を実機の代わりにしていた。

決定的なのは、この進め方が過去には手元に存在していたことだ。記録を遡ると、初期はこうだった。複数関節テスト(LOG124)は J1+J2 → J1+J2+J3 → 全軸と3段階に割り、各段の前後に HOME 復帰を挟んで一段ずつ実機で確認していた。PICK & PLACE(LOG129)は Step を順に送り、PLACE でグリッパーが開かない症状が出たらその場で「箱の縁に当たっている」と切り分けて、PLACE の姿勢だけ直していた。DESCEND(LOG217)は一発の大降下にせず中間ウェイポイントで段階的に降ろし、各段は到達可能フィルタ済みで、すり抜けても落差が小さい設計だった。

どれも、小さく作って・都度実機で通して・問題が出たらその一段だけ切り出して直す、というやり方だ。前のスクリプトはそう作られていた。今回それを守らず、全ブロックを積んでから初めて実機に出した。過去に機能していたやり方を捨てた正当な理由は、無かった。

次からの作り方

動きの核を最小構成で実機に通してから、記録・安全・検証を必要な分だけ後ろに足す。

具体的には、受け渡しをやり直すなら最初の一個は過去と同じ粒度に戻す。HOME → PRESENT_MID → PRESENT の姿勢だけを送角し、各段で HOME 復帰を挟み、実機で当たらず到達するかを見る。通ったら次の一段を足す。台帳・撮影・VLM・テストは、動きが一段ずつ通って固まってから、必要な分だけ加える。

実機で今回わかった、次に持ち越す事実も残しておく。受け渡し区間(4姿勢の送角)は実機で通ることは確認できた。ただし持ち上げ(LIFT)が8秒以内に目標へ届かずタイムアウトした区間がある(j1 が88°付近まで上がる領域)。グリッパーの開閉は結果コードが常に -1 を返し、実際に掴めた手応えがログに残らない。daemon は listen で確認する。この4つは、次にやり直すときに測り直さずに使える。

※ プロジェクト名「R2R2R」は、UC Berkeley・Toyota Research Instituteの研究「Real2Render2Real」(Yu, Fu et al., 2025)の概念に触発されています。現実空間をデジタルツイン化し、仮想空間での試行から得た知見を実世界のモーター駆動へ還元する——その設計と実装過程を毎日記録しています。技術アプローチおよび目的は独自のものです。