首页/文章/ 详情

ROS2实操-如何实现节点间的通信?

10月前浏览256

作者:李慢慢

来源:微 信公 众号【车路漫漫】

上一期文章“怎么用Python编写多个ROS2节点”实现了多个节点的设计及启动,但是当初设计的节点功能很简单,只是简单的打印消息,无法实现节点间的互相配合。因此,本次来继续研究下节点之间如何实现通信,好为后续自动驾驶功能的不同节点之间的数据传输打打基础。

但是,随意翻看下ROS2的书籍或者网上检索一番,都会发现ROS2有很多种通信方式,初学者很容易迷失在选择中。所以这里简单就ROS2中的通信方式概述一下,初学者的话简单了解下就行。

1、ROS2中有哪些通信方式呢?

在ROS 2中,通信方式主要包括发布/订阅、服务/客户端、以及动作服务器/客户端。下面是每种通信方式的简要描述、使用场景和示例:

1、发布/订阅(Publish/Subscribe):

  • 功能: 允许节点在一个特定话题上发布消息,而其他节点可以订阅该话题以接收消息。
  • 使用场景: 适用于需要广播信息到多个节点的场景,例如传感器数据的发布。
  • 示例: 激光传感器节点发布激光扫描数据到/scan话题,地图构建节点和导航节点订阅该话题以进行环境建模和路径规划。

2、服务/客户端(Service/Client):

  • 功能: 允许节点提供某种服务,而其他节点可以请求并等待响应。
  • 使用场景: 适用于点对点通信的场景,其中一个节点请求另一个节点执行某个任务。
  • 示例: 图像处理节点提供一个服务,可以接收图像并返回处理后的结果,其他节点通过客户端调用该服务。

3、动作服务器/客户端(Action Server/Client):

  • 功能: 结合了发布/订阅和服务/客户端的特性,支持长时间运行的目标。
  • 使用场景: 适用于需要执行长时间运行任务的场景,如执行某个动作或路径规划。
  • 示例: 机器人执行路径规划的节点可以作为动作服务器,用户界面通过动作客户端发送路径规划请求和监控任务执行进度。

这三种通信方式是ROS 2的核心通信机制,它们为不同类型的通信场景提供了灵活的解决方案。这些通信方式可以根据你的应用需求进行组合和选择。每种通信方式都有其特定的用途,选择合适的通信方式可以提高系统的灵活性和性能。通过这些通信方式的组合,可以构建出灵活且高效的机器人系统。

那么问题来了,就算理解了上述通信方式的使用场景,我还是不太确认我该用哪一种。

那么我们来换一个问法,想要学习自动驾驶技术,ROS2中最主要的通信方式是哪种?

2、最主要的通信方式是哪种?

在自动驾驶技术中,ROS 2通常使用发布/订阅(Publish/Subscribe)作为最主要的通信方式。这是因为自动驾驶系统需要实时地获取和共享各种传感器数据,以及向其他系统发送控制命令和状态信息。

在自动驾驶中,传感器节点(如激光雷达、相机、雷达等)通常通过发布/订阅机制将实时采集的环境数据发布到相应的话题。同时,感知模块、路径规划、控制等其他组件可以订阅这些话题,从而实现实时的数据共享和协同工作。

以下是在自动驾驶技术中使用ROS2发布/订阅通信方式的一些常见应用:

  1. 传感器数据发布: LiDAR、相机等传感器节点将实时采集的环境数据发布到相应话题。
  2. 感知模块: 障碍物检测、目标识别等感知模块通过订阅传感器数据话题获取环境信息。
  3. 路径规划: 路径规划模块通过订阅环境地图和其他信息,发布规划好的路径到相应话题。
  4. 控制命令发布: 控制模块通过发布控制命令到相应话题,指导车辆执行特定动作。

通过使用发布/订阅通信方式,ROS 2能够提供灵活、实时的数据传递机制,使得不同模块之间可以高效地协同工作,是自动驾驶系统中常见的通信模型。需要注意的是,具体应用中也可能使用服务/客户端和动作服务器/客户端等通信方式,根据具体场景进行选择。

那么,接下来我们就来攻克怎么用发布/订阅某个话题的方式来实现两个节点之间的通信吧。

3、节点通信的代码实现

3.1、工作空间准备

首先进入ROS2_study,创建我们这次要实验的工作空间:ros2_ws_demo004_publish_subscribe。并在工作空间里完成本次实验基础环境的准备。具体操作及说明如下。

