Windows 端 ROSless ORB-SLAM3 的移植

Windows 端 ROSless ORB-SLAM3 的移植

最近项目上有对放射源进行三维扫描与辐射场建模的需求,而必要的能谱分析软件是基于 Windows 端开发的,故需要在 Win 端实现 SLAM 建图。目前实际应用中 ORB-SLAM 为效果最好的 基于特征点的视觉 SLAM 系统,而其 v3 版本相较于 v2 版本增加了使用 IMU 与视觉里程计融合的功能并提高了运算速度,因此便选择使用 ORB-SLAM3 实现功能,同时准备把其基于 Linux gcc 平台实现的库移植到 Win MSVC 平台上。

GitHub 上可以搜到一些移植完成的项目,绝大多数都没有移植过程的记录,移植的基本上都是 v2 版本,极少数 ORB-SLAM3 使用的都是 v0.x rc 版本。考虑到项目有稳定与安全性方面的要求,我最终选择 ORB-SLAM3 使用最新版本、所有依赖库均使用官方 Release 的方式来构建项目。构建完成的项目会放到 GitHub 上,但还是建议过一遍本文的流程,了解移植时在什么地方做了什么修改有什么坑,触类旁通的同时还能便于以后在 ORB-SLAM3 库更新版本时能够及时跟得上。

移植前准备

工具链

构建工具链大版本一致就不会有什么问题,但我还是把我的版本放在这里:

  • CMake v3.31.4
  • MSVC 2022 v14.43.34808 on Windows 11

依赖库

ORB-SLAM3 有着众多的依赖包。除了 Repo 里 Thirdparty 文件夹中自带的魔改过的 DBoW2、g20 和 Sophus 外,其它的均需要手动下载编译。这里先列个清单,附上我在编译时实际使用的参考版本及下载地址:

示例数据集

由于我使用的是 Orbbec Astra 2,这是一个奥比中光推出的 带 IMU 的单目结构光深度相机,因此选择的示例数据为 RGB-D 类型。
这是 RGB-D 数据集下载地址:cvg.cit.tum.de/data/datasets/rgbd-dataset/download。由于 ORB-SLAM3 Repo 中的 Examples 文件夹自带有 associations 文件(关联 RGB 与 Depth 图片的 txt 文件),因此不用再下载 associate.py 脚本来生成。

其它类型(单目、双目等)可在这篇知乎专栏中找到:https://zhuanlan.zhihu.com/p/625605417

ORB-SLAM3 库 构建流程

unistd.h

unistd.h 文件在 Unix 类系统中的作用类似于 Windows 中的 Windows.h 文件。从 Linux 移植到 Windows 平台的程序大多需要手动创建一个,避免编译时出现 无法打开源文件 的错误。

在 msvc include 文件夹(...\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.43.34808\include)中添加一个 unistd.h 文件:

/** This file is part of the Mingw32 package.
unistd.h maps (roughly) to io.h
*/
#ifndef _UNISTD_H
#define _UNISTD_H
#include <io.h>
#include <process.h>
#endif /* _UNISTD_H */

注意⚠️:文件末尾需留有一行空行,否则编译时会报 Unexpected end of file 错误。

P.S. MSVC 工具链升级更新后 Visual Studio 会在原版本工具链的文件夹下保留该文件,需要手动移动至新版本工具链的 include 目录中。

OpenCV 安装

直接下载后解压到 Program Files 即可。

  • OpenCV_DIR 目录为 .\build

Win32OpenSSL 安装

ORB-SLAM3 使用到了 md5 相关函数。如果缺少该库,在其编译时会出现 无法打开包括文件: “openssl/md5.h”: No such file or directory 的错误。

直接下载后安装到 Program Files 即可。

  • include 目录为 .\include
  • lib 目录为 .\lib\VC\x64\MT

Boost 编译

v1.87.0 版本的 Boost 可直接双击 bootstrap.bat 脚本即可生成 Boost 专用的构建工具 b2.exe(Boost-Build)。
然后使用下面的命令来生成 debug/release 版本的动态与静态库:

.\b2.exe stage --stagedir=".\stage" link=static runtime-link=shared runtime-link=static threading=multi debug release
  • Boost_DIR 目录为 .\stage\lib

DBoW2 编译

