最近项目上有对放射源进行三维扫描与辐射场建模的需求,而必要的能谱分析软件是基于 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 来进行项目的生成和编译了。
步骤大致都相同,这里先详细列出,后面基本相同的地方便不再赘述:
- 在待编译项目的根目录下创建一个
build
文件夹 - 打开 CMake GUI,
Where is the source code
填写CMakeLists.txt
所在位置(都是根目录),Where to build the binaries
填写刚创建的build
文件夹位置 - 点击 Configure 后,在弹出的对话框中选择使用的 MSVC 编译器版本,多次点击 Configure 和填写 CMakeLists 中空缺的变量值(大多都是类似 OpenCV /Boost 等依赖库所在的位置),直到点击 Generate 后不再有报错
- 若在生成时出现
Invalid escape sequence \U
的错误,可将路径中的反斜杠替换为斜杠,例如:C:\
=>C:/
- 成功生成项目后 Open Project 以在 Visual Studio 中打开
- 顶部菜单栏选择编译类型为 Release
- 由于接下来编译的几个库都使用 静态链接 的方式生成,因此需要
- 右键项目名->属性->常规->配置类型 选择
静态库(.lib)
- 高级->目标文件扩展名 填写为
.lib
- C/C++->代码生成->运行库 选择
多线程(/MT)
- 右键 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 来验证移植结果:
- 同样地,需要右键 rgbd_tum 项目->属性->C/C++->预处理器->预处理器定义 中添加一项
COMPILEDWITHC11;
(等价于在所有源文件中添加#define COMPILEDWITHC11
宏定义),否则会出现"monotonic_clock": 不是 "std::chrono" 的成员
及类似的编译期错误 - 链接器->高级->导入库 删除所有项
- 尝试右键项目->生成,若出现找不到链接库的错误时,可再打开项目属性,在链接器->输入->附加依赖项中添加即可
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 大概是能成功运行起来咯:

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