Skip to content

理解 ROS 2 话题

目标: 使用 rqt_graph 和命令行工具查看 ROS 2 话题。

教程级别: 初级

预计用时: 20 分钟

ROS 2 将复杂系统分解为多个模块化的节点。话题(topic)是 ROS graph 的重要组成部分,充当节点之间交换消息的总线。

Topic-SinglePublisherandSingleSubscriber

一个节点可以向任意数量的话题发布数据,同时也可以订阅任意数量的话题。

Topic-MultiplePublisherandMultipleSubscriber

话题是节点之间、乃至整个系统不同部分之间传输数据的主要方式。

上一篇教程介绍了有关节点的背景知识,本教程将在此基础上展开。

与往常一样,每次打开新终端时不要忘记 source ROS 2。

到目前为止,你应该已经熟悉如何启动 turtlesim 了。

打开一个新终端并运行:

Terminal window
$ ros2 run turtlesim turtlesim_node

打开另一个终端并运行:

Terminal window
$ ros2 run turtlesim turtle_teleop_key

回顾上一篇教程,默认情况下这些节点的名称分别是 /turtlesim 和 /teleop_turtle。

在本教程中,我们将使用 rqt_graph 可视化不断变化的节点和话题,以及它们之间的连接关系。

turtlesim 教程中介绍了如何安装 rqt 及其所有插件,包括 rqt_graph。

要运行 rqt_graph,打开一个新终端并输入命令:

Terminal window
$ ros2 run rqt_graph rqt_graph

你也可以通过打开 rqt 然后选择 Plugins > Introspection > Node Graph 来打开 rqt_graph。

rqt_graph

你应该能看到上述节点和话题,以及图中外围的两个动作(暂且忽略它们)。将鼠标悬停在中心的话题上,会看到如上图所示的高亮效果。

该图展示了 /turtlesim 节点和 /teleop_turtle 节点如何通过一个话题相互通信。/teleop_turtle 节点向 /turtle1/cmd_vel 话题发布数据(即你输入的移动乌龟的按键),而 /turtlesim 节点订阅该话题来接收数据。

当检查有许多节点和话题以多种不同方式连接的复杂系统时,rqt_graph 的高亮功能非常有用。

rqt_graph 是图形化查看工具。接下来看看用于查看话题的命令行工具。

在新终端中运行 ros2 topic list 命令,将返回系统中当前所有活跃的话题:

Terminal window
$ ros2 topic list
/parameter_events
/rosout
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

ros2 topic list -t 返回的话题列表相同,但会在括号中附加话题类型:

Terminal window
$ ros2 topic list -t
/parameter_events [rcl_interfaces/msg/ParameterEvent]
/rosout [rcl_interfaces/msg/Log]
/turtle1/cmd_vel [geometry_msgs/msg/Twist]
/turtle1/color_sensor [turtlesim_msgs/msg/Color]
/turtle1/pose [turtlesim_msgs/msg/pose]

这些属性(尤其是类型)让节点能够确认彼此通过话题传输的是同一类信息。

如果你想知道这些话题在 rqt_graph 中的位置,可以取消勾选 Hide: 下的所有复选框。

unhide

不过现在请保持这些选项勾选,以免界面过于杂乱。

要查看某个话题上正在发布的数据,请使用:

Terminal window
$ ros2 topic echo <topic_name>

既然我们知道 /teleop_turtle 通过 /turtle1/cmd_vel 话题向 /turtlesim 发布数据,就用 echo 来查看该话题:

Terminal window
$ ros2 topic echo /turtle1/cmd_vel

起初这个命令不会返回任何数据,因为它在等待 /teleop_turtle 发布内容。

回到运行 turtle_teleop_key 的终端,用方向键移动乌龟。同时观察运行 echo 的终端,你会看到每次移动时发布的速度数据:

Terminal window
linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
---

现在回到 rqt_graph 并取消勾选 Debug 复选框。

debug

/_ros2cli_26646 是刚才运行的 echo 命令创建的节点(数字可能不同)。现在你可以看到发布者通过 cmd_vel 话题发布数据,而有两个订阅者订阅了它。

话题的通信不局限于一对一,也可以是一对多、多对一甚至多对多。

另一种查看方式是运行:

Terminal window
$ ros2 topic info /turtle1/cmd_vel
Type: geometry_msgs/msg/Twist
Publisher count: 1
Subscription count: 2

要获取话题更详细的信息,可以使用 --verbose(或 -v)选项:

Terminal window
$ ros2 topic info /turtle1/cmd_vel --verbose

这将返回额外的详细信息,包括:

  • 发布者和订阅者的节点名称与命名空间
  • 话题类型
  • QoS profile
