ROS服务通信

ROS有两种通信方式:话题通信、服务通信。

话题通信是基于发布订阅模式的,即:一个节点发布消息,另一个节点订阅该消息。发布方和订阅方不相互影响,也就是发布方只负责发消息,订阅方只负责订阅消息。

服务通信是基于请求响应模式的,是一种应答机制。也即:一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。

用一个案例来学习ros服务通信:

一、服务通信理论模型

二、服务通信自定义srv

step1:在功能包中定义一个srv文件夹,在这个文件夹中定义xxx.srv文件。

step2:在xxx.srv中规定数据类型。---上部分是请求部分,下部分为响应数据。

step3:为xxx.srv添加编译规则

打开package.xml文件,添加以下两行代码:

打开CMakeList.txt文件, 在find_package中添加:

找到下方示例代码,将刚刚定义的xxx.srv添加进去,作用是让在srv文件夹中生成服务:

我们定义的xxx.srv是基于std_msgs依赖的,所以需要添加依赖项。

在catkin_package中添加message_runtime。(find_package是指当前的功能包所依赖的包,而catkin_package是指依赖包所依赖的其他功能包)

step4:编译,会生成中间文件。在工作空间中的devel文件夹中,会生成以下三种头文件。这三个文件是写c++代码使用的。

在lib中会有以下文件,这是编写python代码时使用的。

三、服务通信自定义srv调用c++实现

配置c_cpp_properies.json 文件。这样可以使用代码补齐等功能。这一步可以没有!!!

3.1 服务端C++实现

step1:在src目录下创建xxx.cpp文件

step2:添加头文件,第二个头文件是指自定义的srv文件,格式是:功能包名/xxx.h

step3:编写代码

