简介:面向视觉 SLAM 初学者和机器人定位建图研究者,这份资源围绕 ORB-SLAM2 处理自定义图片序列数据集展开,解决从零准备数据到成功运行系统的常见问题。资源为 gz 压缩包,共 4 个文件,约 4.38MB,内含 Python 脚本(视频转图片序列、生成时间戳)、示例视频和 rgb.txt 格式参考,可帮助快速理解 ORB-SLAM2 对输入数据的要求。已有 5381 人学习,适合对照实践。内容覆盖完整的数据组织流程:从视频逐帧提取图片、按序命名,到构造带时间戳的 rgb.txt,再到修改相关配置文件中的参数(如特征数量、关键帧间隔),让用户能用自己的图片序列跑通 ORB-SLAM2 的初始化、跟踪、闭环检测与全局建图。读者可获得可复用的脚本与格式参考,少走弯路,快速验证自定义数据集。
1. 拿到ORB SLAM2先别急着跑:你的图片离数据集还差几步
做SLAM的人多半经历过这个瞬间:ORB SLAM2的源码编译通过,TUM官方的示例序列跑得行云流水,可一旦换上自己手机拍的图片序列,程序要么启动即崩,要么轨迹在原点打转,要么跑到一半直接丢失跟踪。问题不在算法本身,而在你的图片序列根本没达到ORB SLAM2的输入门槛。这个项目正是解决这件事——把任意来源的普通图片,加工成ORB SLAM2能稳定消费的序列数据集,并给出从采集、预处理到参数配置的完整落地路径。适合刚编译完源码、准备用真实场景数据做验证的从业者,也适合在标准数据集上跑腻了、想用自采数据分析算法边界的人。它不教你SLAM理论,只解决“怎么让自己的图片跑起来”这个具体问题。
2. 数据集的正确打开方式:先搞清楚ORB SLAM2认什么格式
2.1 TUM格式与KITTI格式的选用边界
ORB SLAM2官方源码里带了两个单目例程,一个叫mono_tum,一个叫mono_kitti,对应的数据接口完全不同。不理解这两者的差异,是后面所有翻车的源头。
TUM格式的核心是“彩色图+深度图+时间戳”三件套,但单目模式只用彩色图和时间戳。它的图片文件名没有强制规则,真正关键的是associations.txt文件——每行由时间戳和图片路径两列组成,程序按这个文件逐帧读取。KITTI格式则简单粗暴,图片按000000.png、000001.png的序号排列,程序直接按顺序读,不存在时间戳概念,运行速度由代码里的固定频率决定。
选哪个取决于你手上的数据长什么样。如果你是用手机或工业相机采集的连续帧,带系统时间戳,TUM格式最合适,因为时间戳能反映真实采集节奏,后续跑evo评估轨迹时也能对齐真值。如果你的图片是从视频里抽帧出来的,文件名已经天然有序,或者你根本不在乎时间戳,只想看算法能不能跑通,KITTI格式省事得多。
我自己通常用TUM格式,原因是评估阶段需要时间戳对齐,没有真实采集时间的数据对齐起来非常痛苦。但有个例外:纯做算法验证、不涉及定量评估时,KITTI格式能帮你省掉写associations文件的步骤。两者没有绝对优劣,按你的下游任务取舍。
2.2 从零构造TUM格式数据集:目录结构、时间戳与associations文件
TUM格式的数据集结构虽然简单,但目录组织直接影响调试效率。我习惯这样组织:
dataset/ ├── rgb/ │ ├── 1712376000.000000.png │ ├── 1712376000.033333.png │ └── ... ├── depth/ # 单目模式下可留空 ├── groundtruth.txt # 可选,真值轨迹 └── associations.txt时间戳文件名是TUM格式的灵魂。TUM官方数据集的命名规则是浮点秒.微秒.png,文件名本身就是采集时刻。如果你手上的图片文件名是IMG_20240406_123456.jpg这种,必须重命名。
下面是转换脚本的核心逻辑:
import os import re from datetime import datetime def rename_to_timestamp(src_dir, dst_dir): os.makedirs(dst_dir, exist_ok=True) pattern = re.compile(r'IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})\.jpg') idx = 0 for fname in sorted(os.listdir(src_dir)): m = pattern.match(fname) if not m: continue y, mo, d, h, mi, s = map(int, m.groups()) dt = datetime(y, mo, d, h, mi, s) ts = dt.timestamp() # 保证帧间时间戳单调递增,同秒帧自动加微小偏移 if idx > 0 and ts <= last_ts: ts = last_ts + 0.001 new_name = f"{ts:.6f}.png" # 统一转成png,避免jpg压缩影响特征提取 os.system(f"ffmpeg -i {os.path.join(src_dir, fname)} -q:v 2 {os.path.join(dst_dir, new_name)}") last_ts = ts idx += 1这段脚本做三件事:从文件名解析采集时间并转成Unix时间戳;处理同一秒内多帧的情况,强制时间戳单调递增;通过ffmpeg统一转成png格式。ts <= last_ts的判断是坑点,手机连拍模式下所有照片的时间戳可能完全相同,如果不加偏移,ORB SLAM2读取时会因为时间戳不递增直接卡死。另一个细节是转png而非保留jpg,jpg的有损压缩会在纹理区域引入伪影,ORB特征点提取对这些伪影很敏感,同一场景下特征点数量和稳定性都有可见差异。
生成时间戳文件后,还要写associations.txt。这个文件每行两列,第一列是时间戳,第二列是相对路径。
python3 - << 'EOF' import os rgb_dir = "dataset/rgb" with open("dataset/associations.txt", "w") as f: for fname in sorted(os.listdir(rgb_dir)): if fname.endswith(".png"): ts = fname.replace(".png", "") f.write(f"{ts} rgb/{fname}\n") EOF逻辑很简单,但注意路径分隔符必须用正斜杠,Windows下生成的路径如果带着反斜杠,ORB SLAM2在Linux下读取时会找不到文件。这个细节我踩过不只一次。
2.3 单目相机的去畸变:不做这一步,轨迹精度打折
手机、运动相机、USB摄像头拍出来的原始图都有明显畸变,尤其是画面边缘的直线会弯掉。ORB SLAM2的相机模型假设输入图像已经去畸变,如果你直接喂原始图,算法内部虽然也有畸变参数,但效果远不如预先校正。
去畸变我用OpenCV的实现:
import cv2 import numpy as np import os K = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]]) D = np.array([k1, k2, p1, p2, k3]) src_dir = "dataset/rgb_raw" dst_dir = "dataset/rgb_undistorted" os.makedirs(dst_dir, exist_ok=True) map1, map2 = cv2.initUndistortRectifyMap( K, D, None, K, (width, height), cv2.CV_32FC1 ) for fname in sorted(os.listdir(src_dir)): if not fname.endswith(".png"): continue img = cv2.imread(os.path.join(src_dir, fname)) undist = cv2.remap(img, map1, map2, cv2.INTER_LINEAR) cv2.imwrite(os.path.join(dst_dir, fname), undist)initUndistortRectifyMap预计算两张映射表,之后每帧只做一次remap,速度很快。内参矩阵K和畸变系数D怎么来?用棋盘格标定,或者从相机官方参数文档里抄。如果你用的是手机,很多手机厂商不公开完整畸变参数,这时只能自己标定。我一般用OpenCV的cv2.calibrateCamera配合打印的棋盘格,拍15-20张不同角度的照片就能得到可用参数。
去畸变后注意图像尺寸可能变化,rectify map输出的图像大小要和原图一致,否则后面配置文件的width和height对不上,程序启动时会报尺寸断言错误。
3. 编译与配置:让ORB SLAM2吞下你的数据
3.1 依赖安装的坑:Pangolin、OpenCV、Eigen3版本互锁
ORB SLAM2的编译依赖环环相扣,版本不匹配会生产一连串让人头皮发麻的编译错误。我的安装顺序是:Eigen3 → Pangolin → OpenCV → DBoW2和g2o(源码内嵌,自动编译)。
Eigen3选3.3.x版本,太新的4.x版会导致g2o编译时报Eigen/src/Core/Matrix.h相关错误,太旧的3.2.x又缺少ORB SLAM2用到的部分特性。Pangolin选0.6或0.8,这两个版本都用CMake构建,比较容易过。OpenCV是最大的变量源,3.4.x和4.x版本的API有差异,ORB SLAM2源码是为OpenCV 3.x时代写的,如果用4.x会碰到CV_LOAD_IMAGE_UNCHANGED这类常量被删除的问题。
sudo apt install libeigen3-dev git clone https://github.com/stevenlovegrove/Pangolin.git cd Pangolin && mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j4 && sudo make installPangolin编译时如果报Could NOT find GLEW,说明缺少OpenGL相关的开发包,装一下libglew-dev libglfw3-dev就能过。编译时间有点长,建议留出耐心。
3.2 OpenCV版本适配:少走弯路的一条命令
如果你装的是OpenCV 4.x,直接编译ORB SLAM2大概率会报错。最常见的错误是:
error: ‘CV_LOAD_IMAGE_UNCHANGED’ was not declared in this scope这个常量在OpenCV 4.x中被删除,替换成cv::IMREAD_UNCHANGED。ORB SLAM2源码里多处用到旧常量,最简单的办法是全局替换:
sed -i 's/CV_LOAD_IMAGE_UNCHANGED/cv::IMREAD_UNCHANGED/g' src/*.cc sed -i 's/CV_RGB2GRAY/cv::COLOR_RGB2GRAY/g' src/*.cc sed -i 's/CV_BGR2GRAY/cv::COLOR_BGR2GRAY/g' src/*.cc sed -i 's/CV_GRAY2BGR/cv::COLOR_GRAY2BGR/g' src/*.cc换完之后还可能出现CV_FM_L8之类的常量报错,照葫芦画瓢替换成cv::FM_L8。这些替换不影响算法逻辑,只是API层面的适配。如果你不想折腾,直接装OpenCV 3.4.16也能顺利编译过,但考虑到新项目普遍用4.x,做一次适配更省心。
3.3 单目配置文件的逐项拆解:内参不是随便填的
ORB SLAM2的单目模式跑之前,要改Examples/Monocular/下的yaml配置文件。这份文件决定算法眼里的世界长什么样,填错任何一个数都会让结果失真。
以TUM1.yaml为例,核心参数分为四组:
| 参数 | 含义 | 我的经验值 |
|---|---|---|
Camera.fx, fy, cx, cy | 相机内参(像素) | 必须来自标定或厂商文档,不能拿默认值硬套 |
Camera.k1, k2, p1, p2, k3 | 畸变系数 | 未去畸变图像填标定值;已去畸变图像建议全填0,省得算法二次校正 |
Camera.fps | 采集帧率 | 必须和真实采集帧率一致,影响运动模型的速度估计 |
ORBextractor.nFeatures | 每帧特征点数上限 | 默认1000,纹理稀疏场景调到1500-2000 |
ORBextractor.scaleFactor | 图像金字塔缩放系数 | 默认1.2,图像分辨率低时改1.1能提高远点匹配率 |
Viewer.KeyFrameLineWidth | 可视化线宽 | 只影响显示,不影响精度 |
内参这块最容易翻车。有人拿着官方示例的TUM1.yaml直接跑自己手机拍的图,因为懒得标定。结果轨迹飘得离谱,还以为是算法问题。相机内参不是随便填的,fx和fy差几十像素都会让深度估计严重偏移。
畸变系数的处理有个细节:如果你的图片已经在预处理阶段去畸变,这里填0就行。如果不过预处理想靠算法内部纠偏,算法虽然支持,但效果差不少,而且会在初始化阶段消耗额外计算量。我的做法是预处理阶段去畸变,yaml里全填0,简单直接。
Camera.fps这个参数容易被忽略。它不直接影响每帧处理速度——ORB SLAM2的处理速度由实际输入帧率决定——但会影响匀速运动模型对帧间位移的预测。你采集时是30fps,配置文件里写15,初始化阶段的运动估计就会偏差明显。
4. 把图片序列喂给ORB SLAM2:命令行参数与运行日志解读
4.1 四个启动参数的语义:路径、词汇表、配置文件、数据序列
ORB SLAM2编译好后,启动单目TUM例程的命令是:
./Examples/Monocular/mono_tum \ Vocabulary/ORBvoc.txt \ Examples/Monocular/TUM1.yaml \ /path/to/dataset \ /path/to/dataset/associations.txt四个参数各有各的坑。第一个是词袋模型文件路径,启动时会加载,耗时几秒到十几秒不等,这是正常现象不是卡死。第二个是刚才调好的yaml配置文件路径。第三个是数据集根目录,注意程序会在这个路径下拼接associations.txt里写的相对路径,如果associations里写的是rgb/xxx.png,那此处的路径必须包含rgb目录。第四个参数是associations文件路径,相对路径和绝对路径都行。
运行后终端会出现一堆日志,关键看这几个输出:
ORB-SLAM2 Copyright ... Loading ORB Vocabulary. This could take a while... Vocabulary loaded! Created with frame id: 0 Initialization is not achieved. Retrying...Initialization is not achieved反复出现说明单目初始化还在尝试,正常情况几秒内能成功。如果卡在这个状态超过几十秒,多半是特征点数量太少或图像质量有问题。
4.2 单目模式跑起来的完整流程与输出文件
程序跑通后,可视化窗口会显示当前帧的特征点、地图点和关键帧轨迹。跑完或者按下Ctrl+C退出后,工作目录下会生成:
CameraTrajectory.txt # 每一帧的估计轨迹(含时间戳) KeyFrameTrajectory.txt # 关键帧轨迹 MapPoints.txt # 地图点(含世界坐标、描述子等)这三个文件是后续所有分析的基础。CameraTrajectory.txt每行格式为时间戳 tx ty tz qx qy qz qw,前四维是位置,后四维是四元数姿态。MapPoints.txt的内容更丰富,每行前三位是世界坐标,之后是RGB颜色、描述子索引、观测次数等。
如果要停止跑并保存轨迹,直接Ctrl+C可能不触发保存逻辑。正确做法是让程序自然跑完所有帧序列,或者等待Press q to quit提示后按q。中途强制结束导致轨迹文件缺失或半截,是常见问题。
# 查看轨迹文件的前几行 head -5 KeyFrameTrajectory.txt跑完你会发现,单目SLAM输出的轨迹没有绝对尺度,所有坐标是任意单位下的相对值。这是单目系统的固有限制——尺度不可观。如果你期望轨迹的位移和真实世界米数一致,要么加双目或深度相机,要么事后用已知尺寸的参照物做尺度恢复。
4.3 数据集跑完怎么看结果:轨迹文件与可视化确认
拿到轨迹文件后,第一件事不是急着评估,而是目视检查。用Python简单画一下轨迹形状:
import numpy as np import matplotlib.pyplot as plt traj = np.loadtxt("KeyFrameTrajectory.txt") x, z = traj[:, 1], traj[:, 3] plt.figure(figsize=(8, 8)) plt.plot(x, z, linewidth=1.5) plt.xlabel("X (m)") plt.ylabel("Z (m)") plt.title("Estimated Trajectory Top View") plt.axis("equal") plt.savefig("traj_topview.png")轨迹形状和实际路线对得上,说明系统整体是健康的。如果轨迹在某处突然跳变或画圈,说明那附近的帧有跟踪丢失或错误闭环。这一步先于任何定量分析,能快速暴露明显问题。
5. 避坑:跑自己的数据集,最容易翻车的六个现场
5.1 启动即崩溃:配置文件里的图像尺寸对不上
现象:程序启动后立即报Assertion failed: (mIm.cols == w) && (mIm.rows == h)之类的错误,整个进程退出。
原因:yaml里Camera.width和Camera.height填的和实际图片分辨率不一致。比如你的图是1280x720,配置文件还留着示例的640x480。
解决:改配置文件前先看一眼图片实际尺寸,用python3 -c "import cv2; img=cv2.imread('某张图.png'); print(img.shape)"确认,再把宽高填进去。同时记得畸变参数也要配套,否则即使不崩,轨迹也会飘。
5.2 单目初始化永远不成功:特征点太少或分布太集中
现象:终端持续输出Initialization is not achieved. Retrying...,几十帧过去了还在重试,轨迹一直停在原点附近。
原因:单目初始化需要两帧之间匹配到足够多且分布均匀的特征点。低纹理场景(白墙、天空、光滑地面)特征点提取不到200个,或者所有特征点挤在画面一角,都无法满足基础矩阵计算条件。
解决:三个方向排查。第一,检查图像是否过曝或过暗,ORB特征点对亮度变化敏感,适当调整采集曝光;第二,降低ORB特征点提取阈值,把配置文件里ORBextractor.nFeatures调高到1500-2000;第三,初始化阶段尽量缓慢平移相机而不是原地旋转,旋转会大幅降低特征匹配率。平移几厘米到十几厘米再停下来,初始化成功率显著提升。
5.3 跑到一半丢失跟踪:运动太快或帧率太低
现象:中途日志出现Lost tracking,可视化界面里的点消失,轨迹中断。
原因:ORB SLAM2对帧间运动量有硬性约束。相邻两帧的特征点重叠区域太小,特征匹配失败,系统就认为跟踪丢了。手持设备快速转动、走路时边晃边拍、采集帧率只有10fps,都会触发这个问题。
解决:降低运动速度是第一选择,采集时保持匀速慢速移动;或者提高采集帧率,让帧间位移变小。如果你的序列已经拍完无法重采,就调大ORBextractor.nFeatures和金字塔层数,让算法在更大尺度范围找匹配。但这个方法有上限,帧间重叠太少时调参数也没用。
5.4 时间戳错位导致评估结果一团糟
现象:轨迹跑完用evo做APE评估,误差大得离谱,画出来的时间对齐图完全是乱的。
原因:associations.txt里写的时间戳和真实采集时刻不一致。比如你用ffmpeg从视频抽帧时,文件名是frame_0001.jpg这种序号,你写associations时用了1.0, 2.0, 3.0这类假时间戳,而真值轨迹的时间戳是真实采集时刻,两者完全对不上。
解决:从视频抽帧时保留原始时间戳信息。ffmpeg抽帧时可以输出每帧的PTS:
ffmpeg -i input.mp4 -vsync 0 -f image2 frame_%04d.png ffprobe -select_streams v -show_frames input.mp4 | grep pts_time > timestamps.txt然后用timestamps.txt里的时间戳生成文件名和associations文件。如果实在没有真实时间戳,就别做定量评估,只做定性轨迹观察。
5.5 图片路径带空格或中文导致启动失败
现象:程序启动时报Failed to open file: ...,但路径看起来明明是对的。
原因:数据集所在目录或图片文件名里有空格或中文,ORB SLAM2内部用的文件流库对这类路径支持不完善。
解决:数据集放在纯英文、无空格的路径下,图片文件名保持纯数字加时间戳。很多人从Windows拷数据过来时文件名自带中文或空格,顺手改掉能省大量排查时间。
5.6 特征点全部堆在图像一角:VA直方图均衡化问题
现象:跑出来的轨迹是歪的,画图时看到大部分特征点集中在画面某个区域,地图点分布极不均匀。
原因:手机HDR或自动曝光导致画面明暗分布极端,ORB特征点天然倾向于找亮度梯度大的区域,如果画面一半亮一半暗,亮区边缘的特征点会扎堆。
解决:在预处理阶段做直方图均衡化,让全图光照更均匀。ORB SLAM2配置里本身没有全局直方图均衡项,需要在数据预处理阶段完成。用OpenCV的cv2.equalizeHist对灰度图处理后另存。做这一步后特征点分布会明显散开。
6. 拿轨迹文件做定量验证:evo评估与无真值时的自检技巧
没有验证的SLAM结果不值得信任。轨迹文件生成后,至少要做一次定量评估才能确认数据集的可用性。最实用的工具是evo,Python生态里的SLAM轨迹评估利器,支持ATE、RPE等多种指标。
pip install evo --upgrade --no-binary evo如果你有真值轨迹文件,比如用运动捕捉系统或高精度RTK采集的groundtruth.txt,评估命令很直接:
evo_ape tum KeyFrameTrajectory.txt groundtruth.txt -a -s-a参数做轨迹对齐,-s参数做尺度修正。单目轨迹没有绝对尺度,这两项必须加,否则误差数值没有任何参考意义。输出会给出RMSE、Mean、Max等误差统计。RMSE小于0.1米说明数据集和参数配置都健康,0.1到0.5米说明有轻微漂移但可接受,超过0.5米就要回查预处理或相机参数。
没有真值时,有两个替代自检手段。第一个是检查轨迹的闭合性——你走的是闭合路线,跑完看轨迹终点和起点是否大致重合,偏差越小说明积累漂移越小。第二个是检查MapPoints.txt里地图点的数量和质量:
import numpy as np pts = np.loadtxt("MapPoints.txt") valid = pts[pts[:, 3] > 0] # 第4列是观测次数 print(f"总地图点: {len(pts)}, 有效点: {len(valid)}")有效地图点少于5000个,说明特征提取或匹配太稀疏,后续建图效果会很差。正常室内场景应该在10万量级的原始点,有效点几万。
精度验证之外,还要检查单目轨迹的尺度一致性。选一张已知尺寸的参照物(比如门宽0.9米、走廊长度10米),在重建的地图里量对应距离,和真实值对比。比值就是当前轨迹的尺度因子。如果多次对比的尺度因子各不相同,说明系统不稳定,问题多半出在初始化阶段或相机内参上。从那以后,我每次拿新数据集跑ORB SLAM2,都会强制走一遍完整检查序列:先看轨迹形状,然后对真值算APE,再看特征点分布,最后验证尺度一致性。这套流程能挡住绝大多数低级错误,希望帮到你。
本文还有配套的精品资源,点击获取