ROS2智能机器人开发实践-阅读分享11-ros2编译colcon build详细解析
对于和我一样不是计算机出生的同学们一定很疑惑,colcon build到底干了啥?我来大概讲一下我的理解,我们来以一个c++的ros2工作空间为例子,这是一个工作空间的项目录,它包含很多东西不仅仅有src文件夹还有python和scripts和include和config文件夹
root@ubuntu:~/dev_ws/src/origincar/origincar_base# tree
.
├── CMakeLists.txt
├── config
│ ├── ekf.yaml
│ └── imu.yaml
├── include
│ └── origincar_base
│ ├── origincar_base.h
│ └── Quaternion_Solution.h
├── launch
│ ├── base_serial.launch.py
│ ├── ekf.launch.py
│ ├── origincar_bringup.launch.py
│ ├── __pycache__
│ │ └── testtwo.launch.cpython-38.pyc
│ └── robot_mode_description.launch.py
├── msg
│ └── Position.msg
├── package.xml
├── scripts
│ └── cmd_vel_to_ackermann_drive.py
└── src
├── origincar_base.cpp
└── Quaternion_Solution.cpp
8 directories, 15 files
在 ROS 2 中,colcon build 是用于构建 ROS 包的现代构建工具。它会根据你的工作空间(workspace)中的包定义(如 CMakeLists.txt 和 package.xml),完成代码编译、依赖管理、安装部署等任务。然后调用构建工具(如 CMake 或 Python setuptools来进行打包工作空间。
我们仔细查看cmake和package的里面的内容。以下是cmakelist.txt的内容:
# 设置 CMake 的最低版本要求为 3.5,确保兼容性和功能支持
cmake_minimum_required(VERSION 3.5)
# 定义项目名称为 origincar_base,并隐式初始化 CMake 变量(如 PROJECT_NAME)
project(origincar_base)
# 设置默认的 C 和 C++ 语言标准
# 如果用户未指定 CMAKE_C_STANDARD,则默认使用 C99 标准
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99) # C99 标准提供现代 C 特性
endif()
# 如果用户未指定 CMAKE_CXX_STANDARD,则默认使用 C++14 标准
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14) # C++14 提供平衡的现代特性和兼容性
endif()
# 针对 GCC 或 Clang 编译器启用额外警告选项,提升代码质量
# CMAKE_COMPILER_IS_GNUCXX 表示 GNU 编译器,Clang 的 ID 匹配则检测 Clang
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic) # 启用所有警告、额外警告和严格模式
endif()
# 查找并加载 ROS 2 的构建系统依赖包
find_package(ament_cmake REQUIRED) # ROS 2 的 CMake 基础工具
find_package(geometry_msgs REQUIRED) # 几何数据类型支持
find_package(tf2_geometry_msgs REQUIRED) # TF2 与几何消息的转换
find_package(nav2_msgs REQUIRED) # 导航2相关消息定义
find_package(nav_msgs REQUIRED) # 导航基础消息
find_package(rclcpp REQUIRED) # ROS 2 C++ 客户端库
find_package(rclpy REQUIRED) # ROS 2 Python 客户端库(此处可能未直接使用)
find_package(sensor_msgs REQUIRED) # 传感器数据类型
find_package(std_msgs REQUIRED) # 标准基础数据类型
find_package(tf2 REQUIRED) # TF2 库
find_package(tf2_ros REQUIRED) # TF2 的 ROS 接口
find_package(std_srvs REQUIRED) # 标准服务定义
find_package(ackermann_msgs REQUIRED) # 阿克曼转向消息类型(用户自定义或第三方包)
find_package(origincar_msg REQUIRED) # 项目自定义消息包
find_package(serial REQUIRED) # 串口通信库(如 libserial)
find_package(rosidl_default_generators REQUIRED) # ROS 接口定义语言生成器
find_package(origincar_description REQUIRED) # 机器人描述文件(如 URDF)
find_package(robot_localization REQUIRED) # 机器人定位算法库
find_package(ldlidar REQUIRED) # 特定型号激光雷达驱动
# 生成自定义 ROS 接口文件(消息、服务、动作)
# 使用 rosidl_generate_interfaces 生成 Position.msg 对应的 C++ 和 Python 代码
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Position.msg" # 消息文件路径,生成在 devel/include/<project>/msg/
)
# 若启用测试(BUILD_TESTING 默认为 ON),配置代码检查工具
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED) # 自动化代码风格检查工具集
ament_lint_auto_find_test_dependencies() # 自动发现测试依赖的 linter 工具
endif()
# 添加头文件包含路径,确保编译器能找到项目的头文件
include_directories(
include # 项目自定义头文件目录
)
# 定义源文件列表变量 origincar_base_node_SRCS
# 包含两个源文件:主节点实现和四元数解算工具
set(origincar_base_node_SRCS
src/origincar_base.cpp
src/Quaternion_Solution.cpp
)
# 创建可执行文件 origincar_base_node,并指定源文件
add_executable(origincar_base_node
src/origincar_base.cpp
src/Quaternion_Solution.cpp
)
# 为可执行文件添加依赖的 ROS 包和第三方库
# ament_target_dependencies 会自动处理包含目录和链接库
ament_target_dependencies(origincar_base_node
tf2_ros # TF2 ROS 接口
tf2 # TF2 库
tf2_geometry_msgs # TF2 与几何消息转换
rclcpp # ROS 2 C++ 客户端库
std_msgs # 标准消息类型
robot_localization # 定位功能
nav_msgs # 导航消息
std_srvs # 标准服务
sensor_msgs # 传感器数据
ackermann_msgs # 阿克曼转向消息
serial # 串口库
origincar_msg # 自定义消息包
origincar_description # 机器人描述
)
# 安装 Python 脚本到 lib/<project_name> 目录,确保运行时可访问
install(PROGRAMS scripts/cmd_vel_to_ackermann_drive.py
DESTINATION lib/${PROJECT_NAME}
)
# 安装可执行文件到 ROS 2 的标准位置
install(TARGETS origincar_base_node
# 可执行文件安装到 lib/<project_name>
DESTINATION lib/${PROJECT_NAME}
# 以下为传统 catkin 兼容性设置,ROS 2 中可能不必要
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# 安装 launch 和 config 目录到 share/<project_name>
# launch 包含启动文件,config 存放参数配置文件
install(
DIRECTORY launch config
DESTINATION share/${PROJECT_NAME}
)
# 完成 ROS 2 包的配置,生成必要的环境钩子和包信息文件
ament_package()
find packages
内容详解:里面find packages 道理在哪里find.在 ROS 2 中,find_package(ament_cmake REQUIRED) 是 CMake 的 find_package 命令,用于查找名为 ament_cmake 的包。它的搜索路径由 CMake 的搜索规则和 ROS 2 的环境变量共同决定。
这个ament_make非常重要,一般只有官方的包下面有这个ament_make,它是 ROS 2 的构建系统核心工具,提供 CMake 宏和函数,用于:
- 编译 ROS 2 包。
- 导出包的依赖关系(如头文件、库文件、消息接口等)。
- 生成包的配置文件(如 YourPackageConfig.cmake)。
除此之外我们自己colcon build的文件会生成ament_index放置cmake信息它是 ROS 2 的资源索引工具,用于:
- 记录包的资源路径(如消息定义、服务定义、启动文件等)。
- 在运行时动态查找包的资源(例如 ros2 pkg list 命令会用到 ament_index)。
- 所以我们的c++的cmakelist里面一定要find这个ament_cmake,其实这里一般也可以find比如OpenCV 库(前提是你要安装,而且在/usr/lib/aarch64-linux-gnu/cmake/里面有这个opencv的配置文件)
下面是ament_make的地点。
root@ubuntu:/opt/ros/foxy/share/ament_cmake# ls
cmake environment local_setup.bash local_setup.dsv local_setup.sh local_setup.zsh package.dsv package.xml
rosidl_generate_interfaces
这个是ros2的特定用来生成ros2的消息接口和服务类型的东西,如果你需要使用就调用这个。
install
这个就相当于复制过去,这样做的原因是为了可以方便使用罢了。
差不多就这样了,一般情况下由于python不需要编译成二进制文件一般都是直接检查有没有语法错误之后直接复制到install里面,但是c++不一样,他需要你自己找到依赖的包然后把二进制代码放进install里面。
下面这个是package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!-- XML 版本和格式验证声明 -->
<package format="3">
<!-- 包的基本信息 -->
<name>origincar_base</name> <!-- 包名,需与文件夹名一致 -->
<version>0.0.0</version> <!-- 版本号,建议遵循语义化版本规则(如 0.1.0) -->
<description>ROS2 origincar_base for origincar_base</description> <!-- 描述需更具体,例如说明包的功能 -->
<maintainer email="ps-micro@todo.todo">ps-micro</maintainer> <!-- 维护者邮箱需替换为真实地址 -->
<license>TODO: License declaration</license> <!-- 必须填写有效许可证(如 Apache-2.0、BSD-3-Clause) -->
<!-- 构建工具依赖 -->
<buildtool_depend>ament_cmake</buildtool_depend> <!-- ROS 2 核心构建系统 -->
<buildtool_depend>rosidl_default_generators</buildtool_depend> <!-- 用于生成消息/服务接口代码 -->
<!-- 测试依赖 -->
<test_depend>ament_lint_auto</test_depend> <!-- 自动化代码检查工具 -->
<test_depend>ament_lint_common</test_depend> <!-- 提供通用 lint 规则 -->
<test_depend>boost</test_depend> <!-- Boost 库(仅测试阶段需要) -->
<!-- 构建依赖 -->
<build_depend>tf2_geometry_msgs</build_depend> <!-- 编译时需要 TF2 几何消息支持 -->
<!-- 构建导出依赖 -->
<build_export_depend>tf2_geometry_msgs</build_export_depend> <!-- 下游包构建时需依赖的库 -->
<!-- 运行时依赖 -->
<exec_depend>rosidl_default_runtime</exec_depend> <!-- ROS 接口运行时支持 -->
<exec_depend>ament_index_cpp</exec_depend> <!-- ROS 资源索引的 C++ 实现 -->
<exec_depend>geometry_msgs</exec_depend> <!-- 几何消息类型 -->
<exec_depend>rclcpp</exec_depend> <!-- ROS C++ 客户端库 -->
<exec_depend>rclcpp_action</exec_depend> <!-- Action 接口的 C++ 支持 -->
<exec_depend>sensor_msgs</exec_depend> <!-- 传感器消息类型 -->
<exec_depend>std_msgs</exec_depend> <!-- 标准消息类型 -->
<exec_depend>std_srvs</exec_depend> <!-- 标准服务类型 -->
<exec_depend>tf2</exec_depend> <!-- TF2 库 -->
<exec_depend>tf2_ros</exec_depend> <!-- TF2 的 ROS 接口 -->
<exec_depend>rclpy</exec_depend> <!-- ROS Python 客户端库(若包含 Python 代码) -->
<exec_depend>serial</exec_depend> <!-- 串口通信库 -->
<exec_depend>nav2_msgs</exec_depend> <!-- 导航2消息类型 -->
<exec_depend>nav_msgs</exec_depend> <!-- 导航消息类型 -->
<exec_depend>ackermann_msgs</exec_depend> <!-- 阿克曼转向消息 -->
<exec_depend>origincar_msg</exec_depend> <!-- 自定义消息包 -->
<exec_depend>origincar_description</exec_depend> <!-- 自定义描述包 -->
<exec_depend>robot_localization</exec_depend> <!-- 机器人定位功能包 -->
<!-- 接口包组声明 -->
<member_of_group>rosidl_interface_packages</member_of_group> <!-- 表示此包定义了接口(需在 CMakeLists 中生成接口) -->
<!-- 导出构建类型 -->
<export>
<build_type>ament_cmake</build_type> <!-- 声明使用 ament_cmake 构建系统 -->
</export>
</package>
首先我们要知道无论是cpp还是python都有package.xml,这是一个申明文件和那个cmakelist.txt不一样,
CMakeLists.txt 配置了构建规则(如编译哪些文件、链接哪些库), package.xml 的作用是 声明包的元数据和依赖关系,二者分工明确:
文件 |
作用 |
CMakeLists.txt |
定义如何编译代码(如源文件、头文件路径、链接库、生成可执行文件等)。 |
package.xml |
定义包的元数据(名称、版本、维护者)和依赖关系(构建、运行时、测试依赖)。 |
我们来举一个例子:
- 开发者 A
- 在 package.xml 中声明依赖 rclcpp 和 geometry_msgs。
-
- 运行 rosdep install,自动安装所有 package.xml 中声明的依赖。
- colcon build 根据 CMakeLists.txt 编译代码,并链接已安装的库。
-
- ros2 run、ros2 launch 通过 package.xml 的元数据找到包的可执行文件。
ok ,大概就这样,至于linux系统和cmake的具体东西我也不是很了解,我就解析一下cmakelist和package是用来干嘛的,干了哪些活就行。