ORB—SLAM3的运行情况,单目
ORB-SLAM3 是第一个能够使用单目、立体和 RGB-D 相机,采用针孔和鱼眼镜头模型,实现视觉、视觉-惯性以及多地图 SLAM 的实时 SLAM 库。我们提供了一些示例,用于处理使用 ROS 的单目、单目惯性、立体、立体惯性或 RGB-D 相机的输入。以上的所有研究都是基于单目的情况,没有惯性单元的情况,单目是日常生活当中最容易模仿的情形。,该模型假设相机的成像是通过一个小孔投影到图像平面上
ORB-SLAM3 是第一个能够使用单目、立体和 RGB-D 相机,采用针孔和鱼眼镜头模型,实现视觉、视觉-惯性以及多地图 SLAM 的实时 SLAM 库。在所有传感器配置中,ORB-SLAM3 的鲁棒性不亚于文献中可用的最佳系统,并且精度显著更高。
关于这两个模型:
针孔模型:如果使用的是普通的相机(如单目、立体或RGB-D相机),这些相机通常使用的是针孔模型,该模型假设相机的成像是通过一个小孔投影到图像平面上的,适用于大多数标准相机系统。
鱼眼镜头模型:当使用具有鱼眼镜头的相机时(例如超广角相机),则使用鱼眼镜头模型来描述更大视场角和显著的畸变。鱼眼镜头模型能够更好地适应和校正由于镜头畸变带来的影响,通常会用于需要宽视场角的应用场景。
这两个模型是根据所用的相机类型动态选择的,而不是同时运行。换句话说,ORB-SLAM3会根据你选择的相机配置来决定使用针孔模型或鱼眼模型,并不会同时使用这两种模型。
我们已在 Ubuntu 16.04 和 18.04 上测试了该库,但它应该很容易在其他平台上编译。一台强大的计算机(例如 i7)将确保实时性能并提供更稳定、更准确的结果。
这里虽然使用的是Ubuntu16和18测试,但是20情况也可以使用的。
关于ROS
我们提供了一些示例,用于处理使用 ROS 的单目、单目惯性、立体、立体惯性或 RGB-D 相机的输入。构建这些示例是可选的。这些示例已在 Ubuntu 18.04 下的 ROS Melodic 上进行了测试。
使用自己的相机运行代码
使用普通的 USB 相机运行 ORB-SLAM3,主要涉及以下步骤:
标定相机:获得内参文件(your_camera.yaml)。
修改示例代码:适配你的相机。
构建程序:编译并链接程序。
连接并运行:连接相机并运行 SLAM 系统。
标定的过程之前有,这是文件路径
/home/croco/桌面/SLAM3/ORB_SLAM3/Examples/ROS/ORB_SLAM3/Asus.yaml
可能是opencv版本的问题,等后面去实验一下。
在之前slam2的项目当中版本是

成功完成了在SLAM3当中使用相机实时检测:
这个文件是myslam.cpp文件和相关的文件是平行关系。
#include <opencv2/opencv.hpp>
#include "System.h"
#include <string>
#include <chrono>
#include <iostream>
#include <cstdlib> // for system()
using namespace std;
string parameterFile = "/home/croco/桌面/SLAM3/ORB_SLAM3/Examples/Monocular/TUM1.yaml";
string vocFile = "/home/croco/桌面/SLAM3/ORB_SLAM3/Vocabulary/ORBvoc.txt";
string saveDir = "./SLAM_Output"; // 轨迹文件保存路径
int main(int argc, char **argv) {
// 自动创建保存目录(Linux/macOS,兼容C++14)
string mkdirCommand = "mkdir -p " + saveDir;
system(mkdirCommand.c_str());
// 初始化 SLAM 系统
ORB_SLAM3::System SLAM(vocFile, parameterFile, ORB_SLAM3::System::MONOCULAR, true);
cv::VideoCapture cap(0); // 改成你自己的摄像头编号
if (!cap.isOpened()) {
cerr << "无法打开摄像头!" << endl;
return -1;
}
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
auto start = chrono::system_clock::now();
while (1) {
cv::Mat frame;
cap >> frame;
if (frame.empty()) {
cerr << "捕获到空帧!" << endl;
break;
}
auto now = chrono::system_clock::now();
auto timestamp = chrono::duration_cast<chrono::milliseconds>(now - start);
SLAM.TrackMonocular(frame, double(timestamp.count()) / 1000.0);
if (cv::waitKey(1) == 27) break; // ESC 退出
}
// 保存轨迹和关键帧
SLAM.SaveTrajectoryTUM(saveDir + "/Trajectory.txt");
SLAM.SaveKeyFrameTrajectoryTUM(saveDir + "/KeyFrameTrajectory.txt");
SLAM.Shutdown();
cout << "保存完成,文件保存在: " << saveDir << endl;
return 0;
}
使用的yaml文件是系统中TUM数据集的相关文件。当然这个也可以根据自己的相机进行标定。
运行代码之后会生成两个相关文件
使用evo工具可以进行对比
evo_traj tum CameraTrajectory.txt KeyFrameTrajectory.txt -p
这样会生成一个对比图
evo_ape tum CameraTrajectory.txt KeyFrameTrajectory.txt -a -r full --save_results results.zip --plot --plot_mode xyz
生成使用 evo 进行误差评估

