Implementing ROS Service Communication in C++
ROS supports three primary communication mechanisms: topics, services, and the parameter server. This article focuses on implementing service-based communication using C++.
Service communication follows a request-response model where a client sends a request to a server and waits for a reply. The key components involved are:
- The service server
- The service client
- The service definition (
.srvfile)
Implementation Steps
- Configure the development environment (e.g., VS Code with ROS support)
- Implement the service server
- Implement the service client
- Udpate build configuration files
- Compile and run the nodes
Service Server Implementation
The server must:
- Include necessary headers
- Initialize a ROS node
- Create a
NodeHandle - Advertise a service with a callback function
- Enter a spin loop to handle incoming requests
#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"
bool handleAddition(plumbing_server_client::AddInts::Request& req,
plumbing_server_client::AddInts::Response& res) {
res.sum = req.num1 + req.num2;
ROS_INFO("Received: %d + %d = %d", req.num1, req.num2, res.sum);
return true;
}
int main(int argc, char** argv) {
ros::init(argc, argv, "addition_server");
ros::NodeHandle nh;
ros::ServiceServer service = nh.advertiseService("add_two_ints", handleAddition);
ROS_INFO("Ready to add integers.");
ros::spin();
return 0;
}
Service Client Implementation
The cliant must:
- Include the same service header
- Initialize a ROS node
- Create a
NodeHandle - Create a service client
- Populate the request and call the service
#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"
int main(int argc, char** argv) {
if (argc != 3) {
ROS_ERROR("Usage: add_two_ints_client X Y");
return 1;
}
ros::init(argc, argv, "addition_client");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("add_two_ints");
plumbing_server_client::AddInts srv;
srv.request.num1 = atoi(argv[1]);
srv.request.num2 = atoi(argv[2]);
if (client.call(srv)) {
ROS_INFO("Result: %d", srv.response.sum);
} else {
ROS_ERROR("Failed to call service");
return 1;
}
return 0;
}
Supporting Late Server Startup
To allow the client to start before the server, use ros::service::waitForService():
ros::service::waitForService("add_two_ints");
This blocks until the service becomes available.
Build Configuration
In CMakeLists.txt, ensure the following targets are properly cnofigured:
add_executable(addition_server src/server.cpp)
add_executable(addition_client src/client.cpp)
add_dependencies(addition_server plumbing_server_client_generate_messages_cpp)
add_dependencies(addition_client plumbing_server_client_generate_messages_cpp)
target_link_libraries(addition_server ${catkin_LIBRARIES})
target_link_libraries(addition_client ${catkin_LIBRARIES})
Execution Order
While it's typical to start the server first, using waitForService() enables flexible startup ordering. After compiling with catkin_make (or via VS Code’s build task), run:
# Terminal 1
roscore
# Terminal 2
rosrun plumbing_server_client addition_server
# Terminal 3
rosrun plumbing_server_client addition_client 5 7