第3章:ROS2基础:架构、节点与话题、编译系统、工作空间搭建

各位同学,欢迎来到ROS2的世界。

说实话,我第一次接触ROS2的时候,心里是有点抵触的。毕竟ROS1用了那么多年,顺手得很。但后来在智驾项目里,ROS1的实时性短板和单点故障问题,让我吃了不少苦头。嗯,从那以后,我就彻底转向ROS2了。

这一章,我们不讲虚的。直接上手,把ROS2的骨架搭起来。你想想看,中间件就像汽车的底盘,底盘不稳,上面装再好的算法也是白搭。

3.1 ROS2架构:从“主节点”到“去中心化”

ROS1有个致命问题——它依赖一个叫roscore的主节点。一旦roscore挂了,整个系统就瘫痪了。我在一个园区物流项目里就遇到过,车跑到一半,roscore进程莫名其妙崩了,车直接停在路中间……

ROS2把这个设计彻底推翻了。它基于DDS(数据分发服务)协议,每个节点都是对等的,没有中心节点。说白了,就是每个节点自己就能发现别人、跟别人通信。

核心架构分层:

  • 应用层:你的算法节点、控制节点、感知节点
  • 中间件层:ROS2客户端库(rclcpp、rclpy)
  • DDS层:真正的通信骨干,负责发现、序列化、传输
  • 传输层:UDP/TCP/共享内存,根据QoS策略选择

我个人的习惯是,把DDS想象成一个“智能的快递网络”。每个节点只管往网络里扔包裹(消息),网络会自动把包裹送到订阅了它的人手里。不需要快递总站(roscore)来调度。

避坑指南:我曾经在调试多机通信时,发现两个机器上的节点互相发现不了。折腾了半天,原来是DDS的发现协议被公司防火墙拦了。解决办法是设置DDS的发现端口,或者用共享内存模式。

下面这张图,是我自己画的ROS2架构简图,帮你理清关系:

应用层 感知节点 | 规划节点 | 控制节点 | 定位节点 ROS2中间件层 rclcpp / rclpy | 话题 / 服务 / 动作 DDS通信层 数据发现 | 序列化 | 传输 | QoS策略 UDP / TCP / 共享内存

3.2 节点与话题:ROS2的“细胞”和“血管”

节点(Node)是ROS2里最小的执行单元。每个节点只干一件事,比如摄像头节点只管采集图像,激光雷达节点只管发点云。我经常跟团队说:一个节点只做一件事,把它做好。

话题(Topic)是节点之间通信的“血管”。发布者往话题里发消息,订阅者从话题里收消息。这是一种异步的、一对多的通信方式。

节点与话题的关系:

  • 发布者(Publisher): 只管发,不管谁在收
  • 订阅者(Subscriber): 只管收,不管谁在发
  • 话题名: 字符串,比如 /camera/image_raw
  • 消息类型: 定义好的数据结构,比如 sensor_msgs/Image

举个例子,我在做自动泊车项目时,超声波节点发布 /ultrasonic/raw 话题,融合节点订阅它。如果以后想换更好的超声波雷达,只要保证话题名和消息类型不变,融合节点一行代码都不用改。这就是解耦的好处。

个人经验:话题命名要规范。我见过有人用 /a、/b 这种名字,三个月后自己都看不懂。建议用 功能域/模块/数据 的格式,比如 /perception/lidar/points

3.3 编译系统:Colcon 的使用

ROS1用catkin,ROS2用colcon。说白了,colcon就是catkin的升级版,编译更快,依赖管理更智能。

我第一次用colcon时,最直观的感受是:它不会把编译产物和源码混在一起。所有编译出来的东西都放在 install 目录里,源码目录干干净净。这对版本管理来说太友好了。

常用的colcon命令,我列个表:

命令 作用 我常用的场景
colcon build 编译所有包 第一次编译,或者改了很多代码
colcon build --packages-select 包名 只编译指定包 只改了一个包,不想等全部编译
colcon build --symlink-install 使用符号链接安装 开发阶段,改Python代码不用重新编译
colcon test 运行测试 提交代码前,跑一遍单元测试

注意:每次编译前,记得先 source 一下环境变量。我见过太多人编译完发现找不到包,就是因为忘了 source install/setup.bash。建议把这个命令写到 ~/.bashrc 里。

3.4 工作空间搭建:从零开始

搭建工作空间,说白了就是建几个文件夹。但顺序不能乱,否则后面会出各种奇怪的问题。

我一般按这个步骤来:

  1. 创建工作空间目录: mkdir -p ~/ros2_ws/src
  2. 进入src目录: cd ~/ros2_ws/src
  3. 创建功能包: ros2 pkg create my_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
  4. 编译: cd ~/ros2_ws && colcon build
  5. 生效环境: source install/setup.bash

这里有个细节:--build-type 参数决定了你的包是用CMake还是Python。C++项目用 ament_cmake,纯Python项目用 ament_python。我建议智驾项目里,性能关键模块用C++,工具脚本用Python。

一个完整的工作空间结构:

ros2_ws/
├── src/          # 源码目录
│   ├── pkg_a/    # 功能包A
│   ├── pkg_b/    # 功能包B
│   └── ...
├── build/        # 编译中间文件(不用管)
├── install/      # 编译产物(source这个目录)
└── log/          # 编译日志(出问题时看这里)
  

避坑指南:我曾经在同一个工作空间里混用了ROS1和ROS2的包,结果编译报了一堆莫名其妙的错。后来我养成了习惯:一个工作空间只放一个ROS版本的包。如果你需要同时开发ROS1和ROS2项目,用不同的工作空间,切换时source对应的setup.bash。

嗯,到这里,ROS2的基础骨架就搭好了。你有了架构认知,知道了节点和话题怎么用,学会了colcon编译,也亲手建了一个工作空间。这些是后面所有实战内容的地基。

记住一句话:中间件不生产数据,它只是数据的搬运工。但怎么搬得稳、搬得快、不出错,就是我们要学的本事。


专注资料整理