news 2026/6/14 10:22:41

FAST-LIVO2 源码精读(二):环境搭建与编译避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FAST-LIVO2 源码精读(二):环境搭建与编译避坑

FAST-LIVO2 源码精读(二):环境搭建与编译避坑

本文是「FAST-LIVO2 激光-惯性-视觉里程计源码精读」专栏第二篇。上一篇确立了系统全局认知与 19 维状态向量的精确构成;从这一篇起进入实战。编译通过是阅读源码、调参、改代码的前提——在此之前任何"理解"都悬在空中。本篇逐项拆解 FAST-LIVO2 的依赖树,重点讲清三处极易踩错的版本地雷,并给出catkin_make常见报错的速查处置表。


一、引子:第一道门槛——依赖版本不对,编译必然失败

许多研究者在初次接触 FAST-LIVO2 时遭遇的第一个困境不是算法理解,而是编译报错。这并非偶然——FAST-LIVO2 对外部依赖的版本约束比一般 ROS 包严格得多,且 README 中的说明较为精简,关键的"踩坑点"往往只在代码注释或社区 issue 中出现。

其中最突出的一条:Sophus 必须切换到a621ff这一特定提交,而非直接克隆主分支或安装系统包。Sophus 在后续版本引入了模板化接口(Sophus::SE3<double>等),与 FAST-LIVO2 源码中的用法不兼容。主流 Ubuntu 20.04 的 apt 源没有提供对应版本,仅凭sudo apt install ros-noetic-sophus也会安装到错误版本,导致编译阶段即报模板实例化错误。

第二条同样高频:rpg_vikit 必须克隆xuankuzcr的分支,而非uzh-rpg的原版仓库。FAST-LIVO1 所用的 vikit 来自另一个分支,两者接口有差异;若复用旧工作空间中的 vikit 而未更新来源,vikit_common头文件缺口会造成视觉子系统整块无法编译。

理解这两条约束的成因,需要先了解整套依赖树的结构与 CMakeLists 的编译逻辑。


二、依赖全景:九棵树、两块版本地雷

FAST-LIVO2 的依赖可按来源分为三层:ROS/catkin 生态独立安装的 C++ 库需手动克隆入工作空间的 catkin 包

图 1 FAST-LIVO2 完整依赖树:橙色节点为版本有特定约束的依赖,绿色为可 apt 安装的通用依赖

2.1 第一层:ROS Noetic + catkin 生态

FAST-LIVO2 官方支持 Ubuntu 18.04–20.04,推荐 Ubuntu 20.04 + ROS Noetic

ROS Noetic 一步安装:

sudoaptinstallros-noetic-desktop-full

desktop-full包含rvizrosbagtfimage_transportcv_bridgepcl_ros等,覆盖绝大部分 catkin 层依赖。安装后执行source /opt/ros/noetic/setup.bash,并建议将此行加入~/.bashrc以持久生效。

echo"source /opt/ros/noetic/setup.bash">>~/.bashrcsource~/.bashrc

2.2 第二层:独立 C++ 库(含版本地雷)

Eigen ≥ 3.3.4

ROS Noetic 附带的 Eigen 版本通常已满足需求,可通过以下命令核查:

pkg-config--modversioneigen3

若低于 3.3.4,需从源码编译并安装较新版本。FAST-LIVO2 大量使用Eigen::Map、块操作及模板表达式,3.3 以下的 API 有细微差异,会触发编译警告乃至错误。

PCL ≥ 1.8

PCL 1.8 是 Ubuntu 20.04 apt 源的默认版本,直接安装即可:

sudoaptinstalllibpcl-dev

FAST-LIVO2 主要依赖 PCL 的VoxelGrid滤波器与PointXYZI/PointXYZRGB类型,无高版本特性调用,1.8 完全满足。

OpenCV ≥ 4.2

OpenCV 4.2 是 Ubuntu 20.04 默认版本(sudo apt install libopencv-dev)。ROS Noetic 的cv_bridge也已与 OpenCV 4 对齐。

