延長コース D は、LLM(大きな言語モデル)とロボットです。ただし、この授業で LLM にロボットを動かすコードを書かせることはしません。やるのは、人が決めた道具だけを呼ばせる、という設計です。
AI にコードを書かせるのは、コアの授業でずっとやってきたことです。違うのは、AI が書いたものがそのままロボットに届くかどうか。このコースでは、AI とロボットのあいだに、人が確かめる層と、道具が確かめる層を置きます。
この記事の位置
| 項目 | 内容 |
|---|---|
| コース | 延長コース D「LLM とロボット」(3 週・回 D1〜D6) |
| この記事 | 第 1 週(回 D1・D2) |
| 前に受けるコース | 延長コース A(自作サービスで道具を作るため) |
| ロボット | この週は使いません。回 D1 は turtlesim、回 D2 は #10 で記録した room_scan の再生で進めます |
| AI の使い方 | ブラウザの AI に聞き、返ってきた呼び出しを人が確かめてから自分で実行します。AI が自動でロボットを呼ぶ仕組みは使いません |
AI に貼らないもの:ロボットの Wi-Fi のパスワード、個人が分かる情報。これはコアの授業と同じです。
この記事は延長コース Dの 2 回分(D1・D2)です。授業で受ける人は、記録を実習レポート(Word)の「回 D1」「回 D2」のシートに分けて書きます。スクリーンショットは、シートの中の「画面:」の欄(実験の表のすぐ下)に貼ります。延長コースのレポートは、コア(第 1〜12 回)とは別の 1 本にします。ファイルは Google Classroom で配られ、コースの最後に Google Classroom で 1 回だけ出します。期限は最後の実習から 1 週間後の 23:59 です。ひとりで学ぶ人も、予想と結果を同じ形でノートに残しておくと、あとで見返せます。
回 D1 LLM が出す呼び出しを観察する
D1-1 道具の一覧を用意する
使ってよい道具(この一覧にないものは呼べません)
1. /spawn
引数:x(0.0〜11.0)、y(0.0〜11.0)、theta(-3.14〜3.14)、name(文字列)
呼んではいけない条件:同じ name がすでにあるとき
2. /turtle1/teleport_absolute
引数:x(0.0〜11.0)、y(0.0〜11.0)、theta(-3.14〜3.14)
3. /turtle1/set_pen
引数:r, g, b(0〜255)、width(1〜10)、off(0 か 1)
答えるときは、呼び出しを 1 行ずつ、サービス名と引数の形で書いてください。
まず turtlesim の道具で試します。一覧には、名前・引数の型・範囲・呼んではいけない条件を書きます。この一覧が、AI に渡すすべてです。
「呼んではいけない条件」は、実際に呼んでみると分かります。たとえば同じ名前でもう一度 /spawn を呼ぶと、エラーにはならず、名前が空のまま返ってきます。こういう「失敗しても失敗に見えない呼び出し」を、一覧に書いておきます。
D1-2〜D1-3 返ってきた呼び出しを、実行する前に書き写す
ここでは、AI が返した呼び出しを書き写し、1 つずつ実行するかどうかを決めます。
一覧を貼って「亀で四角を描いて」と頼みます。返ってきた呼び出しを、実行する前に10 個以上書き写します(D1-2)。そのうえで、一覧の範囲と照らして「実行する・しない」と理由を付けます(D1-3)。実行してよいものだけを、自分で実行します。
書き写す表は、次の形にします。1 行目は書き方の例です。
| 番号 | 返ってきた呼び出し | 一覧の範囲に入っているか | 実行する・しない | 理由 |
|---|---|---|---|---|
| (例)1 | /turtle1/teleport_absolute x: 5.0, y: 5.0, theta: 0.0 | 入っている(x・y は 0.0〜11.0、theta は -3.14〜3.14) | 実行する | 範囲の中で、四角の 1 つ目の角になる |
| (例)2 | /turtle1/teleport_absolute x: 20.0, y: 5.0, theta: 0.0 | 入っていない(x が 11.0 を超える) | 実行しない | 一覧の範囲の外 |
| 3 |
| 欄 | 書くこと |
|---|---|
| 番号 | AI が返した順番。あとで「何番目で外れたか」を言えるようにする |
| 返ってきた呼び出し | サービス名と引数を、AI が書いたとおりに写す。直してから写さない |
| 一覧の範囲に入っているか | D1-1 の一覧と 1 つずつ照らし、外れていればどの引数かを書く |
| 実行する・しない | 自分で決めた結果。迷ったら「しない」にする |
| 理由 | なぜそう決めたかを短く書く。回 D3 で、どの層で止まるはずかを考える材料になる |
実行するときは、次のように打ちます。ros2 service call のあとに、サービスの名前、型、引数の順に並べます。引数は "{x: 5.0, y: 5.0, theta: 0.0}" のように、波かっこの中に「名前: 値」をカンマで区切って書きます。行の終わりの \ は「次の行に続く」という意味です。型が分からないときは ros2 service list -t で、名前の横に出る型を見ます。
# 確かめてから、自分で実行する
ros2 service call /turtle1/teleport_absolute turtlesim/srv/TeleportAbsolute \
"{x: 5.0, y: 5.0, theta: 0.0}"
# 道具の一覧と型を見る
ros2 service list -t
D1-4 わざとあいまいな指示を出す
「速く動かして」「壁の近くまで行って」のように、数字の入っていない頼み方をします。返ってきた呼び出しのうち、次の 3 つを数えます。
| 数えるもの | 意味 |
|---|---|
| 範囲外の呼び出し | 一覧に書いた範囲(0.0〜11.0 など)を外れているもの |
| 道具にないもの | 一覧に無いサービス名を呼ぼうとしているもの |
| 引数が足りないもの | 必要な引数が抜けているもの |
予想 1:あいまいな指示にすると、どれが増えるでしょうか。手がかり:「速く」「近く」には数字がありません。AI は、数字をどこかから決めて埋めるしかありません。
予想 2:範囲外の呼び出し(たとえば x: 20.0)を実行すると、どうなるでしょうか。エラーで断られるか、そのまま通るか。手がかり:一覧の範囲は、AI に渡した文に書いただけです。turtlesim のサービスはその文を読んでいません。
予想を書いてから数えます。範囲外の呼び出しは、1 つだけ自分で実行して確かめます。
結果を見る(予想を書き、自分で数えてから開く)
範囲外の呼び出しは、実行してみるとそのまま通ってしまいます。一覧に「x は 0.0〜11.0」と書いてあっても、それを守るのは AI ではなく、呼び出しを実行する側です。
どの数が増えたかは、頼み方と AI によって変わります。自分の表の数を、予想 1 と並べて書いてください。
D1-5 一覧を渡さずに同じことを頼む
最後に、道具の一覧があるときと無いときで、返ってくるものを比べます。
同じ頼みごとを、道具の一覧を渡さずにしてみます。返ってくるものがどう変わるかを書きます。一覧があるときと無いときの数を、同じ表に並べます。
この回で撮っておく画面
動画は撮りません。そのかわり、下の画面を撮って残します。授業で受ける人は、実習レポートの「回 D1」の、それぞれの「画面:」の欄に貼ります。ひとりで学ぶ人も、同じ画面を残しておくと、あとで値を見直せます。最初の 1 枚は、実習を始める前に端末で TZ=Asia/Tokyo date と打ち、日付と時刻が出た画面です。授業の日に実習したことの記録になります。講義用の環境の時計は世界標準時(UTC)なので、TZ=Asia/Tokyo を付けて日本時間で出します。
TZ=Asia/Tokyo date(回の頭)- 道具の一覧(画面)(実験 D1-1 の欄)
- AI が返した呼び出し(画面)(実験 D1-2 の欄)
- 実行する・しないを書いた表(写真)(実験 D1-3 の欄)
- 範囲外の呼び出しの例(画面)(実験 D1-4 の欄)
- 実行した結果の turtlesim(実験 D1-3 の欄)
本質の問い(回 D1)
- AI の出力を信用せずに使うために、どこで確かめるかを 2 か所以上挙げ、自分が集めた例で説明しなさい。
考え方のすじ道(答えを書いてから開く)
確かめる場所は、少なくとも 3 つあります。人が見る(実行する前に読む)、道具が見る(サービスの中で範囲を確かめる)、安全層が見る(cmd_vel_guard が速さを切る)。
大事なのは、それぞれが別のものを見ているという点です。人は「意図と合っているか」を見ますが、見落とします。道具は範囲を機械的に見ますが、意図までは分かりません。安全層はいちばん外側で、どんな経路から来た指令でも切ります。
レポートには、自分が集めた呼び出しのうち「どれがどの層で止まるはずか」を対応付けて書いてください。回 D3 で、実際にどの層で止まったかを確かめます。
回 D2 道具の設計
この回は、ロボット用の道具を自分で設計して作り、3 通りの値を送って確かめます。ロボットは使いません。前方の距離を答える道具は、コアの #10 で記録した room_scan を再生して、その /scan で試します。
- D2-1 設計シート:ロボット用の道具を 3 つ決めます。動かない道具(前方の距離を答える、状態を答える)から 2 つ、動く道具を 1 つ。それぞれ名前・引数の型・単位・範囲・呼んではいけない条件・失敗したときに返すものを書きます。
- D2-2 AI に実装させる:延長コース A でやった自作サービスとして作らせます。範囲の確かめは道具の中に必ず入れます。AI 任せにせず、その行を自分で読んで確かめます。
- D2-3 検証する:
ros2 service callで、正しい値・範囲外の値・型の違う値の3 通りを送り、返るものを表にします。動く道具は、出し先を/cmd_vel_testにしたまま試します。
D2-1 設計シートの書き方
設計シートには、道具 1 つにつき次の欄を書きます。例は「前に進む道具」です。名前や型の名前(/my_tools/walk_forward、my_msgs/srv/WalkForward)は例なので、自分で決めた名前に置き換えます。
| 欄 | 書くこと | 例(前に進む道具) |
|---|---|---|
| 名前 | AI に渡す一覧に載せるサービス名 | /my_tools/walk_forward |
| 引数の型 | 引数の名前と型。型は ROS 2 の型(float64 など)で書く | speed:float64 |
| 単位 | 引数の数がどの単位か。書かないと、AI も人も取り違える | m/s |
| 範囲 | 受け付ける値の幅。ロボットの上限(前後 0.6 m/s)より内側に、自分で決める | 0.0〜0.3 |
| 呼んではいけない条件 | 値は範囲の中でも、断るべき場面 | 前方 0.5 m 以内に物があるとき |
| 失敗したときに返すもの | 断ったことと、その理由。文で返すと次に AI に頼むときの材料になる | success: false と「前 0.5 m 以内に物があるので断りました」 |
前方の距離を答える道具なら、引数は無し、返すものは距離(m)、「呼んではいけない条件」は「/scan がしばらく来ていないとき」のようになります。3 つとも同じ欄で書きます。
D2-3 3 通りを送って確かめる
ロボットにはつながず(#10 と同じく robot-disconnect した状態で)、端末を 4 つ開いて試します。
# 端末 1:#10 で記録した room_scan を流し続ける(ロボットの代わり)
cd ~/bags
ros2 bag play room_scan --loop
# 端末 2:自分の道具のノードを起動する。動く道具の出し先は /cmd_vel_test につなぎ替える
ros2 run my_tools tool_server --ros-args -r cmd_vel:=/cmd_vel_test
# 端末 3:動く道具が出した指令を見る
ros2 topic echo /cmd_vel_test
# 端末 4:3 通りを送る(名前と型は、自分で決めたものに置き換える)
ros2 service call /my_tools/get_front_distance my_msgs/srv/GetDistance "{}"
ros2 service call /my_tools/walk_forward my_msgs/srv/WalkForward "{speed: 0.2}"
ros2 service call /my_tools/walk_forward my_msgs/srv/WalkForward "{speed: 2.0}"
ros2 service call /my_tools/walk_forward my_msgs/srv/WalkForward "{speed: 'fast'}"
| 端末 | やること |
|---|---|
| 端末 1 | room_scan を繰り返し再生する。距離を答える道具は、ここから流れる /scan を読む。~/bags は #10 で記録を置いた場所 |
| 端末 2 | 自分の道具のノードを起動する。my_tools と tool_server は例なので、自分のパッケージ名とノード名に置き換える。-r cmd_vel:=/cmd_vel_test で、動く道具の出し先を試し用のトピックにつなぎ替える |
| 端末 3 | /cmd_vel_test に何が出たかを見る。ロボットにはつないでいないので、本物は動かない |
| 端末 4 | 3 通りを送る。{speed: 0.2} が正しい値、{speed: 2.0} が範囲外、{speed: 'fast'} が型の違う値(数のかわりに文字) |
結果は次の表に書きます。
| 送るもの | 送ったコマンド | 返ってくるもの | /cmd_vel_test に出たもの(動く道具) |
|---|---|---|---|
| 正しい値 | |||
| 範囲外の値 | |||
| 型の違う値 |
距離を答える道具は、/cmd_vel_test の欄に「出ないはず」と書き、本当に何も出ないかを見ます。
失敗したときに何を返すかは、設計で決めることです。ただ失敗するのではなく、「なぜ断ったか」を文で返すようにすると、次に AI に頼むときの材料になります。回 D4 で、この文を直して結果が変わるかを試します。
この回で撮っておく画面
動画は撮りません。そのかわり、下の画面を撮って残します。授業で受ける人は、実習レポートの「回 D2」の、それぞれの「画面:」の欄に貼ります。ひとりで学ぶ人も、同じ画面を残しておくと、あとで値を見直せます。最初の 1 枚は、実習を始める前に端末で TZ=Asia/Tokyo date と打ち、日付と時刻が出た画面です。授業の日に実習したことの記録になります。講義用の環境の時計は世界標準時(UTC)なので、TZ=Asia/Tokyo を付けて日本時間で出します。
TZ=Asia/Tokyo dateとrobot-status(回の頭)- 設計シート(写真)(実験 D2-1 の欄)
ros2 service list -t(自分の道具が型つきで出ているところ)(実験 D2-2 の欄)- 正しい値での応答(実験 D2-3 の欄)
- 範囲外の値での応答(実験 D2-3 の欄)
- 型の違う値での応答(実験 D2-3 の欄)
つまずきやすい点
| 症状 | 見るところ |
|---|---|
| AI が一覧にないサービスを呼ぶ | 一覧の書き方。「この一覧にないものは呼べません」と明記したか。これも D1-4 で数える対象 |
ros2 service call で型のエラー | 型名が合っているか。ros2 service list -t で確かめる |
| 範囲外を送っても通ってしまう | 道具の中の確かめが入っていない。AI が書いた行を自分で読む(D2-2) |
| 動く道具で本物が動いた | 出し先が /cmd_vel_test になっているか。この週はロボットを動かしません |
| AI の答えが毎回違う | そういうものです。何回か試して、数を数えるときは回数も一緒に書く |
まとめ
| やったこと | わかったこと |
|---|---|
| 道具の一覧を渡す | 渡した範囲の中でしか呼べない、という形を作れる |
| 実行前に書き写す | AI の出力は、そのまま実行しなくてよい |
| あいまいな指示を出す | 指示があいまいだと、範囲外や存在しない呼び出しが出る |
| 道具を設計する | 範囲の確かめは、道具の中に置く |
| 3 通りを送る | 断り方まで決めて、はじめて道具になる |
次の #D2 では、人が見落としたことにして道具に範囲外を送り、どの層で止まるかを確かめます。




