# 80 · 包构建机制 colcon / ament(完全指南) > **目标**:理解 ROS2 一个包从源码到 `ros2 run` 能找到的完整流程,能自己写 ament_python / ament_cmake 包。 --- ## 目录 - [1. 工作空间结构](#1-工作空间结构) - [2. 包类型](#2-包类型) - [3. package.xml(包身份证)](#3-packagexml包身份证) - [4. ament_python 包详解](#4-ament_python-包详解) - [5. ament_cmake 包详解](#5-ament_cmake-包详解) - [6. colcon build 内部流程](#6-colcon-build-内部流程) - [7. ament_index 与 ros2 工具发现](#7-ament_index-与-ros2-工具发现) - [8. 依赖解析与 rosdep](#8-依赖解析与-rosdep) - [9. 自定义消息 / 服务 / Action(进阶)](#9-自定义消息--服务--action进阶) - [10. 常见构建错误 + 解决](#10-常见构建错误--解决) - [11. 在本仓库里跑](#11-在本仓库里跑) - [12. 进阶:多包 workspace / release 流程](#12-进阶多包-workspace--release-流程) --- ## 1. 工作空间结构 ``` ros2_ws/ ← 工作空间根 ├── src/ ← 包源码(你写代码的地方) │ ├── pkg_a/ ← 一个 ROS 包 │ ├── pkg_b/ │ └── ... ├── build/ ← colcon build 中间产物 ├── install/ ← colcon install 产物(ros2 唯一识别) ├── log/ ← build/test 日志 └── ... ``` **`install/` 是唯一能被 `ros2` 命令识别的目录**。每次进新 shell 必须: ```bash source install/setup.bash ``` --- ## 2. 包类型 ROS2 有两种主流 build_type: | build_type | 文件 | 用途 | |---|---|---| | **ament_python** | `setup.py` + `package.xml` | Python 包 | | **ament_cmake** | `CMakeLists.txt` + `package.xml` | C++ 包 | | ament_cmake_python | 两者混合 | 复杂项目 | --- ## 3. package.xml(包身份证) ```xml my_pkg 0.1.0 ... Name Apache-2.0 rclpy rosidl_default_generators rclpy std_msgs python3-pytest ament_python ``` | 字段 | 含义 | |---|---| | `` | 构建 + 运行 | | `` | 仅构建 | | `` | 仅运行 | | `` | 仅测试 | | `` | `ament_python` / `ament_cmake` | --- ## 4. ament_python 包详解 ### 4.1 目录结构 ``` py_pubsub/ ├── package.xml ├── setup.py ← 标准 setuptools ├── setup.cfg ← ament_python 脚本目录约定 ├── resource/ │ └── py_pubsub ← 空文件,ament_index marker(必填!) ├── py_pubsub/ ← Python 模块 │ ├── __init__.py │ └── *.py ├── launch/ │ └── *.py └── test/ └── test_*.py ``` ### 4.2 setup.py ```python from setuptools import find_packages, setup package_name = 'py_pubsub' setup( name=package_name, version='0.1.0', packages=find_packages(exclude=['test']), data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), ('share/' + package_name + '/launch', ['launch/*.py']), ], install_requires=['setuptools'], zip_safe=True, maintainer='xs', maintainer_email='dev@example.com', description='Python talker/listener demo for ROS2 Humble', license='Apache-2.0', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'talker = py_pubsub.publisher_member_function:main', 'listener = py_pubsub.subscriber_member_function:main', ], }, ) ``` **关键点**: - `data_files` 必须把 `package.xml` + `launch/` 拷到 `share//` - `resource/` 空文件 → ament_index marker(必填!) - `entry_points/console_scripts` 暴露 `ros2 run` 可执行名 ### 4.3 setup.cfg ```ini [develop] script_dir=$base/lib/py_pubsub [install] install_scripts=$base/lib/py_pubsub ``` 控制 `console_scripts` 装到 `$base/lib//`,ROS2 启动器能找到。 --- ## 5. ament_cmake 包详解 ### 5.1 目录结构 ``` cpp_pubsub/ ├── package.xml ├── CMakeLists.txt ├── src/ │ └── *.cpp ├── include/ ← 公开头文件(可选) ├── launch/ │ └── *.py ├── test/ │ └── test_*.cpp └── config/ ← YAML 配置(可选) ``` ### 5.2 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.16) project(cpp_pubsub VERSION 0.1.0) if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() # GCC/Clang 编译警告 if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) include_directories(include) # 节点可执行 add_executable(talker src/publisher_member_function.cpp) ament_target_dependencies(talker rclcpp std_msgs) add_executable(listener src/subscriber_member_function.cpp) ament_target_dependencies(listener rclcpp std_msgs) # 装到 install/lib// install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME}) # launch 文件装到 share//launch/ install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/) # 测试 if(BUILD_TESTING) find_package(ament_cmake_gtest REQUIRED) ament_add_gtest(test_pub_sub test/test_pub_sub.cpp) ament_target_dependencies(test_pub_sub rclcpp std_msgs) endif() ament_package() ← 必填! ``` **关键**: - `ament_target_dependencies( pkg1 pkg2)` 同时设置 include path 和 link - `install(TARGETS ...)` 把可执行装到 install - `install(DIRECTORY launch ...)` 把 launch 装到 share - `ament_package()` 末尾必填 --- ## 6. colcon build 内部流程 ```bash colcon build --symlink-install --packages-select ``` 按依赖顺序,每个包: ``` ┌──────────────────────────────────────────────────────────┐ │ 1. 发现 src// 下的 package.xml │ │ 2. 读 ,决定 build type │ │ 3. 调用对应 build_type 的 hook: │ │ ament_python → python setup.py build + install │ │ ament_cmake → cmake + make + ament_package │ │ 4. 写 ament_index 标记 (share/ament_index/...) │ │ 5. 把产物装到 install// │ └──────────────────────────────────────────────────────────┘ ``` 加 `--symlink-install`: - Python:源码**软链**到 install,改源码立即生效(不用重 build) - C++:可执行仍硬编,但 launch / config 文件软链 --- ## 7. ament_index 与 ros2 工具发现 `ros2 run / launch / pkg executables / topic info` 等命令都靠 **ament_index**: - 启动时扫 `install//share/ament_index/resource_index/packages/` 文件 - 找到包路径后,扫 `install//lib//` 找可执行 - 扫 `install//share//launch/` 找 launch 文件 **手动重建索引**(极少需要): ```bash # 索引在 install/share/ament_index/ 里,正常情况下 colcon build 自动维护 ros2 doctor --report # 看索引健康 ``` --- ## 8. 依赖解析与 rosdep `package.xml` 的 `` 让 colcon 自动排构建顺序: - 你的 `py_pubsub` 依赖 `rclpy`,`rclpy` 是 apt 包,colcon 跳过 - `py_srv` 依赖 `example_interfaces`,同上 - colcon 只 build 我们 src/ 下的包 **`rosdep`** 是 apt 包管理: ```bash sudo rosdep init && rosdep update sudo rosdep install -i --from-paths src/ # 装齐所有 apt 依赖 ``` 本仓库基础镜像 `osrf/ros:humble-desktop` 已装齐所有 ROS2 客户端,**不需要额外 rosdep**。 --- ## 9. 自定义消息 / 服务 / Action(进阶) 需要: 1. 在包内建 `msg/`、`srv/`、`action/` 目录 2. 写 `.msg` / `.srv` / `.action` 文件 3. `package.xml` 加: - `rosidl_default_generators` - `rosidl_default_runtime` 4. `CMakeLists.txt` 加 `rosidl_generate_interfaces(${PROJECT_NAME} ${MSG_FILES} ...)` 5. `setup.py` `data_files` 加 `('share//msg': ['msg/*.msg'], ...)` 6. **ament_python 需要单独 `rosidl` 包**(混合构建) **本仓库只用 example_interfaces 自带的 Fibonacci / AddTwoInts**(由 ROS2 系统包提供),避开这个复杂度。 --- ## 10. 常见构建错误 + 解决 ### 10.1 `Could not find a package configuration file provided by "rclpy"` 少装 apt 包: ```bash sudo apt install ros-humble-rclpy # 镜像外 ``` ### 10.2 `Package 'X' not found in colcon build` ament_index 没刷新。重启容器 + source install/setup.bash。 ### 10.3 `ament_python` install 0 files `setup.py` 的 `packages=` 不对。检查 `py_pubsub/` 下有 `__init__.py`。 ### 10.4 launch 文件未找到 检查 `setup.py`: ```python ('share/' + package_name + '/launch', ['launch/*.py']), ``` **`launch/*.py` 是 glob**,不是 `'launch/foo.py'`。 ### 10.5 ROS Domain 冲突 `ROS_DOMAIN_ID=0` 默认。多项目用 `ROS_DOMAIN_ID=42` 隔离。 ### 10.6 Cyclone DDS 缓存了 CMakeCache `Could not find ROS middleware implementation 'rmw_cyclonedds_cpp'` 但又设了 `RMW_IMPLEMENTATION=rmw_cyclonedds_cpp` → CMake 缓存了。 **修法**: ```bash rm -rf build install log colcon build ... ``` --- ## 11. 在本仓库里跑 ### 11.1 全部 build ```bash cd /root/ros2_ws colcon build --symlink-install \ --packages-select py_pubsub cpp_pubsub py_srv py_action_demo cpp_robot_tf2 py_vision_demo bringup ``` ### 11.2 增量 build(只编改的) ```bash colcon build ``` ### 11.3 清理后重 build ```bash rm -rf build install log colcon build --symlink-install ``` ### 11.4 单包 build ```bash colcon build --packages-select py_pubsub ``` --- ## 12. 进阶:多包 workspace / release 流程 ### 12.1 多 workspace 覆盖 ```bash # 用 setup.bash 叠加 source /opt/ros/humble/setup.bash source /workspace1/install/setup.bash source /workspace2/install/setup.bash # 后 source 的覆盖前面 ``` ### 12.2 Release 流程 ```bash # 1) bloom-generate 准备 release sudo apt install python3-bloom bloom-generate rosdebian --os-only ubuntu:jammy # 2) 构建 .deb 包 bloom-release --rosdistro humble --track humble --new 0.1.0 my_pkg # 3) 推送到 ROS 仓库(rosindex / GitHub) ``` **本仓库未发布**,如要 release 走标准流程。 --- ## 接下来读 | 主题 | 文档 | |---|---| | Launch 文件 | [`70-launch.md`](70-launch.md) | | Docker 开发 | [`85-docker.md`](85-docker.md) | | 测试策略 | [`90-testing.md`](90-testing.md) | | 三机部署 | [`100-embedded-deployment.md`](100-embedded-deployment.md) | --- --- ## 📖 阅读路径导航 > 💡 这是仓库 `doc/` 下所有文档的推荐阅读顺序。[返回 README 总导航](../README.md#-23-篇文档怎么读) > > ⏱ **本文预计阅读时间**: 40 分钟 > 📍 **当前位置**: 第 19 / 24 篇 - ⏮ **上一篇**: [launch 文件系统](70-launch.md) - ⏭ **下一篇**: [Docker 容器化开发](85-docker.md)