プラスプラス開発者ブログ

岩手県盛岡市のシステム開発会社「株式会社プラスプラス」の開発者ブログです。

ROSの基本プログラミング(トピック)

こんにちは。プラスプラスの真山です。気づいたら体重が3キロ増えていました。

今回はROSの基本プログラミングとして実際にパッケージを作成してノードを作りメッセージの送受信を行います。
作業の流れとしては、

  • パッケージの作成
  • 設定ファイルの修正
  • メッセージファイル、送受信ノードの作成
  • ビルド
  • 実行

というような流れになっております。

パッケージの作成

まずはじめにパッケージを~/catkin_ws/srcフォルダに作成します。パッケージ名はなんでもいいので今回はros_kihonにします。パッケージを作成するときに依存パッケージを指定します。
今回のパッケージを使うためには、新しくメッセージを生成するときに必要なmessage_generation、ROSの標準メッセージパッケージstd_msgs、ROSでC/C++言語を使うためのクライアントライブラリroscppが必要なのでパッケージ名のあとに記述します。
それではコマンドです。

$ cd ~/catkin_ws/src
$ catkin_create_pkg ros_kihon message_generation std_msgs roscpp

タターン!とエンターキーを押すと~/catkin_ws/srcフォルダにros_kihonパッケージが生成されその中に見覚えのないファイルなどが作られていると思います。なんだこれは?cd移動してlsで確認すると

$ cd ros_kihon
$ ls
include         ← ヘッダーファイルのフォルダ
src            ← ソースコードフォルダ
CMakeLists.txt     ← ビルド設定ファイル
package.xml     ← パッケージ設定ファイル

と出ます。次はこの下のふたつの設定ファイルを修正します。

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

package.xmlファイルはパッケージの情報を記載したもので、パッケージ名、著作者、ライセンス、依存パッケージなどを定義する重要な設定ファイルです。エディタを使って中身を修正します。

$ gedit package.xml

ずら〜っとよくわかんないことが書かれています。ほとんどはコメントアウトされていますが。
この中身を書き換えて最終的にはこうなります。

<?xml version="1.0"?>
<package>
  <name>ros_kihon</name>
  <version>0.1.0</version>
  <description>The ros_kihon package</description>
  <maintainer email="ros-basic@todo.todo">ros-basic</maintainer>
  <license>TODO</license>
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>message_runtime</run_depend>
</package>
ビルド設定ファイル(CMakeLists.txt)の修正

このファイルには実行ファイルの生成、依存パッケージ優先のビルド、リンク生成など、ビルドの詳細が記載されています。
こちらもエディタで修正します。

$ gedit CMakeLists.txt

こちらも簡潔にすると

#パッケージの名前です
cmake_minimum_required(VERSION 2.8.3)
project(ros_kihon)

#ビルドをする際に必要なパッケージを記載します。
find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  std_msgs
)

#メッセージを宣言します。
add_message_files(
  FILES
  MsgKihon.msg
)

#依存するメッセージを設定します。std_msgsが無いとビルドでエラーが出ます。
generate_messages(
  DEPENDENCIES
  std_msgs
)

#catkinパッケージオプションのライブラリ、catkinビルドの依存性、システム依存パッケージについて記述します。
catkin_package(
 LIBRARIES ros_kihon
 CATKIN_DEPENDS roscpp std_msgs
)

#インクルードディレクトリを設定します。
include_directories(
  ${catkin_INCLUDE_DIRS}
)

