目录

一、概念

二、何时使用服务

三、话题通信与服务通信的区别

四、案例

4.1 C++实现

4.1.1 服务端

4.1.2 客户端

4.1.3 测试执行

4.2 Python实现

4.2.1 服务端

4.2.2 客户端

4.2.3 客户端优化——动态传参

4.2.4 客户端优化——等待服务端启动后再发起请求


一、概念

服务通信涉及两个角色:

  • 服务服务器 (Service Server)

    • 提供特定功能或数据的节点。

    • 它会“打广告”说:“我能提供XX服务!”(例如,“我能计算两个整数的和”)。

    • 它平时处于待命状态,一旦接收到请求,就执行相应的任务,并返回一个结果。

  • 服务客户端 (Service Client)

    • 需要某个特定功能或数据的节点。

    • 它会向服务器发送一个具体的请求(例如,“请帮我计算 3 和 5 的和”)。

    • 发送请求后,它会暂停自己的工作,一直等待,直到收到服务器的响应。

 

二、何时使用服务

当你需要立即得到一个确切的答复触发一个必须完成的远程操作时,就应该使用服务。

典型应用场景:

  • 查询数据: “机器人,你现在的坐标是多少?”

  • 触发动作: “机械臂,移动到指定位置。” / “相机,拍一张照片。”

  • 执行计算: “路径规划器,帮我计算一条从A到B的最优路径。”

  • 更改状态: “机器人,切换到自动驾驶模式。”

 

三、话题通信与服务通信的区别

服务通信 (Service)话题通信 (Topic)
通信模型请求/响应发布/订阅
数据流向双向单向 
同步性同步 - 客户端会阻塞等待异步 - 发布后立即返回
连接关系一对一 一对多/多对多
数据流类型离散的、事务性的连续的数据流
主要目的执行远程调用、获取确切结果持续广播状态、传感器数据

四、案例

客户端发送两个整数,服务端计算它们的和并返回

4.1 C++实现

4.1.1 服务端

进入到工作空间的src目录下,输入如下指令来创建一个名为“plumbing_server_client”的功能包

catkin_create_pkg plumbing_server_client roscpp rospy std_msgs

在功能包中创建一个srv目录

在srv目录中创建一个.srv文件,这里命名为“AddTwoInts.srv”

“AddTwoInts.srv”内容如下:

int64 a
int64 b
---
int64 sum

打开功能包中的“package.xml”,添加如下两行内容

<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>

打开功能包下的“CMakeLists.txt”,添加如下内容:

添加“message_generation”

取消注释并添加“AddTwoInts.srv”

取消注释

取消注释

Ctrl+Shift+B编译一下

在功能包的src目录中新建一个.cpp文件,这里命名为“server.cpp” 

在“server.cpp” 中添加如下代码

#include "ros/ros.h"
#include "plumbing_server_client/AddTwoInts.h"// 服务处理函数。当收到请求时,ROS会调用这个函数。
// 函数的返回值是 bool 类型,如果成功处理返回 true,否则返回 false。
// 参数是请求对象(req)和响应对象(res)的引用。
bool add(plumbing_server_client::AddTwoInts::Request  &req,plumbing_server_client::AddTwoInts::Response &res)
{// 从请求对象中取出数据res.sum = req.a + req.b;// ROS_INFO 用于在终端打印日志信息,类似于C++的coutROS_INFO("请求: x=%ld, y=%ld", (long int)req.a, (long int)req.b);ROS_INFO("发送响应: sum=%ld", (long int)res.sum);return true; // 表示服务成功执行
}int main(int argc, char **argv)
{setlocale(LC_ALL,"");// 1. 初始化ROS节点ros::init(argc, argv, "add_two_ints_server");// 2. 创建节点句柄ros::NodeHandle n;// 3. 创建一个名为 "add_two_ints" 的服务//    它会调用 add 函数来处理请求ros::ServiceServer service = n.advertiseService("add_two_ints", add);ROS_INFO("服务已就绪,等待客户端请求...");// 4. 进入循环,等待回调函数的触发ros::spin();return 0;
}

4.1.2 客户端

在功能包的src目录下添加一个.cpp文件,这里命名为“client.cpp”

在“client.cpp”中添加如下代码

