ros|ROS服务通信(七)C++、Python实现

目录
简介
理论模型
服务通信自定srv
创建srv
编辑配置文件
C++实现
vscode配置
服务端实现

客户端实现
优化
Python实现
服务端实现
客户端实现

简介 服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A
比如如下场景:
机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人... 此时需要拍摄照片并留存。
上述场景中,就使用到了服务通信。

  • 一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果
与上述应用类似的,服务通信更适用于对时时性有要求、具有一定逻辑处理的应用场景。
ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

服务器启动,客户端发送客户数据,服务端进行计算并返回结果
理论模型
ros|ROS服务通信(七)C++、Python实现
文章图片

服务通信自定srv ros|ROS服务通信(七)C++、Python实现
文章图片

创建一个新的功能包
ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

建立依赖
ros|ROS服务通信(七)C++、Python实现
文章图片

创建srv 然后是创建自定义的srv
ros|ROS服务通信(七)C++、Python实现
文章图片

复制以下代码
int32 num1 int32 num2 --- int32 sum

注意:---用来分隔请求与响应

编辑配置文件 ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

构建的时候依赖这个包
ros|ROS服务通信(七)C++、Python实现
文章图片

生成服务
ros|ROS服务通信(七)C++、Python实现
文章图片

生成依赖
ros|ROS服务通信(七)C++、Python实现
文章图片

这是find_package里面依赖的包
然后
ctrl shift b编译一下
ros|ROS服务通信(七)C++、Python实现
文章图片


会生成很多中间文件
ros|ROS服务通信(七)C++、Python实现
文章图片

生成请求端(客户端)文件
生成服务端文件
ros|ROS服务通信(七)C++、Python实现
文章图片

请求内容
【ros|ROS服务通信(七)C++、Python实现】 ros|ROS服务通信(七)C++、Python实现
文章图片

服务内容
python里也生成了对应文件
ros|ROS服务通信(七)C++、Python实现
文章图片

小结:
实际上跟msg很像,只不过多了一个---来区分请求和响应的消息
C++实现 ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

流程
  1. 编写服务端实现;
  2. 编写客户端实现;
  3. 编辑配置文件;
  4. 编译并执行。
vscode配置 ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

复制路径
ros|ROS服务通信(七)C++、Python实现
文章图片

服务端实现 复制以下代码
/* 需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器 服务器需要解析客户端提交的数据,相加后,将结果响应回客户端, 客户端再解析服务器实现: 1.包含头文件 2.初始化 ROS 节点 3.创建 ROS 句柄 4.创建 服务 对象 5.回调函数处理请求并产生响应 6.由于请求有多个,需要调用 ros::spin()*/ #include "ros/ros.h" #include "demo03_server_client/AddInts.h"// bool 返回值由于标志是否处理成功 bool doReq(demo03_server_client::AddInts::Request& req, demo03_server_client::AddInts::Response& resp){ int num1 = req.num1; int num2 = req.num2; ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2); //逻辑处理 if (num1 < 0 || num2 < 0) { ROS_ERROR("提交的数据异常:数据不可以为负数"); return false; }//如果没有异常,那么相加并将结果赋值给 resp resp.sum = num1 + num2; return true; }int main(int argc, char *argv[]) { setlocale(LC_ALL,""); // 2.初始化 ROS 节点 ros::init(argc,argv,"AddInts_Server"); // 3.创建 ROS 句柄 ros::NodeHandle nh; // 4.创建 服务 对象 ros::ServiceServer server = nh.advertiseService("AddInts",doReq); ROS_INFO("服务已经启动...."); //5.回调函数处理请求并产生响应 //6.由于请求有多个,需要调用 ros::spin() ros::spin(); return 0; }

CMakeLists配置
ros|ROS服务通信(七)C++、Python实现
文章图片

ctrl / 取消注释
ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

上面的有些错误看下图改正一下即可ros|ROS服务通信(七)C++、Python实现
文章图片

然后测试一下
ros|ROS服务通信(七)C++、Python实现
文章图片


客户端实现

