【ROS2】初级:客户端-编写一个简单的服务和客户端(Python)

news2024/11/27 20:37:36

目标:使用 Python 创建并运行服务节点和客户端节点。

 教程级别:初学者

 时间:20 分钟

 目录

  •  背景

  •  先决条件

  •  任务

    • 1. 创建一个包

    • 2. 编写服务节点

    • 3. 编写客户端节点

    • 4. 构建并运行

  •  摘要

  •  下一步

  •  相关内容

 背景

当节点通过服务进行通信时,发送数据请求的节点称为客户端节点,而响应请求的节点称为服务节点。请求和响应的结构由一个 .srv 文件决定。

这里使用的例子是一个简单的整数加法系统;一个节点请求两个整数的和,另一个节点则响应结果。

 先决条件

在之前的教程中,您学习了如何创建工作区和创建包。

 任务

1. 创建一个包

打开一个新的终端并且初始化您的 ROS 2 安装,这样 ros2 命令就会生效。

导航到在之前教程中创建的 ros2_ws 目录。

请记住,包应该在 src 目录中创建,而不是在工作区的根目录中。导航到 ros2_ws/src 并创建一个新包:

cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_python --license Apache-2.0 py_srvcli --dependencies rclpy example_interfaces
going to create a new package
package name: py_srvcli
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_python
dependencies: ['rclpy', 'example_interfaces']
creating folder ./py_srvcli
creating ./py_srvcli/package.xml
creating source folder
creating folder ./py_srvcli/py_srvcli
creating ./py_srvcli/setup.py
creating ./py_srvcli/setup.cfg
creating folder ./py_srvcli/resource
creating ./py_srvcli/resource/py_srvcli
creating ./py_srvcli/py_srvcli/__init__.py
creating folder ./py_srvcli/test
creating ./py_srvcli/test/test_copyright.py
creating ./py_srvcli/test/test_flake8.py
creating ./py_srvcli/test/test_pep257.py

您的终端将返回一条消息,确认您的包 py_srvcli 及其所有必要的文件和文件夹已创建。

`--dependencies` 参数会自动将必要的依赖行添加到 `package.xml`。`example_interfaces` 是包含 `.srv` 文件的包,您将需要它来构建您的请求和响应结构:

int64 a
int64 b
---
int64 sum

请求的前两行是参数,破折号以下的是响应。

 1.1 更新 package.xml 

因为在创建包时您使用了 --dependencies 选项,所以您无需手动将依赖项添加到 package.xml 中。

一如既往,不过,请确保将描述、维护者电子邮件和姓名以及许可信息添加到 package.xml 。

<description>Python client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
<?xml version="1.0"?>
# 声明XML版本为1.0


<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
# 指定XML模式文件的位置和命名空间


<package format="3">
# 定义包的格式版本为3


  <name>py_srvcli</name>
  # 包的名称为'py_srvcli'


  <version>0.0.0</version>
  # 包的版本号为0.0.0


  <description>Python client server tutorial</description>
  # 包的描述信息为'Python客户端服务器教程'


  <maintainer email="cxy@126.com">cxy</maintainer>
  # 维护者的名字是cxy,电子邮件是cxy@126.com


  <license>Apache-2.0</license>
  # 包的许可证为Apache-2.0


  <depend>rclpy</depend>
  # 依赖项为rclpy


  <depend>example_interfaces</depend>
  # 依赖项为example_interfaces


  <test_depend>ament_copyright</test_depend>
  # 测试依赖项为ament_copyright


  <test_depend>ament_flake8</test_depend>
  # 测试依赖项为ament_flake8


  <test_depend>ament_pep257</test_depend>
  # 测试依赖项为ament_pep257


  <test_depend>python3-pytest</test_depend>
  # 测试依赖项为python3-pytest


  <export>
    # 导出部分开始


    <build_type>ament_python</build_type>
    # 构建类型为ament_python


  </export>
  # 导出部分结束


</package>
# 包定义结束
 1.2 更新 setup.py 

将相同的信息添加到 setup.py 文件中的 maintainer 、 maintainer_email 、 description 和 license 字段:

maintainer='Your Name',
maintainer_email='you@email.com',
description='Python client server tutorial',
license='Apache-2.0'

2. 编写服务节点

在 ros2_ws/src/py_srvcli/py_srvcli 目录中,创建一个名为 service_member_function.py 的新文件,并将以下代码粘贴其中:

from example_interfaces.srv import AddTwoInts  # 从example_interfaces.srv模块导入AddTwoInts服务


import rclpy  # 导入rclpy库
from rclpy.node import Node  # 从rclpy.node模块导入Node类


class MinimalService(Node):  # 定义一个继承自Node类的MinimalService类


    def __init__(self):  # 初始化方法
        super().__init__('minimal_service')  # 调用父类的初始化方法,并命名节点为'minimal_service'
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)  
        # 创建一个服务,服务类型为AddTwoInts,服务名为'add_two_ints',回调函数为add_two_ints_callback


    def add_two_ints_callback(self, request, response):  # 定义服务的回调函数
        response.sum = request.a + request.b  # 将请求中的两个整数相加,并将结果赋值给响应的sum字段
        self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))  
        # 记录日志,显示收到的请求中的两个整数a和b


        return response  # 返回响应


def main():  # 主函数
    rclpy.init()  # 初始化rclpy


    minimal_service = MinimalService()  # 创建MinimalService类的实例


    rclpy.spin(minimal_service)  # 保持节点运行,等待并处理请求


    rclpy.shutdown()  # 关闭rclpy


if __name__ == '__main__':  # 如果该文件是作为主程序运行
    main()  # 调用主函数
检查代码 2.1

第一个 import 语句从 example_interfaces 包中导入 AddTwoInts 服务类型。接下来的 import 语句导入了 ROS 2 Python 客户端库,特别是 Node 类。

from example_interfaces.srv import AddTwoInts


import rclpy
from rclpy.node import Node

MinimalService 类构造函数用名称 minimal_service 初始化节点。然后,它创建一个服务并定义类型、名称和回调。

def __init__(self):
    super().__init__('minimal_service')
    self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)

服务回调的定义接收请求数据,对其求和,然后将总和作为响应返回。

def add_two_ints_callback(self, request, response):
    response.sum = request.a + request.b
    self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))


    return response

最后,主类初始化 ROS 2 Python 客户端库,实例化 MinimalService 类以创建服务节点,并旋转节点以处理回调。

2.2 添加一个入口点 

要允许 ros2 run 命令运行您的节点,您必须将入口点添加到 setup.py (位于 ros2_ws/src/py_srvcli 目录中)。

在 'console_scripts': 括号之间添加以下行:

'service = py_srvcli.service_member_function:main',

3. 编写客户端节点

在 ros2_ws/src/py_srvcli/py_srvcli 目录中,创建一个名为 client_member_function.py 的新文件,并将以下代码粘贴其中:

import sys
# 导入sys模块


from example_interfaces.srv import AddTwoInts
# 从example_interfaces.srv模块中导入AddTwoInts服务


import rclpy
# 导入rclpy库


from rclpy.node import Node
# 从rclpy.node模块中导入Node类


class MinimalClientAsync(Node):
    # 定义一个继承自Node类的MinimalClientAsync类


    def __init__(self):
        # 初始化函数
        super().__init__('minimal_client_async')
        # 调用父类的初始化函数,并命名节点为'minimal_client_async'
        self.cli = self.create_client(AddTwoInts, 'add_two_ints')
        # 创建一个客户端,使用AddTwoInts服务,服务名为'add_two_ints'
        while not self.cli.wait_for_service(timeout_sec=1.0):
            # 循环等待服务可用,每次等待1秒
            self.get_logger().info('service not available, waiting again...')
            # 如果服务不可用,打印日志信息
        self.req = AddTwoInts.Request()
        # 创建一个AddTwoInts请求对象


    def send_request(self, a, b):
        # 定义发送请求的函数
        self.req.a = a
        # 设置请求的第一个参数
        self.req.b = b
        # 设置请求的第二个参数
        return self.cli.call_async(self.req)
        # 异步调用服务并返回结果


def main():
    # 主函数
    rclpy.init()
    # 初始化rclpy库


    minimal_client = MinimalClientAsync()
    # 创建MinimalClientAsync对象
    future = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2]))
    # 发送请求,参数为命令行输入的两个整数
    rclpy.spin_until_future_complete(minimal_client, future)
    # 等待请求完成
    response = future.result()
    # 获取请求结果
    minimal_client.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (int(sys.argv[1]), int(sys.argv[2]), response.sum))
    # 打印结果日志信息


    minimal_client.destroy_node()
    # 销毁节点
    rclpy.shutdown()
    # 关闭rclpy


if __name__ == '__main__':
    main()
    # 如果脚本是直接执行的,则调用main函数
