ROS2高效学习第四章 -- ros2 topic 编程之测试 DDS 的 QoS 其三

本文深入探讨了ROS2中DDS的QoS配置,通过具体的Python和C++示例代码,展示了如何确保新节点能收到历史消息,以及订阅者如何接收错过的消息。文章详细解释了Reliability、History、Durability等QoS参数的作用,并提供了实践案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言与资料

ROS2高效学习第一章 – ros2整体介绍及DDS引入中,我们系统介绍了 ros2 引入 DDS(Data Distribution Service,数据分发服务)的前因后果。作为 ros2 topic 编程第三篇,本文仍将复用第一篇ROS2高效学习第四章 – ros2 topic 编程之收发 string 并使用 ros2 launch创建的两个软件包,即 pubsub_cpp 和 pubsub_py,在此基础上开发 QoS(Quality of Service,服务质量) 样例,实际测试 ros2 DDS 的功能。
本文参考资料如下:
(1)ROS2高效学习第一章 – ros2整体介绍及DDS引入
(2)古月 ros2 DDS
本系列博客汇总:ROS2 高效学习系列

2 正文

2.1 DDS QoS 引入

(1)QoS 含义:Quality of Service,服务质量,这是通信领域一个非常重要的概念,用于控制发布者和订阅者之间的通信策略。只看他的名字很难直接理解他的含义,但可以把 QoS 映射为几个对应的配置进行直观理解。请执行下面的命令,查看 QoS 默认配置。

# 随便发一个 topic
ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42"
# 新开一个窗口,查看这个 topic 的默认 QoS profile
ros2 topic info /chatter --verbose

在这里插入图片描述
(2)QoS 配置逐项解析:

Reliability:消息传达的可靠性;
	RELIABLE(default): 保证传输所有消息,丢失的消息会重传,类似 TCP 。
	BEST_EFFORT: 尽最大努力传送消息,但不保证交付,不会重传丢失的消息,类似 UDP。
补充:机器人系统中,传感器数据通常使用BEST_EFFORT,而参数更新需要RELIABLE可靠性。

History:消息的历史记录方式,当订阅者由于忙碌错过了消息,其仍能收到历史消息。这个参数只有在 Reliability 为 RELIABLE 时才有用。
	KEEP_LAST(default): 缓冲区保留最后几条消息。
	KEEP_ALL: 缓冲区保留所有传递过的消息。
Depth:与 History 搭配使用,当 History 为 KEEP_LAST 时,指定需要保留的消息个数,默认是10个;

Durability:消息是否应该在网络中持久化,以便新加入的订阅者也能接收。这个参数只有在 Reliability 为 RELIABLE 时才有用
	TRANSIENT_LOCAL(default): 消息在发布者端持久化,新加入的订阅者可以接收到旧消息,接收个数由History和Depth决定。
	VOLATILE: 消息不在节点之间持久化,新加入的订阅者接收不到旧消息。

Lifespan:消息应该在缓存中保留多长时间。如果超过这个时间,消息会被丢弃,即使还没有被任何订阅者接收,默认值是 Infinite。
	提示:Lifespan 的设置要与 History 和 Depth 对应起来,两个配置遵循木桶原理,最短的起作用。

Deadline:确定消息应当被发布和接收的最大期限,超出后会执行特定的回调函数,默认值是 Infinite。这个参数一般用于实时系统。
	举例:deadline=Duration(seconds=2, nanoseconds=0),

Liveliness: 节点如何通告自己的活动状态,用来确定节点是否还活着。一般用于分布式系统
    AUTOMATIC(default): 节点自动报告其存活状态。
    MANUAL_BY_TOPIC: 发布者需要手动报告其针对特定话题的存活状态。
Liveliness lease duration: 与 Liveliness 搭配使用,表示节点或话题必须在此期限内报告其存活状态,否则认为已经不活跃,默认值是 Infinite。

(3)发布者和订阅者 QoS 配置要求:针对 topic 配置 QoS ,一般要求同一个 topic 收发两端的 QoS 配置要一致,不然就会有奇奇怪怪的问题。
(4)命令行测试 QoS 的方法:ros2 topic 命令行有一组qos选项,可以手动测试 QoS 。