从这个依赖开始,下面的都需要使用 CMake GUI 来进行项目的生成和编译了。
步骤大致都相同,这里先详细列出,后面基本相同的地方便不再赘述:

  1. 在待编译项目的根目录下创建一个 build 文件夹
  2. 打开 CMake GUI,Where is the source code 填写 CMakeLists.txt 所在位置(都是根目录),Where to build the binaries 填写刚创建的 build 文件夹位置
  3. 点击 Configure 后,在弹出的对话框中选择使用的 MSVC 编译器版本,多次点击 Configure 和填写 CMakeLists 中空缺的变量值(大多都是类似 OpenCV /Boost 等依赖库所在的位置),直到点击 Generate 后不再有报错
  4. 若在生成时出现 Invalid escape sequence \U 的错误,可将路径中的反斜杠替换为斜杠,例如:C:\ => C:/
  5. 成功生成项目后 Open Project 以在 Visual Studio 中打开
  6. 顶部菜单栏选择编译类型为 Release
  7. 由于接下来编译的几个库都使用 静态链接 的方式生成,因此需要
  • 右键项目名->属性->常规->配置类型 选择 静态库(.lib)
  • 高级->目标文件扩展名 填写为 .lib
  • C/C++->代码生成->运行库 选择 多线程(/MT)
  1. 右键 ALL_BUILD->生成

由于是使用 MSVC 编译,gcc 标准库中的 stdint-gcc.h 头文件自然是找不到的,但由于里面的类型由 MSVC 的其它头文件定义过,因此可以直接注释掉 Thirdparty\DBoW2\DBoW2\FORB.cpp 文件中的 include 引用:

//--//#include <stdint-gcc.h>
  • lib 目录为 .\lib\Release

Eigen3 编译

这个似乎没坑,略了。

  • Eigen3_DIR 目录为 .\lib\share\eigen3\cmake

g2o 编译

g2o 的 CMakeLists.txt 中警告层级 -W 参数没有值,在 gcc 下为默认打印所有警告,而 MSVC 下必须显示赋值,因此手动去掉或修改为 -W1 即可:

#-- Explicitly set Warning param
%(AdditionalOptions) -W1 -O3 -march=native

项目属性->C/C++->预处理器->预处理器定义 中需要添加一项 WINDOWS; (等价于在所有源文件中添加 #define WINDOWS 宏定义),否则会出现 vasprintf 未定义符号 的问题。

  • lib 目录为 .\build\Release

Pangolin 编译

Pangolin 建议严格使用 v0.6 版本(release date 与 ORB-SLAM3 v1.0 最近)。

Pangolin v0.6 存在一个 已知问题 #609,在编译过程中会出现大量类似于 没有可用于扩展的参数包使用类模板需要模板参数列表 的错误。
解决方法为,打开 include\mpark\variant.hpp 文件:

#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
//-- Commenting out line for Known-Issue: https://github.com/stevenlovegrove/Pangolin/issues/609#issuecomment-645653656
//--//#define MPARK_CPP14_CONSTEXPR
#endif

将中间的 define MPARK_CPP14_CONSTEXPR 行注释掉即可。

然后在 CMake 生成项目时配置 CMAKE_INSTALL_PREFIX 为 Thirdparty\Pangolin\lib,则

  • include 目录为 Thirdparty\Pangolin\lib\include
  • lib 目录为 Thirdparty\Pangolin\lib\lib

ORB-SLAM3 编译

先对 CMakeLists.txt 进行修改。

第 #27 行,由于 MSVC 跳过 C++11 直接支持的 C++14,因此这里注释掉 FATAL_ERROR:

# Check C++11 or C++0x support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
···
#-- Commenting out for compiling with MSVC C++14 support
#--#else()
#--#   message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

找到刚才 Thirdparty 中编译出的静态链接库位置,然后编辑 CMakeLists.txt 第 #221 行处的 target_link_libraries

target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBS}
${EIGEN3_LIBS}
${Pangolin_LIBRARIES}
#-- Update thirdparty dependencies static libs
${PROJECT_SOURCE_DIR}/Thirdparty/DBoW2/lib/Release/DBoW2.lib
${PROJECT_SOURCE_DIR}/Thirdparty/g2o/build/Release/g2o.lib
-lboost_serialization
-lcrypto
)

