ROSのTOPIC通信プログラム

ROSのTOPIC通信

 ROSの実行プログラムは、ノードと呼ばれています。ノードは、他のノードとTOPICと呼ばれるメッセージ通信で、データのやり取りを行います。
 ノードがトピックへメッセージを送ることを配信(Publish)と呼び、メッセージを特定のトピックへ配信するノードを配信者(Publisher)と呼んでいます。また、ノードがトピックからメッセージを受け取ることを購読(Subscribe)と呼び、特定のトピックを購読対象として登録し、メッセージを購読するノードを購読者(Subscriber)と呼んでいます。
 今回は、配信者ノードと購読者ノードを作成し、ROS Wikiのチュートリアルを参考にして、ノード間でトピック通信ができるプログラムを作成してみます。元のソースは英語のコメントが多くて見難いので、改良しています。
 このページのパッケージのソースは、https://github.com/joe-ash/my_topicにアップしてあります。

ROSのTOPIC通信パッケージの作成

パッケージの新規作成

 パッケージは、下記のコマンドで作成します。

$ cd ~/catkin_ws/src
$ catkin_create_pkg my_topic std_msgs roscpp

パッケージ設定ファイル(package.xml)の修正

 パッケージ設定ファイル(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)の修正

 ビルド設定ファイル(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})

受信側(Subscriber)ソースファイル(src/listener.cpp)の作成

#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();
}

送信側(Publisher)ソースファイル(src/talker.cpp)の作成

#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

ROSのTOPIC通信パッケージの実行

 実行する前にroscoreを起動し、rosrunコマンドで、送信側ノードを起動します。

$ roscore &
$ rosrun my_topic talker

 次に、別の端末を開き、rosrunコマンドで、受信側ノードを起動します。

$ rosrun my_topic listener

 送信側ノードが送った、「Hello world!」の文字が、受信側ノードの画面に表示されます。

ROSのデータの確認

 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での実行

 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ファイルで起動できました。

roslaunchを使った複数ノードの実行

 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の付いたノードが、それぞれの名前空間で起動されているのが分かります。


Copyright (C) ASH Joe Masumura