需特别注意:不可使用 OpenCV 3.xvio.cpp中对cv::NORM_HAMMING2、部分仿射变换 API 及金字塔图像结构的调用依赖 OpenCV 4 接口,使用 3.x 会在链接阶段出现undefined reference错误,且报错指向位置较为隐蔽。

若系统同时安装了多版本 OpenCV,需在 CMakeLists 中显式指定路径:

set(OpenCV_DIR "/usr/lib/x86_64-linux-gnu/cmake/opencv4")

Sophus(⚠ 版本地雷 1)

这是整套依赖中版本约束最严格的一项。Sophus 仓库在a621ff提交之后逐步向模板化 API 迁移(即Sophus::SE3d改为Sophus::SE3<double>),而 FAST-LIVO2 使用的是非模板版写法:

// src/LIVMapper.cpp 中典型用法(非模板版)Sophus::SO3d rot;

若安装了新版 Sophus,编译时会遭遇如下报错:

error: 'class Sophus::SO3d' has no member named '...' fatal error: sophus/so3.h: No such file or directory

正确安装步骤如 README 所示,必须精确 checkout 到a621ff

gitclone https://github.com/strasdat/Sophus.gitcdSophusgitcheckout a621ff# ← 关键:切到非模板版本mkdirbuild&&cdbuild cmake..&&makesudomakeinstall

该提交对应 Sophus 1.x 的最后一批稳定非模板版提交,在 cmake 安装后会在/usr/local/include/sophus/下提供so3.hse3.h等纯头文件接口。

Boost::thread

sudo apt install libboost-dev libboost-thread-dev即可,Ubuntu 20.04 自带 Boost 1.71,满足要求。

2.3 第三层:需克隆入工作空间的 catkin 包

rpg_vikit(⚠ 版本地雷 2)

vikit_commonvikit_ros是 FAST-LIVO2 视觉子系统的基础库,提供相机模型、双线性插值、图像导数等工具函数。关键细节:

  • FAST-LIVO1使用的是uzh-rpg/rpg_vikit主仓库;
  • FAST-LIVO2使用的是xuankuzcr/rpg_vikit(维护者郑纯然的 fork),两者头文件结构与部分函数签名已有差异。

README 中的注释明确标注:

# Different from the one used in fast-livo1gitclone https://github.com/xuankuzcr/rpg_vikit.git

若沿用旧工作空间中的uzh-rpg/rpg_vikitvio.cpp#include <vikit/...>会在编译时找不到对应头文件,报错类似:

fatal error: vikit/abstract_camera.h: No such file or directory

livox_ros_driver

Livox 官方驱动包提供livox_ros_driver::CustomMsg消息类型,preprocess.cpp在 Livox Avia 路径下直接使用该自定义消息:

// src/preprocess.cpp(Livox 分支)voidLivoxFeatureExtract(constlivox_ros_driver::CustomMsgConstPtr&msg,...)

获取方式有两种:其一,按官方文档安装livox_ros_driver(较老版本为livox_ros_driver,较新版本对应livox_ros_driver2,两者消息格式不同);其二,若仅使用标准机械式雷达(Ouster、Velodyne),可将CMakeLists.txt及相关源文件中的 Livox 分支条件性地移除,但此操作超出本篇范围。


三、CMakeLists 关键行解读

了解依赖之后,再看CMakeLists.txt的核心设计,有助于理解编译过程中出现某类错误时该从哪里入手。

图 2 CMakeLists 编译结构:5 个内部静态库并行编译,最终链接为单一可执行文件

3.1 C++ 标准与编译优化

# CMakeLists.txt:6 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF)

FAST-LIVO2 要求C++17,这是强制项(REQUIRED ON)。Ubuntu 20.04 默认的 GCC 9 已完整支持 C++17。若在较老系统上以 GCC 7 编译,std::optional、结构化绑定等 C++17 特性会导致编译失败;需手动升级 GCC 或指定编译器路径。

