【ROS】Service Communication

Service communication is also an extremely common communication mode in ROS. Service communication is based on the request response mode, which is a response mechanism. That is: a node A sends a request to another node B, and B receives the processing request and generates a response result back to A. For example, the following scenario:

During the robot patrol, the control system analyzes the sensor data and finds suspicious objects or people... At this point, you need to take photos and keep them.

In the above scenario, service communication is used.

  • A node needs to send a photo request to the camera node, the camera node processes the request, and returns the processing result

Similar to the above applications, service communication is more suitable for application scenarios that require timeliness and have certain logic processing.

concept

The communication mode of data interaction between different nodes is realized in a request-response manner.

effect

It is used for occasional data transmission scenarios that require timeliness and certain logic processing requirements.

1 Service communication theoretical model

Service communication is simpler than topic communication. The theoretical model is shown in the figure below. Three roles are involved in the model:

  • ROS master (manager)

  • Server (server)

  • Client

ROS Master is responsible for keeping the information registered by Server and Client, and matching Server and Client with the same topic, helping Server and Client to establish a connection. After the connection is established, Client sends request information and Server returns response information.

The whole process is realized by the following steps:

 

(0). Server registration

After the server is started, it will register its own information in the ROS Master through RPC, which contains the name of the provided service. ROS Master will add the registration information of the node to the registry.

(1) Client registration

After the Client is started, it will also register its own information in the ROS Master through RPC, including the name of the service that needs to be requested. ROS Master will add the registration information of the node to the registry.

(2). ROS Master realizes information matching

ROS Master will match the Server and Client according to the information in the registry, and send the Server's TCP address information to the Client via RPC .

(3) Client sends a request

The Client uses TCP to establish a network connection with the Server according to the response information in step 2, and sends the requested data.

(4) Server sends a response

The Server receives and parses the requested data, and generates a response result and returns it to the Client.

note:

1. When the client request is processed, it is necessary to ensure that the server has been started;

2. There can be multiple servers and clients.

2 Service communication custom srv

demand:

In service communication, the client submits two integers to the server, and the server sums and responds with the result to the client. Please create a data carrier for the communication between the server and the client.

srv = request + response

Process:

The available data types in the srv file are the same as the msg file, and the process of defining srv is similar to that of custom msg:

  1. Create srv file in a fixed format

  2. Edit configuration file

  3. Compile and generate intermediate files

(1) Define the srv file

In service communication, data is divided into two parts, request and response. The request and response are ---divided in the srv file . The specific implementation is as follows:

Create a new srv directory under the function package, add the xxx.srv file, and the content:

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

 

(2) Edit the configuration file

Add compilation dependency and execution dependency in package.xml

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend 以前对应的是 run_depend 现在非法
	添加构建依赖 和 执行依赖
  -->
Copy

CMakeLists.txt edit srv related configuration

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

