ROSの実行プログラムは、ノードと呼ばれています。ノードは、他のノードとTOPICと呼ばれるメッセージ通信で、データのやり取りを行います。
ノードがトピックへメッセージを送ることを配信(Publish)と呼び、メッセージを特定のトピックへ配信するノードを配信者(Publisher)と呼んでいます。また、ノードがトピックからメッセージを受け取ることを購読(Subscribe)と呼び、特定のトピックを購読対象として登録し、メッセージを購読するノードを購読者(Subscriber)と呼んでいます。
今回は、配信者ノードと購読者ノードを作成し、ROS Wikiのチュートリアルを参考にして、ノード間でトピック通信ができるプログラムを作成してみます。元のソースは英語のコメントが多くて見難いので、改良しています。
このページのパッケージのソースは、https://github.com/joe-ash/my_topicにアップしてあります。
パッケージは、下記のコマンドで作成します。
$ cd ~/catkin_ws/src
$ catkin_create_pkg my_topic std_msgs roscpp
パッケージ設定ファイル(package.xml)を下記のように修正します。
<?xml version="1.0"?>
<package>
<name>my_topic</name>
<version>0.0.0</version>
<description>topic test package</description>
<maintainer email="joe@ash.jp">joe</maintainer>
<license>BSD</license>
<url type="website">http://joe.ash.jp/</url>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>std_msgs</build_depend>
<build_depend>roscpp</build_depend>
<run_depend>std_msgs</run_depend>
<run_depend>roscpp</run_depend>
<export></export>
</package>
ビルド設定ファイル(CMakeLists.txt)を下記のように修正します。
cmake_minimum_required(VERSION 2.8.3)
project(my_topic)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
catkin_package(
CATKIN_DEPENDS roscpp std_msgs
)
include_directories(
${catkin_INCLUDE_DIRS}
)
add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(listener ${catkin_LIBRARIES})
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* ROSのシンプルなメッセージ受信プログラム(Subscriber)
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg) {
/* chatterトピックを購読したときに呼ばれる処理 */
ROS_INFO("I heard: [%s]", msg->data.c_str());
/* 受信したメッセージを標準出力に表示 */
}
int main(int argc, char **argv) {
ros::init(argc, argv, "listener");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("chatter", 100, chatterCallback);
/* chatterトピックをバッファサイズ100で購読開始 */
ros::spin();
}
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* ROSのシンプルなメッセージ送信プログラム(Publisher)
*/
int main(int argc, char **argv) {
ros::init(argc, argv, "talker");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise("chatter", 100);
/* chatterトピックにバッファサイズ100で配信開始 */
ros::Rate loop_rate(10); /* 1秒間に10回(10Hz)の頻度で実行 */
while (ros::ok()) {
std_msgs::String msg;
msg.data = "Hello world!";
ROS_INFO(msg.data.c_str()); /* 標準出力にメッセージを出力 */
pub.publish(msg); /* chatterトピックにメッセージを配信 */
ros::spinOnce();
loop_rate.sleep();
}
}
パッケージをビルドして、エラーがなければ、実行可能になります。
$ cd ~/catkin_ws && catkin_make
実行する前にroscoreを起動し、rosrunコマンドで、送信側ノードを起動します。
$ roscore &
$ rosrun my_topic talker
次に、別の端末を開き、rosrunコマンドで、受信側ノードを起動します。
$ rosrun my_topic listener
送信側ノードが送った、「Hello world!」の文字が、受信側ノードの画面に表示されます。
ROSには、デバッグ用の便利な機能がたくさんあります。別の端末でrqt_graphコマンドを起動すると、ノードとトピックの関係を見ることができます。
$ rqt_graph
起動すると、下記のような画面になります。
rqt_graphでは、ノードを○で表示し、トピックを□で表示されています。rqt_graphを見ると、送信側のtalkerノードから、chatterトピックを経由して、受信側のlistenerノードにメッセージが送られていることが分かります。
ROSコマンドでは、各種情報をテキスト形式で詳細に見ることができます。
ノード情報を見る場合は、rosnodeコマンドを使います。
$ rosnode list
/listener
/rosout
/talker
トピック情報を見る場合は、rostopicコマンドを使います。
$ rostopic list
/chatter
/rosout
/rosout_agg
chatterトピックの詳細情報を見ることもできます。
$ rostopic info chatter
Type: std_msgs/String
Publishers:
* /talker (http://localhost:40341/)
Subscribers:
* /listener (http://localhost:45917/)
roslaunchを使うと、複数のノードを同時に起動することができます。また、roscoreも自動で実行してくれます。roslaunchを使うためには、各パッケージ内にlaunchディレクトリを作成し、launchという拡張子のファイルを作成します。
$ vi single.launch
<launch>
<node name="my_talker" pkg="my_topic" type="talker" />
<node name="my_listener" pkg="my_topic" type="listener" output="screen"/>
</launch>
launchファイルを作成したら、実行します。
$ roslaunch my_topic single.launch
コマンドラインで起動した3つのコマンドが1つのlaunchファイルで起動できました。
launchファイルを利用すると、ノード名を変えて、同じノードを複数起動することができます。下記のようなlaunchファイルを作成します。
$ vi double.launch
<launch>
<node name="my_talker01" pkg="my_topic" type="talker"/>
<node name="my_listener01" pkg="my_topic" type="listener" output="screen"/>
<node name="my_talker02" pkg="my_topic" type="talker"/>
<node name="my_listener02" pkg="my_topic" type="listener" output="screen"/>
</launch>
下記のコメンドで実行すると、複数のノードが起動されます。
$ roslaunch my_topic double.launch
確認のために、rqt_graph で見ると、下記のような画面になります。
それぞれ、01と02の付いた別のノードとして起動されているのが分かります。
ただ、2つのノードは、同じトピックを使っていますので、listenerは、01と02の両方のtalkerの情報を表示します。もし、01と02で別々にデータのやりとりを行なりたい場合は、groupタグを使って、名前空間を分ける必要があります。この場合は、次のようなlaunchファイルを作成します。
$ vi group.launch
<launch>
<group ns="ns01">
<node name="my_talker" pkg="my_topic" type="talker" />
<node name="my_listener" pkg="my_topic" type="listener" output="screen"/>
</group>
<group ns="ns02">
<node name="my_talker" pkg="my_topic" type="talker" />
<node name="my_listener" pkg="my_topic" type="listener" output="screen"/>
</group>
</launch>
下記のコメンドで実行すると、別の名前空間で、複数のノードが起動されます。
$ roslaunch my_topic group.launch
確認のために、rqt_graph で見ると、下記のような画面になります。
01と02の付いたノードが、それぞれの名前空間で起動されているのが分かります。