ROS C++ Practice - Service
ROS Service and Client Nodes Using C++
Step 1: Set Up Your ROS Workspace - Before you create nodes, make sure you have a ROS workspace set up. If you don’t have one, you can create it using the following commands:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/ catkin_make
Step 2: Create a ROS Package
Inside the src directory of your ROS workspace, create a new ROS package. Replace ros_tutorials_service with your desired package name:
cd ~/catkin_ws/src catkin_create_pkg ros_tutorials_service roscpp std_msgs
Step 3: Create the Service Node
Navigate to your package directory:
cd ~/catkin_ws/src/ros_tutorials_service/src
Create a source file for the server node. For example, service_server.cpp:
#include "ros/ros.h" // ROS Default Header File #include "ros_tutorials_service/SrvTutorial.h"// SrvTutorial Service File Header (Automatically created after build) // The following process is executed when a service request is received. // The service request is declared as 'req', and the service response is declared as 'res'. bool calculation(ros_tutorials_service::SrvTutorial::Request &req, ros_tutorials_service::SrvTutorial::Response &res) { // The service name is 'ros_tutorial_srv', and it invokes the 'calculation' function upon receiving a service request. res.result = req.a + req.b; // Displays the values of 'a' and 'b' used in the service request, and // the 'result' value corresponding to the service response. ROS_INFO("Received request: a=%ld, b=%ld", (long int)req.a, (long int)req.b); ROS_INFO("Sending back response: %ld", (long int)res.result); return true; } int main(int argc, char **argv) // Node Main Function { ros::init(argc, argv, "service_server"); // Initializes Node Name ros::NodeHandle nh; // Node handle declaration // Declares a service server 'ros_tutorials_service_server' // using the 'SrvTutorial' service file in the 'ros_tutorials_service' package. // The service name is 'ros_tutorial_srv', and it invokes the 'calculation' function // upon receiving a service request. ros::ServiceServer ros_tutorials_service_server = nh.advertiseService("ros_tutorial_srv", calculation); ROS_INFO("Ready service server!"); ros::spin(); // Waits for service requests return 0; }
Step 4: Create the Client
Create another source file for the client node. For example, service_client.cpp:
#include "ros/ros.h" // ROS Default Header File #include "ros_tutorials_service/SrvTutorial.h"// SrvTutorial Service File Header (Automatically created after build) #include <cstdlib> // Library for using the "atoll" function int main(int argc, char **argv) // Node Main Function { ros::init(argc, argv, "service_client"); // Initializes Node Name if (argc != 3) // Input value error handling { ROS_INFO("Usage: rosrun ros_tutorials_service service_client arg0 arg1"); ROS_INFO("Arguments: arg0 (double number), arg1 (double number)"); return 1; } ros::NodeHandle nh; // Node handle declaration for communication with ROS system // Declares service client 'ros_tutorials_service_client' // using the 'SrvTutorial' service file in the 'ros_tutorials_service' package. // The service name is 'ros_tutorial_srv' ros::ServiceClient ros_tutorials_service_client = nh.serviceClient<ros_tutorials_service::SrvTutorial>("ros_tutorial_srv"); // Declares the 'srv' service that uses the 'SrvTutorial' service file ros_tutorials_service::SrvTutorial srv; // Parameters entered when the node is executed as a service request value are stored at 'a' and 'b' srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); // Request the service. If the request is accepted, display the response value if (ros_tutorials_service_client.call(srv)) { ROS_INFO("Sent service request, srv.Request.a and b: %ld, %ld", (long int)srv.request.a, (long int)srv.request.b); ROS_INFO("Received service response, srv.Response.result: %ld", (long int)srv.response.result); } else { ROS_ERROR("Failed to call service ros_tutorial_srv"); return 1; } return 0; }
Step 5: Update CMakeLists.txt
Make the CMakeLists.txt file in your package directory (ros_tutorials_service) and add the following lines to configure the build for both the service and client nodes:
cmake_minimum_required(VERSION 2.8.3) project(ros_tutorials_service) find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp) add_service_files(FILES SrvTutorial.srv) generate_messages(DEPENDENCIES std_msgs) catkin_package( LIBRARIES ros_tutorials_service CATKIN_DEPENDS std_msgs roscpp ) include_directories(${catkin_INCLUDE_DIRS}) add_executable(service_server src/service_server.cpp) add_dependencies(service_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(service_server ${catkin_LIBRARIES}) add_executable(service_client src/service_client.cpp) add_dependencies(service_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(service_client ${catkin_LIBRARIES})
Step 6: Create the launch File
Create a union_svc.launch file for nodes:
mkdir -p ~/catkin_ws/src/launch cd ~/catkin_ws/src/launch touch union_svc.launch
<launch> <node pkg="ros_tutorials_service" type="service_server" name="service_server"/> <node pkg="ros_tutorials_service" type="service_client" name="service_client"/> </launch>
Step 7: Build and Run
Build your package using catkin_make:
cd ~/catkin_ws catkin_make
Source the setup script:
source devel/setup.bash
Now you can run the service and client nodes in separate terminal windows:
Terminal 1 (for the service node):
rosrun ros_tutorials_service service_server
Terminal 2 (for the client node):
rosrun ros_tutorials_service service_client
Or, you can run the service and client at once using a launch file.
roslaunch ros_tutorials_service union_svc.launch
You should see the client printing the messages published by the service server.