ROS服务通信实操C++
- 步骤流程
- VScode 配置
- 服务端
- 客户端
- 编辑配置文件
- 编译并执行
- 优化
- 实现参数的动态提交
- 优化先启动客户端后启动服务端
ROS服务通信的理论查阅:ROS服务通信流程理论
ROS服务通信的自定义srv数据的准备可以查阅:ROS服务通信自定义srv
在模型实现中,ROS master
不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 服务端
- 客户端
- 数据
步骤流程
实现ROS服务通信的步骤分为以下5步:
- 1.
VScode
配置 - 2.编写服务端实现;
- 3.编写客户端实现;
- 4.编辑配置文件;
- 5.编译并执行。
VScode 配置
需要像之前自定义 msg
实现一样配置 c_cpp_properies.json
文件,如果以前已经配置且没有变更工作空间,可以忽略,如果需要配置,配置方式与之前相同:
服务端
-
基本流程
- 1.包含头文件;
- 2.初始化ROS节点;
- 3.创建6节点句柄;
- 4.创建一个服务对象;
- 5.处理请求并产生响应;
- 6.spin()
-
代码实现
#include "ros/ros.h" #include "plumbing_server_client/AddInts.h"/*服务端实现:解析客户端提交的数据,并运算再进行响应1.包含头文件;2.初始化ROS节点;3.创建6节点句柄;4.创建一个服务对象;5.处理请求并产生响应;6.spin() */bool doMsg(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 sum = num1 + num2;response.sum = sum;ROS_INFO("求和结果:sum = %d", sum);return true;}int main(int argc, char *argv[]) {setlocale(LC_ALL, "");// 2.初始化ROS节点;ros::init(argc, argv, "heiShui");// 3.创建6节点句柄;ros::NodeHandle nh;// 4.创建一个服务对象;ros::ServiceServer server = nh.advertiseService("addInts", doMsg);ROS_INFO("服务器端启动");// 5.处理请求并产生响应;// 6.spin()ros::spin(); //处理回调函数return 0; }
-
验证是否有问题
-
编辑配置文件
修改 plumbing_server_client 功能包下的CMakeLists.txt,找到add_executable、add_dependencies和target_link_libraries,修改成如图所示:
-
按快捷键
ctrl + shift + B
编译 -
开启一个Terminal,运行 roscore 命令;再开启一个新的Terminal,运行
source ./devel/setup.bash rosrun plumbing_server_client demo_server_c
;再开启一个Terminal,运行rosservice call addInts "num1: 1 num2: 2"
(num1 num2是按Tab键自动补齐,然后修改数字),查看服务请求数据并响应
-
客户端
-
基本流程
- 1.包含头文件;
- 2.初始化ROS节点;
- 3.创建节点句柄;
- 4.创建一个客户端对象;
- 5.提交请求并处理响应
-
代码实现
#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, "daZhuang");// 3.创建节点句柄;ros::NodeHandle nh;// 4.创建一个客户端对象;ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");// 5.提交请求并处理响应plumbing_server_client::AddInts add; // 创建一个 AddInts 对象// 5-1 组织请求add.request.num1 = 100;add.request.num2 = 300;// 5-2 处理请求bool flag = client.call(add); // 处理请求,传入AddInts对象, 返回响应结果,响应成功flag为true,失败为flaseif(flag){ROS_INFO("响应成功");ROS_INFO("响应结果:sum = %d", add.response.sum);}else{ROS_INFO("响应失败...");}return 0; }
编辑配置文件
修改 plumbing_server_client 功能包下的CMakeLists.txt,找到add_executable、add_dependencies和target_link_libraries,修改成如图所示:
编译并执行
-
编译代码
按快捷键 ctrl + shift + B 编译 -
执行代码
-
1.启动 roscore;
开启一个Terminal,启动 roscore
-
2.启动服务节点;
开启一个新的Terminalcd topic_ws/ source ./devel/setup.bash rosrun plumbing_server_client demo_server_c
-
3.启动客户节点。
cd topic_ws/ source ./devel/setup.bash rosrun plumbing_server_client demo_client_c
-
正常情况下的服务通信必须得先启动服务端,然后启动客户端
优化
实现参数的动态提交
前面响应的数据都是写死的,将数据实现动态的输入,主要是修改客户端代码,服务端不用修改。
格式:rosrun xxxxx xxxxxx num1 num2
代码实现
#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, ""); // 处理中文乱码// 优化实现,获取命令中的参数if (argc != 3){ROS_INFO("传入参数的个数不对!!!");return 1;}// 2.初始化ROS节点;ros::init(argc, argv, "daZhuang");// 3.创建节点句柄;ros::NodeHandle nh;// 4.创建一个客户端对象;ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");// 5.提交请求并处理响应plumbing_server_client::AddInts add; // 创建一个 AddInts 对象// 5-1 组织请求// add.request.num1 = 100;// add.request.num2 = 300;add.request.num1 = atoi(argv[1]); // atoi()将字符串转成 int 类型add.request.num2 = atoi(argv[2]);// 5-2 处理请求bool flag = client.call(add); // 处理请求,传入AddInts对象, 返回响应结果,响应成功flag为true,失败为flaseif(flag){ROS_INFO("响应成功");ROS_INFO("响应结果:sum = %d", add.response.sum);}else{ROS_INFO("响应失败...");}return 0;
}
编译后,启动 roscore
和服务端,然后启动客户端,同时输入请求的参数 num1
和 num2
,如图所示:
优化先启动客户端后启动服务端
正常情况下是先启动服务端,然后在启动客户端。如果有先启动客户端,挂起等待服务端启动后再请求的需求。就需要调用ROS的内置等待函数 client.waitForExistence()
和 ros::service::waitForService("服务话题")
。
代码实现
#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, ""); // 处理中文乱码// 优化实现,获取命令中的参数if (argc != 3){ROS_INFO("传入参数的个数不对!!!");return 1;}// 2.初始化ROS节点;ros::init(argc, argv, "daZhuang");// 3.创建节点句柄;ros::NodeHandle nh;// 4.创建一个客户端对象;ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");// 5.提交请求并处理响应plumbing_server_client::AddInts add; // 创建一个 AddInts 对象// 5-1 组织请求// add.request.num1 = 100;// add.request.num2 = 300;add.request.num1 = atoi(argv[1]); // atoi()将字符串转成 int 类型add.request.num2 = atoi(argv[2]);// 调用判断服务器状态的函数// client.waitForExistence(); // 创建的客户端自己有的等待函数ros::service::waitForService("addInts"); // ros内置的等待函数,需要传入等待的服务话题// 5-2 处理请求bool flag = client.call(add); // 处理请求,传入AddInts对象, 返回响应结果,响应成功flag为true,失败为flaseif(flag){ROS_INFO("响应成功");ROS_INFO("响应结果:sum = %d", add.response.sum);}else{ROS_INFO("响应失败...");}return 0;
}
启动客户端后,没有启动服务端,结果如图所示:
再启动服务端后,请求进行响应,结果如图所示: