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

目标:使用 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.mfbz.cn/a/779789.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【项目日记(一)】梦幻笔耕-数据层实现

❣博主主页: 33的博客❣ ▶️文章专栏分类:项目日记◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多项目内容 目录 1.前言2.后端模块3数据库设计4.mapper实现4.1UserInfoMapper4.2BlogMapper 5.总结 1.…

机器学习筑基篇,​Ubuntu 24.04 快速安装 PyCharm IDE 工具,无需激活!

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] Ubuntu 24.04 快速安装 PyCharm IDE 工具 描述&#xff1a;虽然在之前我们安装了VScode&#xff0c;但是其对于使用Python来写大型项目以及各类配置还是比较复杂的&#xff0c;所以这里我们还是推…

U盘非安全拔出后的格式化危机与数据拯救策略

在数字化时代&#xff0c;U盘作为便捷的数据携带工具&#xff0c;其重要性不言而喻。然而&#xff0c;许多用户在日常使用中往往忽视了安全退出的重要性&#xff0c;直接拔出U盘后再插入时可能会遭遇“需要格式化”的提示&#xff0c;这一状况不仅令人措手不及&#xff0c;更可…

YOLOv9报错:AttributeError: ‘list‘ object has no attribute ‘view‘

报错信息如下&#xff1a; red_distri, pred_scores torch.cat([xi.view(feats[0].shape[0], self.no, -1) for xi in feats], 2).split( AttributeError: ‘list’ object has no attribute ‘view’ 解决方法&#xff1a; 去yolov9/utils/loss_tal.py把167行代码更改&#…

Android最近任务显示的图片

Android最近任务显示的图片 1、TaskSnapshot截图1.1 snapshotTask1.2 drawAppThemeSnapshot 2、导航栏显示问题3、Recentan按键进入最近任务 1、TaskSnapshot截图 frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java frameworks/base/cor…

Blazor SPA 的本质是什么以及服务器端渲染如何与 Blazor 的新 Web 应用程序配合使用

Blazor 通常被称为单页应用程序 (SPA) 框架。当我第一次开始使用 Blazor 时&#xff0c;我对 SPA 的含义、组件如何为 SPA 架构做出贡献以及所有这些如何与交互性联系在一起感到困惑。 今天&#xff0c;我将解答大家可能关心的三个问题&#xff1a; 什么是 SPA&#xff1f;了…

Sentinel-1 Level 1数据处理的详细算法定义(一)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程&#xff0c;以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下&…

14-42 剑和诗人16 - 如何从一个技术人员到CTO再到投资人的角色转变

​​​​​​ 我清楚地记得我的职业轨迹发生转变的那个关键时刻。当时&#xff0c;我正向整个执行领导团队和董事会成员介绍我们部门的技术路线图&#xff0c;感到说服这些有影响力的利益相关者资助一系列雄心勃勃的计划的压力。我知道他们的支持&#xff08;和资金&#xff09…

英语学习交流小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;每日打卡管理&#xff0c;备忘录管理&#xff0c;学习计划管理&#xff0c;学习资源管理&#xff0c;论坛交流 微信端账号功能包括&#xff1a;系统首页&#xff0c;学习资源&…

基于最大相邻夹角的边缘点提取(matlab)

1、背景介绍 边缘点是指点云数据中代表物体或场景几何形状突变的那些点。在三维点云中&#xff0c;边缘点通常标志着不同表面或物体的分界&#xff0c;或者是物体表面上的不规则性&#xff0c;如裂缝、棱角、突起等。点云边缘检测的作用非常重要&#xff0c;最常见是进行特征点…

应用监控SkyWalking调研

参考&#xff1a; 链路追踪( Skyworking )_skywalking-CSDN博客 企业级监控项目Skywalking详细介绍&#xff0c;来看看呀-CSDN博客 SkyWalking 极简入门 | Apache SkyWalking 使用 SkyWalking 监控 ClickHouse Server | Apache SkyWalking https://zhuanlan.zhihu.com/p/3…

45 mysql truncate 的实现

前言 truncate 是一个我们也经常会使用到的命令 其作用类似于 delete from $table; 但是 他会比 delete 块很多&#xff0c;这里我们来看一下 它的实现 delete 的时候会逐行进行处理, 打上 删除标记, 然后 由后台任务 进行数据处理 truncate table 的实现 执行 sql 如下 …

【测试专题】软件总体计划方案(2024原件word)

测试目标&#xff1a;确保项目的需求分析说明书中的所有功能需求都已实现&#xff0c;且能正常运行&#xff1b;确保项目的业务流程符合用户和产品设计要求&#xff1b;确保项目的界面美观、风格一致、易学习、易操作、易理解。 获取&#xff1a;软件全套文档过去进主页。 一、…

Go语言--工程管理、临时/永久设置GOPATH、main函数以及init函数

工作区 Go 代码必须放在工作区中。工作区其实就是一个对应于特定工程的目录&#xff0c;它应包含3个子目录:src 目录、pkg目录和bin 目录。 src 目录:用于以代码包的形式组织并保存 Go源码文件。(比如:.go.chs等)pkg 目录:用于存放经由 go install 命令构建安装后的代码包(包…

2.3.2 主程序和外部IO交互 (文件映射方式)----C#调用范例

2.3.2 主程序和外部IO交互 &#xff08;文件映射方式&#xff09;----C#调用范例 效果显示 1 说明 1 .1 Test_IOServer是64bit 程序&#xff0c; BD_SharedIOServerd.dll 在 /Debug文件夹中 1 .2 Test_IOServer是32bit 程序&#xff0c; BD_SharedIOClientd.dll (32bit&#…

java join与yield方法

join() join() 方法的主要作用是使当前线程&#xff08;调用 join() 方法的线程&#xff09;等待目标线程完成执行。当目标线程执行完毕后&#xff0c;当前线程才会继续执行。 代码示例&#xff1a; public class JoinExample {public static void main(String[] args) {Thr…

在数字化时代,自助BI是数据价值最大化的必经之路

引言&#xff1a;在数字化时代&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;仅仅拥有海量数据并不足以带来竞争优势&#xff0c;关键在于如何有效地分析并利用这些数据以指导决策、优化运营、提升客户体验&#xff0c;并最终实现业务的持续增长。在一章里笔者…

SpringBoot新手快速入门系列教程七:基于一个低配centoos服务器,如何通过宝塔面板部署一个SpringBoot项目

1&#xff0c;如何打包一个项目 通过IDEA自带的命令行&#xff0c;执行 ./gradlew clean build 2&#xff0c;检查生成的JAR文件 进入 build/libs 目录&#xff0c;你应该会看到一个类似 helloredis-0.0.1-SNAPSHOT.jar 的文件。 3&#xff1a;运行生成的JAR文件 你可以使…

springboot服务启动读取不到application.yml中的nacos.config信息

我的版本&#xff1a; 可以添加bootstrap.yml文件&#xff0c;在里面添加nacos.config的配置信息 也可以添加VM参数 -Dspring.cloud.nacos.discovery.server-addr -Dspring.cloud.nacos.config.server-addr -Dspring.cloud.nacos.config.namespace -Dspring.cloud.nacos.discov…

图片管理新纪元:高效批量横向拼接图片,一键生成灰色艺术效果,打造专业视觉体验!

在数字时代&#xff0c;图片已成为我们生活和工作中不可或缺的一部分。但面对海量的图片&#xff0c;如何高效地进行批量管理、拼接和调色&#xff0c;成为许多人面临的难题。今天&#xff0c;我们为您带来了一款颠覆性的图片管理工具&#xff0c;让您轻松实现图片批量横向拼接…