针对不同 CPU 架构,编译器标志有明确分支:

# CMakeLists.txt:21 if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64|ARM|AARCH64)") if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") # RK3588 / Jetson Orin NX 等 64 位 ARM set(CMAKE_CXX_FLAGS_RELEASE "... -O3 -mcpu=native -mtune=native -ffast-math") else() # 32 位 ARM,额外加 NEON 支持 set(CMAKE_CXX_FLAGS_RELEASE "... -O3 -mcpu=native -mtune=native -mfpu=neon -ffast-math") endif() add_definitions(-DARM_ARCH) else() # x86-64 set(CMAKE_CXX_FLAGS_RELEASE "... -O3 -march=native -mtune=native -funroll-loops") add_definitions(-DX86_ARCH) endif()

-march=native/-mcpu=native意味着编译产物与编译机器的 CPU 微架构绑定,在该机器上有最佳性能,但不可跨机器直接复制二进制文件(在旧 CPU 上运行新 CPU 编译产物会触发非法指令 SIGILL)。嵌入式部署场景下需注意此点。

3.2 OpenMP 多线程并行

# CMakeLists.txt:62 find_package(OpenMP QUIET) if(OpenMP_CXX_FOUND) add_compile_options(${OpenMP_CXX_FLAGS}) endif()

OpenMP 以QUIET模式查找,未找到时不报错、不中断编译,仅静默跳过。FAST-LIVO2 在残差构建环节使用#pragma omp parallel for并行遍历点云,若 OpenMP 不可用,该段回退为串行执行,性能下降但不影响正确性。Ubuntu 20.04 的libgomp随 GCC 一并安装,通常无需额外操作。

3.3 多核数自适应

# CMakeLists.txt:44 ProcessorCount(N) if(N GREATER 4) math(EXPR PROC_NUM "4") add_definitions(-DMP_EN -DMP_PROC_NUM=${PROC_NUM}) elseif(N GREATER 1) math(EXPR PROC_NUM "${N}") add_definitions(-DMP_EN -DMP_PROC_NUM=${PROC_NUM}) else() add_definitions(-DMP_PROC_NUM=1) endif()

ProcessorCount(N)检测 CPU 核数,并将MP_PROC_NUM宏注入编译选项。超过 4 核时限制为 4,防止残差并行线程过多反而因调度开销降低性能。MP_EN宏控制voxel_map.cpp#ifdef MP_EN ... #pragma omp parallel for ...代码段的开关,这一设计使单核嵌入式平台无需修改代码即可正常编译。

3.4 五个内部库的分工

# CMakeLists.txt:121 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_executable(fastlivo_mapping src/main.cpp)

五个内部库与后续源码精读的对应关系:

内部库 源文件 精读篇章 ────────────────────────────────────────────────────── laser_mapping LIVMapper.cpp 第 8、12、14 篇(主循环与融合调度) vio vio / frame / visual_point 第 13、14 篇(视觉直接法) lio voxel_map.cpp 第 9、12 篇(体素地图与 LIO 更新) pre preprocess.cpp 第 10 篇(点云预处理) imu_proc IMU_Processing.cpp 第 11 篇(IMU 传播与去畸变)

这一分拆将编译时间均摊到各模块,修改单一模块后仅重新链接受影响的库,无需全量重编,对频繁迭代调参的场景有明显效率提升。

3.5 Sophus 的特殊链接方式

# CMakeLists.txt:102 set(Sophus_LIBRARIES libSophus.so)

find_package(Sophus REQUIRED)找到的是a621ff版本在/usr/local下的安装文件。注意这里手动设置了Sophus_LIBRARIESlibSophus.so——非模板版 Sophus 会编译出一个独立的共享库(新版模板版则为纯头文件,无对应.so)。若系统误装了新版 Sophus,find_package虽能找到,但链接时因缺少libSophus.so而报错:

cannot find -lSophus

这是另一条 Sophus 版本错误的诊断线索。