3.1 检查代码

在服务代码中,我们首先 import 必要的库。

import sys


from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node

MinimalClientAsync 类构造函数用名称 minimal_client_async 初始化节点。构造函数定义创建了一个与服务节点类型和名称相同的客户端。类型和名称必须匹配,客户端和服务才能够通信。构造函数中的 while 循环每秒检查一次是否有与客户端类型和名称相匹配的服务。最后,它创建了一个新的 AddTwoInts 请求对象。

def __init__(self):
    super().__init__('minimal_client_async')
    self.cli = self.create_client(AddTwoInts, 'add_two_ints')
    while not self.cli.wait_for_service(timeout_sec=1.0):
        self.get_logger().info('service not available, waiting again...')
    self.req = AddTwoInts.Request()

构造函数下面是 send_request 方法,它将发送请求并返回一个可以传递给 spin_until_future_complete 的未来:

def send_request(self, a, b):
    self.req.a = a
    self.req.b = b
    return self.cli.call_async(self.req)

最后我们有 main 方法,它构建一个 MinimalClientAsync 对象,使用传入的命令行参数发送请求,调用 spin_until_future_complete ,并记录结果:

def main():
    rclpy.init()


    minimal_client = MinimalClientAsync()
    future = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2]))
    rclpy.spin_until_future_complete(minimal_client, future)
    response = future.result()
    minimal_client.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (int(sys.argv[1]), int(sys.argv[2]), response.sum))


    minimal_client.destroy_node()
    rclpy.shutdown()

3.2 添加一个入口点

与服务节点一样,您还需要添加一个入口点才能运行客户端节点。

您的 setup.py 文件中的 entry_points 字段应该如下所示:

entry_points={
    'console_scripts': [
        'service = py_srvcli.service_member_function:main',
        'client = py_srvcli.client_member_function:main',
    ],
},
from setuptools import find_packages, setup
# 从setuptools模块中导入find_packages和setup函数


package_name = 'py_srvcli'
# 定义包名为'py_srvcli'


setup(
    name=package_name,
    # 设置包的名称
    version='0.0.0',
    # 设置包的版本号
    packages=find_packages(exclude=['test']),
    # 查找包,排除'test'目录
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        # 指定数据文件的位置和名称
        ('share/' + package_name, ['package.xml']),
        # 指定package.xml文件的位置和名称
    ],
    install_requires=['setuptools'],
    # 指定安装依赖项
    zip_safe=True,
    # 指定包是否可以安全地作为zip文件分发
    maintainer='cxy',
    # 指定维护者的名字
    maintainer_email='cxy@126.com',
    # 指定维护者的电子邮件
    description='Python client server tutorial',
    # 包的描述信息
    license='Apache-2.0',
    # 指定包的许可证
    tests_require=['pytest'],
    # 指定测试依赖项
    entry_points={
        'console_scripts': [
            'service = py_srvcli.service_member_function:main',
            # 定义控制台脚本'服务',指向py_srvcli.service_member_function模块的main函数
            'client = py_srvcli.client_member_function:main',
            # 定义控制台脚本'客户端',指向py_srvcli.client_member_function模块的main函数
        ],
    },
)
# 调用setup函数,传入包的配置信息

4. 构建并运行

在工作区的根目录运行 rosdep ( ros2_ws )以检查构建前缺失的依赖项是一个好习惯:

rosdep install -i --from-path src --rosdistro jazzy -y

返回到您的工作区根目录, ros2_ws ,然后构建您的新包:

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select py_srvcli
Starting >>> py_srvcli
Finished <<< py_srvcli [7.05s]           


Summary: 1 package finished [17.1s]

打开一个新的终端,导航到 ros2_ws ,并且导入设置文件:

source install/setup.bash

现在运行服务节点:

ros2 run py_srvcli service

节点将等待客户端的请求。

打开另一个终端,并再次从 ros2_ws 内部加载设置文件。启动客户端节点,后面跟着任意两个用空格分隔的整数:

ros2 run py_srvcli client 2 3

如果您选择了 2 和 3 ,例如,客户将收到这样的回应:

[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5

返回到运行服务节点的终端。当它收到请求时,你会看到它发布了日志消息:

[INFO] [minimal_service]: Incoming request
a: 2 b: 3

233fd5f1335d24e429252f9b14f48993.png

在服务器终端输入 Ctrl+C 以停止节点旋转。

 摘要

您创建了两个节点,通过服务请求和响应数据。您将它们的依赖项和可执行文件添加到包配置文件中,以便您可以构建并运行它们,从而让您看到服务/客户端系统的工作情况。

 下一步

在过去的几个教程中,您一直在使用接口来跨主题和服务传递数据。接下来,您将学习如何创建自定义接口。https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Custom-ROS2-Interfaces.html

 相关内容 

  • 有几种方法可以用 Python 编写服务和客户端;请查看 ros2/examples 仓库https://github.com/ros2/examples/tree/jazzy/rclpy/services 中的 minimal_client 和 minimal_service 包。

  • 在本教程中,您在客户端节点中使用了 call_async() API 来调用服务。Python 还有另一种服务调用 API,称为同步调用。我们不推荐使用同步调用,但如果您想了解更多关于它们的信息,请阅读《同步与异步客户端指南》。https://docs.ros.org/en/jazzy/How-To-Guides/Sync-Vs-Async.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】机器学习重塑广告营销:精准触达,高效转化的未来之路

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f4d2;1. 引言&#x1f4d9;2. 机器学习基础与广告营销的结合&#x1f9e9;机器学习在广告营销中的核心应用领域&#x1f339;用…

将大型语言模型模块化打造协作智能体

B UILDING C OOPERATIVE E MBODIED A GENTS MODULARLY WITH L ARGE L ANGUAGE M ODELS 论文链接&#xff1a; https://arxiv.org/abs/2307.02485https://arxiv.org/abs/2307.02485 1.概述 在去中心化控制及多任务环境中&#xff0c;多智能体合作问题因原始感官观察、高昂…

穿梭印度风情记:维乐 Angel Revo Halo坐垫,让每一寸旅程闪耀光辉!

想象骑乘在印度的万花筒世界中&#xff0c;斑斓色彩与悠久历史交织&#xff0c;每一转轮都是对神秘东方的深刻探索。在这样的骑行之旅中&#xff0c;维乐Angel Revo Halo坐垫不仅是你的坐骑上的宝石&#xff0c;更是舒适与探险的完美媒介。    探索印度的色彩与灵魂&#x…

每日一题~oj(贪心)

对于位置 i来说&#xff0c;如果 不选她&#xff0c;那她的贡献是 vali-1 *2&#xff0c;如果选他 &#xff0c;那么她的贡献是 ai. 每一个数的贡献 是基于前一个数的贡献 来计算的。只要保证这个数的前一个数的贡献是最优的&#xff0c;那么以此类推下去&#xff0c;整体的val…

【项目设计】负载均衡式——Online Judge

负载均衡式——Online Judge&#x1f60e; 前言&#x1f64c;Online Judge 项目一、项目介绍二、项目技术栈三、项目使用环境四、项目宏观框架五、项目后端服务实现过程1、comm模块设计1.1 Log.hpp实现1.2 Util.hpp实现 2、compiler_server 模块设计2.1compile.hpp文件代码编写…

【QT】容器类控件

目录 概述 Group Box 核心属性 Tab Widget 核心属性 核心信号 核心方法 使用示例&#xff1a; 布局管理器 垂直布局 核心属性 使用示例&#xff1a; 水平布局 核⼼属性 (和 QVBoxLayout 属性是⼀致的) 网格布局 核心属性 使用示例&#xff1a; 示例&#x…

【C++ OpenCV】机器视觉-二值图像和灰度图像的膨胀、腐蚀、开运算、闭运算

原图 结果图 //包含头文件 #include <opencv2/opencv.hpp>//命名空间 using namespace cv; using namespace std;//全局函数声明部分//我的腐蚀运算 Mat Erode(Mat src, Mat Mask, uint32_t x0, uint32_t y0) {uint32_t x 0, y 0;Mat dst(src.rows, src.cols, CV_8U…

设计模式之状态机模式

一、状态机模式介绍 状态机模式&#xff08;State Machine Pattern&#xff09;是一种用于描述对象行为的软件设计模式&#xff0c;属于行为型设计模式。在状态机模式中&#xff0c;对象的行为取决于其内部状态&#xff0c;并且在不同的状态下&#xff0c;对象可能会有不同的行…

RAG 案框架(Qanything、RAGFlow、FastGPT、智谱RAG)对比

各家的技术方案 有道的QAnything 亮点在&#xff1a;rerank RAGFLow 亮点在&#xff1a;数据处理index 智谱AI 亮点在文档解析、切片、query改写及recall模型的微调 FastGPT 优点&#xff1a;灵活性更高 下面分别按照模块比较各框架的却别 功能模块QAnythingRAGFLowFastG…

【手写数据库内核组件】01 解析树的结构,不同类型的数据结构组多层的链表树,抽象类型统一引用格式

不同类型的链表 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 不同类型…

Day05-04-持续集成总结

Day05-04-持续集成总结 1. 持续集成2. 代码上线目标项目 1. 持续集成 git 基本使用, 拉取代码,上传代码,分支操作,tag标签 gitlab 用户 用户组 项目 , 备份,https,优化. jenkins 工具平台,运维核心, 自由风格工程,maven风格项目,流水线项目, 流水线(pipeline) mavenpom.xmlta…

Android 10年,35岁,该往哪个方向发力

网上看到个网友发的帖子&#xff0c;觉的这个可能是很多开发人员都会面临和需要思考的问题。 不管怎样&#xff0c; 要对生活保持乐观&#xff0c;生活还是有很多的选择和出路的。 &#xff08;内容来自网络&#xff0c;不代表个人观点&#xff09; 《Android Camera开发入门》…

关闭vue3中脑瘫的ESLine

在创建vue3的时候脑子一抽选了ESLine,然后这傻卵子ESLine老是给我报错 博主用的idea开发前端 ,纯粹是用不惯vscode 关闭idea中的ESLine,这个只是取消红色波浪线, 界面中的显示 第二步,在vue.config.js中添加 lintOnSave: false 到这里就ok了,其他的我试过了一点用没有

专业140+总分420+天津大学815信号与系统考研经验天大电子信息与通信工程,真题,大纲,参考书。

顺利上岸天津大学&#xff0c;专业课815信号与系统140&#xff0c;总分420&#xff0c;总结一些自己的复习经历&#xff0c;希望对于报考天大的同学有些许帮助&#xff0c;少走弯路&#xff0c;顺利上岸。专业课&#xff1a; 815信号与系统&#xff1a;指定教材吴大正&#xf…

飞书 API 2-4:如何使用 API 将数据写入数据表

一、引入 上一篇创建好数据表之后&#xff0c;接下来就是写入数据和对数据的处理。 本文主要探讨数据的插入、更新和删除操作。所有的操作都是基于上一篇&#xff08;飞书 API 2-4&#xff09;创建的数据表进行操作。上面最终的数据表只有 2 个字段&#xff1a;序号和邮箱。序…

(完整音频)DockerHub、OpenAI、GitCode,脱钩时代,我们该如何自处?

本期主播 朱峰&#xff1a;「津津乐道播客网络」创始人&#xff0c;产品及技术专家。&#xff08;微博&#xff1a;zhufengme&#xff09;高春辉&#xff1a;「科技乱炖」主播。“中国互联网站长第一人”&#xff0c;科技、互联网领域的连续创业者。&#xff08;微博&#xff1…

SCI一区TOP|准随机分形搜索算法(QRFS)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年&#xff0c;LA Beltran受到分形几何、低差异序列启发&#xff0c;提出了准随机分形搜索算法&#xff08;Quasi-random Fractal Search, QRFS&#xff09;。 2.算法原理 2.1算法思…

内容营销专家刘鑫炜:网站排名需考虑哪些SEO优化技巧?

网站排名的SEO优化技巧包括&#xff1a; 1. 关键词研究&#xff1a;了解目标受众的搜索关键词&#xff0c;将这些关键词合理地应用在网站的标题、描述、正文和标签中&#xff0c;有助于提高网站排名。 2. 内容优化&#xff1a;创建高质量、有价值的内容&#xff0c;可以吸引搜…

【leetcode52-55图论、56-63回溯】

图论 回溯【56-63】 46.全排列 class Solution:def permute(self, nums):result []self.backtracking(nums, [], [False] * len(nums), result)return resultdef backtracking(self, nums, path, used, result):if len(path) len(nums):result.append(path[:])returnfor i …

【HBZ】高性能zeroCopy零拷贝与普通IO差距与原理

简介 随着IO不断地发展&#xff0c;无论哪种拷贝方式&#xff0c;DMA从磁盘拷贝数据到内核缓冲区&#xff0c;都会拷贝多一些数据, 不会只拷贝用户态的指定size的数据&#xff0c;而是会将目标数据的临近数据也都拷贝到内核缓冲区&#xff0c;以便下次IO操作可以直接从内核缓冲…