欢迎光临
我们一直在努力

ORB-SLAM3代码详解 - 第 02 篇 · 编译、运行与代码工程结构

ORB-SLAM3代码详解 – 第 02 篇 · 编译、运行与代码工程结构

第 01 篇给出了系统的全局结构视图。本篇完成环境搭建与运行验证:从源码编译、
跑通 EuRoC 数据集、对照实际数据理解可视化窗口,到借 build.sh 与 CMakeLists.txt
厘清"依赖关系与构建产物",为后续逐模块的源码分析提供可运行、可调试的实验环境。

本篇命令、行号基于仓库 commit 4452a3c。环境以 Ubuntu 20.04 + OpenCV 4.x 为例。


1. 先看清依赖关系:四类依赖

ORB-SLAM3 的依赖可分为四类,区分清楚是顺利编译的前提:

┌─────────────────────────────────────────┐
│ ORB_SLAM3 (主库) │
│ lib/libORB_SLAM3.so │
└───────────────┬─────────────────────────┘

┌───────────────┬─────────┴────────┬──────────────────┐
▼ ▼ ▼ ▼
系统级第三方库 随仓库自带 头文件依赖 可选
(自己装) (Thirdparty/,build.sh编) (header-only)
───────────── ────────────────── ────────────── ──────────
OpenCV ≥4.4 DBoW2 (词袋) Eigen3 ≥3.1.0 realsense2
Pangolin g2o (图优化) Sophus(也在 (RealSense
Boost(序列化) Sophus(李群,头文件) Thirdparty/) 相机, 可不装)
(crypto/ssl)

1.1 为什么 DBoW2 / g2o / Sophus 要"自带"

这三个库在 Thirdparty/ 里,是作者修改过的特定版本,不能用系统 apt install 的版本替代:

  • DBoW2:作者裁剪了词袋库,专门适配 ORB 的二进制描述子(汉明距离)。
  • g2o:图优化框架,作者固定在某个版本并打了补丁,新版 g2o API 不兼容。
  • Sophus:李群(SE3/Sim3)库,header-only(只有头文件),但仓库里仍给它建了 build 目录(生成测试/示例,主库其实只用头文件)。

关键结论:应使用 Thirdparty/ 内的版本。若系统中另装有其它版本的 g2o,需避免其 include 路径污染本工程的编译。

1.2 系统级依赖与版本红线

依赖版本要求(CMakeLists 强制)作用
OpenCV find_package(OpenCV 4.4),找不到 >4.4 直接 FATAL_ERROR 图像 I/O、特征、矩阵
Eigen3 find_package(Eigen3 3.1.0 REQUIRED) 线性代数(贯穿全系统)
Pangolin REQUIRED 3D 可视化窗口
Boost 链接 -lboost_serialization 地图序列化(存/读地图)
OpenSSL 链接 -lcrypto 计算地图校验/哈希
realsense2 find_package(realsense2)(不 REQUIRED) Intel RealSense 实机示例,没有就跳过

需注意:CMakeLists 第 32 行固定为 find_package(OpenCV 4.4)。若环境中为
OpenCV 3.x,将直接报错 OpenCV > 4.4 not found。解决方式是改用 4.x,或将该行
修改为对应版本号(如 find_package(OpenCV 3.2))并自行处理 API 差异。


2. 逐行读懂 build.sh

仓库根目录的 build.sh 就是"一键编译"脚本。它做的事其实非常朴素——按依赖顺序,逐个 cmake + make:

# 1) 编译三个第三方库(注意都是 Release)
cd Thirdparty/DBoW2 && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release && make -j # 词袋

cd ../../g2o && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release && make -j # 图优化

cd ../../Sophus && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release && make -j # 李群

# 2) 解压预训练词袋(100 多 MB 的文本词典)
cd Vocabulary && tar -xf ORBvoc.txt.tar.gz

