硬件可以类比成饭店各个干活的员工,ROS2 就是那个管消息分发、任务派发、协调所有人干活的总调度,让一堆零散硬件 / 算法模块能够互相配合,变成完整能干活的机器人。

ROS2 框架是实现机器人不同部件之间通信的“基础架构”。它包括消息传递、标准接口,以及对多种编程语言和平台的支持。在使用 ROS2 开发或维护应用程序之前,需要先了解框架的基本概念。

ROS2 提供了一套 turtlesim 工具用于快速入门,通过终端调用即可完成简易 demo,这个 CLI 就是终端的意思。

1 使用 turtlesim 小海龟和 rqt

turtlesim 小海龟这玩意儿基本上可以说是贯穿了 ROS2 的学习了。ROS2 提供的这个小海龟其实就是可以将它视为虚拟的仿真的机器人,在一个简单的 2D 可视化仿真界面学习 ROS2。

rqt 是 ROS2 提供的一个图形化用户交互工具,rqt 可以做所有终端能做的事,而且提供了友好的用户交互界面。

1.1 安装 turtlesim 和 rqt

sudo apt update
sudo apt install ros-humble-turtlesim ros-humble-rqt ros-humble-rqt-common-plugins

安装完成后在终端输入 ros2 pkg executables turtlesim,输出以下内容说明小海龟安装成功。

输入 rqt 可以出现一个图形化界面说明 rqt 安装成功。

1.2 启动小海龟

通过 ros2 run turtlesim turtlesim_node​指令启动小海龟,小海龟的颜色是随机生成的, 每一次启动都不一样。

1.3 控制小海龟

ros2 提供了一个键盘工具,可以通过键盘来控制小海龟的移动。再打开一个终端,输入这个指令就行

ros2 run turtlesim turtle_teleop_key​

鼠标点击新开的这个控制的终端,使其保持在最上层,通过键盘的上下左右就可以控制小海龟的移动和方向了

那么,玩这个小海龟有什么意义呢?不妨想想,明明是开了两个不同的终端,为什么在新开的这个终端输入上下左右就可以控制小海龟移动?这里就可以引出 ROS2 的核心概念:node 节点、topic 话题、service 服务以及 action 动作的几大知识点了。

再打开一个终端,输入以下指令可以分别查看都有哪些相关的信息:

ros2 node list
ros2 topic list
ros2 service list
ros2 action list

  • node:可以看到只有 2 个,对应的就是刚才通过两个终端指令启动的 node 节点,一个是小海龟,另一个是监听键盘输入的节点。

  • topic:可以看到 /turtle1 开启了 3 个话题,另外 2 个话题先不管

  • service:可以看到有大量的服务

  • action:可以看到小海龟有一个动作

上面这些内容现在还不需要深入了解,只要知道通过终端开启了这个小海龟,就默默帮我们在后台启动了这么多的节点、话题、服务、动作等内容。

1.4 使用 rqt 操作

前面提到过,rqt 就是方便使用的一套可视化工具,集成了所有的终端指令工具,相当于一台上位机程序。在新建终端中输入 rqt 启动工具,但是启动后会看到其实没有内容,这时候需要选择 Plugins > Services > Service Caller 会开启一个 service caller 界面,可以选择各种服务,点击右侧的 call 即可进行服务的调用。

service 选择 /clear,点击一下 call 调用,会发现小海龟之前行走的路径被清除了,下方还有返回的结果

例如将 service 切换到 /spawn,可以在 request 中配置相关的参数,点击 call 调用,就会发现生成了一个新的小海龟。

这些服务对应的就是前面 ros2 service list 中罗列出来的所有服务,可以再试试调用别的服务来玩玩,加深对于 service 的理解。

所以目前可看出,服务相当于是一个函数,给这个服务提供指定的参数就可以实现相应的功能。

2 ROS2 的主要通信组件

2.1 节点 Node

在 ROS 中,每个节点都负责一个单一的模块化的功能。类比饭店的例子,后厨就是一个 node 节点,主要负责炒菜;前台也是一个 node 节点,主要负责引导。对应到机器人中,控制车轮电机的任务可以视为是一个节点,发布激光传感器数据的任务可以视为一个节点。 我的理解是,节点不是对应某一个硬件设备,而应该是对应一项任务,但是也许既可以对应一项任务,也可以对应一个具体的硬件设备,好像没有人对节点应该对应什么有一个明确的定义,或许需要有经验的人来做个总结吧。

每个 node 节点都可以使用 topic、service、action 进行通信,下图就能很好地描述这个过程。右上角这个 node 作为一个 publisher 可以将 message 发送到 topic,再转发给所有订阅了这个 topic 的 subscriber。

Nodes-TopicandService

还是看之前小海龟的例子,通过 ros2 run turtlesim turtlesim_node 启动小海龟

  • ros2 run <package_name> <executable_name>

    使用 ros2 run 指令,输入对应的 包名可执行命令 的名称,就可以执行相关的功能,关于小海龟这玩意,turtlesim 就是小海龟这个包名,turtlesim_node 就是这个包提供的一个可行性的命令。

  • 再新建一个终端输入 ros2 node list​,可以看到一个 /turtlesim 的 node 节点

  • ros2 node info <node_name>

    该指令可以查看节点的详细信息,例如输入 ros2 node info /turtlesim,可以看到这个节点有 subscribers、publishers、service servers、service clients、action clients 等内容。这些都是对应这个节点所包含的 topic 话题、service 服务与 action 动作的配置情况。

2.2 话题 topic

ROS 2 将复杂系统分解为许多模块化节点,topic 话题充当节点之间交换消息的总线

Topic-MultiplePublisherandMultipleSubscriber1-20260819081917-wtjv3wf

从图中可以很直观看出,一个节点可以作为发布者也可以作为订阅者发布者订阅者都关注了某个话题发布者可以在话题上推送消息,推送的消息会被发送给所有订阅者

我感觉这个图只是做了简化,实际上一个节点既可以是发布者也可以是订阅者,也可以同时关注非常多的话题。其实这个话题相当于是一个微信公众号,这样就好理解了。

看看小海龟里面的话题订阅情况,启动小海龟 ros2 run turtlesim turtlesim_node​,启动键盘监听事件:ros2 run turtlesim turtle_teleop_key

通过 ros2 topic list 可以看到当前启动的话题:

使用 rqt_graph 可以直接看到话题的订阅情况:

椭圆是一个 node 节点,单独一个矩形就是对应一个 topic 话题,话题外层套了一层命名空间 /turtle1 将话题包起来,用于和其他可能出现的同名的话题做区分。

不过 /turtle1/cmd_vel 我理解,是控制小海龟移动的话题,为啥 color_sensor 还有 pose 的话题怎么就没出现呢?反而多了什么 rotate_absolute。不管了。

  • topic 话题的一些 CLI 指令的使用

    • 话题传输的消息类型

      ros2 topic list -t​ 在列举话题列表的时候加入 -t 参数就行

      $ 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/msg/Color]
      /turtle1/pose [turtlesim/msg/Pose]
      
    • 订阅话题,获取实时数据

      ros2 topic echo <topic_name>

      例如想看键盘实时输入的数据,那就 ros2 topic echo /turtle1/cmd_vel

    • 查看话题信息

      ros2 topic info <topic_name>

      例如:ros2 topic info /turtle1/cmd_vel,输出的内容就是这个话题的消息类型、发布者数量、订阅者数量

      Type: geometry_msgs/msg/Twist
      Publisher count: 1
      Subscription count: 1
      

      如果要看更加详细的信息,加上一个 --verbose​(这个 --verbose​在很多 Linux 指令中都是代表详细信息的意思,verbose 实际上是冗长的意思),输入 ros2 topic info /turtle1/cmd_vel --verbose,输出结果如下:

      Type: geometry_msgs/msg/Twist
      
      Publisher count: 1
      
      Node name: teleop_turtle
      Node namespace: /
      Topic type: geometry_msgs/msg/Twist
      Endpoint type: PUBLISHER
      GID: 01.0f.27.28.e5.1e.9c.88.00.00.00.00.00.00.12.03.00.00.00.00.00.00.00.00
      QoS profile:
        Reliability: RELIABLE
        History (Depth): UNKNOWN
        Durability: VOLATILE
        Lifespan: Infinite
        Deadline: Infinite
        Liveliness: AUTOMATIC
        Liveliness lease duration: Infinite
      
      Subscription count: 1
      
      Node name: turtlesim
      Node namespace: /
      Topic type: geometry_msgs/msg/Twist
      Endpoint type: SUBSCRIPTION
      GID: 01.0f.27.28.98.1e.90.0f.00.00.00.00.00.00.1b.04.00.00.00.00.00.00.00.00
      QoS profile:
        Reliability: RELIABLE
        History (Depth): UNKNOWN
        Durability: VOLATILE
        Lifespan: Infinite
        Deadline: Infinite
        Liveliness: AUTOMATIC
        Liveliness lease duration: Infinite
      

      这里面信息量很多,都会在后续慢慢学到,简单了解一下即可。

    • 消息的具体数据类型

      前面虽然是列出了消息的数据类型,但是还是很抽象怎么办?geometry_msgs/msg/Twist 谁能看得懂?

      所以可以通过一个查看接口信息的指令来了解数据类型:

      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
      

      这也是为什么在 ros2 topic echo /turtle1/cmd_vel 的时候返回的数据类型是那样的了

    • 在话题发布消息

      ros2 topic pub <topic_name> <msg_type> '<args>'

      前面知道了话题,也知道了话题传输的消息的数据类型,那么通过 pub 就可以往话题发送消息,例如:

      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}}"
      

      上面的指令会一直往话题发送消息,默认是 1Hz​的频率,如果只想发送一下,只要加一个 --once 的参数即可。

    • 监听话题消息频率

      ros2 topic hz <topic_name>

      该指令可以监听话题的消息的频率,最短间隔多长时间、最长间隔多长时间。

    • 话题数据带宽

      ros2 topic bw <topic_name>

      返回话题消息的带宽,现在传的都是较短的数据,如果后续需要传输图像、视频数据,那就很有意义,可以看出有多少的带宽了。

    • 根据话题类型查找话题

      ros2 topic find <topic_type>

      可以罗列出用到了这个消息类型的话题。不过不知道可能什么时候能用到。

