RuyiSDK Board Docs

RISC-V ROS2 机器人操作系统编程技术

Source repository

ch01 · ROS 2 概述与架构设计

TextbookProgramming languageEnvironmentLessonLab
RISC-VC++17SpacemiT K3 CoM260 Kit / Bianbu 4.0.6 / Humble, with an x86 Ubuntu 22.04 / Humble / Harmonic course containerRead lessonStart lab
x86PythonUbuntu 22.04 / Humble or Ubuntu 24.04 / JazzyRead lessonStart lab

This document is not yet available in English. The Chinese original is shown below.

第1章:ROS 2 概述与架构设计

课程:ROS2 Python 编程
章节:第1章
课时:2 课时(90 分钟)
教学方式:讲授 + 演示


1.1 ROS 2 发展背景与设计目标

知识点 1.1.1:从 ROS 1 到 ROS 2

ROS(Robot Operating System)最初由 Willow Garage 于 2007 年发布,旨在为机器人软件开发提供统一的分布式框架。ROS 1 在学术界和工业界获得了广泛采用,但随着应用场景从实验室扩展到工业级产品,ROS 1 的局限性逐渐暴露出来:首先是单点故障问题,Master 节点(roscore)是所有通信的中枢,一旦崩溃整个系统便陷入瘫痪;其次是实时性不足,基于 TCP 的通信无法满足硬实时控制的需求;再次是安全性缺失,系统没有内置的认证、授权和加密机制;最后是嵌入式支持薄弱,对资源受限的微控制器支持有限。正是针对这些痛点,2014 年 Open Robotics 启动了 ROS 2 项目,完全重新设计了通信架构。

知识点 1.1.2:ROS 2 版本演进

ROS 2 自 2017 年底发布首个版本以来,以约半年一次的节奏迭代,各主要版本的代号、发布日期与维护状态如下表所示。其中 Humble(2022 年 5 月发布)与 Jazzy(2024 年 5 月发布)为长期支持(LTS)版本,是当前工业界与学术界的主力选择。

版本名 代号 发布日期 状态
Ardent Apalone 2017.12 EOL
Bouncy Bolson 2018.07 EOL
Crystal Clemmys 2018.12 EOL
Dashing Diademata 2019.05 EOL
Eloquent Elusor 2019.11 EOL
Foxy Fitzroy 2020.06 EOL
Galactic Geochelone 2021.05 EOL
Humble Hawksbill 2022.05 LTS (推荐)
Iron Irwini 2023.05 EOL
Jazzy Jalisco 2024.05 LTS (最新)

本课程基于 ROS 2 Humble (Ubuntu 22.04) 或 ROS 2 Jazzy (Ubuntu 24.04)。


1.2 DDS 中间件架构

知识点 1.2.1:DDS 概念模型

DDS(Data Distribution Service)是 OMG 制定的分布式实时通信标准,ROS 2 采用它替换了 ROS 1 的自定义 TCP/UDP 协议。DDS 与 ROS 2 的核心概念之间存在清晰的对应关系:

DDS 概念          →    ROS 2 概念
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DomainParticipant →    Node (通过 context)
DataWriter        →    Publisher
DataReader        →    Subscription
Topic             →    Topic
Domain            →    ROS_DOMAIN_ID
图 1-1:DDS 去中心化架构
┌─────────────┐                    ┌─────────────┐
│   Node A    │                    │   Node B    │
│             │                    │             │
│ ┌─────────┐ │    DDS Domain     │ ┌─────────┐ │
│ │Publisher│ │◄════Topic════════►│ │Subscribe│ │
│ └─────────┘ │    (自动发现)      │ └─────────┘ │
└─────────────┘                    └─────────────┘
        │                                  │
        └────────── 去中心化 ───────────────┘
               (无Master节点)

从图 1-1 可以看出,ROS 2 使用 DDS 的自动发现机制,节点可直接对等通信,无需中心化的 Master 节点。

知识点 1.2.2:RMW 抽象层