Terminal window
Type: geometry_msgs/msg/Twist
Publisher count: 1
Node name: teleop_turtle
Node namespace: /
Topic type: geometry_msgs/msg/Twist
Topic type hash: RIHS01_9c45bf16fe0983d80e3cfe750d6835843d265a9a6c46bd2e609fcddde6fb8d2a
Endpoint type: PUBLISHER
GID: 24.ba.3e.e7.c1.51.bb.46.21.41.de.36.1b.14.73.5e
QoS profile:
Reliability: RELIABLE
History (Depth): KEEP_LAST (7)
Durability: VOLATILE
Lifespan: Infinite
Deadline: Infinite
Liveliness: AUTOMATIC
Liveliness lease duration: Infinite
Subscription count: 2
Node name: _ros2cli_300492
Node namespace: /
Topic type: geometry_msgs/msg/Twist
Topic type hash: RIHS01_9c45bf16fe0983d80e3cfe750d6835843d265a9a6c46bd2e609fcddde6fb8d2a
Endpoint type: SUBSCRIPTION
GID: cc.4d.98.79.29.91.fe.25.8a.0a.c9.03.db.1a.ec.81
QoS profile:
Reliability: RELIABLE
History (Depth): KEEP_LAST (5)
Durability: VOLATILE
Lifespan: Infinite
Deadline: Infinite
Liveliness: AUTOMATIC
Liveliness lease duration: Infinite
Node name: turtlesim
Node namespace: /
Topic type: geometry_msgs/msg/Twist
Topic type hash: RIHS01_9c45bf16fe0983d80e3cfe750d6835843d265a9a6c46bd2e609fcddde6fb8d2a
Endpoint type: SUBSCRIPTION
GID: 9c.33.59.38.b2.f2.42.47.69.1b.7f.0e.5e.1d.86.f5
QoS profile:
Reliability: RELIABLE
History (Depth): KEEP_LAST (7)
Durability: VOLATILE
Lifespan: Infinite
Deadline: Infinite
Liveliness: AUTOMATIC
Liveliness lease duration: Infinite

节点通过话题发送消息。发布者和订阅者必须发送和接收相同类型的消息,才能正常通信。

之前运行 ros2 topic list -t 看到的话题类型,就告诉我们每个话题使用的消息类型。回顾一下,cmd_vel 话题的类型是:

Terminal window
geometry_msgs/msg/Twist

这意味着在 geometry_msgs 包中有一个名为 Twist 的消息。

现在我们可以对该类型运行 ros2 interface show <msg_type> 来了解其详细信息,即消息所包含的数据结构。

Terminal window
$ ros2 interface show geometry_msgs/msg/Twist

将返回:

# This expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
float64 x
float64 y
float64 z
Vector3 angular
float64 x
float64 y
float64 z

这说明 /turtlesim 节点期望接收一个包含两个向量(linear 和 angular)、各含三个元素的消息。回想一下我们用 echo 命令看到的 /teleop_turtle 传递给 /turtlesim 的数据,其结构与此完全相同:

Terminal window
linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
---

现在你已经了解了消息的结构,可以用以下命令直接从命令行向话题发布数据:

Terminal window
$ ros2 topic pub <topic_name> <msg_type> '<args>'

'<args>' 参数是你要传递给话题的实际数据,其结构就是上一节中看到的内容。

pub 命令主要有以下四种用法。不过,下面 c. 和 d. 中描述的自动补全功能在 Windows 上不受支持。

a. 发布字典字符串:

要向话题发布数据,需要以 YAML 字符串的形式传递数据。

Terminal window
$ ros2 topic pub /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}}"

不过,你不需要指定整个消息;如果只想改变线速度或角速度,只需指定要修改的值即可。

例如,如果要将线速度改为 2.0 并保持角速度为 1.8,可以这样操作:

Terminal window
$ ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0}, angular: {z: 1.8}}"

b. 发布空消息:

Terminal window
$ ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist

这将以 1 Hz 的频率发布该消息类型的默认值。这种情况下,等同于以下命令:

Terminal window
$ ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}" --rate 1

c. 使用自动补全:

你可以通过以下方式触发终端的自动补全功能:

Terminal window
$ ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist <TAB>
--keep-alive
--max-wait-time-secs
--node-name
--once
--print
--qos-depth
--qos-durability
--qos-history
--qos-liveliness
--qos-liveliness-lease-duration-seconds
--qos-profile
--qos-reliability
--rate
--spin-time
--stdin
--times
--use-sim-time
--wait-matching-subscriptions
--yaml-file
-1
-n
-p
-r
-s
-t
-w
\'linear:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^Jangular:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^J\'

所有选项都可以在输入前几个字符后按 tab 键自动补全。但话题消息的原型只有在输入 \'<TAB> 之后才会自动补全。

这是因为终端不会将单引号识别为自动补全字符串的一部分。因此,需要用 \' 转义才能将其识别为字符串的一部分。

最终自动补全的字符串如下所示:

Terminal window
ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist 'linear:
x: 0.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
'

这个字符串可以编辑,你可以根据需要修改消息中各字段的值。

