ROS学习第九节——服务通信

news/2024/5/19 22:38:36/

1.基本介绍

服务通信较之于话题通信更简单些,理论模型如下图所示,该模型中涉及到三个角色:

  • ROS master(管理者)
  • Server(服务端)
  • Client(客户端)

ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。

整个流程由以下步骤实现:

0.Server注册

Server 启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含提供的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。

1.Client注册

Client 启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要请求的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。

2.ROS Master实现信息匹配

ROS Master 会根据注册表中的信息匹配Server和 Client,并通过 RPC 向 Client 发送 Server 的 TCP 地址信息。

3.Client发送请求

Client 根据步骤2 响应的信息,使用 TCP 与 Server 建立网络连接,并发送请求数据。

4.Server发送响应

Server 接收、解析请求的数据,并产生响应结果返回给 Client。

注意:

1.客户端请求被处理时,需要保证服务器已经启动;

2.服务端和客户端都可以存在多个。

2.具体实现

2.1创建srv文件

创建服务通信功能包

注意是右键工作空间下面的src文件夹Create Kitcan Package ,并添加依赖

新建srv文件夹,添加编程文件

服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用---分割,具体实现如下:

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

 编辑配置文件

package.xml中添加编译依赖与执行依赖

  <build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend><!-- exce_depend 以前对应的是 run_depend 现在非法-->

CMakeLists.txt编辑 srv 相关配置

find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
# 需要加入 message_generation,必须有 std_msgs

add_service_files(FILESAddInts.srv
)

generate_messages(DEPENDENCIESstd_msgs
)

 修改完成以后编译

2.2服务通信自定义srv(C++)

2.2.1服务端

 VScode配置

找到该文件夹,在终端中打开,pwd,查看路径,复制路径到图片所示文件中

在功能包下的src文件夹里面新建编程文件

编写以下代码

/*需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析服务器实现:1.包含头文件2.初始化 ROS 节点3.创建 ROS 句柄4.创建 服务 对象5.回调函数处理请求并产生响应6.由于请求有多个,需要调用 ros::spin()*/
#include "ros/ros.h"
#include "server_client/AddInts.h"// bool 返回值由于标志是否处理成功
bool doReq(server_client::AddInts::Request& req,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;
}

修改配置文件

add_executable(demo01_server src/demo01_server.cpp)

add_dependencies(demo01_server  ${PROJECT_NAME}_gencpp)
target_link_libraries(demo01_server${catkin_LIBRARIES}
)

 运行代码查看效果

 使用ros服务器工具进行查验代码

rosservice call AddInts "num1: 5
num2: 10"

 2.2.2客户端

新建编程文件如图

 输入以下代码

/*需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析服务器实现:1.包含头文件2.初始化 ROS 节点3.创建 ROS 句柄4.创建 客户端 对象5.请求服务,接收响应*/
// 1.包含头文件
#include "ros/ros.h"
#include "server_client/AddInts.h"int main(int argc, char *argv[])
{setlocale(LC_ALL,"");// 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3if (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<server_client::AddInts>("AddInts");//等待服务启动成功//方式1ros::service::waitForService("AddInts");//方式2// client.waitForExistence();// 5.组织请求数据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;
}

修改配置文件

add_executable(demo01_server  src/demo01_server.cpp)
add_executable(demo02_client  src/demo02_client.cpp)add_dependencies(demo01_server  ${PROJECT_NAME}_gencpp)
add_dependencies(demo02_client  ${PROJECT_NAME}_gencpp)target_link_libraries(demo01_server${catkin_LIBRARIES}
)
target_link_libraries(demo02_client${catkin_LIBRARIES}
)

 运行查看结果

https://mp.csdn.net/mp_download/manage/download/UpDetailed

2.3服务通信自定义srv(Python)

路径配置

右键文件夹复制路径

/home/xiaoming/demo0/devel/lib/python2.7/dist-packages

 

2.3.1服务端创建

创建代码文件夹及代码文件

#! /usr/bin/env python
# -*- coding: UTF-8 -*-
"""需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析服务器端实现:1.导包2.初始化 ROS 节点3.创建服务对象4.回调函数处理请求并产生响应5.spin 函数"""
# 1.导包
import rospy
from server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
# 回调函数的参数是请求对象,返回值是响应对象
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)rospy.loginfo("服务器已经启动了!")# 4.回调函数处理请求并产生响应# 5.spin 函数rospy.spin()

