目录
3.1 添加的代码是在判断响应前:client.waitForExistence();
3.3 添加的代码是在判断响应前:ros::service::waitForService("addInts");参数是话题;
一、客户端的实现
1、创建客户端C++文件
2、编程
服务端实现:提交两个整数,并处理响应结果
流程:
1.包含头文件;
2.初始化 ROS 节点
3.创建节点句柄
4.创建客户对象
5.创建请求和处理响应的发起人:提交请求,处理响应
2.1 包含头文件
//1.包含头文件;
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
2.2 初始化 ROS 节点
setlocale(LC_ALL,"");//防止乱码
//2.初始化 ROS 节点
ros::init(argc,argv,"client");
2.3 创建节点句柄
//3.创建节点句柄
ros::NodeHandle nh;//相当于一个重命名 NodeHandle = nh
2.4 创建客户对象
这部分和我们之前的认知不一样。
//4.创建客户对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("addInts");
可以用这部分的代码和话题通信的发布方以及服务通信的客户端进行一个对比,对比如下(关注一下标黄的部分)
//4.创建订阅者对象
ros::Subscriber sub = nh.subscribe("chatter",10,doMsg);
//4.创建发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
//4.创建服务对象
ros::ServiceServer server = nh.advertiseService("addInts",doNums);
//4.创建客户对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("addInts");
2.5 创建请求和处理响应的发起人:提交请求,处理响应
我本来以为根据需求,那2个整数是由客户端的对象提交上去的,服务端的响应也是客户端对象接收的,但是发现官方给的例程和我的想法不是很一样,他像是让我们这个自定义的消息作为一个对象来提交与接收数据,其实也能解释的清楚,毕竟你的提交的数据与接收的数据我们都是在自定义文件里进行声明的。
//5.提交请求,处理响应
//5.0 创建请求与处理响应的对象
plumbing_server_client::Addints ai;
//5.1 组织请求
ai.request.num1 = 100;
ai.request.num2 = 200;
//5.2 处理响应
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功");
ROS_INFO("响应结果:= %d",ai.response.sum);
}else
{
ROS_INFO("处理失败");
}
部分代码解释
bool flag = client.call(ai);
需要处理的数已经做好了准备,但你得传进去,这个代码的作用就是将数传进去 ,传进去只要服务端没问题就会有响应,这个函数的返回值是bool也就是正确或者错误,有响应就是正确,无响应就是错误,也就是下面的if指令,下面这个if语句,我们需要关注的就是ai.response.sum,他就是响应的结果。
if (flag)
{
ROS_INFO("响应成功");
ROS_INFO("响应结果:= %d",ai.response.sum);
}else
{
ROS_INFO("处理失败");
}
3、配置
配置三个东西,可看前面的内容
4、编译与执行
二、优化
1、优化1
上面的例子只能对固定的数进行运算,而我们的想法是我们输入数字,他运算。
我们提交的数据实在main函数里的,我们要做的其实就是改变参数的值,一个赋值的操作。
这个时候就需要用到main函数里的argc和argv了。argc代表参数的个数,argv是一个数组
1.1 获取命令中的参数
为什么是3,因为节点名是一个参数,我们给的2个值是2个参数
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");//防止乱码
//优化实现:获取命令中的参数
if (argc != 3)
{
ROS_INFO("参数不够");
return 1;
}
//2.初始化 ROS 节点
ros::init(argc,argv,"client");
1.2 赋值
相当于进行一个赋值的操作,将argv数组里的第一个num1,将argv数组里的第二个num2,atoi
这个指令的作用:字符转换为整数。记得你的主函数里的类型char *argv[],是字符型的。而你是数。
//5.提交请求,处理响应
//5.0 创建请求与处理响应的对象
plumbing_server_client::Addints ai;
//5.1 组织请求
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//5.2 处理响应
bool flag = client.call(ai);
2、编译与执行
三、优化
记得我们在服务通信的时候讲到过得先启动服务端再启动客户端,但是在ros中有很多节点,我们不能保证节点的顺序的,为了避免先启动客户端端再启动服务端,会产生抛异常的问题,我们是有解决办法的。
问题:
如果先启动客户端,那么会请求异常
需求:
如果先启动客户端,不要抛异常,而是挂起,等服务器启动,在正常启动
解决:
在ROS中内置了相关函数,这些函数可以让客户端挂起,去等待服务器启动。
3.1 添加的代码是在判断响应前:client.waitForExistence();
//5.1 组织请求
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//调用判断服务器状态的函数
//函数1
client.waitForExistence();
//5.2 处理响应
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功");
ROS_INFO("响应结果:= %d",ai.response.sum);
}else
{
ROS_INFO("处理失败");
}
3.2 结果
3.3 添加的代码是在判断响应前:ros::service::waitForService("addInts");参数是话题;
//5.提交请求,处理响应
//5.0 创建请求与处理响应的对象
plumbing_server_client::Addints ai;
//5.1 组织请求
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//调用判断服务器状态的函数
//函数1
//client.waitForExistence();
//函数2
ros::service::waitForService("addInts");
//5.2 处理响应
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功");
ROS_INFO("响应结果:= %d",ai.response.sum);
}else
{
ROS_INFO("处理失败");
}
return 0;
3.4 结果