另外,由于 C++ 实现的不同以及平台更换后标准库间的差异,需要对 ORB-SLAM3 库项目 源码进行修改:

MSVC 特性:

打开 mappoint.cc 文件,找到第 #371 行:

//-- Replacing float 2d-array with vector for MSVC not supporting dynamic-length array.
//--//float Distances[N][N];
vector<vector<float> > Distances(N, vector<float>(N));

float Distances[N][N]; 替换为 vector<vector<float> > Distances(N, vector<float>(N));

以及同一个文件第 #388 行:

//--//vector<int> vDists(Distances[i],Distances[i]+N);
vector<int> vDists(Distances[i].begin(), Distances[i].end());

标准库符号缺失:
编译时会出现大量 “usleep”: 找不到标识符 的问题,可使用 StackOverflow 上一个回答中的一个 usleep 实现。
编辑 System.cc,添加函数定义:

void usleep(__int64 usec) 
{ 
    HANDLE timer; 
    LARGE_INTEGER ft; 

    ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time

    timer = CreateWaitableTimer(NULL, TRUE, NULL); 
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 
    WaitForSingleObject(timer, INFINITE); 
    CloseHandle(timer); 
}

编辑 System.h,添加函数签名:

//-- Added usleep implementation. Source: http://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw
void usleep(__int64 usec);

然后再在下面各文件中 include System.h

  • Tracking.cc
  • LocalMapping.cc
  • LoopClosing.cc
  • MapPoint.cc
  • Atlas.cc

与前面相同的原因,需要在 ORBmatcher.cc 文件中直接注释掉 #include <stdint-gcc.h>

项目属性->C/C++->预处理器->预处理器定义 中需要添加一项 COMPILEDWITHC11; (等价于在所有源文件中添加 #define COMPILEDWITHC11 宏定义),否则会出现 "monotonic_clock": 不是 "std::chrono" 的成员 及类似的编译期错误。
最后就可以右键 ORB_SLAM3 项目->生成。可能会看到许多的 Warning,但最终是能够构建成功的。
如果在链接时报错 无法解析的外部符号 MD5_Init 等 MD5 相关错误,则链接器未找到 OpenSSL 库,可手动添加 OpenSSL-Win64\lib\VC\x64\MT\libcrypto.lib

  • lib 目录为 .\build\Release

Demo 构建流程

最后准备编译 RGB-D Example 来验证移植结果:

  1. 同样地,需要右键 rgbd_tum 项目->属性->C/C++->预处理器->预处理器定义 中添加一项 COMPILEDWITHC11;(等价于在所有源文件中添加 #define COMPILEDWITHC11 宏定义),否则会出现 "monotonic_clock": 不是 "std::chrono" 的成员 及类似的编译期错误
  2. 链接器->高级->导入库 删除所有项
  3. 尝试右键项目->生成,若出现找不到链接库的错误时,可再打开项目属性,在链接器->输入->附加依赖项中添加即可

P.S. 若在链接时出现 error LNK2038: 检测到“RuntimeLibrary”的不匹配项 问题时,先查看是否更改 项目属性->C/C++->代码生成->运行库 为 多线程(/MT),然后查看 报错的动态运行时链接库所对应的静态运行时链接库是否在 链接器->输入->附加依赖项 中出现。若未出现,应手动添加。

构建完成后,可打开 Examples/RGB-D/Release 文件夹,将生成的文件连同 ORB-SLAM3 自带的 TUM1 配置文件、associations 文件夹和下载的数据集,以及 OpenCV 动态链接库 复制到单独的文件夹中,然后右键打开 Terminal,执行命令:

.\rgbd_tum.exe ..\..\Vocabularies\ORBvoc.txt ..\..\Vocabularies\ORBvoc.txt .\tum\TUM1.yaml ..\..\Datasets\rgbd_dataset_freiburg1_room\ ..\..\Datasets\associations\fr1_room.txt

等待几秒 Vocabulary 的加载后,demo 大概是能成功运行起来咯:

后记

后面还会有一篇来详细记录下密集点云建图的实现
(因为搜到了几篇很有意思的论文所以)打算开一个专题,专门记录三维建图算法的折腾。今天先这样啦😊

参考

  1. https://github.com/lydieusang/orbslam3-windows
  2. https://zhuanlan.zhihu.com/p/625605417