2.3 服务 service

service​服务是 ROS 节点之间的另一种通信方式。服务采用请求‑应答模型,这与话题的发布‑订阅模型有所区别。话题允许节点订阅数据流并持续获取更新,而服务仅在被客户端显式调用时才提供数据。简而言之就是,可以多对多连接,但是通信时候是一对一的,且有请求才有相应。

Service-MultipleServiceClient

类似的,service 也有和 topic 类似的 CLI 指令,如:

ros2 service list					# 列举运行的服务
ros2 service type <service_name>	# 某个服务的消息类型
ros2 service list -t				# 列举运行的服务,并显示数据类型
ros2 service find <type_name>		# 根据服务数据类型找到服务
ros2 interface show <type_name>		# 查看服务数据类型的详细信息

不同于 topic,service 要使用的时候是通过调用的方式:

ros2 service call <service_name> <service_type> <arguments>
ros2 service call /clear std_srvs/srv/Empty

这个指令和 1.4 使用 rqt 操作 提到的使用可视化界面发送的 /clear 指令是一样的效果。

2.4 参数 parameter

参数是节点的配置值,可以把参数理解为节点的设置项。节点可以将参数存储为整数、浮点数、布尔值、字符串以及列表。在 ROS 2 中,每个节点维护属于自己的一套参数。

ros2 param list									# 查看有哪些参数
ros2 param get <node_name> <parameter_name>		# 获取某个节点的参数值
ros2 param set <node_name> <parameter_name> <value>		# 给某个节点设置参数值
ros2 param dump <node_name>						# 将某个节点的参数打印出来,结合 >> xxx.yaml 可以输出到文件
ros2 param load <node_name> <parameter_file>	# 给节点加载参数

# 节点启动的时候加载参数值
ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>

2.5 动作 action

动作(Actions)是 ROS 2 中的一种通信类型,专门用于处理长时间运行的任务。动作由三部分组成:目标(goal)、反馈(feedback)和结果(result) 。这三个部分需要牢记,后续编写程序也会用到。

目标(goal):某个动作的目标

反馈(feedback):动作执行过程中的反馈值

结果(result):动作执行完成后返回的结果

动作基于话题与服务实现。它的功能和服务类似,但动作支持取消任务;同时动作可以持续输出反馈,而服务仅返回单次应答。Action 动作实际上就是用 topic 和 service 拼出来,因为该功能比较常用,所以 ROS 专门封装了 Action 这套机制如果要手动来实现对应的功能也行,但是有点重复造轮子,不提倡了。

动作采用客户端‑服务器模型,和发布‑订阅模型有相似之处。动作客户端节点向动作服务端节点发送目标;动作服务端接收确认目标后,会持续返回反馈数据流,最后给出最终结果。

Action-SingleActionClient

在小海龟的例子中,通过 ros2 action list​可以看到系统当前正在运行的 action 动作,根据名称可以看出这是一个旋转的案例,通过前面查看节点信息的指令查看小海龟 /turtlesim​的相关节点 ros2 node info /tutlesim 可以看到启动了一个 action 服务器