ROS 2 通过 RMW(ROS MiddleWare)接口抽象不同的 DDS 实现,其分层结构如图 1-2 所示:最上层是用户直接调用的 rclcpp/rclpy API,其下依次是统一的 ROS Client Library(RCL)与中间件抽象层(RMW),再往下则是可以自由切换的 DDS 实现(如 Fast DDS、Cyclone DDS、RTI Connext),底层以 UDP 或共享内存(SHMEM)完成数据传输。

┌──────────────────┐
│  rclcpp / rclpy  │  ← 用户代码 API
├──────────────────┤
│       RCL        │  ← ROS Client Library
├──────────────────┤
│       RMW        │  ← 中间件抽象层 (可替换)
├──────────────────┤
│  Fast DDS /      │
│  Cyclone DDS /   │  ← DDS 实现 (可切换)
│  RTI Connext     │
├──────────────────┤
│   UDP / SHMEM    │  ← 传输层
└──────────────────┘

图 1-2:ROS 2 分层架构。RMW 层允许用户在不修改应用代码的情况下切换不同的 DDS 实现。

知识点 1.2.3:官方要点——分层架构与 DDS 中间件

官方 About ROS 2 文档将 ROS 2 定位为「面向机器人应用的开源中间件框架」,其与 ROS 1 最本质的架构差异在于采用 DDS(Data Distribution Service,数据分发服务)作为通信中间件。DDS 是一种基于发布-订阅模型的数据分发标准,底层使用 RTPS 协议完成数据的可靠传输与节点发现。ROS 2 通过 rmw(ROS 中间件接口)抽象层将上层 API 与具体的 DDS 实现(如 Fast DDS、Cyclone DDS)解耦,用户使用 rclpy/rclcpp 编写的代码可以无缝切换底层供应商。

由于 DDS 本身就是去中心化的,节点之间通过自动发现(Discovery)机制互相感知,不再需要 ROS 1 时代的中央 Master 节点。这一设计带来三个显著收益:其一,系统天然支持多机分布部署,机器人本体与上位机可以位于不同主机;其二,QoS(服务质量)策略内建于通信层,可在可靠传输与实时低延迟之间按需取舍;其三,协议栈符合实时通信标准,为机械臂控制、自动驾驶等硬实时场景提供了基础。


1.3 Quality of Service (QoS) 策略

知识点 1.3.1:QoS 策略维度

QoS 定义了数据传输的质量保证机制,ROS 2 提供以下核心策略维度:

策略维度 选项 含义
Reliability RELIABLE / BEST_EFFORT 可靠传输(重传) / 尽力传输(允许丢包)
Durability VOLATILE / TRANSIENT_LOCAL 不保存历史 / 保存历史给后来者
History KEEP_LAST(n) / KEEP_ALL 保留最后 n 条 / 保留所有
Depth 正整数 队列深度
Deadline 时间间隔 消息更新的最大间隔
Lifespan 时间间隔 消息的有效期
Liveliness AUTOMATIC / MANUAL 发布者存活检测

其中 Reliability 决定消息是否重传,Durability 决定后加入的订阅者能否收到历史消息,History 与 Depth 共同控制缓存队列的长度,Deadline、Lifespan 与 Liveliness 则分别约束消息的更新频率、有效时长与发布端存活检测。

知识点 1.3.2:预定义 QoS 配置文件

为了让常见场景开箱即用,ROS 2 在 rclpy.qos 模块中预置了若干配置文件,例如传感器数据流使用高频允许丢包的配置,服务通信则使用可靠传输的配置;同时用户也可以显式构造 QoSProfile 自定义每一维度的取值:

# ROS 2 预定义 QoS 配置文件
from rclpy.qos import QoSProfile, qos_profile_sensor_data, qos_profile_services_default
 
# 传感器数据:高频,允许丢包
qos_sensor = qos_profile_sensor_data  # BEST_EFFORT, KEEP_LAST(5)
 
# 服务通信:可靠传输
qos_service = qos_profile_services_default  # RELIABLE, KEEP_LAST(10)
 