/*需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析服务器实现: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;}//如果没有异常,那么相加并将结果赋值给 respresp.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;
}

回调函数必须返回一个bool值,形参指的是自定义srv文件中的请求和响应部分的数据。格式为:功能包名::xxx::Requset,他里面包含的有num1,num2两个数据。

step4:添加编译规则。打开CMakeList.txt文件:

step5:编译

step6:测试。可以启动服务端节点,查看设置的打印语句是否正常输出。

step7:另外起一个端口,运行rosservice,这个命令可以直接调用ros中的service服务。后面加上call和话题名称,再输入发送的数据,服务端就能响应并返回结果。

3.2 客户端C++实现

step1:创建客户端节点

step2:编写代码

/*需求: 客户端节点需要提交两个整数到服务器,并对返回结果进行响应服务器实现: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,"");// 2.初始化 ROS 节点ros::init(argc,argv,"AddInts_Client");// 3.创建 ROS 句柄ros::NodeHandle nh;// 4.创建 客户端 对象ros::ServiceClient client = nh.serviceClient<demo03_server_client::AddInts>("AddInts");// 5.组织请求数据demo03_server_client::AddInts ai;ai.request.num1 = 100;ai.request.num2 = 200;// 6.发送请求,返回 bool 值,标记是否成功bool flag = client.call(ai);// 7.处理响应if (flag){ROS_INFO("请求正常处理,响应结果:%d",ai.response.sum);}else{ROS_ERROR("请求处理失败....");return 1;}return 0;
}

step3: 添加编译规则。打开CMakeList.txt文件:

step4:编译+运行。

3.3 客户端动态提交数据

上述代码中将num1和num2写死了。利用main函数的argc、argv来实现客户端动态提交数据。

需求:

       1、格式 :rosrun 功能包名  节点名  num1   num2

        2、节点执行时,需要获取命令中的参数,并组织进request

step1:优化客户端代码

#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"int main(int argc, char *argv[])
{setlocale(LC_ALL,"");// 调用时动态传值。通过argv传参,参数有两个,因此argc=3,argv的第二三个数值为num1和num2if (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<demo03_server_client::AddInts>("AddInts");//等待服务启动成功//方式1,参数1:被等待的服务话题名称ros::service::waitForService("AddInts");//方式2// client.waitForExistence();// 5.组织请求数据,argv[1]里的数值是string类型,需要将其转换化成intdemo03_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;
}

step2:编译+运行

注意】:

四、服务通信自定义srv调用python实现

配置settings.json  文件。这样可以使用代码补齐等功能。这一步可以没有!!!

4.1 服务端python实现

step1:创建节点

step2:编写代码

#! /usr/bin/env python
"""服务器端实现:1.导包2.初始化 ROS 节点3.创建服务对象4.回调函数处理请求并产生响应5.spin 函数"""
# 1.导包
import rospy
from demo03_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
# 或者写成以下形式
# from demo03_server_client.srv import *
# 回调函数的参数是请求对象,返回值是响应对象
def doReq(req):# 解析提交的数据sum = req.num1 + req.num2rospy.loginfo("提交的数据:num1 = %d, num2 = %d, sum = %d",req.num1, req.num2, sum)# 创建响应对象,赋值并返回# resp = AddIntsResponse()# resp.sum = sumresp = 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()

step3:添加可执行权限

step4:添加编译规则

step5:编译 +运行(同上)

4.2 客户端python实现

step1:新建节点

step2:编写代码

#! /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")# 方式2client.wait_for_service()# 4.发送请求,接收并处理响应# 方式1# resp = client(3,4)# 方式2# resp = client(AddIntsRequest(1,5))# 方式3req = 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)

step3:添加可执行权限

step4:添加编译规则

step5:编译+运行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/597669.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软推出GPT-4 Turbo优先使用权:Copilot for Microsoft 365商业用户享受无限制对话及增强图像生成能力

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

夯实智慧新能源数据底座,TiDB Serverless 在 Sandisolar+ 的应用实践

本文介绍了 SandiSolar通过 TiDB Serverless 构建智慧新能源数据底座的思路与实践。作为一家致力于为全球提供清洁电力解决方案的新能源企业&#xff0c;SandiSolar面临着处理大量实时数据的挑战。为了应对这一问题&#xff0c;SandiSolar选择了 TiDB Serverless 作为他们的数据…

烧坏两块单片机,不知道原因?

没有看你的原理图&#xff0c;以下是造成烧毁芯片的几个环节&#xff1a; 1. 最大的可能性是你的单片机电机控制输出与电机驱动电路没有隔离。 我的经验&#xff0c;使用STM32控制电机&#xff0c;无论是直流电机脉宽调制&#xff0c;还是步进电机控制&#xff0c;控制电路与…

企业级开源路由系统VyOS-构建和使用

介绍 VyOS是一个基于Linux的企业级路由器操作系统&#xff0c;被许多公司和个人用来驱动物理网络设备&#xff0c;如路由器和防火墙。它有一个统一的命令行界面来管理其所有的网络相关功能&#xff08;和Juniper Junos操作很像&#xff09;。VyOS使用Debian GNU/Linux作为其基…

(学习日记)2024.04.01:UCOSIII第二十九节:消息队列实验(待续)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

JavaScript代码小挑战

题目如下&#xff1a; 朱莉娅和凯特正在做一项关于狗的研究。于是&#xff0c;她们分别询问了 5 位狗主人他们的狗的年龄&#xff0c;并将数据存储到一个数组中&#xff08;每人一个数组&#xff09;。目前&#xff0c;她们只想知道一只狗是成年狗还是小狗。如果狗的年龄至少为…

动态规划(Dynamic Programming)详解

动态规划&#xff08;Dynamic Programming&#xff09;是一种重要的算法设计方法&#xff0c;适用于解决具有最优子结构和重叠子问题性质的问题。通过将问题分解为子问题&#xff0c;并利用子问题的解来构建原问题的解&#xff0c;动态规划在解决各种优化问题时展现了强大的效果…

工程监测振弦采集仪在振动监测中的应用与数据处理技术

工程监测振弦采集仪在振动监测中的应用与数据处理技术 振弦采集仪是一种用于振动监测和分析的仪器设备。它采用振弦传感器作为振动信号的采集元件&#xff0c;可以实时测量结构物或设备的振动状态&#xff0c;并将采集到的振动数据进行处理和分析&#xff0c;从而判断结构的工…

智慧数字乡村解决方案大全:标准规范顶层设计指南、供应商整体解决方案及售前PPT、数字乡村标准白皮书等全套460份,一次性打包下载

关键词&#xff1a;数字乡村解决方案&#xff0c;数字乡村标准白皮书&#xff0c;数字乡村建设成功案例&#xff0c;数字乡村发展行动计划&#xff0c;数字乡村建设方案&#xff0c;数字乡村云平台&#xff0c;数字乡村建设指南&#xff0c;智慧乡村建设解决方案&#xff0c;智…

线程安全--深入探究线程等待机制和死锁问题

꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转…

DasViewer中,像下图山坡是选择拟合平面还是自定义平面?还是其他的基准面?

问题如图 如若山坡是计算斜面的土方&#xff0c;可以选择用拟合平面模式&#xff0c;该模式适用斜坡。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方…

Windows XPSDrvSmpl.sln打印驱动项目编译问题汇总

官方参考配置看这里官方独家。如果不想看文档&#xff0c;可以看视频打印编译&#xff0c;安装测试视频不过上面只讲了一般流程&#xff0c;但是随着系统升级&#xff0c;架构变化会出现各种各样的问题。我汇总了一些我遇到的问题&#xff0c;以及解决办法。 error 1297: (NTar…