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、可選安裝。