# kihon_publisherノードのビルドオプションです。
#実行ファイル、ターゲットリンクライブラリ、追加依存性などについて記述します。
add_executable(kihon_publisher src/kihon_publisher.cpp)
add_dependencies(kihon_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(kihon_publisher
  ${catkin_LIBRARIES}
)

# kihon_subscriberノードも同様に書きます。
add_executable(kihon_subscriber src/kihon_subscriber.cpp)
add_dependencies(kihon_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(kihon_subscriber
  ${catkin_LIBRARIES}
)

書くことが多い!!!ここで何かが抜けているとエラーが出てビルドができません。 タイプエラーは危険です。
次にここで定義したメッセージファイルを作ります。

メッセージファイルの作成

CMakeLists.txtファイルで定義した、MsgKihon.msgファイルを作成します。
メッセージファイルはmsgフォルダを新しく作りその中に作ります。

$ roscd ros_kihon    ←(今いると思いますが)パッケージフォルダに移動
$ mkdir msg        ←パッケージフォルダの中にmsgフォルダを生成
$ cd msg          ← magフォルダに移動
$ gedit MsgKihon.msg  ← MsgKihon.msgファイルを書く

MsgKihon.msg

time stamp
int32 data
パブリッシャノードの作成

ビルド設定ファイルで記述したパブリッシャノードkihon_publisherを作成する。

$ roscd ros_kihon/src
$ gedit kihon_publisher.cpp


kihon_publisher

#include "ros/ros.h"
#include "ros_kihon/MsgKihon.h" 
int main (int argc, char **argv)
{
  ros::init(argc, argv, "kihon_publisher");
  ros::NodeHandle nh;
  ros::Publisher ros_kihon_pub =
     nh.advertise<ros_kihon::MsgKihon>
     ("ros_kihon_msg", 100);
  ros::Rate loop_rate(10);
  ros_kihon::MsgKihon msg;
  int count = 0;

  while (ros::ok())
  {
    msg.stamp = ros::Time::now();
    msg.data = count;

    ROS_INFO("send msg = %d", msg.stamp.sec);
    ROS_INFO("send msg = %d", msg.stamp.nsec);
    ROS_INFO("send msg = %d", msg.data);
    ros_kihon_pub.publish(msg);
    loop_rate.sleep();
    ++count;
  }

  return 0;
}
サブスクライバノードkihon_subscriberの作成

ビルド設定ファイルで記述したサブスクライバノードを作成します。

$ roscd ros_kihon/src
$ gedit kihon_subscriber.cpp


kihon_subscriber

#include "ros/ros.h"
#include "ros_kihon/MsgKihon.h"

void msgCallback(const ros_kihon::MsgKihon::ConstPtr& msg)
{
  ROS_INFO("receive msg = %d", msg->stamp.sec);
  ROS_INFO("receive msg = %d", msg->stamp.nsec);
  ROS_INFO("receive msg = %d", msg->data);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "kihon_subscriber");
  ros::NodeHandle nh;
  ros::Subscriber ros_kihon_sub =
          nh.subscribe("ros_kihon_msg", 100, msgCallback);
  ros::spin();

  return 0;
}
ノードをビルド

パッケージ、メッセージ、ノードの作成ができたらcatkin_wsに移動してビルドを行います。

$ cd ~/catkin_ws
$ catkin_make

f:id:plusplus-mayama:20180620175939p:plain 一旦ここで考えるようです。「なにか曲者がいるぞ!」みたいな感じでしょうか。
少し待ってみると・・・

f:id:plusplus-mayama:20180620175803p:plain あれれ〜!おかし〜ぞ〜?(すみませんタイプエラー(曲者)です・・・笑)
気を取り直して(書きなおして)もう一度ビルドすると!? f:id:plusplus-mayama:20180620180933p:plain おお〜〜〜!
無事にビルドができました!実行してみましょう。

パブリッシャノードの実行

roscoreコマンドでマスタを呼び出した後、別のターミナルにrosrun コマンドでパブリッシャノードを実行します。

$ rosrun ros_kihon kihon_publisher

画像のようにテキストが出力されます。もう止めるまで永遠と流れ続けます。飽きるまで見ましょう。 f:id:plusplus-mayama:20180620182340p:plain

kihon_publisherからパブリッシュされるメッセージは、rostopicコマンドで表示できます。

$ rostopic list
/ros_kihon_msg
/rosout
/rosout_agg

トピックリストにros_kihon_msgトピックの確認ができました。 メッセージの内容を確認してみます。

$ rostopic echo /ros_kihon_msg

f:id:plusplus-mayama:20180620183248p:plain
dataが1ずつ増えて永遠と流れ続けてるのがわかります。

サブスクライバノードの実行

次にサブスクライバのノードを実行してみます。
実行するとパブリッシュされたros_kihon_msgトピックのメッセージを受信して画面に出力します。

$ rosrun ros_kihon kihon_subscriber

f:id:plusplus-mayama:20180621101234p:plain

実行中のノードの通信状態を確認

実行中のノードの関係性をグラフで確認できます。

$ rqt_graph

f:id:plusplus-mayama:20180621102205p:plain


今回はROSトピックの連続したデータを送り、またそれを受信するという基本的なプログラミングを行いました。この送受信内容はbagコマンドを使って保存・再生することもできます。
次回からみんなの人気者、Raspberry Piの登場です。