LoginSignup
3
1

実習ROS 2 Pub&Sub通信

Last updated at Posted at 2023-10-30

環境

本記事は以下の環境を想定して記述している。

項目
OS Ubuntu 22.04
ROS ROS 2 Humble

概要

本記事では、ROS(ROS 1、ROS 2共通)の基本となる通信手段であるPub&Sub型通信(トピック通信)について説明し、ROS 2でこれを実装する。
本記事の内容はROS 2公式チュートリアルをベースとしている。(ROS 2公式チュートリアル

本記事で解説していること

  • ROSのトピック通信(Pub&Sub通信)の概念
  • 新規ROS 2パッケージの作成方法
  • Publisherのソースコード(C++)
  • Subscriberのソースコード(C++)
  • Pub&Sub通信における、ROS 1とROS 2のコーディングの差異

Pub&Sub通信

ROSはロボットアプリケーションを分散処理システムとして実現するための仕組みを持つ。プログラムはノードと呼ばれる処理単位に分けられ、それらの間のメッセージ通信によってアプリケーション全体の処理が実現される。

ノード間のメッセージ通信手段の1つとして、トピック通信がある。トピックはデータの型と名前をもつ通信チャネルで、ノードがデータを送信、受信する対象である。

以下にトピック通信の模式図を示す。
図は、simple_talkerという名前のノードがメッセージを送信し、simple_listenerという名前のノードがメッセージを受信する様子を示している。
chatterはトピックの名前で、トピックを送信することを「(メッセージを)publishする」、受信することを「subscribeする」という。
このことから、トピック通信をPub&Sub通信ともいう。
トピック通信では、メッセージを送受信するお互いのノードの存在を意識せずに処理を実装することができる。

トピックには名前のほかに型があり、今回の実装では文字列を表すstd_msgs::msg::String型のメッセージを用いる。
ROS 2のデータ型としては、例えば以下が挙げられる。

  • 64bit長の浮動小数点数を表すstd_msgs::msg::Float64
  • 32bit長の符号あり整数型を表すstd_msgs::msg::Int32
  • カメラ画像を表すsensor_msgs::msg::Image
  • パスを表すnav_msgs::msg::Path

トピック通信の模式図

前準備

ROS 2のプログラムを作成し実行するまでに必要な前準備として、以下を行う必要がある。

  • ROS 2のインストール
  • ワークスペースの作成
  • ROS 2パッケージの作成

ここでは、ROS 2のインストールは終了しており、ワークスペースは~/ros2_lecture_wsにあるという前提で説明する。
パッケージは、以下のように作成したと仮定して説明する。

cd ~/ros2_lecture_ws/src
ros2 pkg create --build-type ament_cmake pub_sub_comm

上記のコマンドでは、ament_cmakeというビルドシステムを使用したpub_sub_commという名前のパッケージを作成している。

ソースファイルの作成

Pub&Sub通信を行うシンプルなROS 2プログラムを作成するために、以下の4つを行う。

  • Publisherのコーディング
  • Subscriberのコーディング
  • 依存関係の設定
  • CMakeLists.txtの設定

ソースファイルの作成は以下のコマンドで行う。

cd ~/ros2_lecture_ws/src/pub_sub_comm
touch src/simple_talker.cpp src/simple_listener.cpp

Publisherのコーディング

以下のリンク先のソースコードを実装する。

simple_talker.cpp

ここでは、publisherを以下のようにSimpleTalkerクラスのメンバ変数として宣言している。

// rclcpp::Nodeを継承したクラスSimpleTalkerの宣言
class SimpleTalker : public rclcpp::Node
{
  // (省略)

private:
  // std_msgs::msg::String型のトピックを送信するpublisher
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
};

publisherは、rclcpp::Nodeクラスが持つ関数create_publisher()によって作成されている。

  // (省略)

  // コンストラクタ
  SimpleTalker()
  : Node("simple_talker")   // ノード名をsimple_talkerで初期化
  {
    // publisherの生成
    // 第一引数はトピック名、第二引数はバッファサイズ
    publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);

    // (省略)
  }

  // (省略)

ROS 1のチュートリアルのpublisherでは定期的な処理をwhileループでコーディングしていた。
一方ROS 2では、定期的な処理をタイマとコールバック関数で実装する。
タイマはrclcpp::spin()関数を実行すると開始される。

Subscriberのコーディング

以下のリンク先のソースコードを実装する。

simple_listener.cpp