#include <opencv2/opencv.hpp>
#include "System.h"
#include <string>
#include <chrono>
#include <iostream>
#include <cstdlib> // for system()
using namespace std;
// 配置路径(请根据你的环境调整)
string parameterFile = "/home/croco/桌面/SLAM3/ORB_SLAM3/Examples/Monocular/TUM1.yaml";
string vocFile = "/home/croco/桌面/SLAM3/ORB_SLAM3/Vocabulary/ORBvoc.txt";
string saveDir = "./SLAM_Output"; // 输出路径
int main(int argc, char **argv) {
// 自动创建输出目录
string mkdirCommand = "mkdir -p " + saveDir;
system(mkdirCommand.c_str());
// 启动 ORB-SLAM3 系统(最后一个 true 表示启用可视化 Viewer)
ORB_SLAM3::System SLAM(vocFile, parameterFile, ORB_SLAM3::System::MONOCULAR, true);
// 打开摄像头(改成你自己的编号)
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "❌ 无法打开摄像头!" << endl;
return -1;
}
// 设置摄像头分辨率
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
auto start = chrono::system_clock::now();
cout << "✅ 启动摄像头,按 ESC 键退出..." << endl;
while (true) {
cv::Mat frame;
cap >> frame;
if (frame.empty()) {
cerr << "⚠️ 捕获到空帧!" << endl;
break;
}
auto now = chrono::system_clock::now();
auto timestamp = chrono::duration_cast<chrono::milliseconds>(now - start);
SLAM.TrackMonocular(frame, double(timestamp.count()) / 1000.0);
// 按 ESC 键退出
if (cv::waitKey(1) == 27) break;
}
// 关闭 SLAM 系统
SLAM.Shutdown();
// 保存轨迹
SLAM.SaveTrajectoryTUM(saveDir + "/Trajectory.txt");
SLAM.SaveKeyFrameTrajectoryTUM(saveDir + "/KeyFrameTrajectory.txt");
cout << "✅ 轨迹保存完成,保存在目录: " << saveDir << endl;
return 0;
}
最后又修改了一下代码


使用摄像头运行完之后我们学习使用自己的视频去运行一下。
以上的所有研究都是基于单目的情况,没有惯性单元的情况,单目是日常生活当中最容易模仿的情形。
evo_traj tum KeyFrameTrajectory.txt MonocularTrajectory.txt --ref=KeyFrameTrajectory.txt --align --plot --plot_mode xyz
这里使用视频

所以SLAM3是没有办法为视频生成相机轨迹文件的。只能模拟一个。

解决办法:

这里写了一个脚本运行一下即可
import cv2
import os
# ==== 参数配置 ====
video_path = 'your_video.mp4' # 替换为你的视频路径
output_dir = 'output_images' # 输出图像目录
timestamp_file = 'times.txt' # 输出时间戳文件名
# ==== 创建输出文件夹 ====
os.makedirs(output_dir, exist_ok=True)
# ==== 打开视频 ====
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f'视频总帧数: {frame_count}, 帧率: {fps} fps')
# ==== 写时间戳文件 ====
with open(os.path.join(output_dir, timestamp_file), 'w') as f:
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
timestamp = frame_idx / fps
filename = f"image_{frame_idx:06d}.png"
filepath = os.path.join(output_dir, filename)
# 保存图像
cv2.imwrite(filepath, frame)
# 写入时间戳
f.write(f"{timestamp:.6f} {filename}\n")
if frame_idx % 50 == 0:
print(f"处理第 {frame_idx} 帧")
frame_idx += 1
cap.release()
print(f"✅ 完成:共导出 {frame_idx} 帧,时间戳保存在 {timestamp_file}")
这样就会跟数据集一样有相关文件了
这里还没有实现。
在重新整理一下数据集的情况。主要现在有三个公开的数据集。
运行双目:
5. Stereo Examples
KITTI Dataset
-
Download the dataset (grayscale images) from The KITTI Vision Benchmark Suite
-
Execute the following command. Change
KITTIX.yamlto KITTI00-02.yaml, KITTI03.yaml or KITTI04-12.yaml for sequence 0 to 2, 3, and 4 to 12 respectively. ChangePATH_TO_DATASET_FOLDERto the uncompressed dataset folder. ChangeSEQUENCE_NUMBERto 00, 01, 02,.., 11.
./Examples/Stereo/stereo_kitti Vocabulary/ORBvoc.txt Examples/Stereo/KITTIX.yaml PATH_TO_DATASET_FOLDER/dataset/sequences/SEQUENCE_NUMBER
EuRoC Dataset
-
Download a sequence (ASL format) from kmavvisualinertialdatasets – ASL Datasets
-
Execute the following first command for V1 and V2 sequences, or the second command for MH sequences. Change PATH_TO_SEQUENCE_FOLDER and SEQUENCE according to the sequence you want to run.
./Examples/Stereo/stereo_euroc Vocabulary/ORBvoc.txt Examples/Stereo/EuRoC.yaml PATH_TO_SEQUENCE/mav0/cam0/data PATH_TO_SEQUENCE/mav0/cam1/data Examples/Stereo/EuRoC_TimeStamps/SEQUENCE.txt
./Examples/Stereo/stereo_euroc Vocabulary/ORBvoc.txt Examples/Stereo/EuRoC.yaml PATH_TO_SEQUENCE/cam0/data PATH_TO_SEQUENCE/cam1/data Examples/Stereo/EuRoC_TimeStamps/SEQUENCE.txt
第二条指令是关于MH系列的,第一条指令是V系列的
使用MH系列做一个例子:
./Examples/Stereo/stereo_euroc ./Vocabulary/ORBvoc.txt ./Examples/Stereo/EuRoC.yaml ../DATASET/MH01 ./Examples/Stereo/EuRoC_TimeStamps/MH01.txt MH01

