catkin工程和CMakelist.txt的基本使用
- 1.catkin工程和CMakelist.txt的基本使用
- 1. 顶部基本信息
- 2. 编译选项 / C++ 标准
- 3. 依赖查找(catkin 包)
- 4. 第三方库查找(非 catkin)
- 5. 导出包信息(catkin_package)
- 6. 头文件与依赖头路径
- 7. 生成可执行文件(节点)
- 8. 链接依赖库
- 9. 安装规则(让 `catkin_make install` 生效)
- 2. 使用方的CMakelist.txt和package.xml
- 问题1:find_package是在哪个路径找到mavros_controller包的?
- 问题2:为什么添加库使用include_directories( ${catkin_INCLUDE_DIRS},而不需要再写 ../mavros_controller/include路径?
- 问题3:target_link_libraries中的mavros_controller 可以省略
- 3. ROS(catkin):最小 CMakeLists.txt
参考: cmakeList语法详解
1.catkin工程和CMakelist.txt的基本使用
基本文件结构
mavros_controller
├─ CMakeLists.txt
├─ package.xml
├─ include
│ └─ mavros_controller.h
└─ src└─ mavros_controller.cpp
package.xml
<?xml version="1.0"?>
<package format="2"><name>mavros_controller</name><version>0.1.0</version><description>MAVROS controller node that manages OFFBOARD mode, arming, and position setpoints, decoupled from planning.</description><maintainer email="maintainer@example.com">maintainer</maintainer><license>BSD</license><buildtool_depend>catkin</buildtool_depend><build_depend>roscpp</build_depend><build_depend>mavros_msgs</build_depend><build_depend>geometry_msgs</build_depend><build_depend>tf</build_depend><exec_depend>roscpp</exec_depend><exec_depend>mavros_msgs</exec_depend><exec_depend>geometry_msgs</exec_depend><exec_depend>tf</exec_depend><export/>
</package>
CMakelist.txt
cmake_minimum_required(VERSION 3.0.2) # CMake 最低版本要求
project(mavros_controller) # 定义工程名,变量 PROJECT_NAME = mavros_controlleradd_compile_options(-std=c++11) # 设置编译选项,使用 C++11# 查找依赖的 catkin 包
find_package(catkin REQUIRED COMPONENTSroscppmavros_msgsgeometry_msgstf
)find_package(Eigen3 REQUIRED) # 查找第三方库 Eigen3# 导出给其他包使用的信息(头文件目录、依赖、库)
catkin_package(CATKIN_DEPENDS roscpp mavros_msgs geometry_msgs tfINCLUDE_DIRS includeLIBRARIES mavros_controller
)# 生成库(libmavros_controller.so),供其他包或本包节点链接
add_library(${PROJECT_NAME}src/mavros_controller.cpp
)# 设置头文件搜索路径
include_directories(include${catkin_INCLUDE_DIRS}${EIGEN3_INCLUDE_DIRS}
)# 生成可执行文件(节点)
add_executable(mavros_controller_nodesrc/mavros_controller.cpp
)# 链接库(目前只链接 ROS 依赖)
target_link_libraries(mavros_controller_node${catkin_LIBRARIES}
)# 安装节点可执行文件到 ROS 的 bin 路径
install(TARGETS mavros_controller_nodeRUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)# 安装头文件目录,供其他包 include
install(DIRECTORY include/DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
package.xml
是 每个包的“元数据文件”,它的作用相当于一个“身份证 + 依赖清单”。
文件 | 作用 | 典型内容 |
---|---|---|
CMakeLists.txt | 定义如何编译(技术层面) | add_library 、add_executable 、target_link_libraries 、install |
package.xml | 描述包信息与依赖(元数据层面) | 包名、版本、维护者、license、依赖 <depend> |
语法说明:
1. 顶部基本信息
cmake_minimum_required(VERSION 3.0.2)
project(mavros_controller)
cmake_minimum_required
:要求 CMake 的最低版本(这里 3.0.2,和 ROS Kinetic/ Melodic/Noetic 常用版本兼容)。project(<name>)
:定义工程名,同时创建同名变量PROJECT_NAME
可在后面复用。
2. 编译选项 / C++ 标准
add_compile_options(-std=c++11)
-
为所有目标添加编译器选项,这里设为 C++11。
-
现代写法(等价且更清晰):
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON)
3. 依赖查找(catkin 包)
find_package(catkin REQUIRED COMPONENTSroscppmavros_msgsgeometry_msgstf
)
- 在 ROS/catkin 环境中查找你依赖的ROS 包(编译/链接时用到的消息、库、头文件等)。
REQUIRED
表示缺少就报错终止。- 这些组件会提供
catkin_INCLUDE_DIRS
与catkin_LIBRARIES
等变量。
4. 第三方库查找(非 catkin)
find_package(Eigen3 REQUIRED)
- 查找系统里的 Eigen3(通常是头文件库),成功后会提供
EIGEN3_INCLUDE_DIRS
(或Eigen3::Eigen
目标)。
5. 导出包信息(catkin_package)
catkin_package(CATKIN_DEPENDS roscpp mavros_msgs geometry_msgs tfINCLUDE_DIRS include
)
-
告诉 catkin:这个包对外暴露什么,即想让其他包用到你的头文件 / 库。
-
如果只写一个节点自己用,不打算给别的包复用,
catkin_package
可以删掉,不影响你自己的节点编译运行。因为自己在本包内部用不到。 -
CATKIN_DEPENDS
:你的包(编译/运行)依赖了哪些其他 catkin 包(会体现在对方包的编译环境里)。 -
INCLUDE_DIRS include
:导出本包的头文件目录(下文install(DIRECTORY include/ ...)
对应安装),使其他包#include
你的头文件时能找到。 -
比如你的包提供了
MavrosController.h
,希望别的包能:#include <mavros_controller/MavrosController.h>
这时候就必须保留
catkin_package(INCLUDE_DIRS include ...)
,否则安装后下游包找不到头文件路径。 -
想让其他包不仅能 include,还能链接到你的库
需要在 catkin_package(...)
里加 LIBRARIES mavros_controller
,并且在 CMake 里 add_library(mavros_controller ...)
。
#include头文件只是告诉编译器“这个类有这些方法”,最后还要把 `.cpp里的实现编译出来,然后 链接进可执行文件。
6. 头文件与依赖头路径
include_directories(include${catkin_INCLUDE_DIRS}${EIGEN3_INCLUDE_DIRS}
)
- 为后续目标添加头文件搜索路径:
include
:你包里的公共头(例如include/mavros_controller/MavrosController.h
)。${catkin_INCLUDE_DIRS}
:上面find_package(catkin ...)
得到的依赖头路径。${EIGEN3_INCLUDE_DIRS}
:Eigen3 的头路径。
7. 生成可执行文件(节点)
add_executable(mavros_controller_nodesrc/mavros_controller.cpp
)
- 以
src/mavros_controller.cpp
源文件编译一个可执行文件mavros_controller_node
。 - 典型 ROS 节点会在此
.cpp
中包含main()
。
8. 链接依赖库
target_link_libraries(mavros_controller_node${catkin_LIBRARIES}
)
-
把 ROS 依赖库(roscpp、tf、mavros_msgs 等)链接到你的可执行文件上(mavros_controller_node)。
-
当你在代码里
#include <ros/ros.h>
并使用ros::init
、ros::spin
等函数时,这些函数的实现实际上在 roscpp 库 里。也可以单独拆开看里面到底有哪些库,然后按需删减。
target_link_libraries(mavros_controller_node${roscpp_LIBRARIES}${tf_LIBRARIES} )
但这没必要,因为
${catkin_LIBRARIES}
已经帮你统一管理了。
9. 安装规则(让 catkin_make install
生效)
install(TARGETS mavros_controller_nodeRUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)install(DIRECTORY include/DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
- 安装二进制可执行文件到
${CATKIN_PACKAGE_BIN_DESTINATION}
(通常是devel/.private/<pkg>/
或install/
对应路径)。 - 安装头文件目录到
${CATKIN_PACKAGE_INCLUDE_DESTINATION}
,这与catkin_package(INCLUDE_DIRS include)
一起,使其它包在安装空间也能通过find_package
找到你的头文件。 - 如果不写
install(...)
,在devel/
里开发调试:用catkin_make
/catkin build
编译后,二进制会放在devel/lib/<package_name>/
,头文件还在源码目录include/
,不写 install() 不影响本地开发和运行。 - 如果不写
install(...)
,在install/
里开发调试:别人用find_package(catkin REQUIRED COMPONENTS mavros_controller)
时,找不到你的头文件/二进制,就没法复用。自己source install/setup.bash
后也没法直接运行节点,因为install/
下压根没有导出二进制。
2. 使用方的CMakelist.txt和package.xml
现在有其他包想调用mavros_controller包中的方法,使用方包的CMakelist.txt
cmake_minimum_required(VERSION 3.0.2)
project(consumer_pkg)set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(catkin REQUIRED COMPONENTSroscppgeometry_msgsmavros_msgstfmavros_controller # 关键:找到并引入你导出的库与头文件
)catkin_package()include_directories(${catkin_INCLUDE_DIRS} # 包含到 mavros_controller 的 include/
)add_executable(consumer_node src/consumer_node.cpp)
target_link_libraries(consumer_node${catkin_LIBRARIES} # roscpp/tf 等mavros_controller # 关键:链接你的库
)# 可选:保证依赖顺序(通常问题不大,这里给出标准写法)
add_dependencies(consumer_node ${catkin_EXPORTED_TARGETS})
使用方包的 package.xml
<package format="2"><name>consumer_pkg</name><version>0.0.1</version><description>Links to mavros_controller library</description><maintainer email="you@example.com">You</maintainer><license>BSD</license><!-- 如果只使用头文件,那么不需要下面这行 --><depend>mavros_controller</depend><depend>roscpp</depend><depend>geometry_msgs</depend><depend>mavros_msgs</depend><depend>tf</depend>
</package>
问题1:find_package是在哪个路径找到mavros_controller包的?
- catkin 是怎么“找到包”的?
当你写:
find_package(catkin REQUIRED COMPONENTS mavros_controller)
CMake 会去找 mavros_controller
这个 ROS 包,方式是:
- 在
ROS_PACKAGE_PATH
中查找package.xml
/manifest.xml
文件;
2. 常见查找路径
devel space(开发空间)
如果你在同一个工作空间里编译了 mavros_controller
,
catkin 会在 devel/share/mavros_controller/cmake/
下生成:
mavros_controllerConfig.cmake
mavros_controllerConfig-version.cmake
这两个文件就是 find_package
用的“入口”。
所以只要你 source devel/setup.bash
,CMake 就能通过环境变量找到这些路径。
install space(安装空间)
如果你执行了:
catkin_make install
会在 install/share/mavros_controller/cmake/
下生成同样的 Config.cmake
。
发布成 deb 包(比如 sudo apt install ros-noetic-mavros-controller
)后,也是这个逻辑。
- CMake 会查
$CMAKE_PREFIX_PATH
(被setup.bash
设置了),里面包含devel/
或install/
。 - 然后拼接路径
share/mavros_controller/cmake/mavros_controllerConfig.cmake
。
问题2:为什么添加库使用include_directories( ${catkin_INCLUDE_DIRS},而不需要再写 …/mavros_controller/include路径?
因为你用了:
find_package(catkin REQUIRED COMPONENTS mavros_controller)
include_directories(${catkin_INCLUDE_DIRS})
-
当你
find_package(... mavros_controller)
的时候,CMake 会读取mavros_controller
包在devel/share/mavros_controller/cmake/
下生成的 Config.cmake 文件。 -
这个文件里包含了
catkin_package()
导出的信息,比如:set(mavros_controller_INCLUDE_DIRS /home/user/ws/devel/include)
或者 install 空间里的
/home/user/ws/install/include
。 -
${catkin_INCLUDE_DIRS}
会自动拼接所有依赖包的INCLUDE_DIRS
,所以你得到的路径已经包含了mavros_controller/include
。
这样,你在源代码里只需要:
#include <mavros_controller/MavrosController.h>
编译时就能找到。
总结:catkin 已经通过 catkin_package(INCLUDE_DIRS include)
导出了,${catkin_INCLUDE_DIRS}
自动包含
问题3:target_link_libraries中的mavros_controller 可以省略
3.1 什么时候可以不写 mavros_controller
在使用方包里,若你这样写了:
find_package(catkin REQUIRED COMPONENTSroscpp geometry_msgs mavros_msgs tfmavros_controller # 引入这个依赖
)add_executable(consumer_node src/consumer_node.cpp)target_link_libraries(consumer_node${catkin_LIBRARIES} # 只写这一行
)
且提供方包已正确导出:
catkin_package(INCLUDE_DIRS includeLIBRARIES mavros_controllerCATKIN_DEPENDS roscpp geometry_msgs mavros_msgs tf
)
那么 ${catkin_LIBRARIES}
会自动包含 mavros_controller
的库,显式再写一次 mavros_controller
并不必要。
3.2 什么时候必须显式写 mavros_controller
- 提供方包没有在
catkin_package(LIBRARIES ...)
里导出库; - 你没有把
mavros_controller
放进find_package(catkin REQUIRED COMPONENTS ...)
; - 你不链接
${catkin_LIBRARIES}
(只链接个别库); - 你的库名和导出目标名不一致或导出有误(安装/导出没配好);
- 你不走库,而是想把
.cpp
独立编进可执行(这时根本不需要链接库,但也失去复用意义)。
推荐
最稳妥写法是只链接:
target_link_libraries(consumer_node ${catkin_LIBRARIES})
并确保提供方正确导出库(catkin_package(LIBRARIES ...)
+ add_library(...)
+ install(TARGETS ...)
)。
若你不确定导出是否规范,加上显式库名也没坏处:
target_link_libraries(consumer_node ${catkin_LIBRARIES} mavros_controller)
3. ROS(catkin):最小 CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2) # 指定所需的最低 CMake 版本(catkin 常见为 3.0.2)
project(my_pkg) # 定义工程名(同时影响生成目标的默认前缀等)find_package(catkin REQUIRED COMPONENTS # 查找 catkin 以及需要的 catkin 组件roscpp # 这里仅依赖 roscpp;有需要可继续添加如 std_msgs、sensor_msgs 等
)catkin_package() # 声明本包为 catkin 包;可在括号内声明导出头文件/库/依赖等,此处最简include_directories( # 为后续目标添加头文件搜索路径${catkin_INCLUDE_DIRS} # 使用 catkin 导出的头文件路径
)add_executable(my_node # 定义可执行目标 my_nodesrc/my_node.cpp # 源文件列表(可添加多个 .cpp)
)target_link_libraries(my_node # 为目标 my_node 链接库${catkin_LIBRARIES} # 链接 catkin 导出的库(包含 roscpp 等)
)install(TARGETS my_node # 安装目标,方便打包/部署与运行环境查找RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} # 安装到 devel/install 的 bin 目录
)
- 这是最基本的 catkin 节奏:找到 catkin 和需要的组件、声明包、包含头文件、添加节点、链接 catkin_LIBRARIES、可选安装。