ここでは、subscriberを以下のようにSimpleListenerクラスのメンバ変数として宣言している。

// rclcpp::Nodeを継承したクラスSimpleListenerの宣言
class SimpleListener : public rclcpp::Node
{
  // (省略)

private:
  // (省略)

  // std_msgs::msg::String型のトピックを受信するsubscriber
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

subscriberは、publisherと同様にrclcpp::Nodeクラスが持つ関数create_subscription()によって作成されている。

  // (省略)

  // コンストラクタ
  SimpleListener()
  : Node("simple_listener")   // ノード名をsimple_listenerで初期化
  {
    // subscriberの作成
    // 第一引数はトピック名
    // 第二引数はメッセージのバッファサイズ、
    // 第三引数は受信したときに呼ばれる関数
    subscription_ = this->create_subscription<std_msgs::msg::String>(
      "chatter", 10, std::bind(&SimpleListener::chatter_callback, this, _1)
    );
  }

  // (省略)

依存関係の設定

パッケージを作成するとpackage.xmlというファイルが自動で生成される。このファイルにはパッケージ全体のメタデータを記述する。
package.xmlの全体像はこちら

以下、自動生成されたファイルから変更した部分を説明する。

  • バージョン(version)、パッケージの説明(description)、メンテナー(maintainer)、ライセンス情報(license)を記述する。
    なお、今回実装するソースコードはBSD-2-Clauseライセンスであるため以下のように記述したが、ROS 2そのものはApache License 2.0で開発されている。
<version>0.0.1</version>
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="maintainer@example.com">Maintainer Name</maintainer>
<license>2-Clause BSD License</license>
  • このパッケージが依存しているパッケージを依存関係(depend)として記述する。ここでは、rclcppパッケージとstd_msgsパッケージを指定する。
<depend>rclcpp</depend>
<depend>std_msgs</depend>

CMakeLists.txtの設定

パッケージを作成するときに--build-type ament_cmakeというオプションを指定するとCMakeLists.txtというファイルが自動で生成される。このファイルにはCMakeでのビルド設定を記述する。
CMakeLists.txtの全体像はこちら

以下、追記した部分について説明する。

  • find_package()ではこのROS 2パッケージが依存するパッケージ名を書く。
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
  • add_executable()では実行ファイル生成の設定を書く。第一引数が実行ファイル名(自由に付けられる)で、第二引数にソースファイルを書く。
  • ament_target_dependencies()では、ライブラリの依存関係を書く。
add_executable(talker src/simple_talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
add_executable(listener src/simple_listener.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
  • install()では、インストール用の設定を書く。インストールするターゲット名と、その場所を書く。
install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})

ビルド

cd ~/ros2_lecture_ws
colcon build --packages-select pub_sub_comm

ワークスペースでcolcon buildコマンドを実行するとプログラムをビルドできる。--packages-selectオプションによって、ビルドするパッケージを選ぶことができる。コンパイルエラーがある場合はここで表示される。

実行

ROS 2では、ROS 1と異なり、roscoreを起動させる必要がない。そのため、simple_talkerを実行するターミナルと、simple_listenerを実行するターミナルの2つで実行が可能である。

  • 1つ目のターミナルで実行
cd ~/ros2_lecture_ws              # ワークスペースへの移動
. install/setup.bash              # ワークスペース環境のセットアップ
ros2 run pub_sub_comm listener    # 実行
  • 2つ目のターミナルで実行
cd ~/ros2_lecture_ws
. install/setup.bash
ros2 run pub_sub_comm talker

結果

simple_talkerを実行させたターミナルからはメッセージをpublishしている表示が現れる。それと同時にsimple_listenerを実行させたターミナルからsubscribeしているメッセージが表示される。これにより/chatterトピックが正しく送受信できていることが確認できる。

result_pubsub

ROS 1とROS 2の違い

ROS 1とROS 2におけるPub&Sub通信の実装、実行方法の大きな違いを以下にまとめる。

  • ROS 1ではマスタノードroscoreを起動させる必要があるのに対し、ROS 2ではその必要がない。
  • ROS 1では定期的な処理をwhileループで実装することが多いが、ROS 2ではタイマとそのコールバック関数で実現する。
    (なお、ROS 1でもタイマコールバックによる定期処理は可能である。)
  • ROS 1ではメッセージ型がstd_msgs::Stringであるが、ROS 2ではstd_msgs::msg::Stringである。

参考

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1