/* 需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器 服务器需要解析客户端提交的数据,相加后,将结果响应回客户端, 客户端再解析服务器实现: 1.包含头文件 2.初始化 ROS 节点 3.创建 ROS 句柄 4.创建 客户端 对象 5.请求服务,接收响应*/ // 1.包含头文件 #include "ros/ros.h" #include "demo03_server_client/AddInts.h"int main(int argc, char *argv[]) { setlocale(LC_ALL,""); // 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3 if (argc != 3) // if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径) { ROS_ERROR("请提交两个整数"); return 1; }// 2.初始化 ROS 节点 ros::init(argc,argv,"AddInts_Client"); // 3.创建 ROS 句柄 ros::NodeHandle nh; // 4.创建 客户端 对象 ros::ServiceClient client = nh.serviceClient("AddInts"); //等待服务启动成功 //方式1 ros::service::waitForService("AddInts"); //方式2 // client.waitForExistence(); // 5.组织请求数据 demo03_server_client::AddInts ai; ai.request.num1 = atoi(argv[1]); ai.request.num2 = atoi(argv[2]); // 6.发送请求,返回 bool 值,标记是否成功 bool flag = client.call(ai); // 7.处理响应 if (flag) { ROS_INFO("请求正常处理,响应结果:%d",ai.response.sum); } else { ROS_ERROR("请求处理失败...."); return 1; }return 0; }

修改CMakeLists
ros|ROS服务通信(七)C++、Python实现
文章图片

然后
ctrl shift b编译
开始测试
ros|ROS服务通信(七)C++、Python实现
文章图片


输入数字ros|ROS服务通信(七)C++、Python实现
文章图片


优化 ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

ros|ROS服务通信(七)C++、Python实现
文章图片

再启动服务端
ros|ROS服务通信(七)C++、Python实现
文章图片

此时就可以了
Python实现 ros|ROS服务通信(七)C++、Python实现
文章图片

服务端实现新建文件并且复制以下代码
ros|ROS服务通信(七)C++、Python实现
文章图片


#! /usr/bin/env python """ 需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器 服务器需要解析客户端提交的数据,相加后,将结果响应回客户端, 客户端再解析服务器端实现: 1.导包 2.初始化 ROS 节点 3.创建服务对象 4.回调函数处理请求并产生响应 5.spin 函数""" # 1.导包 import rospy from demo03_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse # 回调函数的参数是请求对象,返回值是响应对象 def doReq(req): # 解析提交的数据 sum = req.num1 + req.num2#num1、num2来自于.srv文件 rospy.loginfo("提交的数据:num1 = %d, num2 = %d, sum = %d",req.num1, req.num2, sum)# 创建响应对象,赋值并返回 # resp = AddIntsResponse() # resp.sum = sum resp = AddIntsResponse(sum) return respif __name__ == "__main__": # 2.初始化 ROS 节点 rospy.init_node("addints_server_p") # 3.创建服务对象 server = rospy.Service("AddInts",AddInts,doReq) # 4.回调函数处理请求并产生响应 # 5.spin 函数 rospy.spin()

授予执行权限
ros|ROS服务通信(七)C++、Python实现
文章图片

配置CMakeLists
ros|ROS服务通信(七)C++、Python实现
文章图片

然后进行编译
ros|ROS服务通信(七)C++、Python实现
文章图片

客户端实现 新建文件
ros|ROS服务通信(七)C++、Python实现
文章图片


复制以下代码
#! /usr/bin/env python""" 需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器 服务器需要解析客户端提交的数据,相加后,将结果响应回客户端, 客户端再解析客户端实现: 1.导包 2.初始化 ROS 节点 3.创建请求对象 4.发送请求 5.接收并处理响应优化: 加入数据的动态获取""" #1.导包 import rospy from demo03_server_client.srv import * import sysif __name__ == "__main__":#优化实现 if len(sys.argv) != 3: rospy.logerr("请正确提交参数") sys.exit(1)# 2.初始化 ROS 节点 rospy.init_node("AddInts_Client_p") # 3.创建请求对象 client = rospy.ServiceProxy("AddInts",AddInts) # 请求前,等待服务已经就绪 # 方式1: # rospy.wait_for_service("AddInts") # 方式2 client.wait_for_service() # 4.发送请求,接收并处理响应 # 方式1 # resp = client(3,4) # 方式2 # resp = client(AddIntsRequest(1,5)) # 方式3 req = AddIntsRequest() # req.num1 = 100 # req.num2 = 200 #优化 req.num1 = int(sys.argv[1]) req.num2 = int(sys.argv[2]) resp = client.call(req) rospy.loginfo("响应结果:%d",resp.sum)

授予权限
ros|ROS服务通信(七)C++、Python实现
文章图片

修改CMakeLists
ros|ROS服务通信(七)C++、Python实现
文章图片

然后编译
ros|ROS服务通信(七)C++、Python实现
文章图片

进行终端测试
ros|ROS服务通信(七)C++、Python实现
文章图片


    推荐阅读