# 使用 best_effort 发送 /chatter
ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42" --qos-reliability best_effort
# 使用 reliable 订阅 /chatter,这样是收不到的!
ros2 topic echo /chatter  --qos-reliability reliable
# 使用 best_effort 订阅 /chatter,欧凯!
ros2 topic echo /chatter --qos-reliability best_effort

在这里插入图片描述

2.2 pubsub_py – 测试新加入的节点能收到历史消息

(1)创建新的 python 和 launch 文件

cd ~/colcon_ws/src/pubsub_py
touch launch/pubsub_qos_launch.py
touch pubsub_py/pub_hello_qos.py pubsub_py/sub_hello_qos.py

(2)编写 pub_hello_qos.py

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy
from rclpy.node import Node

from std_msgs.msg import String
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy, QoSDurabilityPolicy, LivelinessPolicy
from rclpy.duration import Duration

class Publisher(Node):
    def __init__(self):
        super().__init__('test_qos_publisher')

        qos_profile = QoSProfile(
            # RELIABLE(default), BEST_EFFORT
            reliability = QoSReliabilityPolicy.RELIABLE,
            # VOLATILE(default)
            # TRANSIENT_LOCAL(Only works when reliability is RELIABLE and needs history:KEEP_LAST and depth)
            # 新加入的节点能收到历史消息,这是最重要的配置!!!!!
            durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
            # history: only works when reliability is RELIABLE
            # KEEP_LAST(default), KEEP_ALL
            history = QoSHistoryPolicy.KEEP_LAST,
            # default is 10
            depth=10,
            # default is Infinite
            lifespan=Duration(seconds=5),

            # default is Infinite
            deadline=Duration(seconds=5, nanoseconds=0),
            # AUTOMATIC(default), MANUAL_BY_TOPIC
            liveliness=LivelinessPolicy.AUTOMATIC,
            # default is Infinite
            liveliness_lease_duration=Duration(seconds=5)
        )

        self._publisher = self.create_publisher(String, "hello_topic", qos_profile)
        self._timer = self.create_timer(0.5, self.timer_callback)
        self._i = 0

    def timer_callback(self):
        msg = String()
        msg.data = "hello, i am fine in python! %d" % self._i
        self._publisher.publish(msg)
        self.get_logger().info("publish: %s" % msg.data)
        self._i += 1

def main(args=None):
    rclpy.init(args=args)
    pub_node = Publisher()
    rclpy.spin(pub_node)
    pub_node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

(3)编写 sub_hello_qos.py

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy
from rclpy.node import Node

from std_msgs.msg import String
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy, QoSDurabilityPolicy, LivelinessPolicy
from rclpy.duration import Duration
from time import sleep

class Subscriber(Node):
    def __init__(self):
        super().__init__('test_qos_subscriber')

        qos_profile = QoSProfile(
            # RELIABLE(default), BEST_EFFORT
            reliability = QoSReliabilityPolicy.RELIABLE,
            # VOLATILE(default)
            # TRANSIENT_LOCAL(Only works when reliability is RELIABLE and needs history:KEEP_LAST and depth)
            # 新加入的节点能收到历史消息,这是最重要的配置!!!!!
            durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
            # history: only works when reliability is RELIABLE
            # KEEP_LAST(default), KEEP_ALL
            history = QoSHistoryPolicy.KEEP_LAST,
            # default is 10
            depth=10,
            # default is Infinite
            lifespan=Duration(seconds=5),

            # default is Infinite
            deadline=Duration(seconds=5, nanoseconds=0),
            # AUTOMATIC(default), MANUAL_BY_TOPIC
            liveliness=LivelinessPolicy.AUTOMATIC,
            # default is Infinite
            liveliness_lease_duration=Duration(seconds=5)
        )

        self._subscriber = self.create_subscription(String, "hello_topic", self.topic_callback, qos_profile)

    def topic_callback(self, msg):
        self.get_logger().info("subscribe: %s" % msg.data)
        # sleep(2)        # test qos history and depth parameters