四、完整搭建流程与报错速查

4.1 逐步安装命令

以下步骤针对全新的 Ubuntu 20.04 + ROS Noetic 环境,按顺序执行可一次通过。

步骤一:ROS Noetic 安装

sudosh-c'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > \ /etc/apt/sources.list.d/ros-latest.list'sudoapt-key adv--keyserver'hkp://keyserver.ubuntu.com:80'--recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654sudoaptupdatesudoaptinstall-yros-noetic-desktop-fullsudoaptinstall-ypython3-catkin-tools python3-rosdep ros-noetic-pcl-ros\ros-noetic-cv-bridge ros-noetic-image-transport ros-noetic-tfsource/opt/ros/noetic/setup.bashsudorosdep init&&rosdep update

步骤二:系统级 C++ 依赖

sudoaptinstall-ylibboost-dev libboost-thread-dev libeigen3-dev libpcl-dev\libopencv-dev

步骤三:Sophus(非模板版,精确 checkout)

gitclone https://github.com/strasdat/Sophus.gitcdSophusgitcheckout a621ffmkdirbuild&&cdbuild cmake..-DCMAKE_BUILD_TYPE=Releasemake-j$(nproc)sudomakeinstallcd../..

步骤四:创建 catkin 工作空间并克隆 catkin 包

mkdir-p~/catkin_ws/srccd~/catkin_ws/src# vikit(注意仓库来源,不是 uzh-rpg)gitclone https://github.com/xuankuzcr/rpg_vikit.git# livox_ros_driver(按实际使用的 Livox SDK 版本选择)gitclone https://github.com/Livox-SDK/livox_ros_driver.git# FAST-LIVO2 本体gitclone https://github.com/hku-mars/FAST-LIVO2.git

步骤五:编译

cd~/catkin_ws catkin_make-DCMAKE_BUILD_TYPE=Release -j$(nproc)sourcedevel/setup.bash

编译过程中,CMake 会输出架构检测结果:

-- Current CPU architecture: x86_64 -- Using general x86 optimizations: -O3 -march=native -mtune=native -funroll-loops -- Multithreading enabled. Cores: 4 -- OpenMP found

若五行均无红色错误,最终在devel/lib/fast_livo/fastlivo_mapping处生成可执行文件即为成功。

图 3 完整搭建流程:两处橙色节点为版本约束关键步骤,任一偏离均可能导致后续编译失败

4.2 常见报错速查表

以下汇总实际编译中高频出现的报错信息、根本原因与处置方式。

报错关键词 根本原因 处置方式 ───────────────────────────────────────────────────────────────────────────────── 'class Sophus::SO3d' has no member Sophus 版本错误 重新编译,务必 git checkout a621ff libSophus.so: cannot find -lSophus Sophus 为新版纯头文件版 同上 fatal: vikit/abstract_camera.h 克隆了 uzh-rpg/rpg_vikit 改用 xuankuzcr/rpg_vikit 并重新 clone vikit_common not found vikit 未在工作空间内 确认 catkin_ws/src/rpg_vikit 存在 livox_ros_driver was not found 未克隆 livox_ros_driver 克隆入 src/ 后重新 catkin_make error: 'std::optional' ...C++17 编译器或 std 标准不对 GCC≥7,cmake 传 -DCMAKE_CXX_STANDARD=17 OpenCV 3.x: undefined reference OpenCV 版本低于 4.2 升级或通过 OpenCV_DIR 指定 4.x 路径 SIGILL(运行时崩溃) -march=native 跨机器运行 在目标机重新编译,勿直接复制二进制 catkin_make: Nothing to install 首次未 source setup.bash 执行 source devel/setup.bash 后重试

其中前三条为最高频,覆盖了绝大多数"首次编译失败"的场景。


五、launch 与 yaml 文件速览

编译通过后,运行 FAST-LIVO2 需要正确配置 launch 文件与 yaml 参数文件,二者是驱动系统运行的直接入口。

5.1 launch 文件结构

