Writing a Service Client in ROS2 (C++)

ROSDS Quick Guide

How to open a Shell

In order to open a new Shell just go to the top bar and select Tools. On the menu that appears, select the Shell option.

A new empty Shell like the one below will appear.

Where to find the code

In order to open the IDE, just go to the top bar and select Tools. On the menu that appears, select the IDE option.

A new IDE window will appear, like the one below. Inside the ros2_ws/src folder you will find all the files related to this Tutorial.

How to visualize RViz2?

In order to visualize RViz, just go to the top bar and select Tools. On the menu that appears, select the Graphical Tools option.

A new window will appear. Here, you will have to Log In in order to enter the Graphical Tools.

After you Log In, you will be able to visualize Rviz, or any other graphical tool like rqt.

Of course, bear in mind that you will need to have Rviz running in order to visualize. Otherwise, you will just get an empty screen. In order to start Rviz you just have to open a new Shell and execute the following command:

In [ ]:
rviz2

Writing a Service Client in ROS2 (C++)

Create a package

First of all, you will have to create a new package, where you will place the code for your Service Client node.

In [ ]:
ros2 pkg create --build-type ament_cmake service_client_cpp --dependencies rclcpp example_interfaces

This will automatically generate your package and all its necessary files and folders. Now, navigate to the ~/ros2_ws/src folder in order to create your file.

Write the code

Inside the ~/ros2_ws/src folder, create a new file named service_client.cpp. Into this file, you will copy the following code:

In [ ]:
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/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<example_interfaces::srv::AddTwoInts>::SharedPtr client =
    node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

  auto request = std::make_shared<example_interfaces::srv::AddTwoInts::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::executor::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;
}

Review the code

Similar to the service server node, the following lines of code create the node and then create the client for that node:

In [ ]:
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");
    rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =
    node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

Next, the request is created. Its structure is defined by the .srv file mentioned earlier.

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

The while loop gives the client 1 second to search for service nodes in the network. If it can’t find any, it will continue waiting.

In [ ]:
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");

If the client is canceled (e.g. by you entering Ctrl+C into the terminal), it will return an error log message stating it was interrupted.

In [ ]:
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
    return 0;

Spins theThen the client sends its request, and the node spins until it receives its response, or fails. node, making the service available.

Compile the package

First of all, let's go the ros2_ws folder in order to be able to compile.

In [ ]:
cd ~/ros2_ws;

Let's now execute the colcon command in order to build our node.

In [ ]:
colcon build --symlink-install

Finally, let's source the workspace.

In [ ]:
source ~/ros2_ws/install/setup.bash

Testing the Service Client

First of all, we need to run our service server (so that it becomes available) with the following command:

In [ ]:
ros2 run service_server_cpp service_server_node

Now let's run our client node with the following command:

In [ ]:
ros2 run service_client_cpp service_client_node 2 3

You will get an output like this:

In [ ]:
[INFO] [rclcpp]: Sum: 5