系列文章目录:
ROS开发(ubuntu)笔记·1_嘻·嘻的博客-CSDN博客
ROS通信机制~话题通信(Publisher&Subscriber)·笔记2_嘻·嘻的博客-CSDN博客
服务通信
概念:以请求响应的方式实现不同节点之间数据交互的通信模式。
结合话题通信来理解,两者差别不大,差别在于,话题通信是多对多传递信息的,而服务通信是一对一传递信息
服务通信需要保证顺序,即先启动服务端server,再启动客户端client,否则会报错
不过ROS中有些内置函数(阻塞式)可以解决这个问题,
函数1 client.waitForExistence();
函数2 ros::service::waitForService("服务话题"); 下面代码中有体现。
服务通信自定义srv:(实现传递两个正整数相加的和)
【1】功能包下新建 srv 目录,添加 xxx.srv 文件
【2】编辑配置文件
1.package.xml中添加编译依赖与执行依赖
2.CMakeLists.txt编辑 srv 相关配置
1.
2.
3.
4.
修改,编译后,可C++ 需要调用的中间文件,在devel的include下:
【3】vscode配置(可以代码补齐,防止导包时误抛异常)
文件路径如之前话题通信中的操作相同,都是右击功能包,再点生成集成终端 ,输入pwd即可
【4】
服务端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 )
{
//1.处理请求
int num1 = request.num1;//为什么这里是.不是-> 因为函数的形参不是指针
int num2 = request.num2;
ROS_INFO("收到的请求数据: num1 = %d, num2 = %d",num1,num2);
//逻辑处理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的数据异常:数据不可以为负数");
return false;
}
//2.组织响应
int sum0 = num1 + num2 ;
response.sum = sum0;
ROS_INFO("求和结果: sum = %d",sum0);
return true;
}
int main(int argc ,char *argv[])
{
setlocale(LC_ALL,"");
//2.初始化ROS节点
ros::init(argc,argv,"company");
ros::NodeHandle nh;
//创建一个服务对象
ros::ServiceServer sever = nh.advertiseService("addints",doNums);
ros::spin();
return 0;
}
注意:
当访问地址(指针或迭代器)的成员或数据时,用“->”
当访问直接对象的成员或数据时,用“.”
客户端client
#include"ros/ros.h"
#include"plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,并处理响应的结果
1.包含头文件
2.初始化ROS节点
3.创建节点句柄;
4.创建一个客户端对象;
5.提交请求并响应;
实现参数的动态提交:
1.格式: rosrun xxxxx(功能包名) xxxxx(第一个参数:文件名) 12(第二个参数) 34(第三个参数)
2.节点执行时,需要获取命令中的参数,并组织进 request
问题:
如果先启动客户端,那么会请求异常
需求:
如果先启动客户端,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求
解决:
在ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动
client.waitForExistence();
ros::service::waitForService("服务话题");
*/
int main(int argc , char *argv[])
{ //argc代表传入的参数的总数量
setlocale(LC_ALL,"");
//优化实现,获取命令中参数
if(argc != 3)
{
ROS_INFO("提交的参数个数不对。");
return 1;
}
ros::init(argc,argv,"client");
ros::NodeHandle nh;
//4.创建一个客户端对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("addints");
//5.提交请求并响应
plumbing_server_client::Addints ai;//创建一个Addints对象
//5-1.组织请求
ai.request.num1= atoi(argv[1]);//argv[1]是char类型的,要转化成int类型,使用了atoi
ai.request.num2= atoi(argv[2]);
//5-2.处理响应
//调用函数判断服务器状态
//函数1
//client.waitForExistence();
//函数2
ros::service::waitForService("addints");
// 6.发送请求,返回 bool 值,标记是否成功
bool flag = client.call(ai);
if(flag)
{
ROS_INFO("响应成功!");
//获取结果
ROS_INFO("响应结果 = %d",ai.response.sum);
}
else
{
ROS_INFO("处理失败。。。");
}
return 0;
}
client的代码中实现了动态输入两个整数,用到了argc和argv[],具体解释上面有。
对于atoi函数,实现了数据类型转换的功能,可看源码:
函数1 client.waitForExistence();
函数2 ros::service::waitForService("服务话题"); 的运行效果
【5】 配置 CMakeLists.txt
运行结果: