豆知識：LLMとロボット制御について

# はじめに
始まりは、「え！、LLM知らないの？」でした。  
LLMは、Large Language Modelsの略で、日本語では「大規模言語モデル」というらしい。  
なるほど話題になったChatGPTなどはLLMを使っているということで、意味は理解できました。

LLMを使ったロボットを制御する試みもあると聞いて、興味が湧きました。  
マニピュレータを扱うことが多いことから、**LLM**と**マニピュレータ**について調べることにしました。

調査する中でLLMとロボットを使ったものがリスト化されていました。
この中で、マニピュレータ関連は以下のリストになります。
https://github.com/GT-RIPL/Awesome-LLM-Robotics?tab=readme-ov-file#manipulation  

マニピュレータを使ったLLM関連もかなりありますね。
この中で「[VoxPoser](https://voxposer.github.io/)」のページにインパクトがあったことと、すごくわかりやすそうだったので、これを調べることにしました。  

# VoxPoserについて
「Composable 3D Value Maps for Robotic Manipulation with Langage Models」とあります。
マニピュレーション用の手先軌道を作るのが主な機能のようです。

概念を示しているのが、VoxPoserページにある以下の図です。
![VoxPoser Image](https://voxposer.github.io/media/figures/method.jpg "概念図")

利用者が
「Open the top drawer. Please also watch out for tha vase!」  
と入力すると、マニピュレータが花瓶を避けて一番上の引き出しを開けます。  

具体的には、以下の動画にあるように動きます。  
https://voxposer.github.io/media/videos/teaser.mp4

# 疑問？

ここで疑問がわきました。
 **どうやってLLMがVoxPoserが作ったライブラリのコードを生成・実行しているのか？**

LLMについての知識が浅いのかもしれませんが、
ものすご～く知りたくなりました。

具体的には、VoxPoserの内部でChatGPTのAPIを使って処理しています。
どのように「Open the top drawer. Please also watch out for tha vase!」からVoxPoserで定義しているpythonコードに変換しているのかについて、ソースコードを調べました。

# 調査結果
調査で四苦八苦した部分は省略します…。

VoxPoserでは、ChatGPTに質問だけをPromptとして入力するのではなく、
以下のような構成でPromptとして入力していました。

```sh:Promptの内容
・お願い事項
・回答例
・質問
```

上記の構成でChatGPTのPromptとして入力すると、回答例を参考にして返事を返してくれます。
回答例にVoxPoserで定義しているコードを記述しているので、その内容に合わせた内容を返してくれます。

VoxPoserでは、ChatGPTのAPIを再帰的に3段階コールして、マニピュレータの制御や物体認識のAPIに変換していました。ChatGPTからの回答の一部を再度ChatGPTにお願いする場合を2段階としています。3段階とは、2段階目の回答から再度ChatGPTにお願いしていることを指します。

感覚的にはわかったのですが、実際に試してみたくなりました。
そこで、調査内容が合っているかを実際のChatGPTで試してみました。

## 試した内容

以下、確認した内容です。
回答例のコードは、私が適当に考えた架空のものです。

下記の例では日本語にしていますが、本当は英語の方が良いですね。
また、回答例は多い方が良いと思いますが、簡単に確認したいので少な目にしました。

```
[お願い事項]
あなたは、ロボットアームを制御する経験豊富なpythonプログラマーです。
ロボットアームを制御するためのpythonコードの作成を手伝ってもらいたいです。
Queryの例を提示するので、これを参考にpythonコードを作成してください。
私の質問は、繰り返さないでください。
import文は含めず、外部関数も呼び出せるものとしてください。
また、テキストによる説明も不要です。

[回答例]
# Query: 「ドア」をゆっくりなスピードで開けてください。
pos1 = get_pose(「ドア」手前15cm)
pos2 = get_pose(「ドア」)
speed = set_speed(0.5x)
move(pos1)  # アームを「ドア」手前移動
open_gripper(True) # グリッパを開く 
move(pos2)  # アームを「ドア」に移動
open_gripper(False) # ドアを握る
move(pos1)  # アームを「ドア」手前に移動
# done

# Query: 「マグカップ」に気を付けながら、「青いブロック」を「黄色のブロック」の上においてください。
pos1 = get_pose(「マグカップ」, around=10cm)
pos2 = get_pose(「青いブロック」手前15cm)
pos3 = get_pose(「青いブロック」)
pos4 = get_pose(「黄色ブロック」の上)
speed = set_speed(1.0x)
move(pos1)  # アームを「マグカップ」の周辺に移動
move(pos2)  # アームを「青いブロック」手前15cmに移動
open_gripper(True) # グリッパを開く 
move(pos3)  # アームを「青いブロック」に移動
open_gripper(False) # 「青いブロック」を持つ
move(pos4)  # アームを「黄色ブロック」の上に移動
open_gripper(True) # 「黄色いブロック」に置く
# done


[質問]
# Query: 一番上の引き出しを開けてください。それと、花瓶には気を付けてください。

```
上記をChatGPT4.0のPromptとして入力すると
以下の回答が返ってきました。

```
pos1 = get_pose('花瓶', around=10cm)
pos2 = get_pose('一番上の引き出し手前15cm')
pos3 = get_pose('一番上の引き出し')
speed = set_speed(0.8x)
move(pos1)  # アームを「花瓶」の周辺に移動
move(pos2)  # アームを「一番上の引き出し」手前に移動
open_gripper(True) # グリッパを開く
move(pos3)  # アームを「一番上の引き出し」に移動
open_gripper(False) # 引き出しを握る
move(pos2)  # アームを「一番上の引き出し」手前に移動
# done
```
2つの回答例しか示していないですが、引き出しを開けるコードが出力されました。
すごいですね。ちょっと感動しました！

VoxPoserではpythonを使っていますので、回答のtextを解析してexec関数を使ってpythonコードを実行していました。
また、回答例の書き方も下記のように工夫しているようです。
https://github.com/huangwl18/VoxPoser/tree/main/src/prompts/rlbench
1段階目の質問パターンは、「planner_prompt.txt」で、2段階目のパターンは、「composer_prompt.txt」です。その他は、3段階目になります。

# まとめ
ChatGPTで簡単な質問を繰り返していくパターンはよく使っているのですが、今回のような使い方もあるとは「目から鱗」でした。制御する対象やロボットの動作を上手くグルーピングして回答例を作ればVoxPoserのようにロボットも制御できることがわかりました。  

LLMにフォーカスして調査しましたが、VoxPoserの考え方もすごいですね。アームの手先軌道のみを扱うので、ロボットはなんでもよいことになります。こちらも「目から鱗」でした。