add_service_files(
  FILES
  Addints.srv
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

Note: The official website does not configure message_runtime in catkin_package, it can be configured after testing

(3) Compile

View the compiled intermediate file:

Intermediate files that C++ needs to call (.../workspace/devel/include/package name/xxx.h)

Intermediate files that Python needs to call (.../workspace/devel/lib/python2.7/dist-packages/package name/srv)

When the related srv is subsequently called, it is called from these intermediate files.

3   Service communication custom srv call A (C++)

demand:

To write service communication, the client submits two integers to the server, and the server sums and responds with the result to the client.

analysis:

In the model implementation, the ROS master does not need to be implemented, and the connection establishment has been encapsulated. There are three key points that need to be paid attention to:

  1. Server

  2. Client

  3. data

Process:

  1. Write server-side implementation;

  2. Write the client implementation;

  3. Edit the configuration file;

  4. Compile and execute.

(0) vscode configuration

The c_cpp_properies.json file needs to be configured like the previous custom msg implementation. If it has been configured before and the workspace has not been changed, you can ignore it. If you need to configure it, the configuration method is the same as before:

{
  "configurations": [
    {
      "browse": {
        "databaseFilename": "",
        "limitSymbolsToIncludedHeaders": true
      },
      "includePath": [
        "/home/winter/catkin_wp/devel/include/**",
        "/opt/ros/melodic/include/**",
        "/home/winter/catkin_wp/src/learning_parameter/include/**",
        "/home/winter/catkin_wp/src/learning_service/include/**",
        "/home/winter/catkin_wp/src/learning_tf/include/**",
        "/home/winter/catkin_wp/src/learning_topic/include/**",
        "/usr/include/**",
        "/home/winter/demo03_ws/devel/include/**"
      ],
      "name": "ROS",
      "intelliSenseMode": "gcc-x64",
      "compilerPath": "/usr/bin/gcc",
      "cStandard": "gnu17",
      "cppStandard":"c++17"
    }
  ],
  "version": 4
}

(1) Server

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"

/*
    服务端实现:解析客户端实现的数据,并运算再产生响应
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建服务对象
        5 处理请求并产生响应
        6 spin()函数
*/


//回调函数
bool doNums(plumbing_server_client::Addints::Request& request,
                            plumbing_server_client::Addints::Response& response)
{
    //处理请求
    int num1 = request.num1;
    int num2 = request.num2;

    ROS_INFO("服务器收到的请求数据是:num1 = %d,num2 = %d",num1,num2);

    //响应请求
    int num = num1 + num2;
    response.sum = num;
    ROS_INFO("求和结果是:sum = %d",num);
    return true;
}


int main(int argc, char  *argv[])
{
    //设置中文
    setlocale(LC_ALL,"");

    //2 初始化ros结点
    ros::init(argc,argv,"Server");     //名称保证唯一

    //3 创建结点句柄
    ros::NodeHandle nh;

    //4 创建服务对象
    ros::ServiceServer server = nh.advertiseService("Addints",doNums);

    ROS_INFO("服务端启动了");
    
    //5 回调函数

    //6 spin函数
    ros::spin();

    return 0;
}

Configuration file

add_dependencies(demo01_server ${PROJECT_NAME}_generate_messages_cpp)

test

roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server


source ./devel/setup.bash 
rosservice call Addints "num1: 20
num2: 10" 
sum: 30

(2) Client

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"


/*
    客户端:提交两个整数,处理响应的结果
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建客户对象
        5 提交请求并处理响应
 
*/


int main(int argc, char  *argv[])
{
        setlocale(LC_ALL,"");
        //  2 初始化ros结点
        ros::init(argc,argv,"Client");

        // 3 创建结点句柄
        ros::NodeHandle nh;

        // 4 创建客户对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");

        // 5 提交请求并处理响应

        plumbing_server_client::Addints ai;
        //组织请求
        ai.request.num1 = 100;
        ai.request.num2 = 200;

        //处理响应  客户端访问服务器
        bool flag = client.call(ai);
        if(flag==true)
        {
            ROS_INFO("响应成功");
            //获取结果
             ROS_INFO("响应结果是:%d",ai.response.sum);
        }
        else
        {
             ROS_INFO("处理失败");
        }

    return 0;
}

Configuration file

roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server

rosrun plumbing_server_client demo02_client

(3) Client optimization

Provide data dynamically

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"


/*
    客户端:提交两个整数,处理响应的结果
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建客户对象
        5 提交请求并处理响应

 实现参数的动态提交:
        1 格式 rosrun 包名 结点名 参数1 参数2
        2 节点执行时要获取命令中的参数,并组织进request   使用argc argv

*/


int main(int argc, char  *argv[])
{

           setlocale(LC_ALL,"");
        //优化实现,获取命令中的参数
        /*
            iargc  = 3
             argv[0] 是文件名
             argv[1] 是参数1
             argv[2] 是参数2
        */
        if(argc !=3)
        {
            ROS_INFO("提交的参数个数不对");
            return 1;
        }
       
     
        //  2 初始化ros结点
        ros::init(argc,argv,"Client");

        // 3 创建结点句柄
        ros::NodeHandle nh;

        // 4 创建客户对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");

        // 5 提交请求并处理响应

        plumbing_server_client::Addints ai;
        //组织请求
        ai.request.num1 =atoi( argv[1]);
        ai.request.num2 = atoi(argv[2]);

        //处理响应  客户端访问服务器
        bool flag = client.call(ai);
        if(flag==true)
        {
            ROS_INFO("响应成功");
            //获取结果
             ROS_INFO("响应结果是:%d",ai.response.sum);
        }
        else
        {
             ROS_INFO("处理失败");
        }


    return 0;
}

If you start the client first, then start the server

optimization:

Add before the client sends the request:

client.waitForExistence();

或:ros::service::waitForService("AddInts");

This is a blocking function and will only continue execution after the service is started successfully

Guess you like

Origin blog.csdn.net/Zhouzi_heng/article/details/114602454