以 Livox Avia 对应的launch/mapping_avia.launch为例:

<!-- launch/mapping_avia.launch --><launch><argname="rviz"default="true"/><!-- 加载主配置参数 --><rosparamcommand="load"file="$(find fast_livo)/config/avia.yaml"/><!-- 启动主节点,同时加载相机内参 --><nodepkg="fast_livo"type="fastlivo_mapping"name="laserMapping"output="screen"><rosparamfile="$(find fast_livo)/config/camera_pinhole.yaml"/></node><!-- RViz 可视化(可通过 rviz:=false 关闭) --><groupif="$(arg rviz)"><nodelaunch-prefix="nice"pkg="rviz"type="rviz"name="rviz"args="-d $(find fast_livo)/rviz_cfg/fast_livo2.rviz"/></group><!-- 图像解压(将 compressed 话题转为 raw) --><nodepkg="image_transport"type="republish"name="republish"args="compressed in:=/left_camera/image raw out:=/left_camera/image"output="screen"respawn="true"/></launch>

几处细节值得关注:

双层 rosparam 加载:主节点外的<rosparam command="load">avia.yaml的所有键值对加载到/命名空间下;节点内的<rosparam file>将相机内参覆盖写入/laserMapping命名空间。后者在多相机或换配置时仅修改camera_*.yaml即可,无需改动主配置。

图像解压节点image_transport republish将压缩图像话题实时解压为原始图像格式,原因是 FAST-LIVO2 的 ROS 图像订阅默认使用原始格式,而许多 rosbag 存储的相机数据为压缩格式以节省空间。respawn="true"确保该节点崩溃后自动重启,不影响主节点。

nice前缀:RViz 节点用launch-prefix="nice"以低调度优先级运行,避免 UI 渲染抢占算法节点的 CPU 时间,这在嵌入式或算力紧张的平台上尤为重要。

5.2 yaml 参数文件的层次结构

config/avia.yaml将所有运行参数分为六个命名空间(节选关键字段及物理含义):

# config/avia.yaml(关键字段节选)common:img_topic:"/left_camera/image"# 图像话题名,需与 bag 或驱动一致lid_topic:"/livox/lidar"# 雷达话题名imu_topic:"/livox/imu"# IMU 话题名extrin_calib:extrinsic_T:[0.04165,0.02326,-0.0284]# 雷达→IMU 平移(米)extrinsic_R:[1,0,0,0,1,0,0,0,1]# 雷达→IMU 旋转(行主序 3×3)Rcl:[...]# 相机→雷达旋转Pcl:[...]# 相机→雷达平移preprocess:lidar_type:1# 1=Livox Avia, 2=Velodyne, 3=Ousterblind:0.8# 近距盲区(米),过近点丢弃lio:voxel_size:0.5# 体素边长(米),影响地图分辨率与计算量min_eigen_value:0.0025# 平面判据阈值,越小接受越多平面(第 9 篇详述)vio:max_iterations:5# 视觉 ESIKF 迭代次数outlier_threshold:1000# 光度残差外点阈值exposure_estimate_en:true# 是否启用在线曝光估计(第 1 篇提及的第三反直觉设计)

话题名与外参是接入自己传感器时首先要修改的两处。lidar_type对应preprocess.cpp中的多雷达分支,取值 1/2/3 分别触发 Livox/Velodyne/Ouster 解析路径,第 10 篇将详细展开。

5.3 编译验证:快速确认节点可正常启动

编译通过后,可不播放 rosbag,仅启动节点以验证参数文件加载无误:

source~/catkin_ws/devel/setup.bash roslaunch fast_livo mapping_avia.launch rviz:=false

正常情况下节点输出:

[ INFO] [1234567890.xxx]: FAST-LIVO [ INFO] [1234567890.xxx]: Lidar type: 1 (Livox Avia) [ INFO] [1234567890.xxx]: Waiting for data...

