具身智能工程领域all in one

主要存放机器人技术等相关工程技术,如ros的学习笔记

ros

ros是专门为机器人设计的操作系统, 但是需要在宿主操作系统上运行, 例如支持最完善的linux

就一个简单的项目来说, ros的目录树如上所述, 可以描述为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

WorkSpace --- 自定义的工作空间

|--- build:编译空间,用于存放CMake和catkin的缓存信息、配置信息和其他中间文件。

|--- devel:开发空间,用于存放编译后生成的目标文件,包括头文件、动态&静态链接库、可执行文件等。

|--- src: 源码

|-- package:功能包(ROS基本单元)包含多个节点、库与配置文件,包名所有字母小写,只能由字母、数字与下划线组成

|-- CMakeLists.txt 配置编译规则,比如源文件、依赖项、目标文件

|-- package.xml 包信息,比如:包名、版本、作者、依赖项...(以前版本是 manifest.xml)

|-- scripts 存储python文件

|-- src 存储C++源文件

|-- include 头文件

|-- msg 消息通信格式文件

|-- srv 服务通信格式文件

|-- action 动作格式文件

|-- launch 可一次性运行多个节点

|-- config 配置信息

|-- CMakeLists.txt: 编译的基本配置

相关的命令大部分都很直接, 例如roscd,rosls, rosrun, roslaunch等,除了操作单位是功能包以外和linux系统命令几乎一样, 具体可以参考ROS官方文档
一些独特的命令有:

  1. roscore: 启动ros的核心服务
  2. rosrun: 运行某个功能包中的节点
  3. roslaunch: 根据配置启动某个功能包中的多个节点
  4. rqt_graph: 查看多个节点之间的关系

在视觉 slam 领域里, 简单来说,相机的位姿(pose)就是相机的位置和姿态的合称,它描述了世界坐标系与相机坐标系之间的转换关系。

如上图所示:点 P 的世界坐标为 P_{w},可以通过相机的位姿矩阵 T 转换到相机坐标系下为 P_{c} : \[P_c=T_{cw}P_w\]

可以将点 P 从相机坐标系转换到世界坐标系中: \[P_w=T_{wc}P_c\]

其中 \(T_{cw}\) 为该点从世界坐标系变换到相机坐标系的变换矩阵, \(T_{wc}\) 为该点从相机坐标系变换到世界坐标系的变换矩阵。它们二者都可以用来表示相机的位姿,前者称为相机的外参。

通信

对一个机器人来说, 其功能模块可能非常繁杂, 且内部实现差异很大, 因此ros采取了耦合度低的分布式实现, 各个功能都有自己的进程, 准确地说ROS是进程(也称为Nodes)的分布式框架
ros的通信机制有三种实现策略:

  • 话题通信(发布订阅模式): 在这个模式下有三个角色: 管理者,发布者,订阅者;以名字来说很好理解其结构
  • 服务通信(请求响应模式): 类似网络请求, 以请求响应模式服务和客户端互相通信
  • 参数服务器(参数共享模式): 相当于一个全局状态/数据库, 所有节点都可以访问和修改

slam

FAST-LIVO2: Fast, Direct LiDAR-Inertial-Visual Odometry

以下简称f-l, f-l同时使用激光雷达和rgb摄像头作为输入, 激光提供点云信息, 在rosbag中其输入格式如下:

1
2
3
4
5
6
7
8

types: livox_ros_driver/CustomMsg [e4d6829bdfe657cb6c21a746c86b21a6]
sensor_msgs/Image [060021388200f6f0f447d0fcd9c64743]
sensor_msgs/Imu [6a62c6daae103f4ff57a132d6f95cec2]
topics: /left_camera/image 1355 msgs : sensor_msgs/Image
/livox/imu 27447 msgs : sensor_msgs/Imu
/livox/lidar 1355 msgs : livox_ros_driver/CustomMsg

f-l的整体框架如下:

输入数据的示意图:

因为看不懂细节, 这里只记录大致的流程, 三种数据中雷达(点云)是采样频率最高的, 因此会让它配合最慢的图像数据频率, 过程中不断使用这三种数据进行前向传播和反向优化来生成地图(点云)
下面介绍地图的格式, 地图的抽象格式为八叉树体素地图, 存储上使用哈希表存储根节点, 每个根节点体素是一个0.5m边长的正方体, 其中的叶节点则代表局部平面, 局部平面上存储雷达的原始点, 部分叶节点可以存储视觉补丁(patch), 这里不介绍细节, 地图构建过程中会在到达最大深度或者划分出的子体素代表一个平坦的面前不断划分体素, 并选择合适的体素加入视觉补丁
其构建和更新过程为:

  1. 起初以雷达中心为形心, 将周边的一个方块划为局部地图区域Mc
  2. 以雷达中心p为原点,在一个半径内制图
  3. 当p中心的圆碰到Mc的边缘后, 以p为中心重新划分局部地图区域Mc
  4. 由于Mc划定了存储在内存里的地图, 丢弃掉不属于当前范围的部分地图

  • patch: 图像的局部区域

视觉坐标和雷达坐标转化

该slam主要使用雷达提供点云, 以及存储点云的体素地图, 而视觉信息提供体素图的patch, 这些patch最直接的作用是给点云上色,除此之外, 也能用于校正整个体素图
具体流程如下:

  1. 选取视觉地图点: 简单地说,在体素图中选一些视觉上可见且比较明显的点,并将其映射到相机帧的像素上(相机和雷达是同步的), 这些点的集合称为视觉子图(visual submap), 具体分为以下几步:
    1. 可见点查询: 鉴于体素图中拥有过多的点,更合适的候选对象是当次雷达扫描的体素中的点, 这些点我们有它的世界坐标, 再加上雷达和相机的外参,可以将其转化到相机坐标系中,用一个raycast映射到对应像素。 此外,鉴于局部性原理, 对上一次雷达扫描涉及的有可见点的体素, 也对其做类似的可见点查询
    2. 按需raycast: 1. 中的可见点查询能提供很多视觉点, 但如果雷达因为太靠近扫描区等原因无法正常返回扫描点时, 就需要来自纯粹视觉信息的补充(准确地说是回忆, 因为过去雷达可能扫到了同一个物体的点但没有将其标记为视觉点, 对雷达退化的场合, 也就是是可见点查询无法覆盖整个相机帧时, 我们需要回忆出这些点), 将图像划分为一系列网格(网格大小是超参数), 如果有空白网格存在, 对其沿着射线反投影, 得到射线上一系列固定间隔的采样点(这些射线的采样点坐标会被预计算), 从深度最低的采样点开始, 对采样点所属体素(如果有的话), 如果有投影后位于网格的地图点, 将其加入视觉子图中
    3. 异常值剔除: 分为:
      1. 遮挡关系检查: 由于视觉上可能有很多点存在遮挡关系, 对映射到同一个网格的视觉点, 根据当前雷达位姿计算其深度, 只保留深度最小的
      2. 深度连续性检查: 由于相机帧是二维的, 两个相邻的像素, 其对应深度可能差别巨大;用雷达扫描的最新点投影到当前帧产生深度信息,从而剔除对和相邻点深度梯度较大的点
      3. 视角检查: 法向量和当前视线夹角过大的视觉点会被剔除

总之,这些视觉点就是一些位于场景表面, 能被映射到图像局部的一些扫描点, 主要用于提供patch位置和优化雷达采样准确性

点云语义分割

dualmap中,通过yolo检测rgb帧中物体的类别和bbox, 并用sam模型来进一下划分生成掩码,通过2d掩码结合深度信息, 反向投影得到点云, 再对点云进行聚类算法去噪,产生一个属于某个物体的点云, 并在之后的更新过程中进行一些简单地重叠度检测等操作,从而产生场景中点云的分割
这样的优点是实现简单, 只需要一个rgbd相机就能实现, 对2d图像做分割的算力负担也比较小, 缺点则是精度受限,且得到的点云实际上只有物品信息, 整个空间的建图信息应该是没有slam得到的点云全面的

cmake

modern cmake

cmake 行为准则:
接下来的两个列表很大程度上基于优秀的 gist Effective Modern CMake.

CMake 应避免的行为

不要使用具有全局作用域的函数:这包含 link_directories、 include_libraries 等相似的函数。
不要添加非必要的 PUBLIC 要求:你应该避免把一些不必要的东西强加给用户(-Wall)。相比于 PUBLIC,更应该把他们声明为 PRIVATE。
不要在file函数中添加 GLOB 文件:如果不重新运行 CMake,Make 或者其他的工具将不会知道你是否添加了某个文件。值得注意的是,CMake 3.12 添加了一个 CONFIGURE_DEPENDS 标志能够使你更好的完成这件事。
将库直接链接到需要构建的目标上:如果可以的话,总是显式地将库链接到目标上。
当链接库文件时,不要省略 PUBLIC 或 PRIVATE 关键字:这将会导致后续所有的链接都是缺省的。

