mechArm 270 Pi 動的追従の限界を探る Log#16 | 何がいつ動いているか。ロボット制御パイプラインの可視化
💡 Key Takeaway
スクリプトの中でどのコンポーネントがいつ動いているか見えないことが、認識のズレと問題の発見遅れを生んでいた。HTMLタイムライン(人間が読む)とExport JSON(AIへ渡す)を1ファイルに担わせ、HTMLを唯一の更新点にすることで、この問題を構造的に解決した。
発生した問題 | 現場判断 | 結果 |
|---|---|---|
スクリプトのどのフェーズでどのコンポーネントが動いているか、テキストだけでは把握しにくかった | フェーズを横軸(時間比例幅)・コンポーネントを縦軸に取ったHTMLタイムラインとして図にする | カメラ・FK Lookup・全6関節・検出関数・グリッパー・スクリプトの活動状況が一目で確認できる永続ツールが完成 |
最初の図にj0とj4の2関節しか出ていなかった | 全6関節(j0〜j5)+検出関数4本+スクリプト4本を全量掲載する | DESCENDでj4とj0が両方fail状態で、それ以降のフェーズが全て到達不能という構造がひと目でわかるようになった |
HTMLとJSONを別ファイルで管理すると更新のたびに乖離が生じる | HTMLをデータの唯一の正とし、Export JSONボタンでJSONをオンデマンドに生成する | HTMLのデータ更新→Export JSON→Claude Codeへ貼るという1本のワークフローが確立した |
見えていなかったこと
スクリプトが育つほど、中身が「見えなく」なっていく。
visual_servo_pick.py はいま8つのフェーズを持つ。HOME・OPEN・APPROACH・DESCEND・REFINE・GRIP・LIFT・HOME return。それぞれに上カメラ・横カメラ・FK Lookup・j0〜j5の6関節・グリッパー・4本の検出関数が絡み合っている。
テキストの引き継ぎ書でこれを書くと「DESCENDではj4とj0が動く」程度の記述になる。どのフェーズで何が止まっていて、どのコンポーネントが到達すらできていないか——それを頭の中で一度に把握しておくことが、セッションを重ねるほど難しくなっていた。
問題の根は「見えていないこと」にある。DESCENDが失敗しているとわかっていても、それより先のGRIPやLIFTが「どういう構造で実行される予定なのか」が、コードを開かないとわからない。引き継ぎ書を読んでも「未到達」とあるだけで、なぜ未到達なのか、どのコンポーネントが止まっているのかの絵が浮かびにくい。
このセッションで試みたのは、その「見えていないもの」を図にすることだった。
フェーズとコンポーネントを図にする
ロボットアームへの指示が、どんなタイミングで、どれが動いていて、止まったりしているかのタイムスケジュールや流れを可視化したいので考えてください。現状スクリプトの中身が可視化されていないので課題や詰まっているところがわからなくなる、もしくはお互いの認識が統一されていないなどの問題が起こっている。
この問いから設計を始めた。
参考にしたのはSNSで見かけた「Claudeにアプリ全体を1枚のHTMLと1つのJSONにまとめさせる」という手法だ。HTMLは人間が読む地図として、JSONはAIが読むコンテキストデータとして、2つの役割を分担させる。この構造をロボット制御パイプラインに転用できると判断した。
設計の骨格はシンプルだった。横軸にフェーズ(時間に比例した幅)、縦軸にコンポーネント(カメラ・FK Lookup・検出関数・関節・グリッパー・スクリプト)を取る。各セルに「その組み合わせで何が起きているか」を色として入れる。
値 | 色 | 意味 |
|---|---|---|
2 = Active | 黒 | 積極的に動作・稼働中 |
1 = Move | グレー | ホーム移動などの受動的な動き |
3 = Fail | ストライプ | 動作しているが失敗 |
5 = Partial | 半黒 | 一部のサブフェーズのみ使用 |
4 = Skip | 薄グレー | 未到達・スキップ |
0 = Inactive | 白 | 非稼働 |
フェーズをクリックすると詳細パネルが展開される。使用カメラ・関節・呼び出し関数・遷移条件・既知の問題・根本原因がまとめて読める。Current Design(現行設計)とNext Design(次の設計)をトグルで切り替えられる。
作りながら変わった要件
最初のバージョンにはj0とj4の2関節しか入っていなかった。DESCENDの問題に直接関係する2軸を選んだからだ。
関節ってj0とj4の記載しかないのなんで?
この一言で気づいた。「問題に関係する関節だけ」では、全体の構造が見えない。j1がZ-TRACKサブフェーズだけで使われていること、j2とj3がDESCENDで受動的な状態にあること、j5がホーム移動では動くがそれ以外では一度も命令されていないこと——これらは全6関節を並べて初めてひと目でわかる情報だった。
全6関節(j0〜j5)、検出関数4本(detect_tip_from_frame() / detect_red_block() / detect_tip_side() / detect_red_block_side())、スクリプト4本(visual_servo_pick.py / side_camera_utils.py / mecharm_daemon.py / auto_detect_cameras.py)をすべて追加した。
次の転換点は「このツールの寿命」についてだった。
勘違いしているようだけど、この可視化はLOG202や203だけで使うものではなくて、ロボットアームのスクリプトを使っているうちはずっと使う話です。
この指摘でツールの設計が変わった。「特定セッションの記録」ではなく「スクリプトが生きている間ずっと使う永続ツール」として作り直す必要があった。セッション番号をUI上から完全に排除し、データ(フェーズ定義・コンポーネント定義・アクティビティマップ・ブロッキング課題)を5つのSECTIONとして明示的に分離した。「UIコードは触らなくてOK、データセクションだけ書き換えれば図が更新される」構造にする。
アクティビティマップの構造がその核心になった。
// SECTION 4: アクティビティマップ
// 値: 0=inactive 1=move 2=active 3=fail 4=skip 5=partial
// 配列の順番は KEYS の順番と完全に一致させること。
const ACT = {
a: {
// cam_t cam_s fk f0 f1 f2 f3 j0 j1 j2 j3 j4 j5 gr s0 s1 s2 s3
HOME: [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 2, 0, 2, 1 ],
APPROACH: [ 2, 0, 2, 2, 2, 0, 0, 2, 5, 0, 0, 0, 0, 0, 2, 0, 2, 0 ],
// j1=5(partial): Z-TRACKサブフェーズ中のみ使用
DESCEND: [ 3, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 2, 0, 2, 0 ],
// 3=fail: 動作中だが失敗している
REFINE: [ 4, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0 ],
GRIP_A: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0 ],
},フェーズを追加するには、この配列に1行追加してフェーズ定義にエントリを足す。行(コンポーネント)を追加するには、ROW_GROUPSに1エントリ追加して全フェーズ配列に値を1つ足す。変更が局所的で、どこを触れば何が変わるかが明確になった。
HTMLが唯一の正
JSONデータは必要ないの?
必要だが、作り方が問題だった。HTMLのデータとJSONファイルを別々に手で更新する構造では、どちらかを変えたときにもう一方が古いままになる。
解決策はExport JSONボタンをHTMLに組み込むことだった。ボタンを押すと、HTMLのデータセクションから自動的にJSONが生成される。コピーまたはダウンロードできる。
HTMLのデータを更新
→ Export JSON → Copy
→ Claude Codeセッション冒頭に貼る生成されるJSONには、スクリプト名・更新日・両設計のフェーズ全詳細(説明・カメラ・関節・関数・遷移条件・既知の問題・根本原因)・コンポーネント定義・アクティビティマップ・ブロッキング課題・禁止事項が含まれる。Claude Codeのセッション開始時にこれを渡すことで、スクリプトのアーキテクチャ全体をコンテキストとして注入できる。
HTMLが「唯一の正」になることで、2ファイルの乖離という問題が構造的に消えた。
永続ツールとして
このツールの使い方は3つに絞られる。
セッション開始時に開いて、どのフェーズが動いていてどこが詰まっているかを確認する。設計が変わったらSECTION 2〜4を更新して図に反映する。外部の人に現状を説明するときの資料として使う。
スクリプトの構造が変わるたびに更新され、セッションをまたいで参照される。特定のセッションに紐づかない設計になったことで、このHTMLはスクリプトと同じ寿命を持つドキュメントになった。
ロボット制御スクリプトの「今の状態」を常に1枚のHTMLに保つ。それがこのツールの役割だ。
