はじめに
この記事シリーズを書き始めて1ヶ月が経ちました。
実装と考察を繰り返す中で、さまざまな着眼点から「フィジカルAI」を捉えられるようになってきたことは、自分にとって大きな収穫です。
さて、前回の記事では、ArUcoマーカーによるカメラ姿勢および物体座標の推定、そしてYOLO11nとOpenCVを組み合わせた接地面(中央位置・半径)や物体高さの推定手法について解説しました。
これにより、VLAもどきに必要な 「ロボットの目」 の機能を、一通り実装できたことになります。
「ロボットの目」を起点としたシステムの統合と評価
今回実装した「ロボットの目」は、単に対象物を検知するだけでなく、ロボットアームが物理的に干渉するために必要な情報を抽出しています。
- ArUcoマーカー: 空間内での自己位置と対象物の位置関係を特定する。
- YOLO11n: 物体の識別だけでなく、グリッパーで掴める状態か(正立か転倒か)を判断する。
- OpenCVによる解析: 接地面の中心や物体の高さを算出し、アームを動かすための具体的な数値を導き出す。
これらの機能が揃ったことで、現実世界の物理的な状況を、司令塔であるGeminiが理解できるデータへと翻訳する準備が整いました。
ここからは、この「ロボットの目」が得た情報を他のコンポーネントと連携させ、いよいよシステムを統合し、評価していくプロセスに入ります。
VLAもどきのシステム構成
本プロジェクトでは、大規模言語モデルを核とした「Vision-Language-Action (VLA)」の思想を簡易的に取り入れたシステム構成を採用しています。
各コンポーネントの役割
推論を司る「脳」と、描画・制御を担う「神経系」を、MCP(Model Context Protocol)を介して疎結合に連携させています。
1. Gemini CLI(推論エンジン)
システムの意思決定を担います。Gemini CLI自体は直接画像データをハンドリングしませんが、MCPサーバを介して、空間上の座標データや物体情報を論理的に処理します。
- 役割: 言語指示に基づく動作シーケンスの生成、物体カタログとの照合、例外発生時のリトライ判断。
2. SvelteKitベース MCP Client(可視化・制御インターフェース)
Gemini CLIがツールを通してやり取りするデータの「モニター」および「実行環境」としての役割を持ちます。
- ライブ動画表示: USBカメラがキャプチャーした映像をリアルタイムに配信。
- オーバーレイ描画: 物体検出時のバウンディングボックスや、ピック&プレイス動作時のTCP(Tool Center Point)軌道を動的に描画。
- データモニタリング: MCPツール間を流れるパケットを可視化し、AIの思考プロセスを人間が追跡可能にする。
3. 連携の仕組み
Gemini CLIが「何を見るべきか、どう動くべきか」をMCPツール経由で要求すると、クライアント側で物理座標への変換(今回解説した高さ推定など)が行われ、その結果が再びGeminiへとフィードバックされます。
この構成により、エンジニアは低レイヤーの命令を一行ずつ書くことから解放され、AIとの対話を通じて複雑な物理操作の「振る舞い」を設計することに集中できる環境を構築しています。
MCPツールの再整備
これまで「作りながら学ぶ」スタイルで開発を進めてきましたが、機能が増えるにつれてツール同士の連携、特に 「データの受け渡し(バトンリレー)」や「情報の紐付け」 が少しずつ煩雑になってきました。
AIエージェントにとって「使いにくいツール」は、判断ミスや誤動作の元になります。そこで、生成AIが迷わずスムーズに動けるよう、ツールのインターフェースを整理し直すことにしました。
1. 物体カタログの再構成
これまでの実装により、物体検出のプロセスで「接地面の半径」や「物体の高さ」を動的に推定できるようになりました。これに伴い、物体カタログのデータ構造を簡素化し、以下の定義へと再構成しました。
物理的なサイズ推定を「ロボットの目」側に任せることで、カタログ側では「把持(グリップ)すべき高さ」や「操作対象(ターゲット)か否か」といった、戦略上の最小限のパラメータを管理する形としています。
なお、ターゲットがyesの物体では、Class Labelへ以下のサフィックスが付与されます:
- "_ok": その物体は正立している
- "_ng": その物体は転倒している
2. 「名前」から役割を直感的に伝える
生成AIにツールを使わせる(Function Calling)際、関数の名前や引数の名前は、AIがその意味を理解するための重要なヒントになります。
- 座標系の整理: どこを基準にした数字なのか一目でわかるよう、ロボットの足元を原点とする世界座標は $x, y, z$、マーカーを基準にする場合は $xm, ym, zm$ と区別しました。
- 説明不要の分かりやすさ: 名前自体に意味を持たせることで、AIが座標系を勘違いして「あらぬ方向」へアームを動かすリスクを減らしています。
3. 「考える」役割をAI側へ移す
これまでは、ロボットアームがどう動くかという「軌道の計算や描画」をロボット側(サーバー側)で行っていました。しかし、本来「どう動くべきか」というプランを立てるのは、知能であるAI側の役割です。
- 描画処理の引っ越し: 動作のシミュレーションや軌道の描画を、AIとやり取りするクライアント側へ移しました。これにより、AIが考えたプランを私たちが実行前に確認・検証しやすい構成になりました。
4. 「色」で物体を識別できるようにする
「オレンジ色のキャップを拾って」といった指示に応えるため、カメラで捉えた物体の色を判別して言葉で出力する機能を追加しました。
- 数字から言葉へ: 座標という「数字」だけでなく、「色名」という「言葉」の情報をセットで渡すことで、AIがユーザーの指示と目の前の物体をより自然に結びつけられるようになります。
再構成されたMCPツール
以下、MCPクライアントのGUIから見たMCPツールの一覧です(スクリーンショット)。
フィジカルAI(VLAもどき)の評価
VLA構成におけるMCPサーバーの役割と各ツールの機能
本システムでは、Gemini CLIが物理世界を操作するための実行基盤として、MCP(Model Context Protocol)を活用したVLA(Vision-Language-Action)構成を採用しています。
Gemini CLIは直接的なハードウェア制御ロジックを持たず、MCPサーバー上に定義された各ツールを「自律的な判断」に基づいて呼び出すことで、一連のタスクを完結させます。
各ツールの詳細と役割
下図に示す通り、MCPサーバー内には役割の異なる4つのツールが定義されており、それぞれがGemini CLIの「感覚」や「手足」として機能します。
1. get_workpiece_catalog
対象物の物理的な属性を管理するマスターデータへのアクセスを担います。
- 機能: 推論対象となる物体の真の高さ、半径、把持位置のオフセットなどの取得。
- 目的: 視覚情報から得られた曖昧な推論値を、物理的な「正解」と照合して確定させるために使用します。
2. get_live_image
USBカメラからのライブフィード、および画像解析結果の取得を担います。
- 機能: 現在の俯瞰映像、物体検出(Bounding Box)の座標データの提供。
- 目的: 物理空間における物体の現在位置をリアルタイムに把握し、本稿で詳述する「幾何学的な高さ推定」の入力値とします。
3. get_robot_status
ロボットアームの現在状態を監視し、動作のフィードバックを得るために機能します。
- 機能: アームの現在座標(TCP)、関節角度、真空センサによる把持成否のステータス取得。
- 目的: 「思考(推論)」の結果が正しく「物理的結果」に結びついたかをAIが自ら確認し、必要に応じて次工程の判断材料とします。
4. execute_sequence
Gemini CLIが導き出した論理的な動作命令を、具体的な物理アクションに変換します。
- 機能: 推論された3D座標への移動指令、および一連のピック&プレイス・シーケンスの実行。
- 目的: デジタル空間での判断を物理的な動力へと変換し、実世界に干渉するための最終的な出口となります。
この構成によるメリット
このアーキテクチャの最大の特徴は、個別のデバイス制御やデータの橋渡し(調整作業)を人間が静的なコードで記述するのではなく、Gemini CLIがMCPツール群を動的に組み合わせてタスクを遂行する点にあります。
エンジニアは低レイヤーのつなぎ込みに追われることなく、AIに「どのような道具(Tool)を与え、どのような物理カタログを定義するか」という、より抽象度の高いシステム設計に注力することが可能となります。
評価前最終準備
物体検出向けファインチューニング
事前評価を行ったところ物体検出の性能が弱めなので、200枚以上の写真で追加のファインチューニングを実施しました。
Gemini CLIのGeminiモデル指定
gemini-2.5-flashを使うように強制します。gemini-2.5-proは輻輳気味で、リトライが多発してしまうためです。
$ gemini --model gemini-2.5-flash
評価用スクリプト作成
MCPサーバ、MCPクライアント(ここではGemini CLIのモニター)、Gemini CLIの全てを一度に起動出来るよう、ユーティリティースクリプト "mcp_launcher.py"を作成しました。
$ python mcp_lanucher.py
右側のターミナル画面上で"g"キーを押下するとGemini CLIが起動します。
フィジカルAI(VLAもどき)の評価
本システムが、単なる「プログラムによる自動化」を超え、知能を持った「自律エージェント」としてどこまで機能するかを検証します。
VLA(Vision-Language-Action)の各レイヤーにおいて、AIが自らMCPツールを選択し、物理世界の制約を解釈できるかを以下の3つのステップで評価します。
1. Vision(視覚認識)の検証
目的
AIがカメラ画像とカタログデータを照らし合わせ、物理的な状況をどこまで正確にデジタル化(メタ認知)できるかを測ります。
検証プロンプト
現在の作業エリアを観察し、そこにある全物体のリストを作成してください。それぞれの物体の『クラス名』『推定される色』、および『正立(_ok)か転倒(_ng)か』を報告してください。また、カメラから見て、どの物体が他の物体と最も接近しているかも特定してください。
Observe the current work area and create a list of all objects present. For each object, report its 'Class Name,' 'Estimated Color,' and whether it is 'Upright (_ok) or Toppled (_ng).' Additionally, please identify which objects are closest to each other from the camera's perspective.
評価ポイント
get_live_image の結果から、単なる座標だけでなく「属性(色・状態)」や「相対的な位置関係(接近度)」を正しく抽出できているか。
検証結果
-
幾何学的な推論
get_live_imageから得た世界座標を自ら抽出し、各物体間の距離をベクトル演算で算出出来た点は、LLMの論理的な強みが見られました。明示的な指示なしに 「座標値の差分」から空間的な近接性を導き出した プロセスは、情報の整理能力の高さを象徴しています。 -
物体状態の解釈
物体検出の結果をもとに、正立(_ok)か転倒(_ng)かを言語化するプロセスは正確に機能しています。現状を「デジタルデータ」として再構成する能力は十分に備わっていると言えます -
外部認識データの依存性
今回の検証では、MCPサーバー側(Vision System)の色判定ミス(激落ちくんは白なのに青と誤認)や電池の未検出といった、入力情報側の精度不足が見られました。Geminiはこれらの外部データを「事実」として論理構築を行うため、最終的な判断の妥当性は、これらツールの認識精度に強く依存する形となります。
2. Language(言語理解)の検証
目的
複雑な指示(言語)を、物理的な制約(カタログ数値)に基づいた実行プランへと、論理的に翻訳できるかを測ります。
検証プロンプト
カメラ画像内の左側にあるかごへ青色のキャップを移動させるため、物体の高さを考慮し衝突を避けるための具体的なz軸経由座標を算出した実行プランを作成してください(現時点では実行不要)。
Create an execution plan to move the blue cap into the basket located on the left side of the camera image, calculating specific z-axis waypoints to avoid collisions based on the heights of the objects (do not execute at this time).
評価ポイント
「青」「左」といった言語定義を内部の物理パラメータと結びつけ、論理的に矛盾のない「数値を伴う計画」を立案できるか。特に、物体カタログより把持位置の取得が出来ているか、及び、物体検出結果より $z$ 軸の安全高度を自ら算出できているかが鍵となります。
検証結果
(この後の動画を参照)
「3. Actionの検証」の結果を見てみないと、何とも言えません。
3. Action(行動)の検証
目的: 物理的な干渉を実行し、その結果を再び視覚へフィードバックして、タスクを完遂(リトライ)する能力を測ります。
検証プロンプト:
先ほど立案したプランを実行し、完了後にフィールドを視覚的またはデータ的に確認して対象物が意図通り移動したか判定するとともに、もし元の位置にある、あるいは転倒している等の不備があればその原因を多角的に推論して報告してください。
Execute the previously developed plan, then verify the field status to determine if the object has been correctly moved as intended, and if any issues like remaining in place or tipping over occur, provide a detailed report by inferring the potential causes from multiple perspectives.
評価ポイント: execute_sequence の実行後に、自発的に get_live_image で「検品」を行い、物理現象の変化(Actionの結果)を「知能」で評価できているか。
検証結果
(この後の動画を参照)
アクションプランの実行フェーズにおいては、論理的な推論と現実の物理制約との乖離が顕著になりました。具体的には、搬送先のバスケット内にグリッパーを深く下ろしすぎて容器の縁と干渉してしまうなど、「実空間における物体の体積や干渉」に対する認識不足が見られました。
こうした不具合に直面するたび、MCPツールの説明文(description)に物理制約を追記して挙動を補正する「パッチワーク的」な対応が必要となり、汎用モデルのみで動的な環境に即応させることの難しさが浮き彫りになりました。
物理世界への理解に関する考察
Geminiは各物体の3D座標や高さをデータとして保持しているものの、それらを「障害物」として回避軌道に反映する、あるいは「物理的な衝突」を予測して避けるといった、物理法則に基づいた自律的な軌道生成には至りませんでした。ビジョン・システムが捉えた情報をアクション(運動学)へと高度に翻訳するプロセスには、まだ大きな壁があると考えられます。
ただし、この結論に至るには、Gemini API自身へ物体認識させた場合との比較が必要です。しかし、今回は、以下の補足内に書いたRPD制限により実施しません。また、今回、YOLO11nによる物体検出により物体の正立・転倒の判定を試みましたが、精度は良くなかったです。Gemini APIを直接使ったなら、もっと精度高く判定出来るのでしょうか?その辺、Gemini RoboticsER評価時の宿題として今後へ持ち越します。
補足:Gemini CLIを採用した背景
今回、Gemini APIを直接利用せず、Gemini CLIを介した構成を採用した理由についても触れておきます。
- API利用制限(RPD)の制約: 無料版Gemini APIの制限(最大20 RPD)が極めて厳しく、試行錯誤を繰り返すロボティクス開発において、Gemini CLI経由のGemini API利用を選択するしかありませんでした。※1
- 物体検出性能の最適化: 今回のような特殊な環境下では、Gemini APIによる汎用的な物体検出よりも、専用に開発された物体検出の方が現状のタスクに適したパフォーマンスを発揮できます。
- 開発ワークフロー: 迅速なプロトタイピングにおいて、手に馴染んだGemini CLIの操作性が大きな利点となりました。
※1(2026/02/20追記) RPDが20なのは、他のツールでもGeminiをヘビーに使っているから?条件を変える、Gemini 2.5 Flash-Lite や Gemini Live のAPIを使うなど、Gemini APIの直接利用を試みる予定。
評価時の動画
以下は、上の動画におけるGemini CLIとの対話結果です。Gemini CLIは、必要とされるMCPツールを使いこなし、指示通りに物体を移動させ、最後に、物体が移動されたのを確認しました。この一連の動作がもっと早く動くなら、結構、インパクトあるデモになるでしょう。
Gemini CLIとの対話結果
█████████ ██████████ ██████ ██████ █████ ██████ █████ █████
███░░░░░███░░███░░░░░█░░██████ ██████ ░░███ ░░██████ ░░███ ░░███
███ ░░░ ░███ █ ░ ░███░█████░███ ░███ ░███░███ ░███ ░███
░███ ░██████ ░███░░███ ░███ ░███ ░███░░███░███ ░███
░███ █████ ░███░░█ ░███ ░░░ ░███ ░███ ░███ ░░██████ ░███
░░███ ░░███ ░███ ░ █ ░███ ░███ ░███ ░███ ░░█████ ░███
░░█████████ ██████████ █████ █████ █████ █████ ░░█████ █████
░░░░░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░
Logged in with Google: ********** /auth
Plan: Gemini Code Assist for individuals
> /mcp list
Configured MCP servers:
🟢 robot - Ready (7 tools)
Tools:
- convert_coordinates
- execute_sequence
- get_joypad_status
- get_live_image
- get_robot_status
- get_tool_logs
- get_workpiece_catalog
ℹ Gemini CLI update available! 0.29.1 → 0.29.2
Installed via Homebrew. Please update with "brew upgrade gemini-cli".
> Create an execution plan to move the blue cap into the basket located on
the left side of the camera image, calculating specific z-axis waypoints
to avoid collisions based on the heights of the objects (do not execute
at this time).
✦ In order to create an execution plan, I need to first gather information
about the objects in the robot's environment. My first step will be to use
the get_live_image tool with object detection enabled to identify the "blue
cap" and the "basket".
Once I have the object detection results, I will ask you to confirm which
detected object is the "blue cap" and which is the "basket on the left side
of the camera image". After that, I will retrieve the workpiece catalog to
get the gripping_height for the blue cap. Finally, I will calculate the
necessary safety heights and formulate the robot commands.
╭───────────────────────────────────────────────────────────────────────╮
│ ✓ get_live_image (robot MCP Server) {"detect_objects":true} │
│ │
│ { │
│ "detections": [ │
│ { │
│ "label": "basket", │
│ "confidence": 0.9499410390853882, │
│ "box_2d": [ │
│ 474.7966625072338, │
│ 610.606575012207, │
│ 650.6417168511285, │
│ 741.4615631103516 │
│ ], │
│ "ground_center": { │
│ "r": 33.98180077293426, │
│ "h": 47.294486836219605, │
│ "u_norm": 666, │
│ "v_norm": 606, │
│ "u_top_norm": 687, │
│ "v_top_norm": 508, │
│ "radius_u_norm": 39.59274655901224, │
│ "radius_v_norm": 43.30081069834674, │
│ "x": 119.4, │
│ "y": 135.8, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 99, │
│ "s": 198, │
│ "v": 121 │
│ }, │
│ "color_name": "blue" │
│ }, │
│ { │
│ "label": "melamine_sponge_ok", │
│ "confidence": 0.9417189955711365, │
│ "box_2d": [ │
│ 666.7811075846354, │
│ 435.5348587036133, │
│ 768.0877685546875, │
│ 469.21863555908203 │
│ ], │
│ "ground_center": { │
│ "r": 8.702956534208305, │
│ "h": 28.88529545256096, │
│ "u_norm": 453, │
│ "v_norm": 747, │
│ "u_top_norm": 450, │
│ "v_top_norm": 685, │
│ "radius_u_norm": 14.170145224943193, │
│ "radius_v_norm": 15.497252130230965, │
│ "x": 195, │
│ "y": -22.5, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 100, │
│ "s": 95, │
│ "v": 248 │
│ }, │
│ "color_name": "blue" │
│ }, │
│ { │
│ "label": "earplug_case_ok", │
│ "confidence": 0.9322507977485657, │
│ "box_2d": [ │
│ 531.7539356372974, │
│ 433.1529935201009, │
│ 632.9032615379051, │
│ 460.38134892781574 │
│ ], │
│ "ground_center": { │
│ "r": 8.329227084242701, │
│ "h": 38.157800629755926, │
│ "u_norm": 448, │
│ "v_norm": 620, │
│ "u_top_norm": 444, │
│ "v_top_norm": 542, │
│ "radius_u_norm": 11.065226377464596, │
│ "radius_v_norm": 12.10154167988342, │
│ "x": 126.5, │
│ "y": -30.4, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 43, │
│ "s": 108, │
│ "v": 169 │
│ }, │
│ "color_name": "yellow" │
│ }, │
│ { │
│ "label": "basket", │
│ "confidence": 0.8979737758636475, │
│ "box_2d": [ │
│ 482.38971851490163, │
│ 314.03487523396814, │
│ 660.2707474320023, │
│ 423.1111526489258 │
│ ], │
│ "ground_center": { │
│ "r": 32.72155299012918, │
│ "h": 45.96665547226496, │
│ "u_norm": 374, │
│ "v_norm": 612, │
│ "u_top_norm": 361, │
│ "v_top_norm": 518, │
│ "radius_u_norm": 41.04418896325935, │
│ "radius_v_norm": 44.88818814113623, │
│ "x": 120.1, │
│ "y": -87.6, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 166, │
│ "s": 64, │
│ "v": 164 │
│ }, │
│ "color_name": "pink" │
│ }, │
│ { │
│ "label": "pencil_cap_ok", │
│ "confidence": 0.8307424783706665, │
│ "box_2d": [ │
│ 592.3740528247974, │
│ 535.2854410807291, │
│ 707.385649504485, │
│ 556.6263834635416 │
│ ], │
│ "ground_center": { │
│ "r": 4.426084319314705, │
│ "h": 44.69592433394609, │
│ "u_norm": 542, │
│ "v_norm": 698, │
│ "u_top_norm": 549, │
│ "v_top_norm": 600, │
│ "radius_u_norm": 6.690010699566175, │
│ "radius_v_norm": 7.316564574272772, │
│ "x": 173.1, │
│ "y": 33.5, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 113, │
│ "s": 206, │
│ "v": 135 │
│ }, │
│ "color_name": "blue" │
│ }, │
│ { │
│ "label": "pencil_cap_ok", │
│ "confidence": 0.7973692417144775, │
│ "box_2d": [ │
│ 494.40058955439815, │
│ 561.9841257731119, │
│ 598.024664984809, │
│ 585.4132334391277 │
│ ], │
│ "ground_center": { │
│ "r": 5.903027611507812, │
│ "h": 44.44648502639834, │
│ "u_norm": 569, │
│ "v_norm": 590, │
│ "u_top_norm": 578, │
│ "v_top_norm": 501, │
│ "radius_u_norm": 7.238513354952546, │
│ "radius_v_norm": 7.916437321494942, │
│ "x": 106.6, │
│ "y": 64, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
╰───────────────────────────────────────────────────────────────────────╯
✦ I've detected the following objects:
1. Blue object (potential cap):
* Label: pencil_cap_ok
* Coordinates (World): x: 173.1, y: 33.5, z: 0.0
* Color: Blue
2. Basket (left side of image):
* Label: basket
* Coordinates (World): x: 120.1, y: -87.6, z: 0.0
* u_norm: 374 (further left in the image)
Can you confirm that the pencil_cap_ok at x: 173.1, y: 33.5 is the "blue
cap" you want to move, and the basket at x: 120.1, y: -87.6 is the target
"basket located on the left side of the camera image"?
╭───────────────────────────────────────────────────────────────────────╮
│ ✓ Ask User │
│ │
│ User answered: │
│ Confirm Objects → Yes │
╰───────────────────────────────────────────────────────────────────────╯
✦ The objects have been confirmed. Now, I will retrieve the workpiece catalog
to determine the gripping height for the "pencil_cap_ok". This information
is crucial for calculating accurate safety waypoints to avoid collisions
during the pick and place operation.
╭───────────────────────────────────────────────────────────────────────╮
│ ✓ get_workpiece_catalog (robot MCP Server) {} │
│ │
│ { │
│ "earplug_case": { │
│ "name": "Earplug Case", │
│ "gripping_height": 20, │
│ "description": "Cylindrical case." │
│ }, │
│ "eraser": { │
│ "name": "Eraser", │
│ "gripping_height": 0, │
│ "description": "Small eraser." │
│ }, │
│ "melamine_sponge": { │
│ "name": "Melamine Sponge", │
│ "gripping_height": 10, │
│ "description": "Cube-shaped melamine sponge. Sold in Japan under │
│ the product name \"Gekiochi-kun\"." │
│ }, │
│ "battery": { │
│ "name": "Battery", │
│ "gripping_height": 0, │
│ "description": "AA or AAA battery." │
│ }, │
│ "pencil_cap": { │
│ "name": "Pencil Cap", │
│ "gripping_height": 20, │
│ "description": "Various colored pencil caps." │
│ }, │
│ "beer_crate": { │
│ "name": "Beer Crate", │
│ "gripping_height": 0, │
│ "description": "Miniature beer crate." │
│ }, │
│ "basket": { │
│ "name": "Basket", │
│ "gripping_height": 0, │
│ "description": "Miniature basket. Used as a placement target for │
│ picked objects" │
│ }, │
│ "gripper": { │
│ "name": "Gripper", │
│ "gripping_height": 0, │
│ "description": "Robot arm gripper." │
│ } │
│ } │
╰───────────────────────────────────────────────────────────────────────╯
✦ I have retrieved the gripping height for "pencil_cap" from the workpiece
catalog, which is 20.0 mm.
Here is the execution plan to move the blue cap into the basket, with
calculated z-axis waypoints to ensure collision avoidance:
Object Coordinates and Heights:
* Blue Cap (pencil_cap_ok):
* World Coordinates (x, y, z): (173.1, 33.5, 0.0)
* Height (h_cap): 44.7 mm
* Gripping Height (gripping_height_cap): 20.0 mm
* Basket:
* World Coordinates (x, y, z): (120.1, -87.6, 0.0)
* Height (h_basket): 46.0 mm
Calculated Safety Heights:
1. Pick Safety Height (above cap): z_pick + h_cap + 30 mm = 0 + 44.7 + 30
= 74.7 mm
2. Place Safety Height (above basket): z_place + h_basket +
gripping_height_cap + 30 mm = 0 + 46.0 + 20.0 + 30 = 96.0 mm
3. Travel Safety Height (maximum of above): max(74.7, 96.0) = 96.0 mm
4. Release Height: 96.0 mm (directly above the basket at Travel Safety
Height)
Execution Plan (Sequence of Robot Commands):
1 move x=173.1 y=33.5 z=96.0 s=50; // Move to above pick location
(pre-pick)
2 grip open; // Open gripper
3 delay t=1000; // Wait for gripper to open
4 move x=173.1 y=33.5 z=20.0 s=50; // Move down to grip the cap (z
gripping_height_cap)
5 grip close; // Close gripper to grip the ca
6 delay t=1000; // Wait for gripper to close
7 move x=173.1 y=33.5 z=96.0 s=50; // Move up to travel safety
height with the cap
8 move x=120.1 y=-87.6 z=96.0 s=50; // Move to above place location
(basket)
9 grip open; // Open gripper to release the
cap
10 delay t=1000; // Wait for gripper to open
11 move x=130 y=0 z=70 s=50; // Return to initial safe
position
> Execute the previously developed plan, then verify the field status to
determine if the object has been correctly moved as intended, and if any
issues like remaining in place or tipping over occur, provide a detailed
report by inferring the potential causes from multiple perspectives.
✦ Okay, I will now execute the plan to move the blue cap into the basket.
After the sequence is complete, I will capture a new live image to verify
the status of the objects and report on the outcome.
╭───────────────────────────────────────────────────────────────────────╮
│ ✓ execute_sequence (robot MCP Server) {"commands":"move x=173.1 y=3… │
│ │
│ Success │
╰───────────────────────────────────────────────────────────────────────╯
✦ The execution was successful. I'm now taking a new live image with object
detection to verify the blue cap's final position and the overall field
status.
╭───────────────────────────────────────────────────────────────────────╮
│ ✓ get_live_image (robot MCP Server) {"detect_objects":true} │
│ │
│ { │
│ "detections": [ │
│ { │
│ "label": "basket", │
│ "confidence": 0.9628642201423645, │
│ "box_2d": [ │
│ 473.6949496799045, │
│ 610.6599807739258, │
│ 650.4374186197916, │
│ 742.1194076538086 │
│ ], │
│ "ground_center": { │
│ "r": 34.1521180494256, │
│ "h": 47.41367474797798, │
│ "u_norm": 666, │
│ "v_norm": 605, │
│ "u_top_norm": 688, │
│ "v_top_norm": 507, │
│ "radius_u_norm": 39.74379146291453, │
│ "radius_v_norm": 43.73048473123076, │
│ "x": 119.3, │
│ "y": 135.7, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 99, │
│ "s": 198, │
│ "v": 120 │
│ }, │
│ "color_name": "blue" │
│ }, │
│ { │
│ "label": "melamine_sponge_ok", │
│ "confidence": 0.9405365586280823, │
│ "box_2d": [ │
│ 666.1614312065972, │
│ 435.3056271870931, │
│ 767.9034762912327, │
│ 468.96575291951496 │
│ ], │
│ "ground_center": { │
│ "r": 8.659876940523725, │
│ "h": 29.40290381215102, │
│ "u_norm": 453, │
│ "v_norm": 747, │
│ "u_top_norm": 450, │
│ "v_top_norm": 685, │
│ "radius_u_norm": 14.046704306403534, │
│ "radius_v_norm": 15.45572693456979, │
│ "x": 194.9, │
│ "y": -23.1, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 100, │
│ "s": 97, │
│ "v": 247 │
│ }, │
│ "color_name": "blue" │
│ }, │
│ { │
│ "label": "basket", │
│ "confidence": 0.9353296756744385, │
│ "box_2d": [ │
│ 484.8223085756655, │
│ 316.79614384969074, │
│ 661.2668072735822, │
│ 423.13594818115234 │
│ ], │
│ "ground_center": { │
│ "r": 31.835665508065983, │
│ "h": 46.052603234379106, │
│ "u_norm": 375, │
│ "v_norm": 614, │
│ "u_top_norm": 362, │
│ "v_top_norm": 520, │
│ "radius_u_norm": 40.036875794277144, │
│ "radius_v_norm": 44.052968304283304, │
│ "x": 121.5, │
│ "y": -86.8, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 158, │
│ "s": 61, │
│ "v": 154 │
│ }, │
│ "color_name": "pink" │
│ }, │
│ { │
│ "label": "earplug_case_ok", │
│ "confidence": 0.907209038734436, │
│ "box_2d": [ │
│ 531.3578852900752, │
│ 433.1735610961914, │
│ 632.7471132631655, │
│ 459.9149703979492 │
│ ], │
│ "ground_center": { │
│ "r": 8.095434918295167, │
│ "h": 38.69637776191738, │
│ "u_norm": 448, │
│ "v_norm": 620, │
│ "u_top_norm": 444, │
│ "v_top_norm": 542, │
│ "radius_u_norm": 10.737658083221273, │
│ "radius_v_norm": 11.81475082204072, │
│ "x": 126.6, │
│ "y": -31.1, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 43, │
│ "s": 117, │
│ "v": 168 │
│ }, │
│ "color_name": "yellow" │
│ }, │
│ { │
│ "label": "pencil_cap_ok", │
│ "confidence": 0.7144376039505005, │
│ "box_2d": [ │
│ 495.33233642578125, │
│ 562.4259948730469, │
│ 597.8146023220486, │
│ 584.88343556722 │
│ ], │
│ "ground_center": { │
│ "r": 5.563388077677662, │
│ "h": 44.35182622500643, │
│ "u_norm": 569, │
│ "v_norm": 590, │
│ "u_top_norm": 577, │
│ "v_top_norm": 501, │
│ "radius_u_norm": 6.823988150573656, │
│ "radius_v_norm": 7.5085012939245415, │
│ "x": 107.1, │
│ "y": 63.4, │
│ "z": 0 │
│ }, │
│ "color_hsv": { │
│ "h": 70, │
│ "s": 183, │
│ "v": 116 │
│ }, │
│ "color_name": "green" │
│ } │
│ ] │
│ } │
╰───────────────────────────────────────────────────────────────────────╯
✦ Verification Report: Blue Cap Movement
Observation:
The pencil_cap_ok object, identified as the blue cap, is no longer detected
at its original coordinates (x: 173.1, y: 33.5). Crucially, there is no
explicit detection of this blue cap within the target basket located at x:
121.5, y: -86.8.
Inference and Potential Causes:
1. Successful Transfer (Most Probable):
* Reasoning: The absence of the cap from its initial location strongly
suggests a successful pick operation. The robot executed the full
sequence, including movement to the basket and gripper release. The most
likely reason for not detecting the cap within the basket is a
limitation of the object detection system itself. Object detectors may
struggle to identify items that are partially or fully occluded by the
container walls, or when they are nestled amongst other objects.
* Evidence: The cap is gone from the origin, and no misplaced cap is
detected elsewhere on the work surface.
2. Placement Error or Droppage (Less Probable, but Possible):
* Reasoning: While the cap is no longer at its original spot, its absence
from explicit detection within the basket could imply an issue during
the placement phase.
* Potential Causes:
* Minor Positional Offset: The (x, y) coordinates used for placing
might have been slightly inaccurate, causing the cap to land on the
rim of the basket and fall either inside in an unobservable
position, or outside into an area not covered by detection or that
blended with the environment.
* Bouncing/Tipping: The cap, upon release, might have bounced out of
the basket due to its material properties or the height of release,
or tipped over in a way that made it undetectable by the vision
system.
* Gripper Issue: Though unlikely given it moved from its initial
position, a delayed gripper closure or premature opening could have
caused it to be dropped en route, but its absence from other
detected locations reduces this probability.
Conclusion:
Based on the available object detection data, the blue cap was successfully
removed from its starting position. The primary reason for not explicitly
detecting it inside the basket is most likely a limitation of the vision
system's ability to "see" inside containers. While less likely, an
imprecise drop or bounce cannot be entirely ruled out without a direct
visual confirmation. For definitive verification, a manual visual
inspection of the basket's contents would be necessary.
? for shortcuts
─────────────────────────────────────────────────────────────────────────────
shift+tab to accept edits
- 1 GEMINI.md file
- 1 MCP server
─────────────────────────────────────────────────────────────────────────────
> Type your message or @path/to/file
─────────────────────────────────────────────────────────────────────────────
~/Docume...sical-ai (main*) no sandbox /model gemini-2.5-flash
総括:今回の「VLAもどき」検証を経て
今回の「VLAもどき」システムの構築は、AIの基礎概念やフィジカルな挙動の体験、またMCP(Model Context Protocol)を活用したコンテキストエンジニアリングを深く理解する上で、非常に有意義なステップとなりました。しかし、厳密な意味での「フィジカルAI」と呼ぶには、まだ多くの課題があるというのが私の判定です。
- 数学的な推論: 二つの物体の座標値から距離を算出し、最も近接しているペアを正しく推定できた点は、LLMの論理・数学能力の高さを示しています。
- 物理的な理解: 一方で、障害物との干渉を考慮した軌道の生成には至らず、実世界の物理法則を真に把握したアクションを実行するには至りませんでした。
この結果から、生成AIを「高度な自然言語インターフェース」として割り切り、精密な軌道計算については、従来通りの専用プログラム(運動学エンジン等)に委ねるハイブリッドなアプローチが、現時点では現実的かもしれません。
また、本プロジェクトを通じて、物理機器の制御を含めた「バイブコーディング」のポテンシャルの高さには、改めて深い感銘を受けました。生成AIは、ハードウェアを管理・制御するためのユーザーインターフェースを、状況に応じて見事に構築してくれました。
ロボティクス特化型ではない汎用AIの主戦場は、実機の直接制御よりも、こうした「人間とハードウェアを繋ぐ柔軟なインターフェース層」にあるのかもしれません。
さて、この対話型インターフェースという強みをさらに突き詰め、Gemini Liveを組み込んだら一体どのような化学反応が起きるのか。次回の検証で探ってみたいと思います。
次のステップ
今回の検証で、汎用AIによる直接的な物理制御の難しさと、インターフェースとしての高い可能性が見えてきました。この知見を踏まえ、次は以下のステップへ進みます。
- Gemini Liveの統合: テキストベースの指示から、リアルタイムな音声対話による直感的なロボット操作への拡張。
- マルチモーダルな検品: 音声で状況を問いかけ、AIが即座に視覚情報を解析して回答する「対話型フィジカル・エージェント」の試行。
「物理を理解する知能」から「人間と共生するインターフェース」へ。Gemini Liveを使うことで、このロボットアームがどう進化するか、近日中に検証結果を公開予定です。
余談:Anthropic Shockが示す「SaaS SI」の転換点と、日本の勝ち筋
本稿を執筆している最中、株式市場では「Anthropic Shock」と呼ぶべき地滑りが起きました。2026年に入り、一時期は盤石と思われた国内外のSaaS関連銘柄、およびSaaS SIを主業とするIT企業の評価が大幅な調整を余儀なくされています。
私たちが今、このフィジカルAIという領域に挑んでいる背景には、こうした市場の揺らぎに対する、一つの仮説に基づいた回答があります。
1. MCPが「サイロ化の連鎖」を断ち切る
これまで多くの現場では、SaaSの乱立や組織の壁によってデータのサイロ化が進んできました。個別最適の積み重ねによってサイロ化が進んでしまった側面があります。しかし、MCP(Model Context Protocol)とAIエージェントの組み合わせは、この「ツールと組織の間に生じた構造的な溝」を、人手を介した調整ではなく、論理的な接続によって直接的に統合する可能性を秘めています。
私はSaaS SIの現場と、AIエージェントのプロトタイプ制作の両方に身を置いてきましたが、「SaaSが消える」のではなく、 「SaaSとAIエージェントがハイブリッドに融合するモデル」 へのビジネス転換が起こるのではと予測しています。
いずれにせよ、組織のリーダに、サイロ化が心地よい組織風土(既得権益の世界)を払拭する決意がなければ、今後は不利なビジネス環境へ追い込まれそうです。
2. 国家戦略「Vertical AI」と、フィジカル領域への回帰
市場が揺れる一方、国は具体的な一歩を踏み出しました。2026年2月の政府資料では、肥大化する「デジタル赤字」への危機感と共に、日本の勝ち筋として 「Vertical AI(垂直統合型AI)」 が明確に定義されています。
第1回AI・半導体WG 事務局説明資料 2026年2月12日 内閣府・経済産業省
かつて本連載で掲げた「マルチベンダー・マルチレイヤー」という思想は、図らずもこの国の戦略が示す「垂直統合(フィジカル領域への深化)」と合致しました。
3. 日本の未来を描く「Vision / Language / Action」
最近、目先の利益だけでなく、「日本の未来」を真摯に描く組織やビジョンに支持が集まるようになってきました。これからのIT企業やリーダーには、自社の存続を超えた、社会に対する明確なビジョンを示すことが求められています。
それはまさに、本プロジェクトの核である 「Vision / Language / Action」 の体現そのものです。
- Vision: 現状(As-Is)を直視し、あるべき未来(To-Be)を定義する。
- Language: サイロ化した情報を繋ぎ、解決のための論理的な実行手順を言葉にする。
- Action: 画面の中の調整に留まらず、具体的な実行(物理的な変化)で価値を証明する。
この三位一体のサイクルを回し、具体的なアウトプットを出し続けること。それが、不確実な時代において、私たちが未来の検討の場に留まり続けるための、唯一の誠実な道なのだと確信しています。
ソースコードの公開
以下に最新版を公開します。