#include "ros/ros.h"
#include "plumbing_server_client/AddTwoInts.h"
#include <cstdlib> // 用于 atoll 函数int main(int argc, char **argv)
{setlocale(LC_ALL,"");// 初始化ROS节点ros::init(argc, argv, "add_two_ints_client");// 检查命令行参数是否正确if (argc != 3){ROS_INFO("用法: add_two_ints_client X Y");return 1;}// 创建节点句柄ros::NodeHandle n;// 创建一个客户端,连接到名为 "add_two_ints" 的服务// serviceClient 会一直尝试连接,直到成功ros::ServiceClient client = n.serviceClient<plumbing_server_client::AddTwoInts>("add_two_ints");// 创建一个服务对象 srvplumbing_server_client::AddTwoInts srv;// 将命令行参数转换为 long long (int64) 并填充到请求中srv.request.a = atoll(argv[1]);srv.request.b = atoll(argv[2]);// 调用服务// client.call() 是一个阻塞操作。它会发送请求并等待,直到收到响应。// 如果服务调用成功,call() 返回 true,响应数据会填充到 srv.response 中。// 如果失败,call() 返回 false。if (client.call(srv)){ROS_INFO("响应 Sum: %ld", (long int)srv.response.sum);}else{ROS_ERROR("调用服务失败");return 1;}return 0;
}

打开功能包中“CMakeLists.txt”文件,添加如下内容

编译一下

4.1.3 测试执行

开启三个终端,分别输入如下指令来依次启动ros核心、启动服务端、启动客户端

roscore  //启动ros核心rosrun plumbing_server_client server  //启动服务端rosrun plumbing_server_client client 15 20  //启动客户端

可以看到服务端成功计算并返回计算结果。

但是如果我们先开启客户端再开启服务端就会导致客户端抛出异常:

为了避免这个问题,我们可以给客户端添加如下一行代码,这样就可以客户端就可以等服务端启动后再请求

client.waitForExistence();
// 或
ros::service::waitForService("add_two_ints_server");  //add_two_ints_server为服务器节点名称

可以看到客户端在服务端未启动时一直等待 

等服务端启动后再执行请求

4.2 Python实现

4.2.1 服务端

打开“settings.json”,如果没有如下配置则需要补充

{"editor.tabSize": 4,"cmake.sourceDirectory": "/home/chaochao/demo02_ws/src/helloworld","files.associations": {"sstream": "cpp"},"python.autoComplete.extraPaths": ["/opt/ros/noetic/lib/python3/dist-packages","/home/chaochao/demo02_ws/devel/lib/python3/dist-packages"]
}

新建一个文件夹“scripts”

添加一个Python文件,这里命名为“server_py.py”

在“server_py.py”添加如下代码:

#! /usr/bin/env python
# -*- coding: utf-8 -*-import rospy
from plumbing_server_client.srv import AddTwoInts, AddTwoIntsRequest, AddTwoIntsResponse
# from plumbing_server_client.srv import *"""
服务端:解析客户端请求,产生响应1. 导包2. 初始化 ROS 节点;3. 创建服务端对象;4. 处理逻辑(回调函数)5. spin()
"""# 4. 处理逻辑
def call_doInt(request):num1 = request.anum2 = request.bsum = num1 + num2response = AddTwoIntsResponse()  # 将结果封装进responseresponse.sum = sumrospy.loginfo("服务器收到:num1=%d, num2=%d, 响应:sum=%d", num1, num2, sum)return responseif __name__ == "__main__":# 2. 初始化 ROS 节点;rospy.init_node("server")# 3. 创建服务端对象;server = rospy.Service("AddTwoInts", AddTwoInts, call_doInt)  # "AddTwoInts":服务的名称; AddTwoInts:服务的类型; call_doInt:回调函数;# 5. spin()rospy.spin()

cd到“scripts”目录下,输入如下指令来给python文件添加可执行权限

chmod +x *.py

打开“CMakeLists.txt”,添加如下内容 

编译一下,然后输入如下指令测试

source ./devel/setup.bash
rosrun plumbing_server_client server_py.pysource ./devel/setup.bash
rosservice call AddTwoInts "a: 12 b: 14"

4.2.2 客户端

在“scripts”目录下新建一个python文件,这里命名为“client_py.py”

在“client_py.py”中添加如下代码

#! /usr/bin/env python
# -*- coding: utf-8 -*-import rospy
from plumbing_server_client.srv import AddTwoInts, AddTwoIntsRequest, AddTwoIntsResponse"""
客户端:组织并提交请求,处理服务端响应。1. 导包;2. 初始化 ROS 节点;3. 创建客户端对象;4. 组织请求数据,并发送请求;5. 处理响应。
"""if __name__ == "__main__":# 2. 初始化 ROS 节点;rospy.init_node("client")# 3. 创建客户端对象;client = rospy.ServiceProxy("AddTwoInts", AddTwoInts)# 4. 组织请求数据,并发送请求;response = client.call(12, 24)# 5. 处理响应。rospy.loginfo("响应的数据:%d", response.sum)

给新建的python文件添加可执行权限 

打开“CMakeLists.txt”,添加如下内容

编译后通过如下命令启动服务端和客户端,可以看到客户端正常接收到了服务端响应的数据

rosrun plumbing_server_client server_py.py  //启动服务端rosrun plumbing_server_client client_py.py  //启动客户端

4.2.3 客户端优化——动态传参

如果我们希望客户端的参数是通过命令动态传入的,可以对客户端代码做如下修改:

#! /usr/bin/env python
# -*- coding: utf-8 -*-import rospy, sys
from plumbing_server_client.srv import AddTwoInts, AddTwoIntsRequest, AddTwoIntsResponse"""
客户端:组织并提交请求,处理服务端响应。1. 导包;2. 初始化 ROS 节点;3. 创建客户端对象;4. 组织请求数据,并发送请求;5. 处理响应。
"""if __name__ == "__main__":# 判断参数个数if len(sys.argv) != 3:rospy.logerr("传入参数个数有误...")sys.exit(1)# 2. 初始化 ROS 节点;rospy.init_node("client")# 3. 创建客户端对象;client = rospy.ServiceProxy("AddTwoInts", AddTwoInts)# 4. 组织请求数据,并发送请求;num1 = int(sys.argv[1])num2 = int(sys.argv[2])response = client.call(num1, num2)# 5. 处理响应。rospy.loginfo("响应的数据:%d", response.sum)

此时执行效果如下

4.2.4 客户端优化——等待服务端启动后再发起请求

如果在服务端未启动的情况下启动客户端,会抛出异常

为了解决这个问题,我们可以添加如下一行代码,这样客户端就可以等待服务端启动后再发起请求

client.wait_for_service()# 或rospy.wait_for_service("AddTwoInts")

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

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

相关文章

45.sentinel自定义异常

上文提到Blocked by Sentinel(flow limits) 限流异常,这样返给用户就不太友好,所以需要自定义异常。 默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口: BlockException有很多子类: pac…

f4硬件配置spi

f4型号是stm32f407zgt6用spi来进行MOSI&#xff0c;主机发送从机接收时钟频率设置为1MHzMOSI为PC3&#xff0c;SCK为PB10&#xff0c;CS设置为output->PB12时钟配置如下&#xff1a;波特率计算公式为&#xff1a;128M/(4*Prescaler) 要让波特率为1M&#xff0c;10…

Redis的持久化-RDB

1.持久化一提到持久化&#xff0c;我们就会第一时间联想到M有SQL的事务&#xff0c;MySQL事务有四个比较核心的特征&#xff1a;原子性&#xff08;把多个操作打包成一个整体&#xff09;&#xff0c;一致性&#xff08;事务执行之前和之后&#xff0c;数据都不能离谱&#xff…

前端内存泄漏

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

部署zabbox企业级分布式监控

目录 一、监控系统的基础认知 2.1 监控的定义与核心价值 2.2 监控的五大类型与五层逻辑架构 &#xff08;1&#xff09;五大监控类型 &#xff08;2&#xff09;五层逻辑架构 2.3 主流开源监控产品对比 二、Zabbix 系统深度解析 3.1 Zabbix 的定位与发展历程 3.2 Zabb…

时空数据可视化新范式:基于Three.js的生产全流程时间轴回溯技术解析

内容摘要在现代工业生产中&#xff0c;如何高效地管理和分析生产全流程数据是一个关键问题。传统的数据可视化方法往往只能展示静态的数据快照&#xff0c;难以捕捉和回溯生产过程中的动态变化。然而&#xff0c;基于 Three.js 的时间轴回溯技术为这一难题提供了一种全新的解决…

宝塔面板Nginx报错: IP+端口可以直接从访问,反向代理之后就504了 Gateway Time-out

原因表示代理服务器在等待上游服务器&#xff08;即后端服务&#xff09;响应时超时 &#xff1a;<html><head><title>504 Gateway Time-out</title> </head><body><center><h1>504 Gateway Time-out</h1></center&g…

【ComfyUI学习笔记01】下载安装 | 运行第一个工作流 | 学习思路

【ComfyUI学习笔记01】下载安装 | 运行第一个工作流 | 学习思路前言下载安装ComfyUI的下载和安装ComfyUI Manager 的下载和安装运行第一个工作流初识节点 (Nodes) 工作流案例1 Image Generation绘制流程图&#xff0c;确定关键节点放置关键节点&#xff0c;确定连接顺序补充中间…

numpy库的基础知识

一.numpy是什么 &#xff1f;Numpy 是 Python 中专门用于高性能数值计算的库&#xff0c;其核心是一个功能强大的 n 维数组对象&#xff08;ndarray&#xff09;&#xff0c;可以用来存储和操作大规模的数字矩阵或张量数据。numpy库的作用&#xff1a;核心功能&#xff1a;实现…

在UniApp中防止页面上下拖动的方法

1、pages.json中在某个页面设置禁用弹性滚动的页面 {"path": "pages/yourPage/yourPage","style": {"app-plus": {"bounce": "none"}} } 2、 pages.json中在所有页面设置禁用弹性滚动的页面 {"globalStyl…

LinkedList的模拟实现(双向链表Java)

一&#xff1a;结构LinkedList的底层是双向链表结构(链表后面介绍)&#xff0c;由于链表没有将元素存储在连续的空间中&#xff0c;元素存储在单独的节点中&#xff0c;然后通过引用将节点连接起来了&#xff0c;因此在在任意位置插入或者删除元素时&#xff0c;不需要搬移元素…

Shopify 知识点

&#x1f4dc; 一、Liquid模板语言&#xff08;核心基础&#xff09;语法结构 • 输出变量&#xff1a;{{ product.title }} 动态显示商品标题。 • 逻辑控制&#xff1a;{% if product.available %}…{% endif %} 条件渲染。 • 循环遍历&#xff1a;{% for item in collectio…

Web LLM 安全剖析:以间接提示注入为核心的攻击案例与防御体系

文章目录1 间接提示注入2 训练数据中毒为什么会出现这种漏洞&#xff1f;3 泄露敏感训练数据攻击者如何通过提示注入获取敏感数据&#xff1f;为什么会出现这种泄露&#xff1f;4 漏洞案例间接提示注入利用 LLM 中的不安全输出处理5 防御 LLM 攻击把LLM能访问的API当成“公开接…

ElasticSearch:不停机更新索引类型(未验证)

文章目录**一、前期准备****1. 集群健康检查****2. 备份数据****3. 监控系统准备****二、创建新索引并配置****1. 设计新索引映射****2. 创建读写别名****三、全量数据迁移****1. 执行初始 Reindex****2. 监控 Reindex 进度****四、增量数据同步****1. 方案选择****五、双写切换…

python学智能算法(二十七)|SVM-拉格朗日函数求解上

【1】引言 前序学习进程中&#xff0c;我们已经掌握了支持向量机算法中&#xff0c;为寻找最佳分割超平面&#xff0c;如何用向量表达超平面方程&#xff0c;如何为超平面方程建立拉格朗日函数。 本篇文章的学习目标是&#xff1a;求解SVM拉格朗日函数。 【2】求解方法 【2.…

mac安装node的步骤

适用于macOS 10.15及以上版本。 前提条件 macOS版本&#xff1a;确保系统为macOS 10.15&#xff08;Catalina&#xff09;或更高版本。可在“苹果菜单 > 关于本机”查看。管理员权限&#xff1a;部分安装可能需要管理员权限。网络连接&#xff1a;需要联网下载安装包或工具…

【LeetCode数据结构】栈的应用——有效的括号问题详解

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为…

多尺度卷积模型:Inception块

在GoogLeNet中&#xff0c;基本的卷积块被称为Inception块&#xff08;Inception block&#xff09;。 使用窗口大小为11&#xff0c;33&#xff0c;551\times1&#xff0c;3\times3&#xff0c;5\times511&#xff0c;33&#xff0c;55的卷积层&#xff0c;从不同空间大小中提…

Android 默认图库播放视频没有自动循环功能,如何添加

Android 默认图库播放视频没有自动循环功能, 如何添加 按如下方式添加 开发云 - 一站式云服务平台 .../apps/Gallery2/res/values-zh-rCN/strings.xml | 3 ++ packages/apps/Gallery2/res/values/strings.xml | 3 ++ .../com/android/gallery3d/app/MovieActivity…

7月21日总结

命令执行 RCE RCE&#xff08;remote code execute&#xff09;&#xff1a;远程命令执行或者代码执行&#xff0c;我们平时说的rce&#xff0c;比如thinkPHP的 rce漏洞&#xff0c;即算代码注入漏洞&#xff0c;也算rce漏洞&#xff0c;因为渗透的最终情况可以实现执行命令或…