cd ~/ROS2_study/  # 进入我专门存放各个学习案例的工作空间的文件夹里
mkdir ros2_ws_demo004_publish_subscribe/  # 创建工作空间
cd ros2_ws_demo004_publish_subscribe/  # 进入工作空间
mkdir src  # 创建源代码区
cd src # 进入源代码区
ros2 pkg create package_001 --build-type ament_python --dependencies rclpy   # 创建功能包

然后屏幕上会打印一些内容:

going to create a new package
package name: package_001
destination directory: /home/goodman/ROS2_study/ros2_ws_demo004_publish_subscribe/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['goodman <goodman@todo.todo>']
licenses: ['TODO: License declaration']
build type: ament_python
dependencies: ['rclpy']
creating folder ./package_001
creating ./package_001/package.xml
creating source folder
creating folder ./package_001/package_001
creating ./package_001/setup.py
creating ./package_001/setup.cfg
creating folder ./package_001/resource
creating ./package_001/resource/package_001
creating ./package_001/package_001/__init__.py
creating folder ./package_001/test
creating ./package_001/test/test_copyright.py
creating ./package_001/test/test_flake8.py
creating ./package_001/test/test_pep257.py

此时src文件夹下面会出现一个新的文件夹‘package_001’。至此,工作空间下的文件目录如下:

ros2_ws_demo004_publish_subscribe/
└── src
    └── package_001
        ├── package_001
        │   └── __init__.py
        ├── package.xml
        ├── resource
        │   └── package_001
        ├── setup.cfg
        ├── setup.py
        └── test
            ├── test_copyright.py
            ├── test_flake8.py
            └── test_pep257.py

5 directories, 8 files

3.2、发布者节点创建

首先我们需要在‘package_001’文件夹下新建一个python文件,以作为发布者节点文件。

cd ~/ROS2_study/ros2_ws_demo004_publish_subscribe/src/package_001/package_001
touch my_publisher_node.py  # 新建一个python文件
gedit my_publisher_node.py  #打开该文件进行编辑

填入‘my_publisher_node.py’的内容如下:

# src/package_001/package_001/my_publisher_node.py

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class PublisherNode(Node):
    def __init__(self):
        super().__init__('publisher_node')
        self.publisher = self.create_publisher(String, 'topic_example_001', 10)
        self.timer = self.create_timer(1, self.publish_message)
        self.count = 0

    def publish_message(self):
        msg = String()
        msg.data = f'this is frame: {self.count}'
        self.publisher.publish(msg)
        self.get_logger().info(f'Sending message: {msg.data}')
        self.count += 1

def main(args=None):
    rclpy.init(args=args)
    node = PublisherNode()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

简单说下这个节点的作用,它其实就是不断的发布信息(一个简单的计数信息)到话题’topic_example_001‘中,并打印出来(方便我们知道到底发送了什么信息)。

3.3、订阅者节点创建

同样的,我们需要在‘package_001’文件夹下新建一个python文件,以作为订阅者节点文件。

cd ~/ROS2_study/ros2_ws_demo004_publish_subscribe/src/package_001/package_001
touch my_subscriber_node.py  # 新建一个python文件
gedit my_subscriber_node.py  #打开该文件进行编辑

填入‘my_subscriber_node.py’的内容如下:

# src/package_001/package_001/my_subscriber_node.py

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class SubscriberNode(Node):
    def __init__(self):
        super().__init__('subscriber_node')
        self.subscription = self.create_subscription(
            String, 'topic_example_001',
            self.listener_callback, 10)

    def listener_callback(self, msg):
        self.get_logger().info(f'Received message: {msg.data}')

def main(args=None):
    rclpy.init(args=args)
    node = SubscriberNode()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

简单说下这个节点的作用,它其实就是不断地从话题’topic_example_001‘中接收信息并打印出来。

3.4、配置节点调用

打开节点的配置文件‘setup.py’:

gedit ~/ROS2_study/ros2_ws_demo004_publish_subscribe/src/package_001/setup.py

填入以下内容:

from setuptools import setup

package_name = 'package_001'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='goodman',
    maintainer_email='goodman@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            "my_subscriber_node = package_001.my_subscriber_node:main",
            "my_publisher_node = package_001.my_publisher_node:main"
        ],
    },
)

上面的配置文件中,我这里其实只是在'console_scripts'配置项下加入了本次的两个节点:

'console_scripts': [
            "my_subscriber_node = package_001.my_subscriber_node:main",
            "my_publisher_node = package_001.my_publisher_node:main"
        ],

OK,至此我们已经完成发布者节点的创建,订阅者节点的创建,并在setup.py中完成了节点启动项的配置。

4、运行测试

4.1、编译

回到本次的工作空间下,进行编译:

cd ~/ROS2_study/ros2_ws_demo004_publish_subscribe/
colcon build

4.2、运行节点-发布者

编译完成后,在命令行启动发布者节点:

source install/setup.bash
ros2 run package_001 my_publisher_node

4.3、运行节点-订阅者

然后新打开一个终端,在命令行启动订阅者节点:

source install/setup.bash
ros2 run package_001 my_subscriber_node

4.4、运行效果

两个节点同时运行的效果如下所示:

 

发布者不断发送信息,订阅者不断接收这些信息。我们看到这两个节点已经实现了通信。

通过ros2 node list可以监控到正在运行的节点名称。

 

4.5、rqt_graph可视化工具

通过rqt工具,能以图形界面显示正在通信的两个节点。在命令行输入“rqt_graph”指令可以打开可视化窗口,如下所示。

 

该图能更形象的表达,’publisher_node‘节点向话题’topic_example_001‘发布信息,然后’subscriber_node‘节点从话题’topic_example_001‘订阅信息。等后续节点多了以后,发布和订阅的关系网也将十分复杂,使用该工具能快速且有效的显示现有节点之间的通信关系。

5、后续

本文初步实现了两个节点之间基于发布/订阅模式的通信方式。这个对于后续在自动驾驶系统中处理不同模块之间的通信(比如从传感器-->感知算法-->融合-->规划控制)十分重要,掌握了最基本的通信原理,才能继续基于ROS2搭建出更为完善的整个自动驾驶算法框架并实现整体运行和测试。

接下来,将使用自动驾驶仿真模拟器Carla,创建仿真节点和规控算法节点,继续实现基于仿真数据的闭环控制。欢迎持续关注。


来源:车路漫漫
ACTpython通信自动驾驶机器人控制
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2025-12-10
最近编辑:10月前
李慢慢
硕士 自动驾驶仿真工程师一枚
获赞 14粉丝 93文章 322课程 0
点赞
收藏
作者推荐

玩转CARLA-开门杀如何进行仿真测试

大家好,我是李慢慢。最近发现CARLA模拟器中的车辆可以打开车门,忍不住DIY了一个‘开门杀’的场景,想着对于想要实现开门预警或者开门识别预警的功能可能有些帮助。1、什么是‘开门杀’汽车停靠路边后不看后方情况就开门,导致被后面路过的车辆或者电驴迎面撞上车门,造成伤亡,这种交通事故被称为“开门杀”。 开门杀场景1 开门杀场景2这种开门杀事故,一般都是开门人的主要责任。为了防止开门杀,现在很多车辆都已经开始配置开门预警功能(DOW,Door Open Warning),即当检测到后方有目标接近时,打开车门会有报警或者阻力,这对于车主来讲能有效防止自己开门杀别人。那么假如我正驾驶车辆经过路边车辆时,又如何防止别人开门杀自己呢? 2、关于开门的模拟CARLA中对于carla.Vehicle对象,有两个关于车门的方法,一个是open_door,一个是close_door:上面两个方法的参数是一个carla.VehicleDoor对象,这个对象的描述如下:my_car = world.spawn_actor(v_bp, spawn_point)my_car.open_door(carla.VehicleDoor.FL)#my_car.open_door(carla.VehicleDoor.FR)#my_car.open_door(carla.VehicleDoor.RL)#my_car.open_door(carla.VehicleDoor.RR)#my_car.open_door(carla.VehicleDoor.All)my_car.close_door(carla.VehicleDoor.All) 关于哪一个车门的开启和关闭,可以非常自由。3、‘开门杀’场景重现在CARLA中创建这么个场景还是蛮容易的,在路边生成一排停放的车辆,然后创建一辆主车,低速滑行。当主车接近某一辆车时,这辆车突然来一个开门。4、最后‘开门杀’是比较典型的corner case场景,但对于目前的感知技术来说仍然是难点,如何在频发路段提前减速规避、在开门杀发生时及时识别并做出合理的控制,是目前辅助驾驶乃至自动驾驶需要持续迭代和攻克的问题。来源:车路漫漫

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