4. ROS2工作空间与工程结构

在自动驾驶系统中,ROS2 工程往往由感知、定位、规划、控制、底盘通信、诊断监控等数十个包组成。如果没有统一的工作空间与构建规范,团队很快会陷入“依赖找不到、编译顺序混乱、启动文件散落各处”的困境。本节围绕 colcon 构建系统、包管理、ament_cmake 与 ament_python、launch 文件组织、多包依赖管理 五个核心点,建立可复用的工程结构认知。

一、工作空间:源码、构建与安装的三层结构

ROS2 工作空间本质是一个目录约定,最典型的是 colcon 推荐的 src / build / install / log 四目录布局。其中 src 存放源码包,build 存放中间编译产物,install 存放最终可被 source 的安装结果,log 记录每次构建日志。开发者只应手工维护 src,其余目录由 colcon 自动生成,不应纳入版本管理。

ros2_ws/
├── src/
│   ├── autocar_perception/
│   ├── autocar_localization/
│   ├── autocar_planning/
│   └── autocar_bringup/
├── build/        # 编译中间文件
├── install/      # 安装空间,source 此目录
└── log/          # 构建日志

使用前必须执行 source install/setup.bash,否则 ros2 run、ros2 launch 无法找到自定义包。若希望每次终端自动生效,可将其写入 ~/.bashrc,但要注意多工作空间叠加时的覆盖顺序。

二、colcon 构建系统:包发现、依赖排序与并行编译

colcon 是 ROS2 默认的构建工具,它并不“编译代码”,而是负责发现包、解析包间依赖、按拓扑顺序调用各包的构建类型。每个包通过 package.xml 声明依赖,colcon 据此生成有向无环图,先编译被依赖的包,再编译上层包。

常用命令如下:

colcon build                              # 构建全部包
colcon build --packages-select autocar_planning
colcon build --packages-up-to autocar_bringup
colcon build --symlink-install            # Python 包常用,改源码免重装
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
colcon test && colcon test-result --verbose
在自动驾驶项目中,建议对 Python 包统一使用 --symlink-install,这样修改 launch 或 Python 节点后无需重新 build,直接重启 launch 即可,大幅提升调试效率。

三、ament_cmake 与 ament_python:两种构建类型的取舍

ROS2 的包构建类型由 package.xml 中的 <build_type> 与包内构建文件共同决定。C++ 节点、需要链接 Eigen、PCL、OpenCV、CUDA 的模块,使用 ament_cmake;纯 Python 节点、工具脚本、参数配置包,使用 ament_python。

对比项ament_cmakeament_python
适用语言C/C++Python
核心文件CMakeLists.txt + package.xmlsetup.py + setup.cfg + package.xml
依赖声明find_package / ament_target_dependenciesinstall_requires / package.xml
可执行入口add_executable + install(TARGETS)entry_points console_scripts
典型场景感知推理、控制算法、驱动launch 组织、参数工具、监控脚本

一个常见误区是:把 launch 文件放在 C++ 包里,却忘记在 CMakeLists.txt 中安装 launch 目录,导致 ros2 launch 找不到文件。正确做法是显式安装:

install(DIRECTORY launch config
  DESTINATION share/${PROJECT_NAME})

四、launch 文件组织:从单节点到系统级编排

自动驾驶系统启动通常涉及几十个节点、多个命名空间、参数文件与条件分支。ROS2 launch 支持 Python、XML、YAML 三种描述方式,工程中推荐使用 Python launch,因为它具备变量、条件判断、包含复用等能力。

典型组织策略是:每个功能包自带本包的 launch,再由 autocar_bringup 这类“总装包”通过 IncludeLaunchDescription 组合。这样既保证单包可独立测试,又能实现整车一键启动。

from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    use_sim = DeclareLaunchArgument('use_sim', default_value='false')
    perception = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([
            get_package_share_directory('autocar_perception'),
            '/launch/perception.launch.py']))
    return LaunchDescription([use_sim, perception])

五、多包依赖管理:package.xml 是唯一事实来源

多包依赖管理的核心是 package.xml。它区分 build_depend、exec_depend、buildtool_depend、test_depend 等标签,colcon 与 rosdep 都据此工作。若依赖声明缺失,本机可能因环境残留而编译通过,但在 CI 或新机器上必然失败。

复盘常见故障:Package 'xxx' not found 多数不是代码问题,而是 ① 未 source install/setup.bash;② package.xml 漏写 exec_depend;③ 工作空间叠加顺序错误。排查时先用 ros2 pkg prefix 与 printenv AMENT_PREFIX_PATH 确认包是否可见。
ROS2 工作空间构建与依赖拓扑 src/ 源码空间 autocar_perception ament_cmake C++ 推理节点 autocar_planning ament_cmake 轨迹规划 autocar_tools ament_python 参数/监控脚本 autocar_bringup ament_python 总装 launch colcon 构建调度 ① 扫描 package.xml 发现包 ② 解析依赖生成 DAG ③ 拓扑排序并行编译 ④ 调用 ament_cmake / ament_python 构建 ⑤ 输出到 install/ install/ 安装空间 source install/setup.bash → ros2 run / ros2 launch 可发现所有包与可执行文件 运行时 ros2 launch autocar_bringup system.launch.py use_sim:=false

综上,工作空间是容器,colcon 是调度器,ament_cmake/ament_python 是构建实现,launch 是运行编排,package.xml 是依赖契约。五者协同,才能让自动驾驶多包工程在开发、CI 与实车部署中保持一致、可复现、可扩展。