尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Windows下跑通ORB-SLAM:第三方库配置、编译与标定全解析

Windows下跑通ORB-SLAM:第三方库配置、编译与标定全解析 简介在Windows环境跑通ORBSLAM第三方依赖库的编译常常成为拦路虎尤其对刚接触SLAM的同学来说更加棘手。这份资源包面向SLAM入门者与研究开发者已预先编译好ORBSLAM所需的全部第三方库与属性表props无需手动配置OpenCV、Eigen等依赖简单设置即可直接运行。包体共898个文件压缩后约154.62MB以头文件h、hpp、C源码cpp、静态/动态库lib、dll以及配置文件yaml、props为主同时包含标定板图片jpg和ORB词袋数据可支撑相机标定与算法验证目录结构清晰头文件、库文件与源码分类存放便于按需检索。代码已完成基本封装围绕单目相机SLAM流程提供可扩展接口方便二次开发内含20张张氏标定板图片下载后即可开展标定实验。当前已有362人学习下载适合希望跳过环境搭建、直接聚焦算法本身与实验验证的Windows用户。1. Windows下跑通ORBSLAM别急着改代码先把环境对齐把 ORB-SLAM 从 Ubuntu 挪到 Windows最耗时间的不是看懂特征匹配和位姿图优化而是把 Eigen、DBoW2、g2o 这些依赖按正确顺序编译一遍。网上教程大多停在理论层真正落到 Visual Studio 工程里各种 LNK 错误和路径问题能把人磨得没脾气。这份资源正是冲这个问题来的第三方库已经全部编译好属性表也配完拿到后只需要把 My_Monocular.cc 所在工程导入 Visual Studio配好 ORBvoc.bin 和相机内参就能跑出单目 SLAM 的完整流程。它适合正在做计算机视觉课程设计、SLAM 毕设或者想基于 ORB-SLAM2 做二次开发的从业者。下面我按环境、编译、标定、排错四个环节讲清楚顺序就是我实际在 Windows 上跑通时的操作顺序。这样你能直接照着走而不是把别人的错误提示再复现一遍。2. 资源包里到底有什么第三方库、属性表和 ORBvoc.bin 的来龙去脉2.1 已编译的第三方库Eigen、DBoW2、g2o 分别负责什么ORB-SLAM 不是单文件工程它由前端跟踪、局部建图、回环检测三个线程组成。这三个线程里Eigen 负责所有矩阵运算和位姿表示DBoW2 负责把每一帧 ORB 描述子转成词袋向量回环闭合的候选帧筛选就靠它g2o 负责后端优化把相机位姿和地图点放到图模型里做非线性最小二乘。资源包里出现的 AlignedVector3、ArpackSupport、AutoDiff、Cholesky 这些符号不是额外的一堆库而是 Eigen 头文件里的模块名。Eigen 是模板头文件库多数模块只需要包含路径就能用。你在属性表里看到它们是因为项目把 Eigen 整个 include 目录都暴露给了编译器这不代表链接时需要单独找 .lib只要包含目录没配错这些模块会自动进入编译单元。真正麻烦的是 DBoW2 和 g2o。它们各自依赖 Eigen而且 g2o 还需要 CholMOD、CSparse 这些稀疏线性代数库。Windows 上手动编它们最容易出现版本对不上g2o 用新版 Eigen 的aligned_allocatorORB-SLAM2 老代码可能还在用旧接口。资源包把已经编好的 .lib 和 .dll 一起放进来了省掉的就是这一层版本调配。像 os_specific.c 这种文件属于第三方库里的平台适配代码编译阶段已经处理过不需要你单独再改。我的建议是先把资源包里的目录结构认一遍大概长这样目录内容使用方式thirdparty/EigenEigen 头文件目录编译时包含路径thirdparty/DBoW2词袋库源码与编译产物链接 DBoW2.libthirdparty/g2o图优化库源码与编译产物链接 g2o.libVocabulary/ORBvoc.bin离线词袋文件运行时加载config/my_monocular.yaml单目相机参数运行时读取这份清单能帮你定位大多数报错编译报错看 thirdparty 路径运行崩溃先看 ORBvoc.bin 和 YAML标定异常再回头查内参数值。别一上来就怀疑算法写错了Windows 下 ORB-SLAM 第一次跑不起来九成是环境问题。2.2 属性表的作用与导入方法属性表文件是 Visual Studio 的 .props 文件它把 include 路径、库路径、附加依赖项一次性写死。只要你把属性表挂到工程上编译器就会自动去找第三方库不用每次新建工程都重新配一遍。导入步骤是打开 My_Monocular.cc 所在的 Visual Studio 工程 → 菜单“属性管理器”→ 右键工程名或 Debug|x64 → “添加现有属性表”→ 选择资源包里的 .props 文件。之后在“VC 目录 → 包含目录”和“库目录”里就能看到 Eigen、DBoW2、g2o、OpenCV 的路径。常见做法是新建工程后直接挂这张属性表再把 My_Monocular.cc 拖进源文件目录。如果发现编译时找不到头文件先看属性表有没有被加载而不是去改系统环境变量。环境变量方式在 Visual Studio 里经常失效因为 MSBuild 只在工程配置里找路径你在 Path 里加再多的目录也没用。Import Project$(ProjectDir)..\property\ORB_SLAM.props /这一行会写进 .vcxproj 工程文件。如果你拿到的是源码包而不是完整工程可以手动把这行加进去注意$(ProjectDir)换成属性表实际所在路径。属性表一旦挂上包含目录和库目录都会自动补全后面的编译就少了一半报错。提示Debug 和 Release 最好各挂一份对应的属性表混用会出现下面要说的 LNK2038后面排查起来很折磨。2.3 ORBvoc.bin词袋文件缺失是跑不出回环检测的第一元凶ORBvoc.bin 是 ORB 特征描述子的词袋文件ORB-SLAM2 在构造 System 对象时会先加载它加载失败直接拒绝启动。这个文件是离线训练好的视觉词汇表不需要你自己生成。只要配置文件里VocabularyFile的路径写成相对路径而当前工作目录不是工程目录程序启动到一半就会崩溃。我一般会把 ORBvoc.bin 和配置文件放到同一个目录然后用绝对路径或显式拼路径std::string vocab_path project_root /Vocabulary/ORBvoc.bin; std::string config_path project_root /config/my_monocular.yaml;这样写的好处是VS 里 F5 启动时工作目录经常被改成 exe 所在目录再写成相对路径很容易找不到。project_root可以在启动时通过读取模块路径得到也可以直接定义成硬编码字符串先跑通再说。等你把词袋路径固定下来后面出的问题才是真正的视觉里程计问题而不是“file not found”。3. 编译 My_Monocular.cc从源码到能跑的 ORB-SLAM 单目例程3.1 My_Monocular.cc 封装了哪些流程My_Monocular.cc 是把 ORB-SLAM2 官方mono_tum.cc二次封装后的单目入口。它能做的事就是读配置、加载词袋、创建 System、逐帧读取图像、调用 TrackMonocular、最后保存轨迹。官方例程需要自己传一堆命令行参数封装版本把这些参数收敛到了一个配置文件里所以你不需要在命令行里拼一长串路径。核心代码大概是这样的#include System.h #include opencv2/opencv.hpp int main(int argc, char** argv) { if (argc 3) { std::cerr Usage: ./My_Monocular config_path image_folder std::endl; return -1; } std::string vocab_path Vocabulary/ORBvoc.bin; std::string config_path argv[1]; std::string image_dir argv[2]; ORB_SLAM2::System slam(vocab_path, config_path, ORB_SLAM2::System::MONOCULAR, true); std::vectorstd::string files LoadImagePaths(image_dir); double timestamp 0.0; for (auto file : files) { cv::Mat frame cv::imread(file, cv::IMREAD_COLOR); if (frame.empty()) continue; cv::Mat pose slam.TrackMonocular(frame, timestamp); timestamp 0.01; } slam.Shutdown(); slam.SaveTrajectoryTUM(trajectory.txt); return 0; }这段代码里LoadImagePaths是资源包封装好的工具函数它会把目录下所有 jpg/png 按文件名排序。TrackMonocular返回的是当前帧的相机位姿timestamp 0.01表示固定按 100 帧每秒推进时间轴。如果你手里的数据集有真实时间戳应该用时间戳文件里的值替换这里否则轨迹形状正确但时间尺度不对。LoadImagePaths的实现也不复杂其实就是遍历目录加排序std::vectorstd::string LoadImagePaths(const std::string dir) { std::vectorstd::string paths; for (const auto entry : std::filesystem::directory_iterator(dir)) { if (entry.is_regular_file()) { std::string ext entry.path().extension().string(); if (ext .jpg || ext .png) paths.push_back(entry.path().string()); } } std::sort(paths.begin(), paths.end()); return paths; }注意这里是 C17 的std::filesystem工程属性里要设置语言标准为 C17。如果项目还用旧版本改成_findfirst系列函数也能实现但代码会多不少。封装版本直接给你省了这段不需要你自己处理目录遍历。3.2 用 Visual Studio 编译的完整步骤拿到资源包后我的建议是按照“创建空工程 → 挂属性表 → 添加源文件 → 配置字符集 → 生成”的顺序走。首先打开 VS2019 或 VS2022新建一个 C 空项目把 Debug 切到 x64然后从属性管理器挂上属性表。接着把 My_Monocular.cc 复制到工程的源文件目录下并在解决方案资源管理器里“添加现有项”。如果工程默认使用 Unicode 字符集但代码里用了std::string读取路径记得把字符集改成多字节在项目属性 → 配置属性 → 常规 → 字符集里改。ORB-SLAM2 老代码很多地方直接用 char*Unicode 下会报LNK2001 unresolved external symbol _main之类的入口错误。链接完成后把资源包里的 opencv_world.dll、g2o.dll、DBoW2.dll 复制到 exe 同目录xcopy /Y thirdparty\bin\*.dll x64\Debug\ xcopy /Y opencv\bin\opencv_world*.dll x64\Debug\这两条命令把运行期依赖的 DLL 收集到输出目录。如果不做这一步程序编译成功但启动时会提示找不到opencv_world.dll。在 Visual Studio 里也可以把 DLL 所在目录加到 PATH但每次换机器都得再配一遍直接 xcopy 到 exe 旁边更省事。代码里的argc判断建议保留因为 VS 调试时可以直接在“调试 → 调试属性 → 命令行参数”里填两个路径比每次改代码里的硬编码路径方便。命令行参数顺序要和 main 开头对应第一个是 YAML 配置第二个是图像目录。3.3 参数调整分辨率、相机内参和特征点数量单目 ORB-SLAM 对配置非常敏感。资源包里应该有一份my_monocular.yaml里面最关键的几项是Camera.fx: 520.9 Camera.fy: 521.0 Camera.cx: 325.1 Camera.cy: 249.7 Camera.k1: 0.0 Camera.k2: 0.0 Camera.p1: 0.0 Camera.p2: 0.0 ORBextractor.nFeatures: 1000 ORBextractor.nScaleLevels: 8 ORBextractor.fScaleFactor: 1.2 ORBextractor.fIniThFAST: 20 ORBextractor.fMinThFAST: 7fx/fy/cx/cy直接决定地图尺度初始化成什么样填错的话初始化经常失败画面里特征点数量看着没问题但就是迟迟不进入 SLAM 启动状态。ORBextractor.nFeatures控制每帧提取的特征点上限桌面场景 1000 够用特征稀少的楼道可以降到 600让追踪更不容易飘。fScaleFactor是金字塔缩放比改成 1.2 是图像金字塔常用值不要调成 1.0否则尺度空间就没了。fIniThFAST和fMinThFAST控制 FAST 角点检测的两个阈值前者决定初始提取的敏感度后者用于补提特征点。如果你测试的场景纹理很弱把fMinThFAST降到 5 能多出不少候选点但也会带来更多无效帧。注意如果图像不是灰度图IMREAD_COLOR读进来后 ORB-SLAM 内部会转成灰度。这个转换发生在TrackMonocular里不需要你预处理。4. 相机标定实验20 张张氏标定板图片怎么用4.1 张氏标定原理与拍摄要求张氏标定法只需要拍摄不同角度的平面棋盘格图片通过角点检测和单应矩阵估计出相机内参。资源包附带的 20 张标定板图片模拟的就是“持板在不同距离、不同倾角下拍摄”的过程。标定板最好完整出现在画面里相邻两张图的角度差保持在 15 到 30 度之间这样内参求解时约束足够畸变尺度才能被拉开。使用时要先知道手上棋盘格的两个参数内部角点数boardSize和方格边长squareSize。内部角点是棋盘格内黑白交叉的点不是格子数量。比如常见的 10×7 方块网格内部角点就是 9×6。这一步最容易错错了之后findChessboardCorners要么找不全要么报 pattern not found。20 张图不需要每一张都能检测出全部角点但尽量保证 15 张以上成功。如果某张图只检测到部分角点可以把它从列表里剔除而不是强行参与标定。一个明显倾斜或者被遮挡的棋盘图会把重投影误差拉高出不少。4.2 用 OpenCV 完成角点检测与内参计算在 Windows 上做标定我一般直接用 Python 加 OpenCV速度比写 C 快得多。先安装 opencv-python然后把资源包里的 20 张图片放到chess_imgs目录下运行下面这段import cv2 import numpy as np import glob images sorted(glob.glob(chess_imgs/*.jpg)) board_size (9, 6) # 内部角点数量按实际标定板修改 square_size 25.0 # 毫米按实际打印尺寸改 objp np.zeros((board_size[0] * board_size[1], 3), np.float32) objp[:, :2] np.mgrid[0:board_size[0], 0:board_size[1]].T.reshape(-1, 2) objp * square_size obj_points [] img_points [] for img_file in images: img cv2.imread(img_file) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners cv2.findChessboardCorners(gray, board_size, None) if ret: criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 300, 0.001) corners2 cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners2) cv2.drawChessboardCorners(img, board_size, corners2, ret) ret, K, dist, rvecs, tvecs cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None) print(内参矩阵:\n, K) print(畸变系数:\n, dist)这段代码先用findChessboardCorners找角点再用cornerSubPix做亚像素精化。board_size必须与图片实际匹配square_size只影响尺度的物理单位如果后续知道实际方块边长就填真实毫米值不知道的话填 1.0 也能用因为 ORB-SLAM 建图用的是归一化坐标。calibrateCamera返回的K是 3×3 内参矩阵dist是 4 到 5 个畸变参数。代码里的(9,6)是内部角点数量如果图片里的棋盘网格是 11×8那这里就要改成(10,7)。跑完如果发现成功检测的图片少于 12 张先检查是不是board_size填错了。4.3 标定结果怎么填回 ORB-SLAM 配置OpenCV 标定得到的是像素坐标系下的内参单位是像素ORB-SLAM 的 YAML 配置也使用同样的坐标定义。把K[0][0]、K[1][1]、K[0][2]、K[1][2]分别填入Camera.fx、Camera.fy、Camera.cx、Camera.cy即可。OpenCV 输出ORB-SLAM 配置项说明K[0][0]Camera.fxx 方向焦距K[1][1]Camera.fyy 方向焦距K[0][2]Camera.cx主点 xK[1][2]Camera.cy主点 ydist[0]Camera.k1径向畸变一阶dist[1]Camera.k2径向畸变二阶dist[2]Camera.p1切向畸变一阶dist[3]Camera.p2切向畸变二阶ORB-SLAM2 在读取单目配置时会用这些参数做畸变矫正矫正之后再提取特征点。所以如果你从某个现成数据集里直接抄内参但图片实际来自另一颗镜头前端初始化就会非常飘。用这 20 张标定图自己跑一遍标定再把数据填回去能明显感觉到初始化时地图点收敛得更快。做完这一步建议把dist和K打印结果保存下来后面换测试视频时就不用重新标定。同一颗镜头在分辨率不变的情况下内参可以复用但如果你把视频从 1080p 缩到 720pfx/fy/cx/cy 也要按比例缩放这个很多人会漏掉。5. 避坑手册Windows 下 ORB-SLAM 的五个典型翻车现场5.1 编译链接错误LNK2038 和 EIGEN_ALIGN 对不上现象链接时报LNK2038 mismatch detected for _ITERATOR_DEBUG_LEVEL或者EIGEN_ALIGN相关的模板实例化冲突。原因Debug 工程链接了 Release 版的第三方库或者 g2o 用的 Eigen 版本和属性表里 include 的 Eigen 版本不一致。Windows 上最容易出现的是 debug 和 release 混用因为 ORB-SLAM2 老依赖库经常只生成 release 版本。解决把第三方库的 debug/release 版本分开目录存放Debug 工程只链接 debug 的 .lib。如果属性表里只给了一套 release 库就统一把工程切到 Release 编译。不要试图在 Debug 下硬链接 release 库那会一路报一堆 LNK 错误浪费大量时间。5.2 运行时崩溃ORBvoc.bin 路径不存在现象程序启动后黑框一闪而过或者卡在 “Loading ORB Vocabulary. This could take a while...” 之后直接退出没有打印任何错误。原因ORB-SLAM2 加载词袋文件时如果路径不对文件流会静默返回空内容然后程序读文件时抛异常。由于异常没有处理直接触发 abort。解决在加载词袋后立刻判断文件大小std::ifstream f(vocab_path, std::ios::binary); if (!f.good() || f.tellg() 0) { std::cerr Vocabulary file missing or empty: vocab_path std::endl; return -1; }这个检查只用几分钟可以帮你把路径问题和算法问题彻底分离。每次换工程都值得加一遍尤其是把工程从一台电脑拷到另一台电脑时ORBvoc.bin 路径经常变。5.3 标定失败棋盘格参数与图片不匹配现象findChessboardCorners对部分图片返回False或者检测到的角点连线明显错位。原因board_size写成了格子数量而不是内部角点数量或者图片里有多个棋盘格区域让检测器混淆。张氏标定板要求画面里只有一个完整棋盘格背景杂物太多时会误检。解决先用cv2.imshow把检测结果画出来确认内部角点数量。比如资源包里的标定板内部角点如果是 9×6代码就写(9,6)。最保险的方法是数一排黑格和白格的交界点而不是直接按板子上的格子个数猜。如果检测时经常误检把背景裁剪掉或者换纯色背景重拍。5.4 初始化卡住内参填了但画面里没有足够视差现象程序启动后始终打印 waiting for features几秒后初始化失败重新尝试反复横跳。原因单目初始化需要前后两帧之间有足够的平移视差但你的测试视频可能是原地转动拍摄或者ThDepth设置得太小导致初始化时深度范围估计不足。解决把相机对着有纹理、有深度的场景平移拍摄避免纯旋转。同时检查 YAML 里ThDepth单目配置通常设置为 40 左右表示只考虑深度在 40 倍基线内的点。如果是用手机拍的测试视频先把视频抽帧成图片序列再按时间戳喂给 My_Monocular.cc不要直接拿手机摄像头边跑边初始化。5.5 窗口不显示或崩溃Pangolin 的 OpenGL 上下文问题现象程序编译运行后Pangolin 窗口黑屏或者窗口刚出现就崩溃提示OpenGL error。原因Windows 上 Pangolin 需要 GPU 驱动支持 OpenGL 3.2 以上。很多虚拟机或远程桌面环境没有独立显卡驱动Pangolin 初始化会失败。ORB-SLAM2 的几个可视线程都要依赖 Pangolin 窗口。解决如果不需要看实时窗口把系统构造参数传入false关闭 viewerORB_SLAM2::System slam(vocab_path, config_path, ORB_SLAM2::System::MONOCULAR, false);这样不创建 Pangolin 窗口只跑后端轨迹照常保存到trajectory.txt。需要可视化时再在本地实体机开窗口远程机器上只做离线跑图。这个开关是很多人忽略的我用它绕开了大部分显卡兼容问题。6. 进阶从跑通到改自己的单目 SLAM6.1 把位姿轨迹存成 TUM 格式直接用 EVO 评估跑通之后第一件值得做的事不是改算法而是先保存轨迹并用 EVO 评估。My_Monocular.cc 里已经有SaveTrajectoryTUM输出格式是时间戳加平移加四元数。这个格式可以直接喂给 evopip install evo evo_traj tum trajectory.txt --plot如果你有 GroundTruth 轨迹再用evo_ape tum trajectory.txt GroundTruth.txt -a算绝对位姿误差。这一步能验证你的内参和序列是否真的可用。ORB-SLAM2 保存的轨迹是 RGB 相机位姿做评估前先确认坐标系定义别拿四元数顺序搞错了方向。6.2 换数据集时只需要对齐三处换到自己的数据集我一般只改三处配置文件里的内参、ORBvoc.bin 路径、图像序列的时间戳。时间戳直接决定轨迹输出的横轴尺度如果统一递增轨迹形状没问题但速度完全是假的。如果数据集没有时间戳就按帧率算double timestamp frame_index / fps;这样至少保证了时间轴的物理意义。接下来就是重放一遍整个流程看初始化是否成功、回环有没有触发。回环触发的标志是黑色轨迹突然被拉到原来的路径附近同时地图点数量明显下降。6.3 回环闭合的验证技巧一个很省时间的验证方法跑两遍同一段序列第一遍关闭回环第二遍打开回环对比两张轨迹图闭合段的漂移。如果没有回环长走廊场景的轨迹会越来越飘打开回环后轨迹应该在后半段被拉回起点附近。如果打开和关闭效果一样说明你的词袋文件和特征匹配质量有问题优先检查 ORB 特征点数量是否足够一致以及配置文件里的 ORBextractor 参数是否被误改。如果你手头正好也要在 Windows 上复现这个实验直接拿资源包按这个顺序配一次会比从源码开始省下至少一天时间。资源包里也留有答疑联系方式遇到具体报错卡住的时候比自己在网上翻老帖子高效得多。从那以后我每次改完参数都会强制走一遍“保存轨迹 → EVO 跑 APE → 对比回环开关”三连确认没有回归才继续往下做。希望帮到你。本文还有配套的精品资源点击获取
返回列表