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_files 从 msg 变为 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 进行授权