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.