# 3) 编译主库 + 所有 Examples
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release && make -j4

三个要点:

  • 顺序不能乱:主库链接 Thirdparty/DBoW2/lib/libDBoW2.so 和 Thirdparty/g2o/lib/libg2o.so,所以这两个必须先编出来。
  • 词袋必须解压:ORBvoc.txt.tar.gz 解压成 ~145MB 的 ORBvoc.txt,运行时加载它。忘了解压,运行会卡在"加载词袋"报错。
  • make -j4 vs make -j:主库用了 -j4 限制 4 线程。原因是 Optimizer.cc(5590 行)这类大文件 + 模板展开极吃内存,无限并行容易把小内存机器 OOM 卡死。内存小(<8GB)建议直接 make -j2。
  • 运行:

    chmod +x build.sh
    ./build.sh


    3. 拆解 CMakeLists.txt:产物从哪来

    3.1 编译选项(性能的来源)

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=native")
    IF(NOT CMAKE_BUILD_TYPE)
    SET(CMAKE_BUILD_TYPE Release) # 默认 Release
    ENDIF()

    • -O3:最高级别优化。
    • -march=native:针对当前 CPU 指令集编译(用上 AVX 等)。这是把双刃剑:
      • 好处:特征匹配、矩阵运算明显加速;
      • 代价:编译产物不可跨 CPU 架构拷贝。在较新 CPU 上编译、在较旧 CPU 上运行会触发 Illegal instruction 崩溃,容器与集群分发场景需特别注意。
    • 语言标准为 C++11(CHECK_CXX_COMPILER_FLAG("-std=c++11" …),并 add_definitions(-DCOMPILEDWITHC11))。源码中以 #ifdef COMPILEDWITHC11 切换 std::chrono 计时 API 的写法即源于此。

    3.2 主库 = 一个共享库

    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
    add_library(${PROJECT_NAME} SHARED
    src/System.cc src/Tracking.cc src/LocalMapping.cc src/LoopClosing.cc
    src/ORBextractor.cc src/ORBmatcher.cc src/Optimizer.cc …
    src/CameraModels/Pinhole.cpp src/CameraModels/KannalaBrandt8.cpp …)

    所有 src/*.cc 编进一个 lib/libORB_SLAM3.so。这就是我们整个专栏要逐个剖析的对象。

    3.3 链接清单

    add_subdirectory(Thirdparty/g2o) # 把 g2o 作为子项目一起构建
    target_link_libraries(${PROJECT_NAME}
    ${OpenCV_LIBS} ${EIGEN3_LIBS} ${Pangolin_LIBRARIES}
    ${PROJECT_SOURCE_DIR}/Thirdparty/DBoW2/lib/libDBoW2.so
    ${PROJECT_SOURCE_DIR}/Thirdparty/g2o/lib/libg2o.so
    -lboost_serialization
    -lcrypto)

    注意 include_directories 里把 Thirdparty/Sophus 也加了进来——这印证了 §1.1:Sophus 是以头文件形式被主库使用的,并不链接它的 .so。

    3.4 可执行文件 = 各传感器示例

    CMakeLists 里有 46 个 add_executable(部分被 if(realsense2_FOUND) 包住)。它们按传感器分组,输出到对应的 Examples/<类型>/ 目录:

    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Monocular)
    add_executable(mono_euroc Examples/Monocular/mono_euroc.cc)
    target_link_libraries(mono_euroc ${PROJECT_NAME}) # 每个示例都只链主库

    目录代表可执行数据集
    Examples/Monocular mono_euroc mono_kitti mono_tum EuRoC / KITTI / TUM
    Examples/Stereo stereo_euroc stereo_kitti EuRoC / KITTI
    Examples/RGB-D rgbd_tum TUM RGB-D
    Examples/Monocular-Inertial mono_inertial_euroc EuRoC(含 IMU)
    Examples/Stereo-Inertial stereo_inertial_euroc EuRoC(含 IMU)
    *_realsense_* 需 RealSense SDK 实机相机

    每个示例都只链 libORB_SLAM3.so。这告诉你:示例程序本身很薄,真正的逻辑全在主库里。第 5 节我们就读一个示例,看它有多薄。


    4. 跑通第一个数据集:EuRoC 单目

    4.1 下载数据

    EuRoC MAV 数据集(苏黎世联邦理工无人机数据集)是 ORB-SLAM3 官方主力测试集。下载任意一个序列(如 MH_01_easy)的 ASL 格式,解压后结构:

    MH_01_easy/
    └── mav0/
    ├── cam0/ # 左目相机
    │ ├── data/ # 一堆 <时间戳ns>.png
    │ └── data.csv
    ├── cam1/ # 右目
    ├── imu0/ # IMU 数据
    └── …

    仓库 Examples/Monocular/EuRoC_TimeStamps/MH01.txt 提供了配套的时间戳文件(每行一个纳秒时间戳,对应一张图)。

    4.2 启动命令

    cd ORB_SLAM3
    ./Examples/Monocular/mono_euroc \\
    ./Vocabulary/ORBvoc.txt \\
    ./Examples/Monocular/EuRoC.yaml \\
    /path/to/MH_01_easy \\
    ./Examples/Monocular/EuRoC_TimeStamps/MH01.txt \\
    dataset-MH01_mono # 可选:轨迹输出文件名

    四个必需参数对应 mono_euroc.cc 第 37 行的 Usage:

    ./mono_euroc path_to_vocabulary path_to_settings
    path_to_sequence_folder_1 path_to_times_file_1
    (… 更多序列 …) (trajectory_file_name)

    多序列拼接:参数以 (序列文件夹 + 时间戳) 成对出现,可一次传入多对。
    mono_euroc.cc 第 41 行 num_seq = (argc-3)/2 即据此统计序列段数。连续输入多段序列,
    是观察 Atlas 多地图行为(第 18 篇)的实验入口——系统会在丢失处新建子地图、在重叠处融合。

    4.3 看懂 Pangolin 可视化窗口

    跑起来会弹出两个窗口:

    • Current Frame:当前图像叠加绿色小方框(成功跟踪到的特征点)。绿框数量反映跟踪质量,骤减通常预示即将丢失。
    • Map Viewer(3D):
      • 黑点:地图点(稀疏点云);
      • 蓝色小相机框:关键帧;
      • 绿色相机框:当前帧位姿;
      • 绿色连线:共视图(Covisibility Graph,第 12 篇)——两个关键帧看到足够多共同点就连一条线;
      • 左侧按钮:Follow Camera、Show Points、Show KeyFrames、Show Graph、Localization Mode(纯定位)等开关。

    需注意:mono_euroc.cc 第 83 行构造 System(…, MONOCULAR, false),末位参数 false
    即 useViewer,表示该示例默认不启用内置 Viewer。如需可视化,应将其改为 true
    重新编译,或选用已启用 Viewer 的示例程序。


    5. 读懂示例入口:mono_euroc.cc 到底做了什么

    示例程序薄到只有 228 行,核心就三步。读它能直观感受第 01 篇说的"用户只和 System 打交道":

    // ① 加载图像路径与时间戳(注意时间戳单位换算)
    LoadImages(seqPath + "/mav0/cam0/data", timesFile, vstrImageFilenames[seq], vTimestampsCam[seq]);

    // ② 构造系统(加载词袋、建 Atlas、起三线程——见第 01 篇)
    ORB_SLAM3::System SLAM(argv[1], argv[2], ORB_SLAM3::System::MONOCULAR, false);
    float imageScale = SLAM.GetImageScale();

    // ③ 主循环:每帧喂给 TrackMonocular
    for (int ni = 0; ni < nImages[seq]; ni++) {
    cv::Mat im = cv::imread(vstrImageFilenames[seq][ni], cv::IMREAD_UNCHANGED);
    double tframe = vTimestampsCam[seq][ni];

    if (imageScale != 1.f) { // 可选降采样(见下)
    int width = im.cols * imageScale;
    int height = im.rows * imageScale;
    cv::resize(im, im, cv::Size(width, height));
    }

    SLAM.TrackMonocular(im, tframe); // ← 一切发生在这里
    }

    // ④ 收尾:关闭线程 + 保存轨迹
    SLAM.Shutdown();
    SLAM.SaveTrajectoryEuRoC("CameraTrajectory.txt");
    SLAM.SaveKeyFrameTrajectoryEuRoC("KeyFrameTrajectory.txt");

    两个容易忽略的工程细节:

  • 时间戳单位。LoadImages(第 206 行)里 vTimeStamps.push_back(t*1e-9):EuRoC 的时间戳是纳秒,喂进系统前统一换算成秒。IMU 时间对齐错位、尺度异常,十有八九栽在单位上(第 15 篇还会强调)。
  • imageScale 降采样。GetImageScale() 读的是 yaml 里 Camera.newWidth/newHeight(见下一节)。EuRoC 原图 752×480,配置降到 600×350,牺牲少量精度换帧率。
  • 5.1 轨迹输出格式

    SaveKeyFrameTrajectoryEuRoC 输出 TUM 格式:每行 timestamp tx ty tz qx qy qz qw(平移 + 四元数)。配合 evaluation/ 目录的脚本和数据集真值,就能算 ATE(绝对轨迹误差)/ RPE(相对位姿误差)——这是评测 SLAM 精度的标准指标。

    # 示例:用 evo 工具对比真值
    evo_ape euroc MH01_groundtruth.csv KeyFrameTrajectory.txt -a -s –plot
    # -a 对齐 -s 估计尺度(单目无尺度,必须加 -s)

    单目记得加 -s(Sim3 对齐,允许缩放),否则因尺度不可观,ATE 会大得离谱——这不是算法烂,是单目的固有性质(第 01 篇、第 07 篇)。


    6. 读懂配置文件 EuRoC.yaml

    配置文件是你最常改的东西。新版采用结构化字段(File.version: "1.0"),分四块:

    %YAML:1.0
    File.version: "1.0"

    # ── 相机 ──
    Camera.type: "PinHole" # 针孔;鱼眼填 "KannalaBrandt8"(第 05 篇)
    Camera1.fx: 458.654 # 内参:焦距/主点
    Camera1.fy: 457.296
    Camera1.cx: 367.215
    Camera1.cy: 248.375
    Camera1.k1: -0.28340811 # 畸变系数 k1 k2 p1 p2
    Camera1.k2: 0.07395907
    Camera.width: 752 # 原始分辨率
    Camera.height: 480
    Camera.newWidth: 600 # ← 降采样目标分辨率(对应 §5 imageScale)
    Camera.newHeight: 350
    Camera.fps: 20 # 帧率(影响关键帧插入节奏、IMU 预积分窗口)
    Camera.RGB: 1 # 0=BGR 1=RGB,灰度图忽略

    # ── ORB 特征(第 03 篇)──
    ORBextractor.nFeatures: 1000 # 每帧提多少特征:太少易跟丢,太多拖慢
    ORBextractor.scaleFactor: 1.2 # 金字塔层间缩放比
    ORBextractor.nLevels: 8 # 金字塔层数
    ORBextractor.iniThFAST: 20 # FAST 初始阈值(高)
    ORBextractor.minThFAST: 7 # 提不到点时的降级阈值(低对比度场景调它)

    # ── 可视化 ──
    Viewer.KeyFrameSize: 0.05
    Viewer.GraphLineWidth: 0.9
    # …

    几个高频调参点(这些会在对应章节深入):

    • nFeatures:低纹理/室内调大(1500~2000),算力紧张调小。
    • iniThFAST/minThFAST:图像偏暗、对比度低时调小 minThFAST,能多提点。
    • newWidth/newHeight:删掉(注释)这两行就用原分辨率,精度↑帧率↓。
    • 顶部被注释的 System.LoadAtlasFromFile / SaveAtlasToFile:取消注释即可保存/加载地图,做"预建图 + 纯定位"(第 18 篇)。

    旧版本配置采用扁平字段 Camera.fx,新版本为 Camera1.fx 并由 Settings.cc(第 18 篇)统一解析。两种写法并存属版本差异,含义一致。


    7. 工程提示

    以下为编译、运行、调试三个阶段的常见问题及处理方式。

    编译期

    • OpenCV > 4.4 not found:环境为 OpenCV 3.x 或 CMake 未定位到。安装 4.x,或修改 find_package(OpenCV …) 版本号。
    • Pangolin 链接或编译报错:多由 Pangolin 版本过新、API 变更导致。应安装 Dependencies.md 中推荐的版本。
    • make 阶段内存耗尽(OOM):大文件模板展开占用内存高,将 make -j4 降为 make -j2。
    • 段错误且栈帧集中于 Eigen:通常为 Eigen 内存对齐问题,需确认各编译单元的编译选项一致(避免混用不同 -march)。

    运行期

    • 卡在加载词袋或找不到 ORBvoc.txt:未执行 tar -xf ORBvoc.txt.tar.gz,或路径配置错误。
    • Illegal instruction (core dumped):-march=native 编译的二进制在不同 CPU 架构上运行所致,需在目标机重新编译。
    • 无可视化窗口:检查 System(…) 末位 useViewer 是否为 false(参见 §4.3)。
    • 无显示器服务器批量评测:关闭 Viewer(useViewer=false),避免 Pangolin 因无法连接 X11 而崩溃。

    调试

    • Debug 编译:cmake .. -DCMAKE_BUILD_TYPE=Debug 重新编译后方可在 gdb 中完整查看变量。Debug 构建较 Release 慢数倍,无法满足实时性,仅用于单步分析。
    • 段错误定位:gdb –args ./Examples/Monocular/mono_euroc …,崩溃后执行 bt 查看调用栈。
    • 内存越界或未初始化:用 valgrind 检测(运行极慢,宜选用短序列)。
    • 日志策略:如第 01 篇所述,日志优先加入 LocalMapping/LoopClosing 等后台线程,避免在 Tracking 主循环中引入 I/O 拖慢帧率。

    8. 本篇小结 & 下一篇

    本篇要点:

  • ORB-SLAM3 的依赖分为四类,其中 DBoW2/g2o/Sophus 为作者修改过的特定版本,必须使用 Thirdparty/ 内的版本,不可用系统库替换。
  • build.sh 按 DBoW2 → g2o → Sophus → 解压词袋 → 主库的顺序构建;主库编译为单一共享库 lib/libORB_SLAM3.so,各示例程序仅链接该库。
  • -O3 -march=native 提供性能但使产物不可跨 CPU 架构分发;语言标准为 C++11。
  • 示例程序的骨架为"加载图像 → 构造 System → 逐帧 Track → 保存轨迹",其中 EuRoC 时间戳需由纳秒换算为秒,imageScale 控制输入降采样。
  • 配置文件 .yaml 的高频调参项包括 nFeatures、iniThFAST/minThFAST 与 newWidth/newHeight;轨迹以 TUM 格式输出,可用 evo 计算 ATE/RPE,单目评测须加尺度对齐选项。
  • 第 03 篇进入第一个算法模块 ORBextractor,分析图像金字塔的构建、FAST 角点提取,以及四叉树均匀化如何控制特征点的数量与空间分布。

    赞(0)
    未经允许不得转载:171主机测评 » ORB-SLAM3代码详解 - 第 02 篇 · 编译、运行与代码工程结构
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址