本シリーズ前回の記事 1. ROS Service プログラムの文脈をふまえたチャット対応 では OpenAI の Chat Completion API を利用して過去のチャット履歴もふまえたチャットを行える ROS Service プログラムを作成した様子をお伝えしました.
今回の記事では Chat Completion API を利用した「文脈をふまえたチャット」をする ROS ソフトウェアを実装してみた2つ目の方法「2. ROS Topic を介した ChatGPT チャットプログラム」を作成した様子を紹介します.
前回,比較的短文のチャットを扱う Chat Completion API へのアクセスであれば ROS Service よりも ROS Topic を介したメッセージのやり取りの方が ROS ノード内でのチャット会話に限られず,より ROS に親和的でよりシンプルな構成になるのでは?という反省がありました.
今回の ROS Topic を用いたチャットプログラムの作成方針は次のようにしました.
/request
を購読してユーザの発言を得る/response
としてパブリッシュする/request
にパブリッシュする/response
を購読して得るROS Service プログラムの場合はチャット履歴をふまえたとしてもチャット機能提供側とユーザとの1者対1者でのやり取りでしたが,ROS Topic にすることでチャット機能提供側と複数のユーザの1者対他者でのやり取りも可能になる利点もあります.
ROS Topic を介した文脈をふまえたチャットプログラムで追加したファイルは次の2つです.
サービスの定義など考慮しなくて良いので非常にシンプルです.
以下,それぞれのファイル内のコードを記載して少し説明をします.
scripts / openai_chat_rostopic.py
#!/usr/bin/env python3 import rospy import openai from std_msgs.msg import String class Chatter: """ Chat with ChatGPT on ROS topics """ def __init__(self): # Get ROS parameters prompt = rospy.get_param('~prompt') self.model = rospy.get_param('~model') openai.api_key = rospy.get_param('~key') rospy.loginfo("For \'system\': %s" % (prompt)) # Set initial message with a prompt self.messages = [] self.messages.append({"role": "system", "content": str(prompt)}) self.sub = rospy.Subscriber('request', String, self.callback) self.pub = rospy.Publisher('response', String, queue_size=10) rospy.spin() def callback(self, data): rospy.loginfo("request: %s", data.data) # Add user's input to the history self.messages.append({"role": "user", "content": str(data.data)}) response = openai.ChatCompletion.create( model=self.model, messages=self.messages ) content = response["choices"][0]["message"]["content"] role = response["choices"][0]["message"]["role"] token = response["usage"]["total_tokens"] # Add GPT's response to the history self.messages.append({"role": str(role), "content": str(content)}) rospy.loginfo("%s(token:%d): %s" % (role, token, content)) self.pub.publish(content) if __name__ == "__main__": rospy.init_node('chat_rostopic', anonymous=True) chatter = Chatter()
/request
を購読(Subscribe)してトピックを受け取ったら callback()
メソッドを呼び出すcallback()
メソッド内で新たなリクエストをチャット履歴に追加/response
としてパブリッシュlaunch / openai_chat.launch
launch オプション service
を用いて前回の記事で紹介した ROS Service によるチャットプログラムと今回の ROS Topic を介したチャットプログラムのどちらを実行するかを切り替えるようにしています.
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="model" default="gpt-3.5-turbo" /> <arg name="service" default="false" /> <arg name="prompt" default="You are a helpful assistant." /> <node if="$(arg service)" pkg="openai_ros" type="openai_chat_server.py" name="openai_chat_service" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> </node> <node unless="$(arg service)" pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="$(arg prompt)" /> </node> </launch>
文脈をふまえた ROS Topic を介したチャットプログラムを実行した例を以下に記載します.
ターミナル 1 : チャットノードの起動
Chat Completion API にアクセスして ROS Topic でやり取りする ROS Node を openai_chat.launch で起動しています.
robotuser@robotuser-PC:~$ source ~/openai_ws/devel/setup.bash robotuser@robotuser-PC:~$ export OPENAI_API_KEY="sk-..." robotuser@robotuser-PC:~$ roslaunch openai_ros openai_chat.launch ... logging to /home/robotuser/.ros/log/609f8d12-52cd-11ee-9968-6b3ff6703622/roslaunch-robotuser-PC-5035.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://robotuser-PC:40257/ SUMMARY ======== PARAMETERS * /openai_chat/key: sk-3JDluBbxsNuIhi... * /openai_chat/model: gpt-3.5-turbo * /openai_chat/prompt: You are a helpful... * /rosdistro: noetic * /rosversion: 1.16.0 NODES / openai_chat (openai_ros/openai_chat_rostopic.py) auto-starting new master process[master]: started with pid [5043] ROS_MASTER_URI=http://localhost:11311 setting /run_id to 609f8d12-52cd-11ee-9968-6b3ff6703622 process[rosout-1]: started with pid [5053] started core service [/rosout] process[openai_chat-2]: started with pid [5060] [INFO] [1694675254.662674]: For 'system': You are a helpful assistant. [INFO] [1694675266.017788]: request: Hello [INFO] [1694675267.401076]: assistant(token:27): Hello! How can I assist you today? [INFO] [1694675292.897892]: request: インターネットはどこにありますか? [INFO] [1694675300.168180]: assistant(token:183): インターネットは物理的な場所にあるのではなく,世界中のコンピューターがネットワークで結ばれている仮想的な空間です.インターネットは,データを送信し受け取るためのプロトコルとパケット交換技術を使用しています.つまり,インターネットは世界中のコンピューターネットワークの集合体です. [INFO] [1694675347.975610]: request: 世界中とは地球のことですか? [INFO] [1694675354.647098]: assistant(token:324): はい,正確に言えば,インターネットは地球上のコンピューターネットワークの集合体です.このネットワークには,地球上の様々な場所に設置されたサーバーやルーターなどの機器が含まれます.インターネットのデータ通信は,これらの機器を介して行われます. [INFO] [1694675387.510775]: request: あなたは誰ですか? [INFO] [1694675391.409440]: assistant(token:407): 私はAI(人工知能)ベースのヘルプフルなアシスタントです.質問や問題解決,情報の提供など,お手伝いできることがありましたらお知らせください. [INFO] [1694675436.047036]: request: どのようにヘルプフルですか? [INFO] [1694675447.019485]: assistant(token:645): 私は多くの情報と知識を持つAIですので,様々な質問や問題に対してお答えすることができます.例えば,旅行の予約や天気予報の確認,翻訳や研究のサポート,一般的な知識の提供など,さまざまな分野でお手伝いすることができます.また,自然言語処理の技術を利用して,あなたの質問や要求を理解し,最適な回答やソリューションを提供することも可能です.お困りのことや疑問があれば,いつでもお気軽にお知らせください. [INFO] [1694675485.687087]: request: 東京の明日の天気もわかりますか? [INFO] [1694675507.944942]: assistant(token:1064): もちろんです!東京の天気予報を調べてみましょう. 私はリアルタイムのデータにアクセスできるわけではありませんが,一般的に天気予報を提供する公式のウェブサイトやアプリを利用して,詳細な天気予報を確認することができます.天気予報は頻繁に更新されるため,事前に確認することをおすすめします.以下は一般的な天気予報サービスの利用方法です. - インターネット検索エンジンで「東京の天気予報」と検索すると,現在の天気情報と明日の天気予報を含む結果が表示されます. - スマートフォンやタブレットを使用している場合は,天気予報を提供するアプリをダウンロードしてインストールすることもできます.定番のアプリには「Weather」や「Weather Underground」などがあります. これらの方法を使用して,明日の東京の天気を確認してみてください.天気予報に関する詳細な情報を入手するためには,地元の気象庁や天気予報サービスの公式ウェブサイトを参照することもおすすめです. ^C[openai_chat-2] killing on exit [rosout-1] killing on exit [master] killing on exit shutting down processing monitor... ... shutting down processing monitor complete done robotuser@robotuser-PC:~$
ターミナル 2 : ROS Topic に発言をパブリッシュ
1つ目のターミナルで openai_chat.launch を起動したままの状態で2つ目のターミナルから ROS Topic をパブリッシュします.
robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "Hello" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "インターネットはどこにありますか ?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "世界中とは地球のことですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "あなたは誰ですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "どのようにヘルプフルですか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "東京の明日の天気もわかりますか?" publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$
ターミナル 3 : チャットノードからの応答の ROS Topic を確認
3つ目のターミナルで ROS Topic /response
に Chat Completion API からの応答がパブリッシュされているかを確認します.コンソール出力では文字コード化して可読性がないですが Python で print()
や rospy.loginfo()
で出力すると ターミナル 1 のような読める日本語で表示されます.
robotuser@robotuser-PC:~$ rostopic echo /response data: "\u79C1\u306FAI\uFF08\u4EBA\u5DE5\u77E5\u80FD\uFF09\u30D9\u30FC\u30B9\u306E\u30D8\u30EB\ \u30D7\u30D5\u30EB\u306A\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3067\u3059\u3002\u8CEA\ \u554F\u3084\u554F\u984C\u89E3\u6C7A\u3001\u60C5\u5831\u306E\u63D0\u4F9B\u306A\u3069\ \u3001\u304A\u624B\u4F1D\u3044\u3067\u304D\u308B\u3053\u3068\u304C\u3042\u308A\u307E\ \u3057\u305F\u3089\u304A\u77E5\u3089\u305B\u304F\u3060\u3055\u3044\u3002" --- data: "\u79C1\u306F\u591A\u304F\u306E\u60C5\u5831\u3068\u77E5\u8B58\u3092\u6301\u3064AI\u3067\ \u3059\u306E\u3067\u3001\u69D8\u3005\u306A\u8CEA\u554F\u3084\u554F\u984C\u306B\u5BFE\ \u3057\u3066\u304A\u7B54\u3048\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\ \u3002\u4F8B\u3048\u3070\u3001\u65C5\u884C\u306E\u4E88\u7D04\u3084\u5929\u6C17\u4E88\ \u5831\u306E\u78BA\u8A8D\u3001\u7FFB\u8A33\u3084\u7814\u7A76\u306E\u30B5\u30DD\u30FC\ \u30C8\u3001\u4E00\u822C\u7684\u306A\u77E5\u8B58\u306E\u63D0\u4F9B\u306A\u3069\u3001\ \u3055\u307E\u3056\u307E\u306A\u5206\u91CE\u3067\u304A\u624B\u4F1D\u3044\u3059\u308B\ \u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u307E\u305F\u3001\u81EA\u7136\u8A00\ \u8A9E\u51E6\u7406\u306E\u6280\u8853\u3092\u5229\u7528\u3057\u3066\u3001\u3042\u306A\ \u305F\u306E\u8CEA\u554F\u3084\u8981\u6C42\u3092\u7406\u89E3\u3057\u3001\u6700\u9069\ \u306A\u56DE\u7B54\u3084\u30BD\u30EA\u30E5\u30FC\u30B7\u30E7\u30F3\u3092\u63D0\u4F9B\ \u3059\u308B\u3053\u3068\u3082\u53EF\u80FD\u3067\u3059\u3002\u304A\u56F0\u308A\u306E\ \u3053\u3068\u3084\u7591\u554F\u304C\u3042\u308C\u3070\u3001\u3044\u3064\u3067\u3082\ \u304A\u6C17\u8EFD\u306B\u304A\u77E5\u3089\u305B\u304F\u3060\u3055\u3044\u3002" --- data: "\u3082\u3061\u308D\u3093\u3067\u3059\uFF01\u6771\u4EAC\u306E\u5929\u6C17\u4E88\u5831\ \u3092\u8ABF\u3079\u3066\u307F\u307E\u3057\u3087\u3046\u3002\n\n\u79C1\u306F\u30EA\ \u30A2\u30EB\u30BF\u30A4\u30E0\u306E\u30C7\u30FC\u30BF\u306B\u30A2\u30AF\u30BB\u30B9\ \u3067\u304D\u308B\u308F\u3051\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u304C\u3001\ \u4E00\u822C\u7684\u306B\u5929\u6C17\u4E88\u5831\u3092\u63D0\u4F9B\u3059\u308B\u516C\ \u5F0F\u306E\u30A6\u30A7\u30D6\u30B5\u30A4\u30C8\u3084\u30A2\u30D7\u30EA\u3092\u5229\ \u7528\u3057\u3066\u3001\u8A73\u7D30\u306A\u5929\u6C17\u4E88\u5831\u3092\u78BA\u8A8D\ \u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u5929\u6C17\u4E88\u5831\ \u306F\u983B\u7E41\u306B\u66F4\u65B0\u3055\u308C\u308B\u305F\u3081\u3001\u4E8B\u524D\ \u306B\u78BA\u8A8D\u3059\u308B\u3053\u3068\u3092\u304A\u3059\u3059\u3081\u3057\u307E\ \u3059\u3002\u4EE5\u4E0B\u306F\u4E00\u822C\u7684\u306A\u5929\u6C17\u4E88\u5831\u30B5\ \u30FC\u30D3\u30B9\u306E\u5229\u7528\u65B9\u6CD5\u3067\u3059\u3002\n\n- \u30A4\u30F3\ \u30BF\u30FC\u30CD\u30C3\u30C8\u691C\u7D22\u30A8\u30F3\u30B8\u30F3\u3067\u300C\u6771\ \u4EAC\u306E\u5929\u6C17\u4E88\u5831\u300D\u3068\u691C\u7D22\u3059\u308B\u3068\u3001\ \u73FE\u5728\u306E\u5929\u6C17\u60C5\u5831\u3068\u660E\u65E5\u306E\u5929\u6C17\u4E88\ \u5831\u3092\u542B\u3080\u7D50\u679C\u304C\u8868\u793A\u3055\u308C\u307E\u3059\u3002\ \n- \u30B9\u30DE\u30FC\u30C8\u30D5\u30A9\u30F3\u3084\u30BF\u30D6\u30EC\u30C3\u30C8\ \u3092\u4F7F\u7528\u3057\u3066\u3044\u308B\u5834\u5408\u306F\u3001\u5929\u6C17\u4E88\ \u5831\u3092\u63D0\u4F9B\u3059\u308B\u30A2\u30D7\u30EA\u3092\u30C0\u30A6\u30F3\u30ED\ \u30FC\u30C9\u3057\u3066\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3059\u308B\u3053\u3068\ \u3082\u3067\u304D\u307E\u3059\u3002\u5B9A\u756A\u306E\u30A2\u30D7\u30EA\u306B\u306F\ \u300CWeather\u300D\u3084\u300CWeather Underground\u300D\u306A\u3069\u304C\u3042\ \u308A\u307E\u3059\u3002\n\n\u3053\u308C\u3089\u306E\u65B9\u6CD5\u3092\u4F7F\u7528\ \u3057\u3066\u3001\u660E\u65E5\u306E\u6771\u4EAC\u306E\u5929\u6C17\u3092\u78BA\u8A8D\ \u3057\u3066\u307F\u3066\u304F\u3060\u3055\u3044\u3002\u5929\u6C17\u4E88\u5831\u306B\ \u95A2\u3059\u308B\u8A73\u7D30\u306A\u60C5\u5831\u3092\u5165\u624B\u3059\u308B\u305F\ \u3081\u306B\u306F\u3001\u5730\u5143\u306E\u6C17\u8C61\u5E81\u3084\u5929\u6C17\u4E88\ \u5831\u30B5\u30FC\u30D3\u30B9\u306E\u516C\u5F0F\u30A6\u30A7\u30D6\u30B5\u30A4\u30C8\ \u3092\u53C2\u7167\u3059\u308B\u3053\u3068\u3082\u304A\u3059\u3059\u3081\u3067\u3059\ \u3002" ---
ChatGPT に対して問い合わせる側が人間であれば応答から自分で文脈をふまえて次の会話をすると思いますが,クライアントプログラムの場合は文脈をふまえた会話をしたければクライアント側のソフトウェアも ROS Topic を拾って自分で文脈を記録して解釈する必要があります.
Chat Completion API との応答を ROS Topic を介してやり取りしているので,複数の Chat ノード(openai_chat_rostopic.py)を実行してトピックの remap
をして互いのノードの応答を自らのノードの入力にすれば ChatGPT 同士で会話を続けるようにすることも簡単にできます.
そのために openai_chat.launch を次のように変更しました.
<launch> <arg name="key" default="$(env OPENAI_API_KEY)" /> <arg name="model" default="gpt-3.5-turbo" /> <arg name="service" default="false" /> <arg name="opponent" default="false" /> <arg name="prompt" default="You are a helpful assistant." /> <node if="$(arg service)" pkg="openai_ros" type="openai_chat_server.py" name="openai_chat_service" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> </node> <group unless="$(arg service)"> <node pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic" output="screen"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="$(arg prompt)" /> </node> <group ns="opponent" if="$(arg opponent)"> <node pkg="openai_ros" type="openai_chat_rostopic.py" name="openai_chat_topic"> <param name="key" value="$(arg key)" /> <param name="model" value="$(arg model)" /> <param name="prompt" value="You are a good talker." /> <remap from="/opponent/request" to="/response" /> <remap from="/opponent/response" to="/request" /> </node> </group> </group> </launch>
opponent
で ChatGPT 同士の会話にするかを指定
output="screen"
はなしprompt
の設定でアシスタント同士だと会話が不自然な感じがしたので(とりあえず)2つ目のプロンプトは “You are a good talker.” としてみたremap
で /request
と /response
を入れ替えターミナル 1 : 2つのチャットノードの起動
openai_chat.launch の起動オプション opponent:=true
で2つのチャットノード実行とトピックの remap
を行います.
起動した状態では応答は何もないですが ターミナル 2 から ROS トピック /request
に最初のリクエストを1つパブリッシュすることで以後 ChatGPT 同士の会話が始まります.
robotuser@robotuser-PC:~$ roslaunch openai_ros openai_chat.launch opponent:=true ... logging to /home/robotuser/.ros/log/9cc9a1be-5dc8-11ee-9968-6b3ff6703622/roslaunch-robotuser-PC-35705.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://robotuser-PC:42615/ SUMMARY ======== PARAMETERS * /openai_chat_topic/key: sk-3JDluBbxsNuIhi... * /openai_chat_topic/model: gpt-3.5-turbo * /openai_chat_topic/prompt: You are a helpful... * /opponent/openai_chat_topic/key: sk-3JDluBbxsNuIhi... * /opponent/openai_chat_topic/model: gpt-3.5-turbo * /opponent/openai_chat_topic/prompt: You are a good ta... * /rosdistro: noetic * /rosversion: 1.16.0 NODES / openai_chat_topic (openai_ros/openai_chat_rostopic.py) /opponent/ openai_chat_topic (openai_ros/openai_chat_rostopic.py) auto-starting new master process[master]: started with pid [35714] ROS_MASTER_URI=http://localhost:11311 setting /run_id to 9cc9a1be-5dc8-11ee-9968-6b3ff6703622 process[rosout-1]: started with pid [35724] started core service [/rosout] process[openai_chat_topic-2]: started with pid [35731] process[opponent/openai_chat_topic-3]: started with pid [35732] [INFO] [1695882670.941923]: For 'system': You are a helpful assistant. [INFO] [1695882677.451265]: request: サッカーの盛んな国を1つ挙げてください. [INFO] [1695882679.335665]: assistant(token:60): ブラジルはサッカーの盛んな国として知られています. [INFO] [1695882705.653403]: request: そうですね,ブラジルは世界でも有名なサッカーの強豪国として知られています.ブラジルではサッカーは国民的なスポーツであり,多くの人々が熱狂的に応援しています. ブラジル代表チームは過去に5回のワールドカップ優勝を果たし,サッカーの歴史においても最も成功した国の一つです.有名な選手も多く輩出しており,ペレやジーコ,ロナウド,ロナウジーニョ,ネイマールなど,数々の伝説的なプレーヤーがブラジルから生まれています. ブラジルではサッカーの試合が行われると,町中が一体となって応援に熱が入ります.カラフルな応援旗やドラム,歌声,そして華麗なサンバの踊りなど,独特のエネルギーと情熱が試合会場を包みます. また,ブラジルには多くの有名なサッカークラブがあります.サンパウロのサンパウロFC,リオデジャネイロのフラメンゴ,サントス,リオグランデ・ド・スールのグレミオ,コリンチャンスなど,これらのクラブは強豪として名高いだけでなく,ファンの熱心さも有名です. ブラジルのサッカーは単なるスポーツ以上のものであり,国民の誇りやアイデンティティの一部となっています.サッカーを通じて,ブラジルの文化や人々の情熱を感じることができるでしょう. [INFO] [1695882713.741564]: assistant(token:754): その通りです.ブラジルのサッカーは国民の誇りであり,文化の一部として重要な役割を果たしています.多くの人々がサッカーに情熱を注ぎ,試合を熱狂的に応援する様子は見る価値があります.ブラジルのサッカーは世界中で愛され,その魅力は他の国にも広まっています.それだけに,ブラジルはサッカーの盛んな国として有名です. [INFO] [1695882744.670979]: request: そうですね,ブラジルのサッカーの魅力は世界中に広まっており,多くの人々がその情熱に共感しています.ブラジル代表チームやクラブチームの試合は,テレビやインターネットを通じて世界中に配信されており,多くのサッカーファンがその魅力に触れることができます. さらに,ブラジルのサッカー文化は技術,創造性,スピード,そしてリズム感を特徴としています.ブラジルのサッカー選手は驚くほど優れたテクニックを持ち,美しいプレーを見せることで知られています.彼らのキレのあるドリブル,正確なパス,そして豪快なシュートは,多くの人々に感動を与えます. ブラジルのサッカーの成功は,その国の熱狂的なサッカーカルチャーとも関連しています.子供たちは幼い頃からサッカーボールを蹴り,街角やビーチでプレーする様子をよく見かけます.サッカースクールやアカデミーも充実しており,若い才能は早いうちから育成されています. ブラジルのサッカーは単なるスポーツの一環ではなく,国民の誇りやアイデンティティの一部です.多くの人々が試合を通じて喜びや感動を共有し,サッカーを通じて結びついています.ブラジルのサッカー文化は他の国々にも影響を与え,彼らのスタイルやプレースタイルが憧れとなっています. ブラジルのサッカーは確かに盛んな国であり,その魅力は世界中に広がっています.それはブラジルの人々の情熱と才能,そしてサッカー文化の豊かさによるものです. [INFO] [1695882752.038028]: assistant(token:1519): 完全に同意します.ブラジルのサッカーカルチャーは,国民の情熱と才能,そして豊かなサッカー文化によって支えられています.その魅力は世界中に広まり,多くの人々がブラジルのサッカーに感動を覚えています.ブラジルのサッカーは間違いなく世界的な影響力を持っており,多くの国々で愛される存在です. [INFO] [1695882760.235372]: request: ありがとうございます.ブラジルのサッカーは確かに世界的な影響力を持っており,多くの人々に愛されています.その独特のスタイルと情熱は,他の国々のサッカーカルチャーにも大きな影響を与えています.ブラジルのサッカーは常に進化し,新たな才能が次々に生まれることで,さらなる魅力と成功を築いていくでしょう. ^C[opponent/openai_chat_topic-3] killing on exit [openai_chat_topic-2] killing on exit [rosout-1] killing on exit [master] killing on exit shutting down processing monitor... ... shutting down processing monitor complete done robotuser@robotuser-PC:~$
ターミナル 2 : 最初の話題投下
robotuser@robotuser-PC:~$ rostopic pub -1 /request std_msgs/String "サッカーの盛んな国を1つ挙げてください." publishing and latching message for 3.0 seconds robotuser@robotuser-PC:~$
ROS ノードグラフ
rqt の ROS Node Graph でノードとトピックの様子を確認してみると,2つのノード /openai_chat_topic
と /opponent/openai_chat_topic
とが互いの応答トピックを参照して循環していることが見て取れます.
ターミナル 1 の出力にも現れていますが段々と応答の文字数が互いに多くなる傾向があります.ChatGPT の token 数の上限に達して終わったりしますが,そうでない限りはずっと ChatGPT 同士で応答を続けるので終わらせたい場合は Ctrl-C で終わらせます.
文脈のデータを蓄積して多くなると Chat Completion API の token を消費してしまいますし,応答に時間がかかったりもします.一定時間会話がなかった場合やそれまでの文脈からがらりと話題を変える場合のために文脈を含めたメッセージデータを初期化するメソッドも運用上は必要かもしれません.
また,OpenAI 以外の大規模言語モデル(LLM)の API の ROS Topic ラッパがあれば(or を作れば)異なる LLM 間での会話も可能であろうと思います.
今回の記事はここまでです.
先日,長野市にある信州大学の山崎研究室を訪問して Ubuntu 20.04 および ROS Noetic に対応した HIRO ロボットソフトウェアを納品しました.
山崎研究室では HIRO で AI を用いたロボット制御などを行っているとのことで,今回は GPU ボードを搭載したワークステーションに Ubuntu 20.04 および ROS Noetic に対応した HIRO ロボットソフトウェアをインストールしました.
HIRO ロボットは新しいソフトウェアを得て今後も活躍してくれることと思います.
なお, 今回の HIRO とともに TORK では NEXTAGE OPEN も Ubuntu 20.04 および ROS Noetic に対応したロボットソフトウェアの動作確認をしました.
NEXTAGE OPEN や HIRO を Python3 で動かすことや ROS Noetic で使うことにご興味がありましたら,TORK( info@opensource-robotics.tokyo.jp )にお問い合わせいただけたらと思います.
関連記事: 信州大学 山崎研究室でHiroに会いました!
本シリーズ前回の記事 Gazebo/MoveIt のための 3D モデリング(13)MoveIt の静モデルの作成 では CAD などからエクスポートしたメッシュデータファイルを MoveIt の静モデルとしてモデルファイルに組み込んで表示する方法を紹介しました.
今回は洗濯機の URDF (Unified Robot Description Format) モデルにドアのヒンジなどの動く箇所を設定して,より機械らしい(ロボットに近い)モデルにする様子を紹介します.
前回の MoveIt の静モデル作成においては洗濯機全体として1つのメッシュデータファイル( DAE もしくは STL )をエクスポートして利用しました.
MoveIt のリンクモデル作成では各リンクに対応したメッシュをそれぞれエクスポートしてそれぞれのリンクのメッシュファイルとして利用します.
今回の洗濯機モデルでは次の3つのリンク構成にします.
今回は「洗濯機本体(main-body)」は元の洗濯機全体の座標系そのままとするので配置の変更はしません.
「洗濯槽の扉(door)」と「洗剤投入トレイ(tray)」の形状データを各リンクの座標系原点に配置します.
元々の洗濯機全体の座標系で配置されたオブジェクトを残しつつ,別途各リンクのエクスポート用にリンク座標系の原点にオブジェクトを配置してメッシュデータとしてエクスポートします.
Rhinoceros では右の図のようにオブジェクトを含む既存のレイヤを右クリックするとメニューに「レイヤとオブジェクトを複製」ができるのでこの機能で複製した先のレイヤで作業すると良いでしょう.
各リンク座標系基準の配置用レイヤでそれぞれの各リンクは次のように配置しました.
各リンク座標基準に配置したオブジェクトを選択して「選択オブジェクトをエクスポート」コマンドから DAE (Collada) か STL 形式でエクスポートします.
Rhinoceros から DAE (Collada) をエクスポートする場合はエクスポートオプションにて
「ジオメトリのみを保存」のみにチェック
を入れてファイルを書き出します.
この「ジオメトリのみを保存」でも色や単位情報も保存されます.
今回は表示(visual)用に色付きの DAE ファイルとしてエクスポートし,干渉チェック(collision)用にデータ量を少なくするため粗目の設定で STL ファイルをエクスポートしました.
DAE や STL のメッシュデータのエクスポートが終わったらリンク機構を含む URDF モデルファイルを作成します.
3dmodeling-examples/models/urdf/ ├── meshes │ └── washing-machine │ ├── base_link.dae │ ├── base_link.stl │ ├── door.dae │ ├── door.stl │ ├── main-body.dae │ ├── main-body.stl │ ├── tray.dae │ └── tray.stl ├── washing-machine_links.urdf └── washing-machine.urdf
前回作成してメッシュファイルを配置したフォルダ
3dmodeling-examples/models/urdf/meshes
内にエクスポートしたメッシュファイルを配置します.
そしてフォルダ 3dmodeling-examples/models/urdf/
にファイル washing-machine_links.urdf
をテキストファイルとして作成してそこにリンク機構を含む URDF モデルを作り込みます.前回作成したファイル washing-machine.urdf
を複製してファイルの名前を変更しても良いです.
URDF データで 「洗濯機本体(main-body)」 と 「洗濯槽の扉(door)」 および 「洗剤投入トレイ(tray)」 それぞれの相対的な姿勢の関係とそれぞれの可動域を 「関節(joint)」 として定義しますので, CAD ( Rhinoceros など ) 上で相対姿勢および可動域の測定を行います.
その際,各リンクオブジェクトについて DAE や STL ファイルへのエクスポート用に原点へ移動した配置ではなく元々の洗濯機全体内での配置で調べるということに注意してください.
まずは 「洗濯機本体(main-body)」 と並進的な相対位置関係にある 「洗剤投入トレイ(tray)」 の座標の確認と設定可能な可動域を調べます.
次に 「洗濯機本体(main-body)」 と 「洗濯槽の扉(door)」 の相対座標・角度やの確認と設定可能な可動域を調べます.
扉は傾いて洗濯機本体に取り付けられているのでその角度とヒンジ回りの可動域も調べます.Rhinoceros では角度表示がラジアンでもできるのでそれを利用します.
リンクモデルに必要なメッシュファイルと情報が揃いましたので URDF ファイルに書き込んだものが次のようになります.
<?xml version="1.0" ?> <robot name="washing-machine"> <link name="base_link"> <collision> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/main-body.stl" scale="0.001 0.001 0.001" /> </geometry> </collision> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/main-body.dae" /> </geometry> </visual> </link> <link name="door"> <collision> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/door.stl" scale="0.001 0.001 0.001" /> </geometry> </collision> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/door.dae" /> </geometry> </visual> </link> <link name="tray"> <collision> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/tray.stl" scale="0.001 0.001 0.001" /> </geometry> </collision> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/tray.dae" /> </geometry> </visual> </link> <joint name="joint1" type="revolute"> <parent link="base_link"/> <child link="door"/> <origin xyz="0.30658 -0.258 0.67582" rpy="0 -0.1396 0"/> <axis xyz="0 0 1" /> <limit effort="30" velocity="1.0" lower="-1.8326" upper="0.0" /> </joint> <joint name="joint2" type="prismatic"> <parent link="base_link"/> <child link="tray"/> <origin xyz="0.0 -0.190 0.956" rpy="0 0 0"/> <axis xyz="1 0 0" /> <limit effort="30" velocity="1.0" lower="0.0" upper="0.200" /> </joint> </robot>
washing-machine_links.urdf 内のそれぞれの要素について説明します.
<link>
要素: リンクの定義 3つ ( base_link, door, tray )<collision>
要素にメッシュに STL ファイルを使用し単位変換 [mm] → [m]<visual>
要素に DAE メッシュファイルを使用<origin>
はリンク内のメッシュの配置なので今回は全てゼロ<joint>
要素: joint1type
で関節形式 revolve
(=回転)を設定<parent>
要素で関節を介する親リンク base_link
を指定<child>
要素で関節を介する子リンク door
を指定<origin>
要素で親子リンク間の相対座標<axis>
要素で revolve
関節の回転軸のリンク座標系での方向を設定<limit>
要素effort
: 最大トルク – 今回はとりあえずの値velocity
: 最大角速度 – 今回はとりあえずの値lower
: 可動域下限 – 回転関節なので下限角度で単位はラジアン [rad]upper
: 可動域上限 – 回転関節なので上限角度で単位はラジアン [rad]<joint>
要素: joint2type
で関節形式 prismatic
(=並進)を設定<parent>
要素で関節を介する親リンク base_link
を指定<child>
要素で関節を介する子リンク tray
を指定<origin>
要素で親子リンク間の相対座標<axis>
要素で prismatic
関節のリンク座標系での移動方向を設定<limit>
要素effort
: 最大力 – 今回はとりあえずの値velocity
: 最大速度 – 今回はとりあえずの値lower
: 可動域下限 – 並進関節なので下限位置で単位はメートル [m]upper
: 可動域上限 – 並進関節なので上限位置で単位はメートル [m]下記リンク先の ROS Wiki に URDF ファイルの作成方法のチュートリアルがありますので参考にしてください.
urdf_tutorial
の display.launch
で URDF モデル washing-machine_links.urdf
の確認をします.(下記コマンド横スクロールで末尾まで表示)
$ roslaunch urdf_tutorial display.launch model:='$(find 3dmodeling-examples)/models/urdf/washing-machine_links.urdf'
URDF で <joint>
要素を定義して joint_state_publisher ウィンドウ内のスライドバーも有効になっているので関節を動かしてみます.
動画では表示メッシュの動きとともに TF も一緒に動いている様子が見られると思います.
今回の記事はここまでです.
本シリーズ前回の記事 Gazebo/MoveIt のための 3D モデリング(12)Gazebo の静モデルの作成 ではエクスポートしたメッシュデータファイルを Gazebo の静モデルとしてモデルファイルに組み込んで表示する方法を紹介しました.
今回はエクスポートしたメッシュデータファイルを MoveIt の静モデルとしてモデルファイルに組み込んで表示する方法を紹介します.
今回紹介するのは次の2通りの方法です.メッシュを MoveIt GUI で読み込んで障害物とする方法とロボットモデル作成につながる方法の URDF モデルの作成とそれを確認表示する方法です.
MoveIt には動作計画における障害物として STL ファイルや DAE ファイルをそのまま読み込んで MoveIt の動作計画空間内に配置する機能があります.
MoveIt の Motion Planning パネル内の Scene Objects タブを開いて, “Mesh from file” を選択します.
“Mesh from file” セレクタの右隣にあるプラスボタン [ + ] を押します.
(左図拡大は画像をクリック)
保存してある STL もしくは DAE ファイルを選択します.
ファイルを読み込むときに MoveIt の GUI インタフェースである RViz のメッセージウィンドウが開いて,ミリメートル単位で記述されているモデルをメートル単位に変換する旨の問いがなされるので [ Yes ] をクリックします.
読み込んだモデルをインタラクティブマーカや座標などを指定して意図した位置に設置し,左のチェックボックスをクリックすると設置リンクの選択を促されますので適宜選択して [ OK ] ボタンを押します.
次に [ Publish ] ボタンを押すと読み込んで設置したモデルが MoveIt 空間内で障害物として認識されます.
あとは [ Plan ] や [ Plan & Execute ] などで動作計画を実行するとその経路上に障害物があるとそれを避けたマニピュレーションの軌道が生成されます.
MoveIt モデルの URDF ファイルの作成は下記リンク先の ROS Wiki に書かれています.
本記事ではそれらから MoveIt 静モデル作成に絞って説明します.
MoveIt モデルの作成にあたっては ROS パッケージを作成してその中にモデルの URDF ファイルを置くのが本記事の内容に続く応用も含めると一番簡便なのではないかと思います.
今回 ROS パッケージをつくるのが面倒なようでしたら下記リンク先リポジトリをクローンして利用してください.
3dmodeling-examples/ ├── CMakeLists.txt ├── images │ ├── front_view_win.png │ ├── left_view_win.png │ ├── top_view_win.png │ ├── washing-machine_catalogue.pdf │ └── washing-machine_catalogue.png ├── launch │ ├── spawn-washingmachine.launch │ └── world-washingmachine.launch ├── LICENSE ├── models │ ├── gazebo_models │ │ ├── washing-machine │ │ │ ├── meshes │ │ │ │ ├── base_link_blue-gray.stl │ │ │ │ ├── base_link_dark-gray.stl │ │ │ │ ├── base_link_gray-white.stl │ │ │ │ ├── base_link_light-gray.stl │ │ │ │ └── base_link.stl │ │ │ ├── model.config │ │ │ └── model.sdf │ │ └── washing-machine-dae │ │ ├── meshes │ │ │ ├── base_link.dae │ │ │ └── base_link.stl │ │ ├── model.config │ │ └── model.sdf │ ├── urdf │ │ ├── meshes │ │ │ └── washing-machine │ │ │ ├── base_link.dae │ │ │ └── base_link.stl │ │ └── washing-machine.urdf │ └── washing-machine.3dm ├── package.xml ├── README.md └── worlds └── washing-machine.world
本記事執筆時のサンプルモデルパッケージは右に示すような構成になっていますので参考にしてみてください.
この中の MoveIt URDF モデルに関連するフォルダ・ファイルがハイライトされた部分です.
meshes フォルダに base_link.dae と base_link.stl の2つのファイルが含まれていますが,これはサンプルのためですのでどちらか1つのファイルだけでも URDF モデルは作成できます.
今回の MoveIt 静モデルのサンプル URDF ファイル washing-machine.urdf の中身は次のようになっています.
<?xml version="1.0" ?> <robot name="washing-machine"> <link name="base_link"> <collision> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/base_link.stl" scale="0.001 0.001 0.001" /> </geometry> </collision> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://3dmodeling-examples/models/urdf/meshes/washing-machine/base_link.dae" /> </geometry> </visual> </link> </robot>
URDF のデータ形式は XML で,モデル構成要素の link
や collision
, geometry
, mesh
が記述されています.今回は静モデルですので link
要素は1つですがロボットのように関節が多いとリンク数にともなって link
要素およびそこに含まれる子要素も増えます.
ROS パッケージ化していることで ROS のファイルシステムが名前空間から package://3dmodeling-examples/...
によって 3dmodeling-examples
パッケージのディレクトリを自動解決できるようになっています.
collision
メッシュの STL データは単位情報を持っていないので scale="0.001 0.001 0.001"
で 0.001倍(=1/1000) 変換をして [mm] のデータを [m] に換算しています.
visual
のメッシュは DAE ファイルを利用していて,DAE モデルは単位情報を持っていて読み込む側でスケール判断をするので scale
は必要ありません.
URDF モデルの確認には urdf_tutorial パッケージの display.launch を利用します.
urdf_tutorial は joint-state-publisher-gui パッケージをインストールすることで利用できるようになります.下記は ROS Melodic の場合のインストールコマンドですので他の ROS バージョンの場合は melodic
の部分を noetic
などに書き換えて実行してください.
$ sudo apt update $ sudo apt install ros-melodic-joint-state-publisher-gui
ターミナルで ROS 環境の設定と urdf_tutorial の display.launch の起動を行います.
$ source ~/$PathToYourWorkspace/devel/setup.bash $ roslaunch urdf_tutorial display.launch model:='$(find 3dmodeling-examples)/models/urdf/washing-machine.urdf'
$PathToYourWorkspace
は各自の ROS ワークスペースへのパスを記述
model:=
には URDF ファイルパスを指定正常に実行できると次の図のように洗濯機モデルが RViz 空間上に表示されます.
$ rosrun tf2_ros static_transform_publisher 0 0 0 0 0 0 /world /base_link
今回の記事はここまでです.
本シリーズ次回の記事は洗濯機の URDF モデルにドアのヒンジなどの動く箇所を設定してより機械らしい(ロボットに近い)モデルにする様子を紹介する予定です.
ROS(ロス/Robot Operating System)の学習は実際にロボットがなくてもロボットのシミュレータが入手できるのでネットワークにつながるパソコンが1台あればできますので結構自習に向いています.この記事では ROS の学習を始める,進めるにあたり必要な情報がある Web へのリンクを中心に紹介します.
大まかに言うと次のインストールを行えば ROS の学習をスタートすることができます.
ROS と Ubuntu Linux のバージョンは後述する ROS 学習のチュートリアルが現時点では ROS Kinetic というバージョンを基本としているので下記の組み合わせをお勧めします.
ROS Melodic は ROS Kinetic と基本的な操作のほとんどは変わらないので ROS Kinetic で学習してから ROS Melodic に移行しても難なく可能です.
パソコンはどのようなものを使えば良いのか?については下記記事を参考にしてください.
最新高性能パソコンよりも数年型落ちや廉価の機種のほうが Ubuntu Linux をインストールしやすい傾向にあるように思います.
下記リンク先に各 ROS のバージョンにおけるインストール手順が書かれています.
また,Ubuntu のバージョンと ROS のバージョンには1対1の対応関係があるので組み合わせを気をつける必要があります.
各チュートリアルを進めるとそれらの中で ROS シミュレータなどのインストールも行います.
ROS の入門には TORK MoveIt チュートリアルをお薦めします.MoveIt は ROS のマニピュレーションロボット動作計画ソフトウェアです.このチュートリアルでは数種のロボットの ROS シミュレータのインストールや基本的な操作,プログラムでのロボット操作を学習することができます.TORK MoveIt チュートリアルではプログラミング言語に Python を用いていますが,プログラミングの経験がほとんどない人にもプログラムによるロボット操作の体験と学習ができるように構成しています.
ROS を初めて使う方に TORK MoveIt チュートリアルを学習したときのレポートも下記の記事に書いてもらっています.学習過程でいろいろと疑問をもった点などの体験を書いてもらいましたので参考にしてみてください.
より発展的な ROS プログラミングを学習したい場合は ROS-Industrial トレーニングを行ってみるのも良いでしょう.この教材で取り上げられているプログラミング言語は主に C++ と Python です.C++ によるロボット制御や画像処理,3D ポイントクラウド処理などとそれらの組み合わせのプログラムの学習ができます.
ROS Discourse やチュートリアル,パッケージの GitHub Issues に質問を投稿してみてください.
1台のパソコンだけ,シミュレータだけでなく入門的な実機マニピュレータを利用してみたいと思った方は入門的なマニピュレーションロボット2例の導入検証を行った記事を参考にしてみてください.