ROS:ROS2编译
本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。
[TOC]
ROS工作空间
ROS工作空间是一个具有特定结构的目录。通常有一个 src 子目录。在该子目录下是ROS软件包的源代码所在位置。通常该目录开始时为空。
Colcon 工具
Colcon 编译代码的原理
- 工作空间结构
- Colcon 使用特定的目录结构来组织 ROS 2 项目。一个典型的工作空间包含以下目录:
src:存放所有软件包的源代码。build:存放编译过程中生成的中间文件。对于每个软件包,将在其中创建一个子文件夹,例如在该子文件夹中调用 CMake。install:存放编译完成后的安装文件。默认情况下,每个软件包将被安装到单独的子目录中。log:存放编译过程中的日志信息。
- Colcon 使用特定的目录结构来组织 ROS 2 项目。一个典型的工作空间包含以下目录:
- 依赖解析
- Colcon 在编译前会解析每个软件包的依赖关系。依赖关系在软件包的
package.xml文件中声明,包括构建时依赖和运行时依赖。 - Colcon 使用拓扑排序算法来确定构建顺序,确保每个软件包在所有依赖项构建完成后才开始编译。
- Colcon 在编译前会解析每个软件包的依赖关系。依赖关系在软件包的
- 并行构建
- Colcon 支持并行构建,可以通过多线程或分布式构建来提高编译效率。这使得多个包可以同时编译,从而大大缩短了整体构建时间。
- 构建过程
- Colcon 通过调用 CMake 或其他构建系统(如
ament_cmake或ament_python)来执行具体的编译任务。对于 C++ 包,CMakeLists.txt文件定义了编译规则;对于 Python 包,setup.py文件定义了构建规则
- Colcon 通过调用 CMake 或其他构建系统(如
Colcon 如何识别软件包
package.xml文件- 每个 ROS 2 软件包必须包含一个
package.xml文件。这个文件是软件包的元数据描述文件,定义了软件包的基本信息(如名称、版本、描述)、依赖关系、维护者信息等。 - Colcon 通过解析
package.xml文件来识别软件包的存在,并获取其依赖关系和构建顺序。
- 每个 ROS 2 软件包必须包含一个
CMakeLists.txt和setup.py文件- 对于 C++ 包,
CMakeLists.txt文件定义了编译规则、目标文件、依赖查找等内容。 - 对于 Python 包,
setup.py文件定义了 Python 包的元数据和安装规则。 - Colcon 通过这些文件来确定每个软件包的具体构建逻辑。
- 对于 C++ 包,
- 目录结构
- Colcon 会自动扫描
src目录下的所有子目录。每个子目录被视为一个独立的软件包,只要该目录包含package.xml文件。 - 如果需要指定额外的搜索路径,可以使用
--base-paths参数。 - 如果没有
src文件夹也没有指定额外的搜索路径,Colcon 会尝试在当前目录及其子目录中查找包含package.xml文件的目录。
- Colcon 会自动扫描
- 构建类型
- Colcon 支持多种构建类型,如
ament_cmake和ament_python。根据软件包的构建类型,Colcon 会调用相应的构建系统来完成编译。 colcon build参数支持输入cmake参数,如通过--cmake-args输入cmake的-D参数。console_cohesion+表示构建结束后输出到终端,+表示启用,-表示不启用,默认不启用。
- Colcon 支持多种构建类型,如
C++ 编译编译 :ament_cmake
构建信息会收集在两个文件中:package.xml 和 CMakeLists.txt,它们必须位于同一目录中。
package.xml 必须包含所有依赖项和一些元数据,以允许 colcon 找到正确的构建顺序,以在CI中安装所需的依赖项,并为使用 bloom 进行发布提供信息。CMakeLists.txt 包含了构建和打包可执行文件和库的命令。
ament_cmake 命令汇总
| 命令 | 作用 | 是否必须 | 使用场景 |
|---|---|---|---|
ament_package() |
定义一个 ROS 2 包 | ✅ 必须 | 所有 ROS 2 包 |
ament_target_dependencies() |
给 target 添加 ROS 包依赖 | ✅ 强烈推荐 | 链接 rclcpp、std_msgs 等 |
ament_export_dependencies() |
导出构建依赖给下游包 | ✅ 定义接口类/消息时必用 | 下游 find_package() 可用 |
ament_export_include_directories() |
导出头文件路径 | ✅ 如果你有头文件 | C++ 库封装时 |
ament_export_libraries() |
导出链接库 | ✅ 如果你定义库 | 提供 .so 或 .a |
ament_export_targets() |
导出 CMake target | 可选 | 高级 target control |
ament_export_interfaces() |
导出 interface targets | 可选 | 用于 INTERFACE target |
ament_index_register_resource() |
注册资源到 ament 索引 | 可选 | pluginlib 或 rqt 插件 |
ament_lint_auto_find_test_dependencies() |
自动添加 linter 测试依赖 | 可选 | 启用代码检查工具 |
1. 包定义与注册类
🔹 ament_package()
- 作用:声明“这是一个 ROS 2 包”
- 必须:✅ 所有 ROS 2 包必须调用
- 行为:
- 安装
package.xml - 创建 CMake
your_pkg-config.cmake - 导出
ament_export_*声明的信息给下游使用
- 安装
2. 依赖管理类
🔹 ament_target_dependencies(target dep1 dep2 ...)
-
作用:自动添加包含路径、链接库等依赖信息
-
等价于:
-
推荐使用:在 ROS 2 项目中替代手动设置
target_link_libraries
🔹 ament_export_dependencies(...)
- 作用:让依赖你的包的下游包也自动依赖这些依赖
- 典型用途:
- 如果你依赖了
rclcpp,且你导出了接口(如类/消息),就要加上它
- 如果你依赖了
- 配合
ament_package()使用
3. 导出接口类(库)相关
🔹 ament_export_include_directories(...)
-
作用:将头文件路径导出给下游包
-
什么时候必须:
- 如果你的库对外提供
.hpp,就必须 export 出包含路径
- 如果你的库对外提供
-
配套写法:
🔹 ament_export_libraries(...)
- 作用:将你定义的库导出给下游包链接使用
- 适用场景:
- 提供了静态库
.a或共享库.so
- 提供了静态库
🔹 ament_export_targets(...)
- 高级功能:如果你使用了
add_library(... EXPORT)并希望 export target 到下游,可以使用它
4. 索引注册类(插件)
🔹 ament_index_register_resource(NAME resource_name ... )
- 用途:注册资源路径(如 pluginlib 插件、rqt 插件)
- 结果:在构建后
share/ament_index/resource_index/<resource_name>中生成注册记录
5. 代码质量和测试(可选)
🔹 ament_lint_auto_find_test_dependencies()
- 自动添加 linter 依赖,如
ament_cmake_lint_cmake,ament_cmake_cppcheck,ament_cmake_uncrustify等。
PYTHON 编译编译 :setup.py
总结
source install/setup.bash 给了 PYTHONPATH 追加了包的位置
setup.py 生成的脚本 会解析生成的配置文件 取找到入口函数
|
|
local_setup.bash 添加了当前可执行文件的路径
|
|
分步解析过程
1. setup.py 中定义了 entry_points
在你的 setup.py 文件中,有如下配置:
这段代码告诉 setuptools:
当用户运行 patrol_agent 命令时,你应该去加载
robot_patrol_system.core.patrol_agent模块,并调用它的 main() 函数。
2. 安装包时生成入口脚本
当你使用以下命令安装包时:
setuptools 会根据 entry_points 配置自动生成一个可执行脚本文件,比如:
|
|
或者你当前看到的是:
|
|
该脚本内容如下(简化版):
3. load_entry_point 解析模块路径
load_entry_point() 函数是 pkg_resources 或 importlib.metadata 提供的工具函数,它内部会做以下事情:
- 读取元数据
- 它会从已安装的包中读取
.dist-info或.egg-info目录下的entry_points.txt文件。 - 这个文件里保存了你在
setup.py中定义的 entry_points 内容。
- 它会从已安装的包中读取
- 匹配 entry point 名称
- 它会查找
[console_scripts]组下名为patrol_agent的条目。 - 条目值为:
robot_patrol_system.core.patrol_agent:main
- 它会查找
- 动态导入模块并获取函数
- 它会通过 Python 的
importlib.import_module("robot_patrol_system.core.patrol_agent")加载模块。 - 然后通过
getattr(module, "main")获取main()函数对象。
- 它会通过 Python 的
- 执行函数
- 最终调用
main()函数。
- 最终调用
使用nuitka 打包为模块
发布工具 Bloom
Bloom 是一个将 ROS 源码包转换为 Debian 等二进制包的自动化发布工具
Bloom 发布流程
假设你要发布 ROS 2 包 my_robot_package,流程如下:
- 准备包结构:满足 ROS 包格式(含
package.xml) - 版本控制:使用 Git,并准备好 tag
- 使用 bloom 创建 release 配置:
- 生成 Debian 元数据和 release 分支:
|
|
- 自动创建 Git tag、release 分支(如
release/humble/my_robot_package/1.0.0) - 将生成的
source.tar.gz上传到 ROS build farm - 提交到 ROS distro 的
rosdistro仓库 PR(由你或机器人完成) - ROS build farm 自动构建并发布二进制包
ROS build farm
ROS Build Farm 是一个分布式自动化系统,它将 ROS 源代码转换为官方发布的二进制包(如 .deb),并提供持续集成(CI)和持续发布(CD)功能。
官方站点和面板
| 地址 | 内容 |
|---|---|
| https://build.ros2.org | ROS 2 各版本构建状态总览 |
| https://repo.ros2.org | 构建产物仓库 |
| https://github.com/ros-infrastructure/ros_buildfarm | 构建脚本仓库 |
| https://github.com/ros/rosdistro | 各版本包信息仓库(维护 bloom 提交) |
source install/setup.bash 的行为
install/setup.bash 本质上是一个 汇总脚本,其核心行为是:
1. 设置环境变量(以 PATH、PYTHONPATH、AMENT_PREFIX_PATH 为主)
|
|
这些变量用于告诉编译器、运行时环境、Python 解释器去哪里找 ROS 2 资源。
2. 设置 ROS 特有变量
这些环境变量告诉工具链(如 ros2 run、colcon)当前使用的是哪个 ROS 版本。
3. 递归调用各个包的 local_setup 脚本
setup.bash 会递归执行 install/<package>/share/<package>/local_setup.bash 脚本:
|
|
每个包的 local_setup.bash 负责设置它自己特有的路径(如 pluginlib、msg 定义、lib 路径)。
常见影响目录
| 变量 | 意义 |
|---|---|
install/setup.bash |
整个工作区的入口脚本 |
install/setup.sh |
POSIX 兼容版本 |
install/local_setup.bash |
通常不会手动执行,供顶层调用 |
install/share/<pkg>/package.dsv |
构建时生成的 shell 变量定义 |
install/share/<pkg>/local_setup.bash |
每个包自有环境配置 |
COLCON_CURRENT_PREFIX |
指示当前工作空间路径(colcon 使用) |
ROS2 RUN
ros2 run 的行为流程:
-
读取环境变量(特别是
AMENT_PREFIX_PATH/COLCON_PREFIX_PATH); -
在这些路径下查找你指定的
<package_name>安装目录; -
定位
<package_name>/share/<package_name>/package.xml,确认存在; -
读取该 package 的
ament index元信息(在构建或安装时生成); -
查找该 package 下是否声明了
<executable_name>; -
运行实际路径为:
1<install_prefix>/<package_name>/lib/<package_name>/<executable_name>
依赖的环境变量
ros2 run 依赖以下环境变量的正确设置(由 source install/setup.bash 提供):
| 环境变量 | 用途 |
|---|---|
AMENT_PREFIX_PATH / COLCON_PREFIX_PATH |
查找安装路径中有哪些包 |
AMENT_INDEX_ROOT |
查找 ament index 中的资源描述 |
PATH, LD_LIBRARY_PATH |
运行依赖库和命令 |