ROS2 Quality of Service 服务质量 QoS 简介
- 背景介绍
- QoS服务质量简介
- 实例
- 参考资料
ROS在使用过程中最常用的消息之一就是
sensor_msgs
了,但是最近使用ROS2的过程中遇到一个问题,我要订阅一个消息类型为
sensor_msgs/msg/LaserScan
的话题
scan
,在代码中无法获取该消息,但是通过
ros2 topic info /scan
可以看到该话题被订阅了,通过
ros2 topic echo /scan
发现也可以正常的打印话题,就是在代码里面无法获取话题的消息,经过一番查阅资料发现原来是和ROS2的 (Quality of Service)服务质量 (QoS )有关,那到底什么是QoS服务质量,为什么它会导致代码里面无法接收话题的消息。
背景介绍
关于QoS的渊源就牵扯到ROS2的诞生,ROS1通过TCP实现底层数据的传输,TCP虽然比较可靠,但是如果对于网络情况不好的使用场景,经常发生丢包的现象,如果使用TCP进行传输可能一个消息都都不到,经常这个时候我们使用不可靠的UDP反而是一个比较好的选择,虽然这个时候网络不可靠,但是我们至少可以获取消息,保持消息不会中断,对于数据不可靠的问题可以通过其他算法来解决,所以我们平时的聊天软件:QQ、微信都是使用UDP传输。ROS2作为一个应用于DDS分布系统的产品,如何进行可靠的数据传输是一个关键点,而QoS就是对数据如何进行底层的传输进行配置的。
QoS服务质量简介
这里只是简单进行介绍,需要需要深入了解相关内容,请查看文末官网的链接。
在ROS2中QoS有如下的策略选择,我们可以对策略进行配置达到我们的使用需求。
-
历史(History)
-
保存最近的数据(Keep last):只保留N个数据,通过队列深度选项进行配置。
-
保存全部的数据(Keep all):保留所有的数据,但要受限于底层中间件的资源限制。
-
-
深度(Depth)
- 队列大小:仅当与存最近的数据(Keep last)一起使用时才有效。
-
可靠性(Reliability)
-
尽力(Best effort):尝试提供样本,但如果网络不稳定,可能会丢失样本。
-
可靠(Reliable):确保数据的可靠传输,可以多次重试。
-
-
持久性(Durability)
-
本地暂存:发布者负责为晚到的订阅者保留数据。
-
易变:不尝试保留数据。
-
由于配置QoS还是比较麻烦的,我们通常对QoS的要求不是很高,这个时候一些默认的配置文件是很有必要的。
ROS2提供了如下的默认配置:
-
发布者和订阅者的默认QoS配置(Default QoS settings for publishers and subscribers)
-
服务(Services)
-
传感器数据(Sensor data)
-
参数服务器(Parameters)
-
系统默认(System default)
在相应的编程语言的对应的头文件里面都有相应的定义。
例如在Python中我们可以在import rclpy
使用这些默认的配置
rclpy.qos.qos_profile_sensor_data
rclpy.qos.qos_profile_system_default
rclpy.qos.qos_profile_services_default
实例
在ROS1中我们对于发布和订阅往往只需要指定消息缓冲池的大小即可。
我们使用ros2中gazebo的libgazebo_ros_ray_sensor插件发布一个消息类型为sensor_msgs/msg/LaserScan
的话题scan
,如果我们像下面这样使用ros1的那种方式来接收消息,这个时候我们就无法接收到消息。
import rclpy
from sensor_msgs.msg import LaserScandef main(args=None):rclpy.init(args=args)node = rclpy.create_node('laser_sub_node')sub = node.create_subscription(LaserScan, '/scan', lambda msg: node.get_logger().info('I get a msg'), 1)rclpy.spin(node)node.destroy_node()rclpy.shutdown()if __name__ == '__main__':main()
这个就是由于QoS的配置不对导致接收不到消息,我们只需要将QoS进行正确的配置就可以让我们正常的接收到消息了,只需对代码做出小小的修改就可以接收到消息了。
import rclpy
from sensor_msgs.msg import LaserScandef main(args=None):rclpy.init(args=args)node = rclpy.create_node('laser_sub_node')sub = node.create_subscription(LaserScan, '/scan', lambda msg: node.get_logger().info('I get a msg'), rclpy.qos.qos_profile_sensor_data)rclpy.spin(node)node.destroy_node()rclpy.shutdown()if __name__ == '__main__':main()
我们将该话题的QoS的配置为传感器推荐的一个配置,除此之外我们还可以使用qos_profile_system_default,在一般情况下,使用系统默认的是可以可以正常工作的,之前我们只输入了一个队列长度作为参数就会导致QoS配置出问题最终导致无法接收消息,现在我们采用专门为传感器设计的QoS配置文件或者系统默认的配置文件就可以正常工作了。
参考资料
- ROS2官方QoS配置简介https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings/.
- ROS2官方QoS设计简介https://design.ros2.org/articles/qos.html.