典型的 ROS 2 ament_cmake构建CMake脚本中ament相关指令解释

一个典型的基于ament_cmake构建方式的CMakeLists.txt文件

bash 复制代码
cmake_minimum_required(VERSION 3.8)
project(ch2_node_cpp)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 17)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)

set(executable_node2go node2go)
set(executable_node2go2 node2go2)
set(executable_multinode multinode)
set(executable_multilifecyclenode multilifecyclenode)
set(executable_global_arg globalarg)
set(library_node2go nodelib)
set(library_getid getid)
set(library_lifecyclenode lifecyclenode2go)
set(dependencies
  rclcpp
  rclcpp_lifecycle
)

include_directories(include)

add_library(${library_node2go} SHARED
  src/node2go.cpp
)
add_library(${library_getid} SHARED
  src/getid.cpp
)
add_library(${library_lifecyclenode} SHARED
  src/lifecyclenode2go.cpp
)

add_executable(${executable_node2go}
  src/main.cpp
)
add_executable(${executable_node2go2}
  src/main2.cpp
)
add_executable(${executable_multinode}
  src/multinode.cpp
)
add_executable(${executable_multilifecyclenode}
  src/multilifecyclenode.cpp
)
add_executable(${executable_global_arg}
  src/global_arg.cpp
)

ament_target_dependencies(${library_node2go}
  ${dependencies}
)
ament_target_dependencies(${library_getid}
  ${dependencies}
)
ament_target_dependencies(${library_lifecyclenode}
  ${dependencies}
)
ament_target_dependencies(${executable_node2go}
  ${dependencies}
)
ament_target_dependencies(${executable_node2go2}
  ${dependencies}
)
ament_target_dependencies(${executable_multinode}
  ${dependencies}
)
ament_target_dependencies(${executable_multilifecyclenode}
  ${dependencies}
)
ament_target_dependencies(${executable_global_arg}
  ${dependencies}
)

target_link_libraries(${executable_node2go}
  ${library_node2go}
)
target_link_libraries(${executable_node2go2}
  ${library_node2go}
)
target_link_libraries(${executable_multinode}
  ${library_getid}
)
target_link_libraries(${executable_multilifecyclenode}
  ${library_lifecyclenode}
)

install(TARGETS
  ${library_node2go}
  ${library_getid}
  ${library_lifecyclenode}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
)

install(
  TARGETS ${executable_node2go}
  TARGETS ${executable_node2go2}
  TARGETS ${executable_multinode}
  TARGETS ${executable_multilifecyclenode}
  TARGETS ${executable_global_arg}
  RUNTIME DESTINATION lib/${PROJECT_NAME}
)

install(DIRECTORY include/
  DESTINATION include/
)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()

ament_export_include_directories(include)
ament_export_libraries(${library_node2go} ${library_getid} ${library_lifecyclenode})
ament_export_dependencies(${dependencies})
ament_package()

上面贴的这个 CMakeLists.txt 是一个典型的 ROS 2 包的构建脚本,它使用 ament_cmake 作为构建系统。

这几个 ament_* 系列命令其实都是 ROS 2 在标准 CMake 之上封装的一套宏,作用是让包能:

  1. 正确地声明依赖和编译选项(本包如何编译、链接哪些库)
  2. 正确地向其它 ROS 2 包导出你的接口(头文件、库、依赖)
  3. 注册本包 到 ROS 2 的包索引,以便被 colconrosdep 等工具找到并使用

下面将逐个说明它们的作用和为什么需要它们👇


1. ament_target_dependencies()

作用

  • 在 ROS 2 的 CMake 宏里,它会:
    1. 在目标(可执行文件或库)上添加正确的 include_directorieslink_libraries
    2. find_package() 得到依赖包的编译选项和链路选项,并应用到该目标。
    3. 自动添加依赖包的 ament_export_* 信息(这样你的构建知道依赖包在哪里)。

为什么需要

  • 如果你只用原生 CMake 的 target_link_libraries() + target_include_directories(),你需要手工处理依赖的路径和编译选项。
  • ROS 2 为了跨平台和标准化,把这些配置打包在 ament_target_dependencies() 里,更简单可靠。
  • 保证你的 target 能找到 ROS 2 API 头文件(比如 rclcpp)以及正确链接它的库。

2. ament_export_include_directories()

作用

  • 向其它包声明你的头文件路径
  • 当其它包用 find_package(ch2_node_cpp) 时,这个路径会自动加入它们的 include_directories()

为什么需要

  • 如果你的包提供公共头文件(比如 include/ 里的 .hpp),直接放在 include/ 并不会自动让别的包找到。
  • 通过 ament_export_include_directories(include),你告诉构建系统: "如果有人依赖我,就把我的 include/ 加到他们的编译器搜索路径里。"

3. ament_export_libraries()

作用

  • 向其它包声明你的库目标
  • 让依赖你的包知道需要链接哪些库文件,以及它们的名字。

为什么需要

  • 你的包可能编译了比如 nodelibgetid 这样的库,并且这些库在别的包的代码中要调用。
  • 如果不 export,这些库不会在依赖包的 CMake 中自动链接;依赖包必须手动写路径,这不方便也不安全。

4. ament_export_dependencies()

作用

  • 告诉其它包: "如果你依赖我,你也会隐式依赖这些第三方包(比如 rclcpp、rclcpp_lifecycle)"。

为什么需要

  • 依赖链传递:
    如果 A 依赖你的包 B,而 B 又依赖 rclcpp,那么 A 在构建时也必须有 rclcpp 的编译信息和链接信息。
  • ament_export_dependencies() 自动帮你传递这些依赖,避免 A 写重复的 find_package(rclcpp REQUIRED)

5. ament_package()

作用

  • 这是 ament_cmake 的核心收尾命令,必须在 CMakeLists.txt 末尾调用。
  • 主要功能:
    1. 生成 ROS 2 包的 package.xml 对应的 CMake 导出文件。
    2. 收集所有之前 ament_export_* 的信息并打包。
    3. 让其它 ROS 2 包可以用 find_package() 来找到你的包。
    4. colconament_index 注册包信息。

为什么需要

  • 如果不调用 ament_package(),你的包就没有被 ROS 2 的包系统正确注册,别的包用 find_package() 会失败。
  • 这是 ROS 2 中声明"我的包构建完成,可以被别人用"的关键一步。

小结(简单的依赖关系图)

复制代码
ament_target_dependencies()      # 设置目标的编译 & 链接信息(内部使用 find_package 数据)
|
+-- 提供依赖包的 include & library 信息,给当前包用

ament_export_include_directories()  # 导出头文件路径
ament_export_libraries()            # 导出库路径
ament_export_dependencies()         # 导出依赖包

ament_package()  # 收集以上所有导出信息并注册到 ROS 2 包索引

💡 简单理解

  • ament_target_dependencies() ➡️ 我怎么编译我的东西
  • ament_export_* ➡️ 别人怎么用我的东西
  • ament_package() ➡️ 把这些信息打包,并让别人能找到我
相关推荐
Tipriest_21 小时前
详细介绍colcon和ament的关系,以及它们在 ROS 2 构建系统中的角色和区别
ros2·colcon·ament
lqqjuly2 天前
Lidar调试记录Ⅰ之Ubuntu22.04虚拟机安装ROS2(无坑版)
linux·ros2·lidar·ubuntu22.04
nenchoumi31196 天前
ROS2 Humble 笔记(四)ROS 的最小工作单元-- Node 节点
笔记·机器人·ros2
nenchoumi31196 天前
ROS2 Humble 笔记(八)动作 action
笔记·机器人·ros2
nenchoumi31196 天前
ROS2 Humble 笔记(十二)launch 文件与 namespace 启动多个节点
笔记·机器人·ros2
nenchoumi31197 天前
ROS2 Humble 笔记(十)多机分布式通讯 DDS 与宿主机和Docker容器
笔记·机器人·ros2
叠叠乐7 天前
Navigation2 行为树架构源码级分析与设计原理
ros2
nenchoumi31197 天前
ROS2 Humble 笔记(七)标准与自定义 Interface 接口
笔记·机器人·ros2
不懂音乐的欣赏者8 天前
Windows 下 ROS/ROS2 开发环境最优解:WSL 比直接安装、虚拟机、双系统更优雅!
linux·windows·ubuntu·ros·wsl·ros2·双系统