LoginSignup
3
2

実習ROS 2 Service通信

Last updated at Posted at 2023-12-04

環境

本記事は以下の環境を想定した記事である。

項目
OS Ubuntu 22.04
ROS ROS 2 Humble

概要

このページでは、ROS(ROS 1、ROS 2共通)の基本となる通信手段であるService通信について説明し、それをROS 2で実装する。
このページの内容はROS 2公式チュートリアルをベースとする。(ROS 2公式チュートリアル
公式チュートリアルのサンプルコードはmain関数で処理の大半を実装しているが、本記事のコードではクラスの中に通信処理を実装する。

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

  • ROSのService通信の概念
  • clientのソースコード(C++)
  • serverのソースコード(C++)

この記事では、example_interfaces/srv/AddTwoInts型のサービスによるノード間通信をC++で実装する。このサービスは、2つの数値をリクエストとして受け取り、その和をレスポンスとして返すという簡単なサービスである。

Service通信

Service通信はROSのノード間の通信方法の1つである。Service通信はリクエスト・レスポンス型の通信方法であり、Pub&Sub通信とは通信方法が異なる。
Service通信では、clientがある処理のリクエストを送信する。serverはそれを受け取り、処理を行った結果をレスポンスとして返す。clientはこの結果を受け取ることで、通信が成立する。
この概念図を以下に示す。

sevice_comm

トピックと同様に、サービスにもメッセージ型がある。Service通信はメッセージ型が一致している場合にのみ成立する。

サービス定義ファイル

サービスのリクエストとレスポンスには様々なデータを含められる。ROS 2のデータ型を組み合わせることで、独自のサービス型を実装できる。
今回用いるサービス定義ファイルは、以下のようになる。

example_interfaces/srv/AddTwoInts.srv
int64 a
int64 b
---
int64 sum

---の上段はリクエスト、下段はレスポンスのデータである。
int64はROS 2のデータ型である。
今回作成するService通信では、整数値aとbの値がリクエストに含まれ、レスポンスにはそれらの和であるsumが入る。

メッセージ型の説明と、独自のメッセージ型を作成して利用する方法については、「実習ROS 2 ROS 2メッセージ」、「実習ROS 2 カスタムROSメッセージ」で解説する。

前準備

ROS 2のプログラムを作成するためには、Pub&Sub通信のときと同様の前準備が必要である。
ここでは、~/ros2_lecture_wsがROS 2のワークスペースであり、パッケージ名がservice_commであると仮定して説明する。パッケージは以下のコマンドで作成したと仮定する。

ros2 pkg create --build-type ament_cmake service_comm --dependencies rclcpp example_interfaces

--dependenciesオプションによって、パッケージを作る時点で依存パッケージを指定できる。
service_commパッケージはAddTwoIntsサービスのメッセージ型を使用する。
そのため、この型が定義されているexample_interfacesパッケージを依存パッケージとして指定する。

ソースファイルの作成

Service通信を行うプログラムを作成するために、以下の4つを行う。

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

clientのコーディング

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

clientは引数を2つ受け取り、その値をリクエストとしてサーバに送信し、結果を表示するプログラムである。引数のチェックと変換はmain関数で、リクエストの生成と送信、結果の表示はAddTwoIntsClientクラスで行う。
実際にサービスクライアントを生成しているのは、以下の部分である。

    // サービスクライアントの生成
    client_ = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

リクエストを送信する処理は、送信後にレスポンスを待ち続けて処理が止まらないように、非同期(async)で行う。

    // リクエストを送信
    auto result = client_->async_send_request(request);

serverのコーディング

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

serverはPub&Sub通信におけるsubscriberのコードと実装が似ている。subscriberの代わりに、サービスサーバを示す変数によって、サーバを実現する。
サービスサーバを生成している場所は以下の場所である。リクエストを受け取ったときに実行されるコールバック関数として、AddTwoIntsServer::add関数を指定する。

    // サービスサーバの生成
    server_ = this->create_service<example_interfaces::srv::AddTwoInts>(
      "add_two_ints",
      std::bind(&AddTwoIntsServer::add, this, std::placeholders::_1, std::placeholders::_2));

サービス本体の処理になる、2つの数値の加算を行うAddTwoIntsServer::add関数を以下に示す。引数にリクエストとレスポンスを持っており、これらを介してメッセージ通信を行う。

  // リクエストを受け取ったときに呼び出されるコールバック関数の定義
  void add(
    const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
    std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response)
  {
    RCLCPP_INFO(this->get_logger(), "Received request: a = %ld, b = %ld", request->a, request->b);

    // サービスの処理本体
    response->sum = request->a + request->b;

    RCLCPP_INFO(this->get_logger(), "Sending back response: sum = %ld", response->sum);
  }

依存関係の設定

Pub&Sub通信のときと同様に、package.xmlに必要なメタデータを記述する。
以下のリンク先のように記述する。

CMakeLists.txtの設定

Pub&Sub通信のときと同様に、CMakeLists.txtに必要なビルド情報を記述する。
以下のリンク先のように記述する。

ビルド

Pub&Sub通信のときと同様に、以下のコマンドでビルドする。

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

実行

1つ目のターミナルで、以下を実行する。

cd ~/ros2_lecture_ws              # ワークスペースへの移動
. install/setup.bash              # ワークスペース環境のセットアップ
ros2 run service_comm add_two_ints_server    # 実行

2つ目のターミナルで、以下を実行する。
引数の10と20は足し算を行う2つの数字である。

cd ~/ros2_lecture_ws
. install/setup.bash
ros2 run service_comm add_two_ints_client 10 20

結果

clientを起動してからserverを起動すると、以下のような実行結果が得られる。clientで与えた10と20という値の和である30を正しく表示できている。

# clientのターミナル
$ ros2 run service_comm add_two_ints_client 10 20 
[INFO] [1659661416.499417251] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661417.499673994] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661418.499830419] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661419.500242970] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661420.500420660] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661421.500860678] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661422.501306595] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661422.695416949] [add_two_ints_client]: Send Request.
[INFO] [1659661422.695600144] [add_two_ints_client]: Sum: 30

# serverのターミナル
$ ros2 run service_comm add_two_ints_server 
[INFO] [1659661422.695377444] [add_two_ints_server]: Create AddTwoInts service server.
[INFO] [1659661422.695472253] [add_two_ints_server]: Received request: a = 10, b = 20
[INFO] [1659661422.695482863] [add_two_ints_server]: Sending back response: sum = 30

また、serverを起動せずにclientのみを起動し、途中でctrl+Cにより実行を中断すると、以下のような結果になる。

$ ros2 run service_comm add_two_ints_client 10 20 
[INFO] [1659661641.420232901] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661642.420483827] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661643.420632357] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661644.420781304] [add_two_ints_client]: Service is not available. waiting...
[INFO] [1659661645.421282922] [add_two_ints_client]: Service is not available. waiting...
^C[INFO] [1659661645.949606772] [rclcpp]: signal_handler(signal_value=2)

なお、clientを引数なしで実行すると、以下のように表示されて実行が終了する。

$ ros2 run service_comm add_two_ints_client 
[INFO] [1659661670.320935396] [rclcpp]: USAGE: add_two_ints client <int> <int>

補足

  • ROS 1は同期型のService通信のみが可能であったが、ROS 2では同期型に加えて非同期型のService通信も可能になった。
  • 本記事ではint64_t型を出力するための書式指定子は%ldとしたが、正式にはPRId64を用いて以下のように記述する。
#include <cinttypes>
RCLCPP_INFO(this->get_logger(), "Sum: %" PRId64, result.get()->sum);

参考

3
2
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
2