# 自定义 QoS
qos_custom = QoSProfile(
    reliability=rclpy.qos.ReliabilityPolicy.RELIABLE,
    durability=rclpy.qos.DurabilityPolicy.TRANSIENT_LOCAL,
    depth=10
)

1.4 节点生命周期管理

知识点 1.4.1:生命周期状态机

ROS 2 为节点引入了标准化的生命周期状态机,其状态转换关系如图 1-3 所示:节点从 Unconfigured 状态出发,调用 configure() 进入 Inactive,再经 activate() 变为 Active 正常运行;反之可经 deactivate()、cleanup() 逐级退回,并且任意状态下都可以通过 shutdown() 进入 Finalized。

                              configure()
Unconfigured ─────────────────────────► Inactive
     ▲                                      │
     │                                      │ activate()
     │ cleanup()                            ▼
     │                                   Active
     │                                      │
     └────────────────── deactivate() ──────┘

           shutdown() (从任意状态进入 Finalized)

图 1-3:ROS 2 生命周期节点状态转换图。通过状态机管理节点的初始化和资源分配,确保确定的启动和关闭流程。

知识点 1.4.2:生命周期 Python API

在 Python 中实现生命周期节点只需继承 rclpy.lifecycle 模块中的 LifecycleNode,并按需覆写 on_configure、on_activate 等回调,每个回调通过返回 TransitionCallbackReturn.SUCCESS 表示迁移成功:

import rclpy
from rclpy.lifecycle import LifecycleNode, State, TransitionCallbackReturn
 
class MyLifecycleNode(LifecycleNode):
    def __init__(self):
        super().__init__('my_lifecycle_node')
 
    def on_configure(self, state: State):
        # 分配资源、设置参数
        self.get_logger().info('on_configure() called')
        return TransitionCallbackReturn.SUCCESS
 
    def on_activate(self, state: State):
        # 开始处理数据
        self.get_logger().info('on_activate() called')
        return TransitionCallbackReturn.SUCCESS
 
    def on_deactivate(self, state: State):
        # 停止处理
        self.get_logger().info('on_deactivate() called')
        return TransitionCallbackReturn.SUCCESS
 
    def on_cleanup(self, state: State):
        # 释放资源
        self.get_logger().info('on_cleanup() called')
        return TransitionCallbackReturn.SUCCESS

知识点 1.4.3:官方要点——生命周期节点设计动机

官方 Concepts 文档中的 About Lifecycle Nodes 页面解释了生命周期(Lifecycle)节点的设计动机:机器人系统中的硬件驱动、导航算法等组件需要有序的初始化与关停流程,而非一启动就全速运行。生命周期节点将运行状态划分为未配置(Unconfigured)、未激活(Inactive)、激活(Active)与已停止(Shutdown)等状态,外部通过服务调用触发状态迁移。

例如导航系统加载代价地图时,需要先完成参数读取与地图订阅初始化(Unconfigured → Inactive),再一次性切换到 Active 开始运行。rclpy/rclcpp 官方示例包(ros2/examples)提供了完整的生命周期节点实现,与本章演示的生命周期状态机一一对应,可作为进阶实现参考。


1.5 安装与环境搭建

知识点 1.5.1:安装 ROS 2 Humble

在 Ubuntu 22.04 上安装 ROS 2 Humble 分为三步:首先设置系统 locale 并为 apt 添加 ROS 2 软件源,然后安装 ros-humble-desktop-full 完整版,最后安装 colcon、rosdep、vcstool 等开发工具,具体命令如下:

# 设置 locale
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8
 
# 添加 ROS 2 仓库
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
  -o /usr/share/keyrings/ros-archive-keyring.gpg
 
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
  http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
 
# 安装 ROS 2 Humble 完整版
sudo apt update
sudo apt install ros-humble-desktop-full
 
# 安装开发工具
sudo apt install python3-colcon-common-extensions python3-rosdep python3-vcstool

知识点 1.5.2:配置开发环境