若出现Parameter not found: ...Could not load ...类提示,通常意味着 yaml 中的话题名拼写错误或 yaml 文件路径不在find fast_livo可访问的包内,需检查catkin_ws/src/FAST-LIVO2/config/目录完整性。


六、可选性能依赖:mimalloc

CMakeLists.txt中有一项通常被忽略的可选依赖:

# CMakeLists.txt:71 find_package(mimalloc QUIET) if(mimalloc_FOUND) target_link_libraries(fastlivo_mapping mimalloc) endif()

mimalloc是微软开源的高性能内存分配器,在多线程场景下比系统默认的glibc malloc有更低的分配延迟和更少的内存碎片。FAST-LIVO2 在点云处理和视觉 patch 处理环节存在大量小对象的频繁分配与释放,实测在 ARM 平台上启用 mimalloc 后单帧耗时有 5%–10% 的改善。安装方法:

sudoaptinstalllibmimalloc-dev

安装后重新catkin_make,CMake 会自动检测并链接;无需修改任何源码。


七、小结与下篇预告

本篇完成了 FAST-LIVO2 编译环境的完整搭建,要点总结:

  1. Sophus 版本:必须git checkout a621ff,安装非模板版;新版 Sophus 的模板化接口与源码不兼容,是第一高频报错源。
  2. rpg_vikit 来源:克隆xuankuzcr/rpg_vikit,与 FAST-LIVO1 所用分支不同。
  3. OpenCV ≥ 4.2:3.x 版本会在链接阶段产生隐蔽的 undefined reference。
  4. CMakeLists 架构:5 个内部库解耦编译,架构感知的-march=native/-mcpu=native标志在嵌入式部署时需注意不可跨机器复制二进制。
  5. launch / yaml 结构:话题名与外参是接入自己硬件的核心修改点;rviz:=false参数在算力受限时可减少无关负载。

依赖装好、节点启动,标志着可以进入下一个阶段。但此时若播放官方 rosbag,屏幕上的彩色点云究竟从哪里来、各参数实际控制什么行为——这些问题将在下一篇回答。

下一篇:《FAST-LIVO2 源码精读(三):数据集跑通与 RViz 可视化》

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 10:21:52

大模型后处理层为何正在归零?Claude能力内化实证解析

1. 项目概述&#xff1a;这不是一次普通更新&#xff0c;而是模型能力边界的实质性坍缩“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题乍看像科技媒体的夸张头条&#xff0c;但作为连续跟踪Claude系列模型演进三年、在生产环境部署过27个Cla…

作者头像 李华
网站建设 2026/6/14 10:19:57

终极指南:如何用KKManager轻松管理Illusion游戏模组

终极指南&#xff1a;如何用KKManager轻松管理Illusion游戏模组 【免费下载链接】KKManager Mod, plugin and card manager for games by Illusion that use BepInEx 项目地址: https://gitcode.com/gh_mirrors/kk/KKManager 还在为管理Illusion游戏模组而烦恼吗&#x…

作者头像 李华
网站建设 2026/6/14 10:18:05

BBDown:轻松下载B站视频的开源工具,让离线观看更自由

BBDown&#xff1a;轻松下载B站视频的开源工具&#xff0c;让离线观看更自由 【免费下载链接】BBDown Bilibili Downloader. 一个命令行式哔哩哔哩下载器. 项目地址: https://gitcode.com/gh_mirrors/bb/BBDown 你是否经常遇到想要保存B站视频却找不到合适工具的困扰&am…

作者头像 李华
网站建设 2026/6/14 10:16:58

DataX vs. Sqoop vs. Kettle:我们团队最终为什么选择了DataX做数据同步?

DataX vs. Sqoop vs. Kettle&#xff1a;技术团队的数据同步工具选型实战当数据成为企业核心资产&#xff0c;如何高效、稳定地实现异构数据源之间的同步&#xff0c;成为每个技术团队必须面对的挑战。在经历了长达三个月的工具选型后&#xff0c;我们最终选择了阿里巴巴开源的…

作者头像 李华