d. 使用原始自动补全字符串:

如上所述,geometry_msgs/msg/Twist 的自动补全字符串如下所示:

Terminal window
\'linear:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^Jangular:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^J\'

这可以直接用来替代命令行中的 YAML 字符串。

Terminal window
$ ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist \'linear:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^Jangular:\^J\ \ x:\ 0.0\^J\ \ y:\ 0.0\^J\ \ z:\ 0.0\^J\'

乌龟(以及它所模拟的真实机器人)需要持续的命令流才能保持运动。因此,要让乌龟移动并持续移动,可以使用以下字典字符串:

Terminal window
$ ros2 topic pub /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}}"

pub_stream

有时你可能只想向话题发布一次数据(而非持续发布)。要只发布一次命令,请添加 --once 选项。

Terminal window
$ ros2 topic pub --once -w 2 /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}}"

--once 是一个可选参数,意思是”发布一条消息然后退出”。

-w 2 是一个可选参数,意思是”等待两个匹配的订阅者”。这里必须设置,因为 turtlesim 和 topic echo 都订阅了这个话题。

你会在终端中看到以下输出:

Terminal window
Waiting for at least 2 matching subscription(s)...
publisher: beginning loop
publishing #1: geometry_msgs.msg.Twist(linear=geometry_msgs.msg.Vector3(x=2.0, y=0.0, z=0.0), angular=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=1.8))

你会看到乌龟这样移动:

pub_once

你可以刷新 rqt_graph,以图形方式查看发生了什么。你会看到 ros2 topic pub ... 节点(/_ros2cli_30358)正在通过 /turtle1/cmd_vel 话题发布数据,现在该数据同时被 ros2 topic echo ... 节点(/_ros2cli_26646)和 /turtlesim 节点接收。

rqt_graph2

最后,可以在 pose 话题上运行 echo 并重新查看 rqt_graph:

Terminal window
$ ros2 topic echo /turtle1/pose

rqt_graph3

可以看到 /turtlesim 节点也在向 pose 话题发布数据,而新的 echo 节点已订阅了它。

在发布带有时间戳的消息时,pub 有两种方法可以用当前时间自动填充时间戳。对于包含 std_msgs/msg/Header 的消息,可以将 header 字段设置为 auto 来填充 stamp 字段。

Terminal window
$ ros2 topic pub /pose geometry_msgs/msg/PoseStamped '{header: "auto", pose: {position: {x: 1.0, y: 2.0, z: 3.0}}}'

如果消息没有使用完整的 header,但有一个类型为 builtin_interfaces/msg/Time 的字段,可以将其设置为 now。

Terminal window
$ ros2 topic pub /reference sensor_msgs/msg/TimeReference '{header: "auto", time_ref: "now", source: "dumy"}'

你还可以使用以下命令查看数据发布的频率:

Terminal window
$ ros2 topic hz /turtle1/pose
average rate: 59.354
min: 0.005s max: 0.027s std dev: 0.00284s window: 58

返回的是 /turtlesim 节点向 pose 话题发布数据的频率。

回顾一下,之前你用 ros2 topic pub --rate 1 将 turtle1/cmd_vel 的发布频率设置为稳定的 1 Hz。如果用 turtle1/cmd_vel 而非 turtle1/pose 运行上述命令,会看到反映该频率的平均值。

注意:该频率反映的是 ros2 topic hz 命令创建的订阅者上的接收频率,可能会受到平台资源和 QoS 配置的影响,不一定与发布者的频率完全匹配。

可以使用以下命令查看话题使用的带宽:

Terminal window
$ ros2 topic bw /turtle1/pose
Subscribed to [/turtle1/pose]
1.51 KB/s from 62 messages
Message size mean: 0.02 KB min: 0.02 KB max: 0.02 KB

返回的是带宽使用情况以及发布到 /turtle1/pose 话题的消息数量。

注意:该带宽反映的是 ros2 topic bw 命令创建的订阅者上的接收带宽,可能会受到平台资源和 QoS 配置的影响,不一定与发布者的带宽完全匹配。

要列出给定类型的所有可用话题,请使用:

Terminal window
$ ros2 topic find <topic_type>

回顾一下,cmd_vel 话题的类型是:

Terminal window
geometry_msgs/msg/Twist

使用 find 命令可以根据给定的消息类型输出可用的话题:

Terminal window
$ ros2 topic find geometry_msgs/msg/Twist
/turtle1/cmd_vel

此时你会有很多节点在运行。不要忘记在每个终端中按 Ctrl+C 停止它们。

节点通过话题发布信息,任意数量的其他节点都可以订阅并获取该信息。在本教程中,你使用 rqt_graph 和命令行工具检查了多个节点之间通过话题建立的连接。现在你应该对数据在 ROS 2 系统中如何流动有了清晰的认识。

接下来,你将通过教程学习 ROS graph 中的另一种通信类型。