安装完成后,需要把 ROS 2 的环境变量写入 ~/.bashrc 以便每次新开终端自动加载,并初始化 rosdep 用于后续解析包的依赖:

# 将 ROS 2 环境变量添加到 ~/.bashrc
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
 
# 初始化 rosdep(用于安装包依赖)
sudo rosdep init
rosdep update

知识点 1.5.3:工作空间与 colcon 构建

开发机器人功能包时,通常先在主目录下创建 ~/ros2_ws 工作空间并建立 src 子目录存放源码,随后用 colcon 构建整个空间,构建完成后加载 install/setup.bash 即可运行其中的节点,最后用官方的 talker/listener 示例节点验证整个环境是否可用:

# 创建工作空间
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
 
# 编译工作空间
colcon build --symlink-install
 
# 加载工作空间环境
source install/setup.bash
 
# 验证安装
ros2 run demo_nodes_cpp talker
# 在另一个终端:
ros2 run demo_nodes_py listener

知识点 1.5.4:官方要点——快速上手:turtlesim 与命令行工具

官方 Beginner: CLI Tools 系列教程以小乌龟仿真 turtlesim 为教学道具:在终端一运行 ros2 run turtlesim turtle_sim_node 启动乌龟仿真器,终端二运行 ros2 run turtlesim turtle_teleop_key 即可用方向键控制乌龟运动。教程刻意引导读者用 ros2 node list、ros2 node info、ros2 topic list、ros2 service list 等命令观察图形界面背后的节点、话题与服务实体,并演示节点重命名(--ros-args -r __node:=my_turtle)。

这一步与本章练习的仿真节点观察实验思路完全一致:从「黑盒」现象出发,借助命令行工具建立对 ROS 2 实体模型的直观认识。The Construct 的 5 天入门课也保留了这一「先跑仿真、再读命令」的教学法,Articulated Robotics 的视频系列则以更口语化的方式逐条演示这些命令,适合中文读者对照复习。

知识点 1.5.5:官方要点——工作区与 colcon 构建系统

官方 colcon 教程介绍了标准工作区布局:src/(源码)、build/(中间产物)、install/(安装产物)、log/(日志)四个目录。构建命令 colcon build 会递归编译所有包,常用选项包括 --packages-select <pkg>(只编译指定包)、--symlink-install(Python 包以符号链接安装,改代码免重编)以及 --merge-install(合并安装,适合在嵌入式/容器中运行)。

Articulated Robotics 强调初学者应形成「修改源码 → colcon build --symlink-install → source install/setup.bash → ros2 run」的日常循环,并提醒每次新开终端都要重新 source,否则会出现 Package 'x' not found 的错误——这是 ROS 2 初学者最高频的报错之一,根源正是环境变量(如 AMENT_PREFIX_PATH、LD_LIBRARY_PATH)未正确加载。


1.6 本章小结

本章围绕 ROS 2 的设计理念与核心机制展开。首先回顾了 ROS 1 在单点故障、实时性、安全性与嵌入式支持四方面的局限,以及 ROS 2 如何通过全新的通信架构解决这些问题。随后介绍了 DDS 中间件的去中心化自动发现机制:节点之间不再依赖中央 Master,而是通过对等发现直接通信,且底层 DDS 实现可以通过 RMW 抽象层自由切换。在通信质量层面,QoS 策略从可靠性、持久性、历史缓存、队列深度、截止时间、存活周期与存活检测等多个维度提供细粒度的保障。此外,生命周期节点通过标准状态机统一管理节点的配置、激活与关闭流程,保证系统启动和退出行为确定可控。在环境搭建方面,本章给出了 ROS 2 Humble/Jazzy 的 apt 安装步骤,并介绍了以 colcon 为标准构建工具的工作空间管理方式。


1.7 练习题

练习 1.1:简述 ROS 1 的三个主要局限性和 ROS 2 对应的解决方案。