时间戳转换代码:
import csv
input_path = 'MH01/state_groundtruth_estimate0/data.csv'
output_path = 'MH01_groundtruth_tum.txt'
with open(input_path, 'r') as infile, open(output_path, 'w') as outfile:
reader = csv.reader(infile)
next(reader) # 跳过表头
for row in reader:
if row[0].startswith('#') or len(row) < 8:
continue
timestamp_ns = int(row[0])
timestamp = timestamp_ns * 1e-9
tx, ty, tz = row[1], row[2], row[3]
qw, qx, qy, qz = row[4], row[5], row[6], row[7]
outfile.write(f"{timestamp:.9f} {tx} {ty} {tz} {qx} {qy} {qz} {qw}\n")
print(f"✅ Ground truth 已保存为: {output_path}")
这样就可以转换然后去对比了
使用相关工具去展示
evo_traj tum f_MH01.txt kf_MH01.txt MH01_groundtruth_tum.txt -p
结果如下:

发现初始坐标系不一样:
查看错误
found no matching timestamps between MH01_groundtruth_tum.txt and f_MH01.txt with max. time diff 0.01 (s)
报错截图:
这个错误是时间戳文件对不上,所以放大时间容忍度:
evo_ape tum MH01_groundtruth_tum.txt f_MH01.txt -a --align --correct_scale --t_max_diff 0.1 -p
还是不行,我们来检查一下相关的文件,发现时间戳文件的单位是不一样的,使用转换代码:
# === 文件路径配置 ===
input_file = 'MH01_groundtruth_tum.txt' # 原始 groundtruth 文件(单位:秒)
output_file = 'MH01_groundtruth_ns.txt' # 输出文件(单位:纳秒)
# === 开始转换 ===
with open(input_file, 'r') as fin, open(output_file, 'w') as fout:
for line in fin:
parts = line.strip().split()
if len(parts) < 8:
continue # 跳过格式错误的行
try:
# 秒 → 纳秒
timestamp_sec = float(parts[0])
timestamp_ns = int(timestamp_sec * 1e9)
# 其余 7 个数为位置 + 四元数
rest = ' '.join(parts[1:])
# 写入转换后的行
fout.write(f"{timestamp_ns} {rest}\n")
except ValueError:
print(f"⚠️ 跳过无法解析的行: {line.strip()}")
然后进行了转换,单位从秒变成了纳秒不会出现报错了,但是好像世界坐标系还是不统一导致出现的图像还是没有重合。

查阅相关资料后发现:要有一个对齐的参数使用才可以。
evo_traj tum --ref=MH01_groundtruth_ns.txt f_MH01.txt -p -a
evo_traj tum --ref=f_MH01.txt kf_MH01.txt MH01_groundtruth_ns.txt -p -a
如果是单目还要加
evo_traj tum --ref=f_MH01.txt kf_MH01.txt MH01_groundtruth_ns.txt -p -a -s
执行后得到如下:正确的展示了。

下面是evo这个工具的详细使用:
使用evo工具评估ORB_SLAM2在TUM数据集上的运行轨迹-CSDN博客
如果想要在平面展示
evo_traj tum --ref=f_MH01.txt kf_MH01.txt MH01_groundtruth_ns.txt -p -as --plot_mode=xz

evo_traj tum --ref=f_MH01.txt kf_MH01.txt MH01_groundtruth_ns.txt -p -as --plot_mode=xy

这是比较通用的方式。


evo_ape tum MH01_groundtruth_ns.txt f_MH01.txt -a --align --correct_scale --t_max_diff 0.1 -p
这会输出:


通过 evo_ape 得到的 APE(Absolute Pose Error)统计结果,反映了 ORB-SLAM3 输出轨迹与 Ground Truth 轨迹之间的误差分布情况


相似的还有相对轨迹误差
evo_rpe tum MH01_groundtruth_ns.txt f_MH01.txt -a --align --correct_scale --t_max_diff 0.1 -p


更多推荐



所有评论(0)