def main(args=None):
    rclpy.init(args=args)
    sub_node = Subscriber()
    rclpy.spin(sub_node)
    sub_node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

(4)编写 pubsub_qos_launch.py

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import TimerAction

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='pubsub_py',
            namespace='qos',
            executable='talker_qos',
            name='talker_qos'
        ),
        TimerAction(
            period=3.0,   # test qos durability:TRANSIENT_LOCAL
            # period=0.0,
            actions=[
                Node(
                    package='pubsub_py',
                    namespace='qos',
                    executable='listener_qos',
                    name='listener_qos'
                )
            ]
        )
    ])

(5)添加 setup.py

    entry_points={
        'console_scripts': [
            'talker = pubsub_py.publisher:main',
            'talker_diy = pubsub_py.pub_diy_msg:main',
            'listener = pubsub_py.subscriber:main',
            'listener_diy = pubsub_py.sub_diy_msg:main',
            'talker_qos = pubsub_py.pub_hello_qos:main',
            'listener_qos = pubsub_py.sub_hello_qos:main'
        ],
    },

(6)编译并运行

~/colcon_ws
colcon build --packages-select diy_interface pubsub_py
source install/local_setup.bash
ros2 launch pubsub_py pubsub_qos_launch.py

在这里插入图片描述

2.3 pubsub_cpp – 测试订阅者能接收错过的消息

代码链接:pubsub_cpp,这里不再详述,基本就是用 cpp 实现上面的 python 版本。不同的是,cpp 样例重点测试:订阅者因忙碌错过了一些消息,但通过 QoS 仍能接收。
在这里插入图片描述

3 总结

本文的代码托管在本人的github上:pubsub_pypubsub_cpp

### ROSDDS的关系 机器人操作系统(Robot Operating System, ROS)是一种用于开发机器人应用程序的灵活框架[^1]。它提供了多种工具和服务,包括硬件抽象、低级设备控制以及消息传递机制等。然而,在ROS 2版本中,其核心通信架构采用了数据分发服务(Data Distribution Service, DDS)。这使得ROS能够更好地支持分布式系统的实时性和可靠性需求。 #### 数据分发服务(DDSDDS 是一种中间件协议标准,旨在满足高可靠性和高性能的需求,特别是在嵌入式系统领域。通过提供发布/订阅模式的消息传输方式,它可以实现节点之间的高效数据交换,并允许开发者定义服务质量参数(Quality of Service, QoS),从而优化网络性能和资源利用率。 当 ROS 集成 DDS 后,这种组合带来了以下优势: - **跨平台兼容性**:由于 DDS 的广泛适用性,基于此构建的应用程序可以轻松移植到不同平台上运行。 - **增强的功能集**:利用 DDS 提供的强大功能,如时间敏感网络(TSN) 支持、安全性扩展等功能模块,进一步提升了整个生态系统的灵活性。 - **改进的可伸缩性**:借助于 DDS 自身优秀的水平扩展能力,即使面对复杂多变的任务场景也能保持稳定高效的运作状态。 以下是展示如何配置并启动一个简单的 ROS 节点来演示两者之间交互的一个 Python 示例代码片段: ```python import rclpy from std_msgs.msg import String def main(args=None): rclpy.init(args=args) node = rclpy.create_node('dds_example_publisher') publisher = node.create_publisher(String, 'topic', qos_profile=10) msg = String() i = 0 def timer_callback(): nonlocal i msg.data = f'Hello from DDS Integration {i}' i += 1 publisher.publish(msg) node.get_logger().info(f'Sending message: "{msg.data}"') timer_period = 0.5 # seconds timer = node.create_timer(timer_period, timer_callback) try: while True: rclpy.spin_once(node) except KeyboardInterrupt: pass node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` 上述脚本创建了一个名为 `dds_example_publisher` 的 ROS 节点实例,并定期向主题 `/topic` 发布字符串消息。这里特别注意的是设置QoS策略时所使用的数值,默认情况下为10表示历史深度大小。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

界首大学

创作不易,随缘打赏是最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值