ROS2话题
版权信息
Copyright 2023 Herman Ye@Auromix. All rights reserved.
This course and all of its associated content, including but not limited to text,
images, videos, and any other materials, are protected by copyright law.
The author holds all rights to this course and its contents.
Any unauthorized use, reproduction, distribution, or modification of this course
or its contents is strictly prohibited and may result in legal action.
This includes, but is not limited to:
Copying or distributing course materials without express written permission.
Reposting, sharing, or distributing course content on any platform without proper attribution and permission.
Creating derivative works based on this course without permission.
Permissions and Inquiries
If you wish to use or reproduce any part of this course for purposes other than personal learning,
please contact the author to request permission.
The course content is provided for educational purposes, and the author makes no warranties or representations
regarding the accuracy, completeness, or suitability of the course content for any specific purpose.
The author shall not be held liable for any damages, losses,
or other consequences resulting from the use or misuse of this course.
Please be aware that this course may contain materials or images obtained from third-party sources.
The author and course creator diligently endeavor to ensure that these materials
are used in full compliance with copyright and fair use regulations.
If you have concerns about any specific content in this regard,
please contact the author for clarification or resolution.
By enrolling in this course, you agree to abide by the terms and conditions outlined in this copyright notice.
学习目标
- 熟悉ROS2的话题概念
- 了解ROS2话题相关的命令行工具操作
- 熟悉话题发布者与订阅者的代码编写
- 熟悉话题通信的测试手段
难度级别
初级 | 中级 | 高级 |
---|---|---|
√ |
预计耗时
40 mins
学习前提
对象 | 类型 | 状态 |
---|---|---|
ROS2 Humble | 软件 | 已安装 |
Ubuntu22.04操作系统 | 软件 | 已确认 |
Shell的基本使用 | 知识 | 已了解 |
ROS2 节点 | 知识 | 已了解 |
什么是话题
话题(Topic)是ROS 2中的一个关键概念,它在将复杂系统分解为模块化节点(Nodes)并在节点之间交换消息方面发挥着重要作用。
通过话题,节点能够实时共享信息,从而实现协同工作和分布式处理。消息在不同节点之间传递,这些消息可以包括传感器数据、控制指令、状态信息等,话题作为关键的通信桥梁,使它们能够高效地交换信息,以完成各自的任务。
举例来说,假设公司办公室挂着一块与项目A相关的会议时间白板,上面记录着今天的会议时间。行政同事负责将会议时间写在白板上,而与项目A相关的同事则会查看这块白板,以获取会议信息。在这个场景中,行政同事扮演Publisher的角色,项目A相关的会议时间白板充当Topic,而具体的会议时间则是Message,与项目A相关的同事则是Subscriber。
将这个情景与机器人应用相类比,深度相机在这里充当了Publisher的角色。它会持续发布相机采集的图像数据,每一帧图像数据都会被发布到相机数据的特定话题中。这个话题充当了信息传递的通道,就像会议时间白板一样。与此同时,其他的ROS组件,比如视觉算法节点,充当Subscribers,因为它们会订阅相机数据的话题,并不断从话题中接收图像消息,以进行进一步的图像处理和分析。这个发布-订阅机制使得机器人系统的不同部件能够协同工作,从而实现更复杂的任务和功能。
话题的特性
- 多对多通信
话题支持多个节点同时订阅和发布。这意味着多个节点可以参与到同一个话题中,实现数据的共享和交流。通信模式可以是一对一、一对多、多对一、多对多,具体取决于节点之间的关系和需求。
话题的工作方式类似于一个公共讨论话题,多个节点可以自由加入或退出该话题,以便在系统中传递数据。这种模块化的通信方式有助于提高系统的灵活性和可扩展性,使不同部分的节点能够独立开发和测试。
- 单向传递
话题的通信是单向的,信息从发布者流向订阅者。这意味着发布者负责将消息发送到话题,而订阅者负责接收和处理消息,实现单向的数据传递,接收者无法对接收到的信息做出反馈并返还给发布者。
话题的使用
观察话题的简单方法
你可以使用rqt图工具来可视化观察ROS2系统中的话题。
# Open rqt graph tool
rqt_graph
这个图示中展示了ROS2系统中的节点如何通过话题进行通信的示例。具体来说:
/teleop_turtle
节点发布键盘的速度数据到话题/turtle1/cmd_vel
。/turtlesim
节点订阅话题/turtle1/cmd_vel
以接收键盘的速度数据。
通过这个图示,你可以清晰地看到/turtlesim
节点和/teleop_turtle
节点之间是如何通过话题进行数据交换的。这个通信模型遵循ROS2中的发布-订阅模型,使不同节点能够轻松地交换信息。
话题命令工具的使用
查看话题列表
ros2 topic list
命令用于输出当前ROS 2系统中存在的话题(Topic)列表。这些话题是用于在ROS 2中进行通信和数据传输的通道。以下是使用ros2 topic list
命令的示例:
ros2 topic list
输出结果将显示当前存在的话题,通常以斜杠(/)开头,表示话题的命名空间。每个话题的名称旁边还会显示话题的消息类型。这有助于了解话题正在传输的数据的类型。
查看话题消息类型
如果需要查看话题列表中每个话题的消息类型,可以使用ros2 topic list -t
命令。这将同时输出话题列表和各个话题的消息类型(Message Type)。以下是使用ros2 topic list -t
命令的示例:
ros2 topic list -t
输出结果将列出话题的名称,然后在方括号内指定该话题的消息类型,以帮助用户了解话题传输的数据类型。
如果需要查看单个话题的消息类型,可以使用:
ros2 topic type /turtle1/cmd_vel
查看话题数据内容
如果想查看话题列表中特定话题发布的数据,可以使用ros2 topic echo命令,后跟所需话题的名称。此命令将显示来自该话题的消息数据,如果没有数据可供查看,它将保持打开状态,等待数据出现。
ros2 topic echo /turtle1/cmd_vel
输出结果将显示来自该话题的消息数据。在示例输出中,消息是使用YAML格式表示的,显示了线性和角速度等信息。
在命令行中发布话题
ros2 topic pub
命令能在终端中直接发布消息到指定话题,这对于需要在命令行中轻松进行调试的情况非常有用。下面是ros2 topic pub
的基本用法:
ros2 topic pub <topic_name> <msg_type> '<args>'
- 单次发布
通过使用–once参数,可以使发布仅执行一次,然后退出。消息内容通常以YAML语法格式提供,这可能在命令行中看起来比较复杂。以下是一个示例:
ros2 topic pub --once /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"
- 循环发布
通过在ros2 topic pub命令后添加–rate参数,可以指定消息发布的频率。例如,–rate 1表示发布消息以1Hz的频率。以下是一个示例:
ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"
查看话题自身的详细信息
ros2 topic info
命令用于查看指定话题的类型以及发布和订阅该话题的节点数。下面是ros2 topic info
的基本用法:
ros2 topic info /turtle1/cmd_vel
如果需要获取更详细的信息,可以启用-verbose
:
ros2 topic info /turtle1/cmd_vel -v
# ros2 topic info /turtle1/cmd_vel -verbose
查看话题发布频率
使用以下命令可以查看指定话题发布的平均频率(Hz):
ros2 topic hz /turtle1/cmd_vel
查看话题带宽占用
通过以下命令,你可以查看指定话题占用的带宽信息:
ros2 topic bw /turtle1/cmd_vel
通过消息类型反查该类型的话题
通过以下命令,你可以根据消息类型查找与该类型相关的话题:
ros2 topic find geometry_msgs/msg/Twist
查看话题延迟
使用这个命令来查看特定话题中消息时间戳的延迟,以了解消息何时被发布和接收。
很少使用,通常在调试时被使用,此处不涉及。
查看话题消息类型的具体结构
要查看特定话题消息类型的详细结构,可以使用以下命令:
ros2 interface show geometry_msgs/msg/Twist
该命令将显示关于消息类型"geometry_msgs/msg/Twist"
的详细信息,包括其字段和子字段的结构。这个消息类型通常用于描述机器人的运动命令,其中包括线性和角速度信息。
具体而言,该消息类型包含两部分:线性速度(linear)和角速度(angular)。每个部分都有三个子字段,分别是 x、y 和 z,它们分别表示在三个坐标轴上的分量。
话题的代码编写
发布者Publisher
1.新建功能包
首先,我们需要在ROS 2的工作空间内创建一个新的功能包,用于存放我们的发布者节点。可以按照以下步骤执行:
# Go to your src
cd ~/ros2_workspace/src
# Create package
ros2 pkg create --build-type ament_python ros2_learning
2.新建源码文件
在名为ros2_learning
的功能包中,我们需要创建一个新的源码文件,用于编写发布者节点的代码。可以按照以下步骤执行:
# Go to package
cd ros2_learning/ros2_learning/
# Create file
touch topic_demo_publisher.py
3.编写源码
在topic_demo_publisher.py
文件中,我们需要编写发布者节点的源码。
在ROS 2中,可以使用ROS2已提供的消息接口来定义消息类型,以便与其他节点通信,通常是标准的通信接口。
首先,你可以查看ROS2中已定义的消息接口列表,以了解可用的消息类型:
ros2 interface list
然后,你可以查看特定消息类型的结构,例如之后会用到的std_msgs/msg/String消息类型:
ros2 interface show std_msgs/msg/String
发布者编写步骤如下:
- 初始化编程接口。
- 创建节点并初始化。
- 创建发布者对象。
- 创建话题并填充话题消息。
- 发布话题消息。
- 销毁节点并关闭接口。
以下是发布者示例的源代码:
import rclpy # rclpy是ROS Client Library for Python的缩写,与ROS有关的内容在这个库中
from rclpy.node import Node
from std_msgs.msg import String # std_msgs是ROS内置的数据类型库,里面有msg、srv等一系列基础的数据类型
class TopicDemoPublisher(Node): # Node为这个类的父类
def __init__(self):
super().__init__("topic_demo_publisher") # 调用父类中Node的构造函数,并为其命名
# 创建发布者,数据类型为String,话题名为topic_name,队列长度为10,它是QoS (quality of service) setting之一
self.example_publisher = self.create_publisher(
String, "topic_name", 10
) # 创建发布者对象
timer_period = 0.5 # 定时器周期 in seconds
self.timer = self.create_timer(
timer_period, self.timer_callback
) # 创建定时器,每个周期调用一次回调函数self.timer_callback
self.i = 0 # 用于回调函数的一个计数器
def timer_callback(self): # 定时器的回调函数
msg = String() # 定义消息类型为String类
msg.data = "Hello World: %d" % self.i # 添加消息内容
self.example_publisher.publish(msg) # 发布消息
self.get_logger().info('Publishing: "%s"' % msg.data) # 打印日志到控制台console
self.i += 1
def main(args=None):
rclpy.init(args=args) # 初始化 rclpy编程接口
test_topic_demo_publisher = TopicDemoPublisher() # 实例化TopicDemoPublisher类
rclpy.spin(test_topic_demo_publisher) # 循环
# 显式销毁节点
# (可选 - 否则在垃圾回收器销毁节点对象时会自动执行)
test_topic_demo_publisher.destroy_node() # 可以不写,会自动执行
rclpy.shutdown() # 关闭
if __name__ == "__main__":
main()
这段代码演示了如何创建一个ROS 2发布者(Publisher)节点,以发布字符串消息到名为"topic_name"的ROS话题。在此过程中,还包括了初始化ROS 2接口、创建节点、创建发布者对象、发布消息、以及关闭节点的过程。
4.添加依赖
在功能包目录下,打开package.xml文件,以确保功能包的依赖正确配置。
依赖声明对于确保功能包在运行时具有所需的组件和库非常重要。以下是一个示例:
<description>Examples of minimal publisher/subscriber using rclpy</description>
<maintainer email="hermanye233@icloud.com">Herman Ye</maintainer>
<license>Apache License 2.0</license>
接下来,必须明确声明功能包的依赖项。这些声明会告诉ROS 2,当功能包的代码被执行时,需要哪些其他功能包来提供支持。在<depend>
标签中添加以下内容:
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
5.添加入口点
打开功能包的setup.py文件,以添加入口点,这将定义功能包中的可执行命令。入口点对于在ROS 2中运行功能包的节点非常重要。以下是一个示例:
maintainer='Herman Ye',
maintainer_email='hermanye233@icloud.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache License 2.0',
然后,在 entry_points 部分添加以下命令,这将告诉ROS 2 如何运行节点:
'talker = ros2_learning.topic_demo_publisher:main',
这一行代码中,‘talker’ 是要分配给节点的名称,而 ‘ros2_learning.topic_demo_publisher:main’ 是节点的入口点。
6.检查setup.cfg
确保检查setup.cfg文件,它是自动生成的,并定义了可执行文件的位置,以便ROS 2能够正确查找和运行这些文件。以下是一个示例:
[develop]
script_dir=$base/lib/ros2_learning
[install]
install_scripts=$base/lib/ros2_learning
订阅者 subscriber
1.新建源码文件
首先,在功能包的src文件夹下新建一个名为 topic_demo_subscriber.py
的源码文件,可以使用以下命令:
touch topic_demo_subscriber.py
2.编写源码
- 订阅者编写步骤
1.编程接口初始化
2.创建节点并初始化
3.创建订阅者对象
4.通过回调函数处理话题数据
5.销毁节点、关闭接口
下面是一个订阅者的示例源码:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class TopicDemoSubscriber(Node):
def __init__(self):
super().__init__("topic_demo_subscriber")
# 订阅者的构造函数和回调函数不需要定时器Timer,因为当收到Message时将启动回调函数
# 注意此处不是subscriber,而是subscription
# 数据类型,话题名,回调函数,队列长度
self.subscription = self.create_subscription(
String, "topic_name", self.subscriber_callback, 10
)
self.subscription # 防止代码中该变量未使用的警告
def subscriber_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data) # 回调函数内容为打印msg.data里的内容
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
topic_demo_subscriber = TopicDemoSubscriber() # 创建订阅者实例
rclpy.spin(topic_demo_subscriber) # 循环
topic_demo_subscriber.destroy_node() # 销毁节点
rclpy.shutdown() # 关闭rclpy
if __name__ == "__main__":
main()
3.添加依赖
之前已经添加过依赖项,那么不需要再次添加它们。
然而,如果订阅者(subscriber)需要使用额外的库,则需要添加这些新的库以满足其要求。
4.添加入口点
在这一步,我们将配置项目的入口点,以便运行项目的不同组件。我们将修改setup.py文件,以添加所需的入口点。
打开setup.py
文件,并定位到entry_points
部分,然后添加以下命令:
'listener = ros2_learning.topic_demo_subscriber:main',
ros2 run 在功能包中所能观测到的可执行文件的名字即此处设置的talker与listener
5.编译工作空间
在最后,编译工作空间,相关命令在之后的课程里会讲解,此处不涉及。
# Go to your workspace
cd ~/ros2_workspace
# Build workspace
colcon build --symlink-install
测试话题通信
ros2 run ros2_learning listener
ros2 run ros2_learning talker