In this tutorial, we’ll create a ROS 2 service using C++.
A ROS 2 service is a way for different parts of a robot system to communicate with each other by sending a request and receiving a response, similar to making a phone call and waiting for an answer. This form of communication is different from the publish-subscribe communication method, where one part of the system continuously broadcasts information, and any other parts that are interested can listen in, like a radio station broadcasting to many listeners.
With a service, the communication is direct and specific, where the caller sends a request to a particular service provider and waits for a response before proceeding. In contrast, publish-subscribe allows for more flexible and asynchronous communication between multiple parts of the system without waiting for specific responses.
Services are useful when you need a quick but specific task done, and you need to wait for it to finish before moving on. You generally do not want to use a service if you need to make continuous calls for information. If you need to make continuous calls for information, topics are better suited.
Real-World Applications
Here are some real-world examples of how ROS 2 service and client nodes can be used in robotic applications:
- Starting a Robot Vacuum Cleaner
- Service: A service node called start_cleaning is created. When this service is called, it initiates the cleaning process of the robot vacuum cleaner.
- Client: A client node calls the start_cleaning service to begin the cleaning process. It waits for a response from the service confirming that the cleaning process has started.
- Localization on a Map
- Service: A service node determines the current position and orientation of a robot on a map based on a provided map and LIDAR scans.
- Client: A client node requests the current position and orientation of a robot on a map.
- Gripper Control:
- Service: A service node controls the robotic arm’s gripper to open or close.
- Client: A client node sends requests to the gripper service to open the gripper to release an object into a bin.
Prerequisites
- You have completed this tutorial: How to Create an Action – ROS 2 Jazzy
All my code for this project is located here on GitHub.
Creating a Cleaning Service for a Robotic Vacuum Cleaner
Let’s imagine we have a fictitious robotic vacuum cleaner:
- If this service is called with a value of True, the robot will start cleaning.
- If this service is called with a value of False, the robot will stop cleaning.
The name of the service is /set_cleaning_state.
We will call the service using the following syntax:
ros2 service call <service_name> <service_type> <arguments>
To start cleaning, we will run this command:
ros2 service call /set_cleaning_state yahboom_rosmaster_msgs/srv/SetCleaningState "{desired_cleaning_state: true}"
To stop cleaning, we will run this command:
ros2 service call /set_cleaning_state yahboom_rosmaster_msgs/srv/SetCleaningState "{desired_cleaning_state: false}"
Create a Custom Service Definition
Create the srv folder inside the yahboom_rosmaster_msgs package:
cd ~/ros2_ws/src/yahboom_rosmaster/yahboom_rosmaster_msgs
mkdir srv
Create the service definition file SetCleaningState.srv inside the srv folder:
cd srv
Open the SetCleaningState.srv file and add the following content:
# Request
bool desired_cleaning_state # Request: true to start cleaning, false to stop
---
bool success # Response: whether the request was successful
string message # Response: information about the result
Update CMakeLists.txt
Add these lines to yahboom_rosmaster_msgs/CMakeLists.txt:
# Find required packages
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
# Generate interfaces
rosidl_generate_interfaces(${PROJECT_NAME}
"action/TimedRotation.action"
"srv/SetCleaningState.srv"
)
ament_package()
Build the Service Messages
cd ~/ros2_ws
colcon build
source ~/.bashrc
Confirm the service creation:
ros2 interface show yahboom_rosmaster_msgs/srv/SetCleaningState
Write the Service Node
Let’s write the service node.
Create the set_cleaning_state_service.cpp file inside the src directory of yahboom_rosmaster_system_tests:
cd ~/ros2_ws/src/yahboom_rosmaster/yahboom_rosmaster_system_tests/src
Add the following content:
/**
* @file set_cleaning_state_service.cpp
* @brief ROS 2 node that implements a service to set the cleaning state of a robot
*
* This program implements a ROS 2 node that provides a service to set the cleaning state
* of a robot. It demonstrates the use of service servers in ROS 2.
*
* Subscription Topics:
* None
*
* Publishing Topics:
* None
*
* Services:
* /set_cleaning_state (yahboom_rosmaster_msgs/srv/SetCleaningState):
* Sets the cleaning state of the robot
*
* @author Addison Sears-Collins
* @date November 26, 2024
*/
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "yahboom_rosmaster_msgs/srv/set_cleaning_state.hpp"
/**
* @class SetCleaningStateService
* @brief A ROS 2 node that provides a service to set the cleaning state of a robot
*/
class SetCleaningStateService : public rclcpp::Node
{
public:
/**
* @brief Constructor for the SetCleaningStateService class.
*
* Initializes the node with the name "set_cleaning_state_service"
* and creates the service server for the "/set_cleaning_state" service.
*/
SetCleaningStateService()
: Node("set_cleaning_state_service")
{
service_ = this->create_service<yahboom_rosmaster_msgs::srv::SetCleaningState>(
"/set_cleaning_state",
std::bind(&SetCleaningStateService::set_cleaning_state_callback, this,
std::placeholders::_1, std::placeholders::_2));
RCLCPP_INFO(this->get_logger(), "Cleaning Service Server is ready.");
}
private:
/**
* @brief Callback function for the set_cleaning_state service.
*
* This function is called when a request is received for the "/set_cleaning_state" service.
* It checks the desired cleaning state in the request and sets the appropriate response.
*
* @param request The request shared pointer for the set_cleaning_state service.
* @param response The response shared pointer for the set_cleaning_state service.
*/
void set_cleaning_state_callback(
const std::shared_ptr<yahboom_rosmaster_msgs::srv::SetCleaningState::Request> request,
std::shared_ptr<yahboom_rosmaster_msgs::srv::SetCleaningState::Response> response)
{
if (request->desired_cleaning_state) {
RCLCPP_INFO(this->get_logger(), "Starting cleaning...");
response->success = true;
response->message = "Robot started cleaning";
} else {
RCLCPP_INFO(this->get_logger(), "Stopping cleaning...");
response->success = true;
response->message = "Robot stopped cleaning";
}
RCLCPP_INFO(this->get_logger(), "Response sent: %s", response->message.c_str());
}
/// Service server for setting cleaning state
rclcpp::Service<yahboom_rosmaster_msgs::srv::SetCleaningState>::SharedPtr service_;
};
/**
* @brief Main function to run the SetCleaningStateService node.
*
* @param argc Number of command-line arguments.
* @param argv Array of command-line arguments.
* @return int Exit status of the program.
*/
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<SetCleaningStateService>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
Update CMakeLists.txt
Edit the CMakeLists.txt file:
add_executable(set_cleaning_state_service
src/set_cleaning_state_service.cpp
)
ament_target_dependencies(set_cleaning_state_service
rclcpp
yahboom_rosmaster_msgs
)
install(TARGETS
set_cleaning_state_service
DESTINATION lib/${PROJECT_NAME}
)
Build the Package
cd ~/ros2_ws
colcon build
source ~/.bashrc
Run the Service Node
Open a new terminal window, and type:
ros2 run yahboom_rosmaster_system_tests set_cleaning_state_service
ros2 service list -t
The ros2 service list -t command lists all the available services along with their corresponding service types. It shows that the /set_cleaning_state service uses the yahboom_rosmaster_msgs/srv/SetCleaningState service type.
Open a new terminal window, and call the service to start cleaning:
ros2 service call /set_cleaning_state yahboom_rosmaster_msgs/srv/SetCleaningState "{desired_cleaning_state: true}"
Now call the service to stop cleaning:
ros2 service call /set_cleaning_state yahboom_rosmaster_msgs/srv/SetCleaningState "{desired_cleaning_state: false}"
You see how we called the service via the terminal. You can also create an actual program that is the service client that calls the service just like we did in the terminal.
This tutorial showed you a simple service that:
- Listens for requests
- Processes the requests
- Returns appropriate responses
All services are structured this way, so you can use this as a template for any service you create.
That’s it! Keep building!