11 KiB
80 · 包构建机制 colcon / ament(完全指南)
目标:理解 ROS2 一个包从源码到
ros2 run能找到的完整流程,能自己写 ament_python / ament_cmake 包。
目录
- 1. 工作空间结构
- 2. 包类型
- 3. package.xml(包身份证)
- 4. ament_python 包详解
- 5. ament_cmake 包详解
- 6. colcon build 内部流程
- 7. ament_index 与 ros2 工具发现
- 8. 依赖解析与 rosdep
- 9. 自定义消息 / 服务 / Action(进阶)
- 10. 常见构建错误 + 解决
- 11. 在本仓库里跑
- 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 必须:
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 version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd"?>
<package format="3">
<name>my_pkg</name>
<version>0.1.0</version>
<description>...</description>
<maintainer email="x@y.com">Name</maintainer>
<license>Apache-2.0</license>
<depend>rclpy</depend> <!-- 构建 + 运行都需 -->
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
| 字段 | 含义 |
|---|---|
<depend> |
构建 + 运行 |
<build_depend> |
仅构建 |
<exec_depend> |
仅运行 |
<test_depend> |
仅测试 |
<build_type> |
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
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/<pkg>/resource/<pkg>空文件 → ament_index marker(必填!)entry_points/console_scripts暴露ros2 run可执行名
4.3 setup.cfg
[develop]
script_dir=$base/lib/py_pubsub
[install]
install_scripts=$base/lib/py_pubsub
控制 console_scripts 装到 $base/lib/<pkg>/,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_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/<pkg>/
install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME})
# launch 文件装到 share/<pkg>/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(<target> pkg1 pkg2)同时设置 include path 和 linkinstall(TARGETS ...)把可执行装到 installinstall(DIRECTORY launch ...)把 launch 装到 shareament_package()末尾必填
6. colcon build 内部流程
colcon build --symlink-install --packages-select <pkg>
按依赖顺序,每个包:
┌──────────────────────────────────────────────────────────┐
│ 1. 发现 src/<pkg>/ 下的 package.xml │
│ 2. 读 <export><build_type>,决定 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/<pkg>/ │
└──────────────────────────────────────────────────────────┘
加 --symlink-install:
- Python:源码软链到 install,改源码立即生效(不用重 build)
- C++:可执行仍硬编,但 launch / config 文件软链
7. ament_index 与 ros2 工具发现
ros2 run / launch / pkg executables / topic info 等命令都靠 ament_index:
- 启动时扫
install/<pkg>/share/ament_index/resource_index/packages/<pkg>文件 - 找到包路径后,扫
install/<pkg>/lib/<pkg>/找可执行 - 扫
install/<pkg>/share/<pkg>/launch/找 launch 文件
手动重建索引(极少需要):
# 索引在 install/share/ament_index/ 里,正常情况下 colcon build 自动维护
ros2 doctor --report # 看索引健康
8. 依赖解析与 rosdep
package.xml 的 <depend> 让 colcon 自动排构建顺序:
- 你的
py_pubsub依赖rclpy,rclpy是 apt 包,colcon 跳过 py_srv依赖example_interfaces,同上- colcon 只 build 我们 src/ 下的包
rosdep 是 apt 包管理:
sudo rosdep init && rosdep update
sudo rosdep install -i --from-paths src/ # 装齐所有 apt 依赖
本仓库基础镜像 osrf/ros:humble-desktop 已装齐所有 ROS2 客户端,不需要额外 rosdep。
9. 自定义消息 / 服务 / Action(进阶)
需要:
- 在包内建
msg/、srv/、action/目录 - 写
.msg/.srv/.action文件 package.xml加:<build_depend>rosidl_default_generators</build_depend><exec_depend>rosidl_default_runtime</exec_depend>
CMakeLists.txt加rosidl_generate_interfaces(${PROJECT_NAME} ${MSG_FILES} ...)setup.pydata_files加('share/<pkg>/msg': ['msg/*.msg'], ...)- ament_python 需要单独
rosidl包(混合构建)
本仓库只用 example_interfaces 自带的 Fibonacci / AddTwoInts(由 ROS2 系统包提供),避开这个复杂度。
10. 常见构建错误 + 解决
10.1 Could not find a package configuration file provided by "rclpy"
少装 apt 包:
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:
('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 缓存了。
修法:
rm -rf build install log
colcon build ...
11. 在本仓库里跑
11.1 全部 build
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(只编改的)
colcon build
11.3 清理后重 build
rm -rf build install log
colcon build --symlink-install
11.4 单包 build
colcon build --packages-select py_pubsub
12. 进阶:多包 workspace / release 流程
12.1 多 workspace 覆盖
# 用 setup.bash 叠加
source /opt/ros/humble/setup.bash
source /workspace1/install/setup.bash
source /workspace2/install/setup.bash
# 后 source 的覆盖前面
12.2 Release 流程
# 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 |
| Docker 开发 | 85-docker.md |
| 测试策略 | 90-testing.md |
| 三机部署 | 100-embedded-deployment.md |
📖 阅读路径导航
💡 这是仓库
doc/下所有文档的推荐阅读顺序。返回 README 总导航⏱ 本文预计阅读时间: 40 分钟 📍 当前位置: 第 19 / 24 篇
- ⏮ 上一篇: launch 文件系统
- ⏭ 下一篇: Docker 容器化开发