CMake 应遵守的规范

把 CMake 程序视作代码:它是代码。它应该和其他的代码一样,是整洁并且可读的。
建立目标的观念:你的目标应该代表一系列的概念。为任何需要保持一致的东西指定一个 (导入型)INTERFACE 目标,然后每次都链接到该目标。
导出你的接口:你的 CMake 项目应该可以直接构建或者安装。
为库书写一个 Config.cmake 文件:这是库作者为支持客户的体验而应该做的。
声明一个 ALIAS 目标以保持使用的一致性:使用 add_subdirectory 和 find_package 应该提供相同的目标和命名空间。
将常见的功能合并到有详细文档的函数或宏中:函数往往是更好的选择。
使用小写的函数名: CMake 的函数和宏的名字可以定义为大写或小写,但是一般都使用小写,变量名用大写。
使用 cmake_policy 和/或 限定版本号范围: 每次改变版本特性 (policy) 都要有据可依。应该只有不得不使用旧特性时才降低特性 (policy) 版本。

ros的cmake实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

cmake_minimum_required(VERSION 2.8.3)
project(fast_livo)

set(CMAKE_BUILD_TYPE "Release")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Set common compile options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -fexceptions")

# Specific settings for Debug build
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")

# Detect CPU architecture
message(STATUS "Current CPU architecture: ${CMAKE_SYSTEM_PROCESSOR}")

# Specific settings for Release build
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64|ARM|AARCH64)")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
# 64-bit ARM optimizations (e.g., RK3588 and Jetson Orin NX)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -mcpu=native -mtune=native -ffast-math")
message(STATUS "Using 64-bit ARM optimizations: -O3 -mcpu=native -mtune=native -ffast-math")
else()
# 32-bit ARM optimizations with NEON support
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -mcpu=native -mtune=native -mfpu=neon -ffast-math")
message(STATUS "Using 32-bit ARM optimizations: -O3 -mcpu=native -mtune=native -mfpu=neon -ffast-math")
endif()
add_definitions(-DARM_ARCH)
else()
# x86-64 (Intel/AMD) optimizations
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -mtune=native -funroll-loops") #-flto
message(STATUS "Using general x86 optimizations: -O3 -march=native -mtune=native -funroll-loops")
add_definitions(-DX86_ARCH)
endif()

# Define project root directory
add_definitions(-DROOT_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\")

# Detect CPU core count for potential multithreading optimization
include(ProcessorCount)
ProcessorCount(N)
message(STATUS "Processor count: ${N}")

# Set the number of cores for multithreading
if(N GREATER 4)
math(EXPR PROC_NUM "4")
add_definitions(-DMP_EN -DMP_PROC_NUM=${PROC_NUM})
message(STATUS "Multithreading enabled. Cores: ${PROC_NUM}")
elseif(N GREATER 1)
math(EXPR PROC_NUM "${N}")
add_definitions(-DMP_EN -DMP_PROC_NUM=${PROC_NUM})
message(STATUS "Multithreading enabled. Cores: ${PROC_NUM}")
else()
add_definitions(-DMP_PROC_NUM=1)
message(STATUS "Single core detected. Multithreading disabled.")
endif()

# Check for OpenMP support
find_package(OpenMP QUIET)
if(OpenMP_CXX_FOUND)
message(STATUS "OpenMP found")
add_compile_options(${OpenMP_CXX_FLAGS})
else()
message(STATUS "OpenMP not found, proceeding without it")
endif()

# Check for mimalloc support
find_package(mimalloc QUIET)
if(mimalloc_FOUND)
message(STATUS "mimalloc found")
else()
message(STATUS "mimalloc not found, proceeding without it")
endif()

# Find catkin and required dependencies
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
nav_msgs
sensor_msgs
roscpp
rospy
std_msgs
pcl_ros
tf
message_generation
eigen_conversions
vikit_common
vikit_ros
cv_bridge
image_transport
)

find_package(Eigen3 REQUIRED)
find_package(PCL REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Sophus REQUIRED)
find_package(Boost REQUIRED COMPONENTS thread)

set(Sophus_LIBRARIES libSophus.so)

