Working with ROS services
In this section, we are going to create ROS nodes, which can use the services definition that we defined already. The service nodes we are going to create can send a string message as a request to the server and the server node will send another message as a response.
Navigate to mastering_ros_demo_pkg/src, and find nodes with the names demo_service_server.cpp and demo_service_client.cpp.
The demo_service_server.cpp is the server, and its definition is as follows:
#include "ros/ros.h" #include "mastering_ros_demo_pkg/demo_srv.h" #include <iostream> #include <sstream> using namespace std; bool demo_service_callback(mastering_ros_demo_pkg::demo_srv::Request &req, mastering_ros_demo_pkg::demo_srv::Response &res) { std::stringstream ss; ss << "Received Here"; res.out = ss.str(); ROS_INFO("From Client [%s], Server says [%s]",req.in.c_str(),res.out.c_str()); return true; } int main(int argc, char **argv) { ros::init(argc, argv, "demo_service_server"); ros::NodeHandle n; ros::ServiceServer service = n.advertiseService("demo_service", demo_service_callback); ROS_INFO("Ready to receive from client."); ros::spin(); return 0; }
Let's see an explanation of the code:
#include "ros/ros.h" #include "mastering_ros_demo_pkg/demo_srv.h" #include <iostream> #include <sstream>
Here, we included ros/ros.h, which is a mandatory header for a ROS CPP node. The mastering_ros_demo_pkg/demo_srv.h header is a generated header, which contains our service definition and we can use this in our code. The sstream.h is for getting string streaming classes:
bool demo_service_callback(mastering_ros_demo_pkg::demo_srv::Request &req, mastering_ros_demo_pkg::demo_srv::Response &res) {
This is the server callback function executed when a request is received on the server. The server can receive the request from clients with a message type of mastering_ros_demo_pkg::demo_srv::Request and sends the response in the mastering_ros_demo_pkg::demo_srv::Response type:
std::stringstream ss; ss << "Received Here"; res.out = ss.str();
In this code, the string data "Received Here" is passing to the service Response instance. Here, out is the field name of the response that we have given in demo_srv.srv. This response will go to the service client node:
ros::ServiceServer service = n.advertiseService("demo_service", demo_service_callback);
This creates a service called demo_service and a callback function is executed when a request comes to this service. The callback function is demo_service_callback, which we saw in the preceding section.
Next, we can see how demo_service_client.cpp is working.
Here is the definition of this code:
#include "ros/ros.h" #include <iostream> #include "mastering_ros_demo_pkg/demo_srv.h" #include <iostream> #include <sstream> using namespace std; int main(int argc, char **argv) { ros::init(argc, argv, "demo_service_client"); ros::NodeHandle n; ros::Rate loop_rate(10); ros::ServiceClient client = n.serviceClient<mastering_ros_demo_pkg::demo_srv>("demo_service"); while (ros::ok()) { mastering_ros_demo_pkg::demo_srv srv; std::stringstream ss; ss << "Sending from Here"; srv.request.in = ss.str(); if (client.call(srv)) { ROS_INFO("From Client [%s], Server says [%s]",srv.request.in.c_str(),srv.response.out.c_str()); } else { ROS_ERROR("Failed to call service"); return 1; } ros::spinOnce(); loop_rate.sleep(); } return 0; }
Let's explain the code:
ros::ServiceClient client = n.serviceClient<mastering_ros_demo_pkg::demo_srv>("demo_service");
This line creates a service client that has the message type mastering_ros_demo_pkg::demo_srv and communicates to a ROS service named demo_service:
mastering_ros_demo_pkg::demo_srv srv;
This line will create a new service object instance:
std::stringstream ss; ss << "Sending from Here"; srv.request.in = ss.str();
Fill the request instance with a string called "Sending from Here":
if (client.call(srv)) {
This will send the service call to the server. If it is sent successfully, it will print the response and request; if it failed, it will do nothing:
ROS_INFO("From Client [%s], Server says [%s]",srv.request.in.c_str(),srv.response.out.c_str());
If the response is received, then it will print the request and the response.
After discussing the two nodes, we can discuss how to build these two nodes. The following code is added to CMakeLists.txt to compile and build the two nodes:
add_executable(demo_service_server src/demo_service_server.cpp) add_executable(demo_service_client src/demo_service_client.cpp) add_dependencies(demo_service_server mastering_ros_demo_pkg_generate_messages_cpp) add_dependencies(demo_service_client mastering_ros_demo_pkg_generate_messages_cpp) target_link_libraries(demo_service_server ${catkin_LIBRARIES}) target_link_libraries(demo_service_client ${catkin_LIBRARIES})
We can execute the following commands to build the code:
$ cd ~/catkin_ws $ catkin_make
To start the nodes, first execute roscore and use the following commands:
$ rosrun mastering_ros_demo_pkg demo_service_server $ rosrun mastering_ros_demo_pkg demo_service_client
We can work with rosservice using the rosservice command:
- $ rosservice list: This will list the current ROS services
- $ rosservice type /demo_service: This will print the message type of /demo_service
- $ rosservice info /demo_service: This will print the information of /demo_service