添加可执行文件

 编辑可执行文件

catkin_install_python(PROGRAMSscripts/demo01_server_p.pyDESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

 运行显示效果

 2.3.2客户端创建

创建编程文件并输入代码

#! /usr/bin/env python
# -*- coding: UTF-8 -*-
"""需求: 编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析客户端实现:1.导包2.初始化 ROS 节点3.创建请求对象4.发送请求5.接收并处理响应优化:加入数据的动态获取"""
#1.导包
import rospy
from server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
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)

 添加可执行文件

 修改可执行文件

catkin_install_python(PROGRAMSscripts/demo01_server_p.pyscripts/demo02_client_p.pyDESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

运行查看效果

 https://mp.csdn.net/mp_download/manage/download/UpDetailed


http://www.ppmy.cn/news/47324.html

相关文章

Scrum of Scrums规模化敏捷开发管理全流程

Scrum of Scrums是轻量化的规模化敏捷管理模式&#xff0c;Leangoo领歌可以完美支持Scrum of Scrums多团队敏捷管理。 Scrum of Scrums的场景 Scrum of Scrums是指多个敏捷团队共同开发一个大型产品、项目或解决方案。Leangoo提供了多团队场景下的产品路线图规划、需求管理、…

函数式编程、Stream流操作复习

Lambda表达式 使用 当一个接口中只含有一个抽象方法时&#xff0c;这个接口就叫做函数式接口 一般使用FunctionalInterface注解来标识这个接口是一个函数式接口。 但不论有没有标识这个注解&#xff0c;只要接口中只有一个抽象方法&#xff0c;那么这个接口就是函数式接口 …

(PCB系列七)PCB差分信号布线及其要点

1、差分信号的定义 差分传输是一种信号传输的技术&#xff0c;区别于传统的一根信号线一根地线的做法&#xff0c;差分传输在这两根线上都传输信号&#xff0c;这两个信号的振幅相同&#xff0c;相位相反。在这两根线上的传输的信号就是差分信号。信号接收端比较这两个电压的差…

ubuntu虚拟机下搭建zookeeper集群,安装jdk压缩包,搭建Hadoop集群与spark集群的搭建【上篇】

系列文章目录 在vmbox里面安装Ubuntu16.04并且配置jdk以及Hadoop配置的教程【附带操作步骤】 虚拟机vmware下安装Ubuntu16.04修改屏幕尺寸与更新源&#xff0c;以及对应的安装vim和vim常见的操作 Hadoop与主机连接以及20版本的Hadoop配置网络的问题_hadoop连不上网 Hadoop升…

deepstream开发学习笔记: 追踪越界

main.cpp 文件解析 1. 创建元素前的准备 GStreamer是一个开源的流媒体框架&#xff0c;用于构建音频和视频流应用程序。它提供了一组库和工具&#xff0c;可以通过它们将多个组件&#xff08;element&#xff09;组合在一起以构建流媒体应用程序。以下是对几个常见组件的简要解…

校验规则引擎

目录 一 架构设计图 二 表设计及数据展示 三 顶层接口 四 压测结果 五 其他规则引擎比较 适用场景&#xff1a;校验场景以及使用该思想进行可视化配置化开发&#xff08;可大幅提高开发效率&#xff0c;长期维护简单&#xff09; 例如&#xff1a;履约系统下单中的校验&…

数字IC笔试面试常考问题及答案汇总(内含各岗位大厂题目)

经历了无数的笔试面试之后&#xff0c;不知道大家有没有发现数字IC的笔试面试还是有很多共通之处和规律可循的。所以一定要掌握笔试面试常考的问题。 数字IC笔试面试常考问题及答案汇总&#xff08;文末可领全部哦~&#xff09; 验证方向&#xff08;部分题目&#xff09; Q1…

ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放

场景 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放&#xff1a; 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放_霸道流氓气质的博客-CSDN博客 上面讲…

scipy与MATLAB中四元数的不同之处

摘要 除了参数顺序不同scipy:(x,y,z,w)&#xff0c;matlab(a,b,c,d)以外&#xff0c;scipy.spatial.transform.Rotation中的四元数是Shuster’s convention(JPL convention)&#xff0c;与MATLAB中的四元数定义完全不同&#xff01;&#xff01;&#xff01; scipy scipy.sp…

轻松掌握FFmpeg编程:从架构到实践

轻松掌握FFmpeg编程&#xff1a;从架构到实践 (Master FFmpeg Programming with Ease: From Architecture to Practice 引言 (Introduction)FFmpeg简介与应用场景 (Brief Introduction and Application Scenarios of FFmpeg)为什么选择FFmpeg进行音视频处理 (Why Choose FFmpeg…

java基础——迭代器,数据结构,List,Set ,TreeSet集合,Collections工具类

迭代器&#xff0c;数据结构,List,Set ,TreeSet集合,Collections工具类 第一章 Iterator迭代器 1.1 Iterator接口 在程序开发中&#xff0c;经常需要遍历集合中的所有元素。针对这种需求&#xff0c;JDK专门提供了一个接口java.util.Iterator。 想要遍历Collection集合&…

【unity实战】随机地下城生成2——绘制地图Tilemap的使用及一些技巧的使用(含源码)

绘制房间 修改素材配置 切割图片 绘制瓦片地图 先新建我们的调色盘,保存好位置 拖入我们刚才切片好的素材 在房间预设体创建我们的瓦片地图 绘制地图的小技巧 点选移动适合的瓦片移动到位置上绘画 框选复制已经绘制好的地图,快捷键i

rust的并发以及kv server网络处理和网络安全部分

理解并发和并行 Golang 的创始人之一&#xff0c;对此有很精辟很直观的解释&#xff1a;并发是一种同时处理很多事情的能力&#xff0c;并行是一种同时执行很多事情的手段。 我们把要做的事情放在多个线程中&#xff0c;或者多个异步任务中处理&#xff0c;这是并发的能力。在多…

APP渗透—MobSF安全评估、frida、r0capture抓包

APP渗透—MobSF安全评估、frida、r0capture抓包 1. 前言2. 补充AppInfoScanner相关内容3. MobSF安全评估3.1. 下载MobSF3.2. 安装Docker3.2.1. 查看Linux内核版本3.2.2. 更新yum包3.2.3. 安装Docker软件包3.2.4. 设置yum源3.2.5. 查看Docker版本3.2.6. 安装Docker3.2.7. 设置Do…

[Python基础] 序列(列表/元组)和字典的操作详解

文章目录 序列概念序列列表列表的创建和下标访问创建下标 列表的切片操作列表的遍历列表的插入操作列表的查找和删除列表的拼接 元组元组的相关操作 字典概念字典的创建查找字典中的key&#xff0c;value字典的增删改字典的遍历 序列概念 包含若干个元素, 元素有序排列, 可以通…

Chapter10-NameServer 源码解析

10.1 模块人口代码的功能 10.1.1 入口函数 首先看一下 NameServer 的源码目录&#xff08;见图 10-1 &#xff09; 。NamesrvStartup 是模块的启动入 口&#xff0c; NamesrvController 是用来协块各个调模功能的代码。 我们从启动代码开始分析&#xff0c;找到 NamesrvStartup…

JavaScript【一】JavaScript变量与数据类型

文章目录 &#x1f31f;前言&#x1f31f;变量&#x1f31f; 变量是什么&#xff1f;&#x1f31f; 变量提升&#x1f31f; 声明变量&#x1f31f; JavaScript有三种声明方式&#x1f31f; 命名规范&#x1f31f; 注意 &#x1f31f;数据类型以及运算&#x1f31f; 检测变量数据…

MySQL运维28-MySQL复制

文章目录 1、MySQL复制模式2、MySQL复制的兼容性3、与MySQL复制相关的两种日志3.1、中继日志3.2、MySQL复制的状态日志 4、MySQL主从复制的实施示例4.1、主从库安装4.2、主库配置4.3、从库的配置4.4、启动复制和确认成功 5、对MySQL复制的监控6、MySQL监控的运维6.1、在从库修改…

第二部分——长难句——第一章——并列句

conjunction(and,but,if,when(while)) 想把两个句子&#xff08;多件事&#xff09;连在一块&#xff0c;就必须加上连词。 所以长难句到底是啥&#xff1f; 所以长难句&#xff08;直白表达&#xff0c;并不是语法表述&#xff09;就是几个简单句多家上几个连接词就齐活了&am…

python删除csv文件中的某几列或行

1. 读取数据 用pandas中的read_csv()函数读取出csv文件中的数据: import pandas as pddf pd.read_csv("comments.csv") df.head(2)用drop函数进行文件中数据的删除行或者删除列操作。 2. 删除列操作 方法一&#xff1a;假设我们要删除的列的名称为 ‘观众ID’,‘…