# Define the catkin package
catkin_package(
CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs message_runtime cv_bridge vikit_common vikit_ros image_transport
DEPENDS EIGEN3 PCL OpenCV Sophus
)

# Include directories for dependencies
include_directories(
${catkin_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
${PCL_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
${Sophus_INCLUDE_DIRS}
include
)

# Add libraries
add_library(vio src/vio.cpp src/frame.cpp src/visual_point.cpp)
add_library(lio src/voxel_map.cpp)
add_library(pre src/preprocess.cpp)
add_library(imu_proc src/IMU_Processing.cpp)
add_library(laser_mapping src/LIVMapper.cpp)

# Add the main executable
add_executable(fastlivo_mapping src/main.cpp)

# Link libraries to the executable
target_link_libraries(fastlivo_mapping
laser_mapping
vio
lio
pre
imu_proc
${catkin_LIBRARIES}
${PCL_LIBRARIES}
${OpenCV_LIBRARIES}
${Sophus_LIBRARIES}
${Boost_LIBRARIES}
)

# Link mimalloc if found
if(mimalloc_FOUND)
target_link_libraries(fastlivo_mapping mimalloc)
endif()

文件结构分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CMakeLists.txt 文件结构
├── 1. 基础配置 (行 1-5)
│ ├── CMake 最低版本要求
│ └── 项目名称和构建类型

├── 2. 编译器配置 (行 7-48)
│ ├── C++ 标准设置
│ ├── 基础编译选项
│ ├── CPU 架构检测
│ └── 平台特定优化

├── 3. 多线程配置 (行 50-72)
│ ├── CPU 核心数检测
│ ├── OpenMP 支持
│ └── mimalloc 内存分配器

├── 4. 依赖管理 (行 74-104)
│ ├── ROS (Catkin) 依赖
│ ├── 第三方库 (Eigen, PCL, OpenCV, Sophus)
│ └── Catkin 包配置

├── 5. 编译目标 (行 106-130)
│ ├── 库文件定义
│ ├── 可执行文件定义
│ └── 链接配置

└── 6. 条件编译 (行 132-135)
└── mimalloc 可选链接

逐行解析

1
cmake_minimum_required(VERSION 2.8.3)
  • 作用:指定 CMake 的最低版本要求
  • 解释:确保使用的 CMake 版本支持后续的语法和功能
  • 版本 2.8.3:较老的版本,保证在旧系统上的兼容性
1
project(fast_livo)
  • 关键字project(项目名称)
  • 作用:定义项目名称,会自动设置以下变量:
    • PROJECT_NAME:项目名称(fast_livo)
    • PROJECT_SOURCE_DIR:项目源代码根目录
    • PROJECT_BINARY_DIR:构建输出目录
1
2
set(CMAKE_BUILD_TYPE "Release")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
  • set(变量名 值):设置 CMake 变量
  • CMAKE_BUILD_TYPE:构建类型,常用值:
    • Debug:包含调试信息,无优化(适合开发调试)
    • Release:高度优化,无调试信息(适合发布)
    • RelWithDebInfo:优化 + 调试信息
    • MinSizeRel:最小化体积
  • message():输出信息到控制台
    • STATUS:普通信息(绿色)
    • WARNING:警告(黄色)
    • FATAL_ERROR:致命错误,停止配置

1
2
3
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
  • CMAKE_CXX_STANDARD:C++ 标准版本
    • 17:使用 C++17 标准(支持 std::optional、结构化绑定等)
  • CMAKE_CXX_STANDARD_REQUIRED:是否强制要求
    • ON:如果编译器不支持 C++17,报错
    • OFF:降级到编译器支持的最高版本
  • CMAKE_CXX_EXTENSIONS:是否允许编译器扩展
    • OFF:禁用 GNU 扩展(如 -std=gnu++17),使用纯标准 -std=c++17
1
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -fexceptions")
  • CMAKE_CXX_FLAGS:全局 C++ 编译选项
  • -pthread:启用 POSIX 线程支持(必需,因为代码使用 std::thread
  • -fexceptions:启用 C++ 异常处理
1
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
  • CMAKE_CXX_FLAGS_DEBUG:Debug 模式特定的编译选项
  • -O0:关闭所有优化(方便调试)
  • -g:生成调试符号(GDB 可以设置断点、查看变量)

CPU 架构检测与优化
1
message(STATUS "Current CPU architecture: ${CMAKE_SYSTEM_PROCESSOR}")
  • CMAKE_SYSTEM_PROCESSOR:自动检测的 CPU 架构
    • x86_64:Intel/AMD 64 位处理器
    • aarch64:ARM 64 位(如树莓派 4、Jetson Orin)
    • armv7l:ARM 32 位(如树莓派 3)
ARM 架构优化
1
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64|ARM|AARCH64)")
  • if(条件):条件判断
  • MATCHES:正则表达式匹配
  • ^(arm|aarch64|ARM|AARCH64):匹配任何以 arm 或 aarch64 开头的字符串
1
2
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -mcpu=native -mtune=native -ffast-math")
  • 64 位 ARM 优化选项
    • -O3:最高级别优化(激进,可能增加代码体积)
    • -mcpu=native:针对当前 CPU 生成代码(如利用 RK3588 的特殊指令)
    • -mtune=native:调度优化,适应当前 CPU 的流水线特性
    • -ffast-math:放松浮点运算标准,提升速度(可能损失精度)
1
2
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -mcpu=native -mtune=native -mfpu=neon -ffast-math")
  • 32 位 ARM 额外选项
    • -mfpu=neon:启用 ARM NEON SIMD 指令集(向量化加速)
1
add_definitions(-DARM_ARCH)
  • add_definitions():添加预处理器宏定义
  • 效果:代码中可以使用 #ifdef ARM_ARCH 进行条件编译
x86-64 架构优化
1
2
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -mtune=native -funroll-loops")
  • x86-64 优化选项
    • -march=native:使用当前 CPU 的指令集(如 AVX2、AVX-512)
    • -funroll-loops:循环展开,减少跳转指令

项目根目录定义
1
add_definitions(-DROOT_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\")
  • CMAKE_CURRENT_SOURCE_DIR:当前 CMakeLists.txt 所在目录
  • 效果:代码中可以使用 ROOT_DIR 宏访问项目根目录
  • 用途:加载配置文件(如 ROOT_DIR/config/params.yaml

多线程配置
1
2
3
include(ProcessorCount)
ProcessorCount(N)
message(STATUS "Processor count: ${N}")
  • include(模块名):加载 CMake 内置模块
  • ProcessorCount:检测 CPU 核心数,结果存入变量 N
1
2
3
if(N GREATER 4)
math(EXPR PROC_NUM "4")
add_definitions(-DMP_EN -DMP_PROC_NUM=${PROC_NUM})
  • 逻辑
    • 如果 CPU 核心 > 4,使用 4 个核心(避免过度并行开销)
    • 如果 CPU 核心 ≤ 4,使用全部核心
  • math(EXPR 变量 表达式):数学运算
  • 宏定义
    • -DMP_EN:启用多线程模式
    • -DMP_PROC_NUM=4:定义线程数为 4
OpenMP 支持
1
2
3
4
find_package(OpenMP QUIET)
if(OpenMP_CXX_FOUND)
message(STATUS "OpenMP found")
add_compile_options(${OpenMP_CXX_FLAGS})
  • find_package(包名 [QUIET] [REQUIRED]):查找第三方库
    • QUIET:找不到时不报错
    • REQUIRED:找不到时停止配置
  • OpenMP:并行计算框架(自动并行化 for 循环)
  • OpenMP_CXX_FOUND:布尔变量,表示是否找到
  • add_compile_options():添加编译选项到所有目标
mimalloc 内存分配器
1
2
3
find_package(mimalloc QUIET)
if(mimalloc_FOUND)
message(STATUS "mimalloc found")
  • mimalloc:微软开源的高性能内存分配器
  • 用途:替代系统默认的 malloc/free,提升内存分配速度

依赖管理

ROS Catkin 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
nav_msgs
sensor_msgs
roscpp
rospy
std_msgs
pcl_ros
tf
message_generation
eigen_conversions
vikit_common
vikit_ros
cv_bridge
image_transport
)
- catkin:ROS 的构建系统 - COMPONENTS:指定需要的 ROS 包 - roscpp:ROS C++ 客户端库 - sensor_msgs:传感器消息类型(点云、图像) - pcl_ros:PCL 与 ROS 的桥接 - cv_bridge:OpenCV 与 ROS 图像消息转换

第三方库

1
2
3
4
5
find_package(Eigen3 REQUIRED)
find_package(PCL REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Sophus REQUIRED)
find_package(Boost REQUIRED COMPONENTS thread)
- Eigen3:线性代数库(矩阵运算) - PCL (Point Cloud Library):点云处理库 - OpenCV:计算机视觉库 - Sophus:李群/李代数库(SO(3)、SE(3) 旋转表示) - Boost:C++ 通用库,这里只需要 thread 组件

1
set(Sophus_LIBRARIES libSophus.so)
  • 手动设置库名:覆盖 find_package 的默认设置

Catkin 包配置
1
2
3
4
catkin_package(
CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs message_runtime cv_bridge vikit_common vikit_ros image_transport
DEPENDS EIGEN3 PCL OpenCV Sophus
)
  • catkin_package():声明当前包的依赖关系
  • CATKIN_DEPENDS:依赖的 ROS 包
  • DEPENDS:依赖的非 ROS 库
  • 作用:其他包依赖本包时,会自动链接这些库

头文件目录
1
2
3
4
5
6
7
8
include_directories(
${catkin_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
${PCL_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
${Sophus_INCLUDE_DIRS}
include
)
  • include_directories(目录列表):添加头文件搜索路径
  • ${变量名}:引用变量(CMake 语法)
  • 效果:编译器可以找到 #include <eigen3/Eigen/Dense>

库目标定义
1
2
3
4
5
add_library(vio src/vio.cpp src/frame.cpp src/visual_point.cpp)
add_library(lio src/voxel_map.cpp)
add_library(pre src/preprocess.cpp)
add_library(imu_proc src/IMU_Processing.cpp)
add_library(laser_mapping src/LIVMapper.cpp)
  • add_library(库名 源文件列表):创建库目标
  • 默认:生成静态库(.a 文件)
  • 可选参数
    • SHARED:动态库(.so 文件)
    • STATIC:静态库(默认)
  • 模块化:将不同功能编译为独立的库,便于管理

可执行文件定义
1
add_executable(fastlivo_mapping src/main.cpp)
  • add_executable(可执行文件名 源文件):创建可执行目标
  • 输出devel/lib/fast_livo/fastlivo_mapping

链接配置
1
2
3
4
5
6
7
8
9
10
11
12
target_link_libraries(fastlivo_mapping
laser_mapping
vio
lio
pre
imu_proc
${catkin_LIBRARIES}
${PCL_LIBRARIES}
${OpenCV_LIBRARIES}
${Sophus_LIBRARIES}
${Boost_LIBRARIES}
)
  • target_link_libraries(目标 依赖库列表):链接库
  • 链接顺序
    1. 自定义库(laser_mapping、vio 等)
    2. 第三方库(PCL、OpenCV 等)
  • 自动传递依赖:链接 laser_mapping 时,会自动链接其依赖的库

条件链接
1
2
3
if(mimalloc_FOUND)
target_link_libraries(fastlivo_mapping mimalloc)
endif()
  • 条件编译:只有在找到 mimalloc 时才链接
  • 可选依赖:不影响基础功能,但能提升性能

关键语法与关键字

变量操作

命令 语法 示例
设置变量 set(变量 值) set(MY_VAR "hello")
引用变量 ${变量} message("${MY_VAR}")
列表操作 list(APPEND 变量 值) list(APPEND SOURCES main.cpp)
字符串操作 string(TOUPPER ${变量} 输出变量) string(TOUPPER "abc" RESULT)

控制流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 条件判断
if(条件)
# ...
elseif(条件)
# ...
else()
# ...
endif()

# 循环
foreach(变量 IN LISTS 列表)
# ...
endforeach()

# while 循环
while(条件)
# ...
endwhile()

常用命令

命令 作用 示例
project() 定义项目 project(MyProject)
add_executable() 创建可执行文件 add_executable(app main.cpp)
add_library() 创建库 add_library(mylib lib.cpp)
target_link_libraries() 链接库 target_link_libraries(app mylib)
find_package() 查找第三方库 find_package(OpenCV REQUIRED)
include_directories() 添加头文件路径 include_directories(include)
add_definitions() 添加宏定义 add_definitions(-DDEBUG)

内置变量

变量 含义
CMAKE_SOURCE_DIR 顶层 CMakeLists.txt 所在目录
CMAKE_BINARY_DIR 构建目录
CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在目录
PROJECT_NAME 项目名称
CMAKE_CXX_COMPILER C++ 编译器路径
CMAKE_SYSTEM_PROCESSOR CPU 架构