/teleop_turtle​节点中,可以看到是一个 action 的客户端

也就是说,二者之间是通过这个 /turtle1/rotate_absolute 的 action 来进行通信的

看看其数据类型 ros2 interface show turtlesim/action/RotateAbsolute​,可以看到返回了三个参数,用 2 个 --- 分隔符分开,这些参数从上到下依次是:goal、feedback、result。

  • 同样的,这个 action 也是有类似 topic、serve 相似的指令操作

    ros2 action list			# ros2 中的所有 action
    ros2 action list -t			# ros2 中的所有 action,并显示数据类型
    ros2 action info			# 查看ros2的详细信息
    
  • action 的相关操作

    • 往 goal 发送数据

      ros2 action send_goal <action_name> <action_type> <values>

      例如要让小海龟旋转到方向向右,输入的值即为 0。这个 theta 的值是弧度,弧度 0 的时候就是朝向 x 轴方向。

      ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 0}"
      

      输入指令可以看到小海龟旋转,并返回旋转之后的最终朝向

3 ROS2 中的日志功能

ros2 中提供了 rqt_console 的 GUI 工具,用于查看有关系统运行状况的信息。借助 rqt_console,你可以持续收集这些消息,以更规整的方式仔细查看、过滤消息,保存消息,甚至重新加载已保存的文件,在其他时间进行分析。

在小海龟中,如果小海龟移动撞墙了,就会有大量的警告,而使用 rqt_console 就可以可视化显示这些警告信息。

执行以下指令:ros2 topic pub -r 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: 0.0}}"

当小海龟撞墙以后,可以看到弹出非常多的 Warn,这就是日志中的警告功能。

但是实际上运行小海龟的终端一样会输出警告的日志,只是更少一点:

日志分为 5 个等级,根据不同的需求使用日志:

  1. Fatal(致命)消息:表示系统即将终止运行,力求保护自身免受损害。
  2. Error(错误)消息:代表严重问题,这类问题不一定会损坏系统,但会阻碍系统正常工作。
  3. Warn(警告)消息:提示出现异常行为或非理想结果,背后可能存在更深层问题,但不会直接破坏功能。
  4. Info(信息)消息:记录事件与状态更新,用于直观确认系统按预期运行。
  5. Debug(调试)消息:详细输出系统执行完整的分步过程。

可以通过配置 --log-level​来修改日志的等级,执行以下指令将把日志的输出等级修改到 WARN 等级,也就是说,只有 WarnErrorFatal 三个等级被使用。

ros2 run turtlesim turtlesim_node --ros-args --log-level WARN

4 使用 launch 文件

在大多数教程中,每运行一个新节点,你都要打开一个新终端。当你构建愈发复杂的系统,需要同时运行大量节点时,反复打开终端、重新输入配置信息就会变得十分繁琐。

启动文件(launch file)可以同时启动并配置多个包含 ROS 2 节点的可执行程序。

例如:ros2 launch turtlesim multisim.launch.py,这是 ROS 的演示样例,该指令将同时启动两个小海龟

launch​主要是使用 launch​文件进行配置的,这个 multisim.launch.py 文件的内容如下:

from launch import LaunchDescription
import launch_ros.actions


def generate_launch_description():
    return LaunchDescription([
        launch_ros.actions.Node(
            namespace='turtlesim1', package='turtlesim',
            executable='turtlesim_node', output='screen'),
        launch_ros.actions.Node(
            namespace='turtlesim2', package='turtlesim',
            executable='turtlesim_node', output='screen'),
    ])

先不管这个代码,知道可以通过代码来控制 launch 文件来启动多个节点就行了。

5 数据记录和数据回放

ros2 bag 是一款命令行工具,用于记录系统中各个话题上发布的数据。它收集任意数量话题传输的数据,并将其保存到数据库中。之后你可以回放这些数据,复现测试与实验结果。记录话题数据也非常适合用于分享你的工作,方便其他人复现你的实验。

用法:

  • 记录指定话题的数据

    • ros2 bag record <topic_name>
    • 将在终端当前所在目录下,记录指定话题的产生的数据
  • 同时记录多个话题的数据

    • ros2 bag record -o <记录文件名称> <topic1_name> <topic2_name>
  • 查看记录的话题的数据

    • ros2 bag info <bag_file_name>

  • 复现所记录的话题

    • ros2 bag play subset

6 参考文献

  1. Beginner: CLI tools — ROS 2 Documentation: Humble documentation