ros1依赖master,存在中心节点————ros2使用dds通信机制,节点之间可以自动发现彼此 ros1不太能实时控制并传输,通信策略单一————Qos通信,根据不同数据类型设置可靠性队列深度。。。 ros1多机器人、多实验环境相互干扰————通过ROS_DOMAIN_ID划分通信领域 练习 1.2:DDS 域 ID(ROS_DOMAIN_ID)的作用是什么?如果两台机器上的域 ID 不同,它们的节点能通信吗?

作用是隔离通信环境。id不同一般不会互相发现和通信 练习 1.3:以下 QoS 配置能否匹配通信?

  • Publisher: RELIABLE, VOLATILE
  • Subscriber: BEST_EFFORT, VOLATILE 可以。Publisher 使用 RELIABLE,提供可靠传输能力;Subscriber 使用 BEST_EFFORT,只要求尽力接收,因此发布者能力满足订阅者需求。两者 Durability 都为 VOLATILE,也能够匹配。 练习 1.4:查看当前 ROS_DOMAIN_ID 环境变量值,将其修改为 42 并验证。

alt text

练习 1.5:创建一个 ROS 2 工作空间,使用 colcon build 编译,并验证 setup.bash 加载。

alt text

练习 1.6:运行 talker/listener 示例节点,使用 ros2 node list 和 ros2 topic list 查看运行状态。

alt text


仿真结合实例(当前仓库):Gazebo Harmonic 仿真中的 ROS 2 体系验证

目标与知识点对应

本实例把第1章的三个核心概念——DDS 分布式通信、去中心化自动发现、ROS_DOMAIN_ID 域隔离、QoS 策略——放到一个真实的 Gazebo 仿真中验证。当前仓库使用 ROS 2 Jazzy + Gazebo Sim Harmonic,移动机器人入口为 robot_sim_demo。通过观察一个仿真世界里的节点、话题和桥接关系,你能直观理解 DDS 中间件如何在节点之间自动建立连接。

运行命令

在工作区根目录执行,先加载 ROS 2 与工作空间环境:

source /opt/ros/jazzy/setup.bash
source install/setup.bash
 
# 终端 1:启动 Gazebo + 机器人 + 传感器桥(带 RViz)
ros2 launch robot_sim_demo gazebo2.launch.py gui:=true rviz:=true drive:=false

等待 Gazebo 3D 场景出现 TurtleBot3 Burger 机器人、RViz 显示机器人模型与激光雷达点云。随后在独立终端(同样先 source 环境)观察:

# 查看自动发现到的所有节点(DDS 去中心化发现)
ros2 node list
 
# 查看传感器桥发布的话题及其 QoS 与类型
ros2 topic list
ros2 topic info /scan

需要观察到的现象

运行上述命令后应重点观察三类现象。其一,ros2 node list 能看到 gazebo2_bridge、gazebo2_robot_state_publisher、camera_info_publisher 等多个节点,且它们没有中心 master,全部靠 DDS 自动发现(对应 1.1-1.2 节)。其二,ros2 topic info /scan 显示 Message type: sensor_msgs/msg/LaserScan,桥接发布者采用 Lazy 桥(未匹配到订阅者时不发送),体现 QoS Durability/History 的实际影响。其三,在另一个带 source 的终端重新指定一个 ROS_DOMAIN_ID 再执行 ros2 node list,可以看到它与仿真图隔离,对应 1.2.2 节与练习 1.2。

源码与相关位置

本实例涉及的关键文件包括:启动入口位于 src/robot_sim_demo/launch/gazebo2.launch.py,桥配置位于 src/robot_sim_demo/config/gazebo2_bridge.yaml,相机内参发布器位于 src/robot_sim_demo/robot_sim_demo/camera_info_publisher.py,世界与模型文件则位于 src/robot_sim_demo/worlds/museum.sdf 和 src/robot_sim_demo/models/turtlebot3_burger/model.sdf。

说明:真实运行证据(检测到 /clock、/scan、/odom、/tf 桥接)见 lab_manuals/images/runtime/ch09_gazebo_headless.png 及配套 .cast。

ch01 生命周期节点运行输出


学习材料: