文章

ROS学习记录(四):服务通信的基本操作

ROS学习记录(四):服务通信的基本操作

服务通信的基本操作

需求 :

编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。

分析 :

(一)话题通信的基本操作基本类似

vscode配置

(三)使用自定义msg中操作步骤一样,如果工作空间没有变化,则不需要再次进行更改。

srv文件

srv 文件内的可用数据类型与 msg 文件一致,且定义 srv 实现流程与自定义 msg 实现流程类似

1.1定义srv文件

服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用 分割,上方为 server 使用的数据类型,下方为 client 使用的数据类型

#server使用的自定义数据类型
int32 num1
int32 num2
---
#client使用的自定义数据类型
int32 sum

1.2编辑配置文件

需要编写 package.xml ,与 CMakeLists.txt

过程参考自定义msg,不一样的过程为 add_service_filesmsg 变为 srv

2.1服务端

话题通信的操作基本类似,但在服务通信的过程中,因为有请求响应的存在,所以服务的提供者需要回调函数,而客户端只需要调用响应值。

服务端代码编写最需要注意的是回调函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include"ros/ros.h"
#include"test_service/Addint.h"
/*
    1.包含头文件
        #include"test_service/Addint.h"
    2.初始化 ROS 节点
    3.创建 ROS 句柄
    4.创建 服务 对象
    5.回调函数处理请求并产生响应
    6.由于请求有多个,需要调用 ros::spin()
*/
bool test_service_sever_callback(
    test_service::Addint::Request &request,//requset是请求,response是响应
    test_service::Addint::Response &response){
    // 1.处理请求
    int num1 = request.num1;
    int num2 = request.num2;
    ROS_INFO("请求数据num1:%d,num2:%d",num1,num2);
    // 2.给出响应
    int sum = num1+num2;
    response.sum = sum;
    ROS_INFO("求和结果:%d",sum);
    return true;
}
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"test_service_sever");
    // 3.创建 ROS 句柄
    ros::NodeHandle nh;
    // 4.创建 服务 对象
    ros::ServiceServer server = nh.advertiseService("Addints",test_service_sever_callback);
    ROS_INFO("服务器端启动");
    // 6.由于请求有多个,需要调用 ros::spin()
    ros::spin();
    return 0;
}

2.2客户端

注意此处写代码时应该加入下面两个函数中的一种

作用为在服务端没用启动时,客户端对于未响应的数据是等待而不是直接进行。

1
2
client.waitForExistence();
ros::service::waitForService("Addints");

另外,客户端如果需要输入数据,应对输入参数进行判断。 argc 存储的是输入参数的个数 argv 存储的输入参数的具体数据(字符串类型), argv[0] 存储的是程序名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include"ros/ros.h"
#include"test_service/Addint.h"
/*
        1.包含头文件
            #include"test_service/Addint.h"
        2.初始化 ROS 节点
        3.创建 ROS 句柄
        4.创建 client 对象
        5.请求服务,接收响应
    实现参数的动态提交:
        1.格式:rosrun xxxxxx xxxxxx a b
        2.节点执行时,需要获取命令中的参数,并组织进 request
    问题:
        如果现启动客户端,则会请求异常
    需求:
        需要先让客户端等待服务器启动,服务器启动后再运行
    解决:

*/

int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    //获得参数的个数
    if(argc != 3)
    {
        ROS_INFO("参数错误");
        return 1;
    }
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"test_service_client");
    // 3.创建 ROS 句柄
    ros::NodeHandle nh;
    // 4.创建 client 对象
    ros::ServiceClient client = nh.serviceClient<test_service::Addint>("Addints");
    // 5.请求服务,接收响应
    // 5-1定义数据类型
    test_service::Addint data;
    // 5-2设置请求数据
    data.request.num1 = atoi(argv[1]);
    data.request.num2 = atoi(argv[2]);
    //等待服务器启动
    // client.waitForExistence();
    ros::service::waitForService("Addints");//需要传递话题参数
    // 5-3发送请求
    bool flag = client.call(data);
    // 5-4接收数据并判断
    if (flag)
    {
        ROS_INFO("响应成功");
        ROS_INFO("响应值:%d",data.response.sum);
    }else
    {
        ROS_INFO("响应失败");
    }
    return 0;
}

2.3 配置 CMakeLists.txt

注:add_dependencies 与mgs编写形式不同

1
2
3
4
5
6
7
8
9
10
11
12
add_executable(test_server src/test_server.cpp)
add_executable(test_client src/test_client.cpp)

add_dependencies(test_server ${PROJECT_NAME}_gencpp)
add_dependencies(test_client ${PROJECT_NAME}_gencpp)

target_link_libraries(test_server
  ${catkin_LIBRARIES}
)
target_link_libraries(test_client
  ${catkin_LIBRARIES}
)
本文由作者按照 CC BY 4.0 进行授权

热门标签