ROS2 5 (Services (C++))

ROS2のTutorialsをやります。暫くはTutorialsそのままをやります。


Service通信をC++で実装します。

Writing a simple service and client (C++)

srvは前の記事で作成したものを使います。
ROS2 4

環境はUbuntu 22.04です。ROS2 Humbleです。

Services

Servicesについてはこちらが公式の情報。

Topics vs Services vs Actions

Services : 即座に完了する処理をさせる。

例1. nodeの状態を問合せする
例2. ロボットハンドへの指示(ロボットハンドがユーザーに対してサービスをする、という解釈)

長時間の処理の呼び出しには絶対に使うな。

Packageをつくろう !!


$ cd ~/ros2_study
$ cd service_cpp_ws
$ cd src

packageの名前を決めます。
server=svとします。client=clとします。
packageの名前をservice_sv_clとします。

ここで公式の説明に--dependenciesが出てきます。


$ ros2 pkg create service_sv_cl --build-type ament_cmake --node-name add_two_ints_server --dependencies rclcpp

example_interfacesは/opt/ros/humble/share/の下にあります。

公式の説明にあるとおり--dependenciesを使うとCMakeLists.txtにfind_package()が追加され、
package.xmlに<depend>が追加されます。

package.xml

--dependenciesを使ったから、自動で設定されるけど、ライセンスなんかの情報はきっちり書きましょうという話し。


$ cd service_sv_cl (cd ~/ros2_study/service_cpp_ws/src/service_sv_cl)
$ vi package.xml

下記についてfillしろ、と書いてあります。

description tag
maintainer tag / email attribute
license tag

Service Serverをつくろう !!


$ cd src (cd ~/ros2_study/service_cpp_ws/src/service_sv_cl/src)
$ vi add_two_ints_server.cpp

公式のソースコードをCopy & Pasteします。wgetじゃないんかい。

生成させたadd_two_ints_server.cppの内容を全て消して公式のソースコードで上書きします。

srvを自作したので、example_interfacesadd_msgに書き換えます。
srvを自作したので、AddTwoIntsMyAddTwoIntsに書き換えます。

add_two_ints_server.cpp

#include "rclcpp/rclcpp.hpp"
#include "add_msg/srv/my_add_two_ints.hpp"

#include <memory>

void add(const std::shared_ptr<add_msg::srv::MyAddTwoInts::Request> request,
          std::shared_ptr<add_msg::srv::MyAddTwoInts::Response>      response)
{
  response->sum = request->a + request->b;
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
                request->a, request->b);
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");

  rclcpp::Service<add_msg::srv::MyAddTwoInts>::SharedPtr service =
    node->create_service<add_msg::srv::MyAddTwoInts>("add_two_ints", &add);

  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");

  rclcpp::spin(node);
  rclcpp::shutdown();
}

例なので「足し算サービス」をしてくれるサーバーです。
service clientがrequestの中に2つの整数を入れて渡すと、
service serverが足し算の結果をresponseの中に入れて返してくれるというサービスをしてくれます。

CMakeLists.txt

下記を記述します。

installのところはCMakeLists.txtの最後の方かつament_package()の前に書いてください。


add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server rclcpp add_msg)

install(TARGETS
    server
  DESTINATION lib/${PROJECT_NAME})

完成品。clientはまだ。


cmake_minimum_required(VERSION 3.8)
project(service_sv_cl)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(add_msg REQUIRED)

add_executable(server src/add_two_ints_server.cpp)

target_compile_features(server PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17

ament_target_dependencies(
  server
  rclcpp
  add_msg
)

install(TARGETS
  server
  DESTINATION lib/${PROJECT_NAME})

ament_package()

colcon build

Tutorialとして、ここで普通はcolcon buildすると思うのですが、公式は簡単すぎると判断しているのかcolcon buildはclientを作ったあとです。


$ cd ../.. (cd ~/ros2_study/service_cpp_ws)
$ colcon build --packages-select service_sv_cl

勿論、公式に従ってcolcon buildを後回しにしても構いません。

Service Clientをつくろう !!


$ cd src (cd ~/ros2_study/service_cpp_ws/src/service_sv_cl/src)
$ vi add_two_ints_client.cpp

公式のソースコードをCopy & Pasteします。wgetじゃないんかい。

ros2 pkg create --node-nameで複数のnodeを作る方法を探しましたが、簡単に調べた範囲では見つかりませんでした。
だから公式では--node-nameを使わない例を先に示しているのかな ?

srvを自作したので、example_interfacesadd_msgに書き換えます。
srvを自作したので、AddTwoIntsMyAddTwoIntsに書き換えます。

add_two_ints_client.cpp

#include "rclcpp/rclcpp.hpp"
#include "rclcpp/rclcpp.hpp"
#include "add_msg/srv/my_add_two_ints.hpp"

#include <chrono>
#include <cstdlib>
#include <memory>

using namespace std::chrono_literals;

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  if (argc != 3) {
      RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y");
      return 1;
  }

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");
  rclcpp::Client<add_msg::srv::MyAddTwoInts>::SharedPtr client =
    node->create_client<add_msg::srv::MyAddTwoInts>("add_two_ints");

  auto request = std::make_shared<add_msg::srv::MyAddTwoInts::Request>();
  request->a = atoll(argv[1]);
  request->b = atoll(argv[2]);

  while (!client->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
      return 0;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  }

  auto result = client->async_send_request(request);
  // Wait for the result.
  if (rclcpp::spin_until_future_complete(node, result) == rclcpp::FutureReturnCode::SUCCESS)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  } else {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
  }

  rclcpp::shutdown();
  return 0;
}

CMakeLists.txt

完成品です。


cmake_minimum_required(VERSION 3.8)
project(service_sv_cl)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(add_msg REQUIRED)

add_executable( server src/add_two_ints_server.cpp )
ament_target_dependencies( server rclcpp add_msg )

add_executable( client src/add_two_ints_client.cpp )
ament_target_dependencies( client rclcpp add_msg )

target_compile_features(server PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17


install(TARGETS
  server
  client
  DESTINATION lib/${PROJECT_NAME})

ament_package()

colcon build


$ cd ../.. (cd ~/ros2_study/service_cpp_ws)
$ colcon build --packages-select service_sv_cl

run

serverを起動しておきます。
ターミナルを立ち上げます。Ctrl-Alt-t。


$ cd ~/ros2_study/service_cpp_ws
$ . install/setup.bash
$ ros2 run service_sv_cl server

clientを起動します。
別のターミナルを立ち上げます。Ctrl-Alt-t。


$ cd ~/ros2_study/service_cpp_ws
$ . install/setup.bash
$ ros2 run service_sv_cl client 1 2

1と2の和の3が返ってきます。

広告

IT開発関連書とビジネス書が豊富な翔泳社の通販『SEshop』
さくらのレンタルサーバ
ムームードメイン
Oisix(おいしっくす)
らでぃっしゅぼーや
珈琲きゃろっと
エプソムソルト


ROS2 1 (Install ROS2)
ROS2 2 (Workspace, Package)
ROS2 3 (Topics (C++))
ROS2 4 (Creating custom srv file)
ROS2 5 (Services (C++))


«       »