保姆级教程:用D435i录制ROS bag文件,一步步转成BundleFusion能吃的.sens格式
从ROS bag到BundleFusion:D435i深度数据转换全流程实战
在三维重建领域,数据格式的转换往往是阻碍算法落地的第一道门槛。当你好不容易用Intel RealSense D435i录制了ROS bag文件,准备在BundleFusion大展身手时,却发现两者使用的数据格式截然不同——这种"语言不通"的情况让许多SLAM初学者束手无策。本文将彻底解决这个痛点,带你完整走通从.bag到.sens的转换之路,让采集的数据真正为重建算法所用。
1. 环境准备与工具链配置
1.1 基础软件栈检查
确保你的系统已安装以下关键组件:
- ROS Melodic(Ubuntu 18.04)或ROS Noetic(Ubuntu 20.04)
- Python 2.7/3.x并安装
cv_bridge、opencv-python等包 - RealSense SDK 2.0及对应的ROS驱动包
- BundleFusion源代码编译环境(CUDA、VS2013/2015)
注意:BundleFusion对CUDA版本有特定要求,建议使用CUDA 8.0或9.0以避免兼容性问题
1.2 关键工具准备
需要特别准备两个核心脚本:
- bag解析脚本:用于提取深度图与RGB图像
- associate.py:时间戳对齐工具(可从TUM数据集工具包获取)
创建如下目录结构便于项目管理:
project_root/ ├── input/ # 存放原始bag文件 ├── extracted/ │ ├── depth/ # 提取的深度图 │ ├── rgb/ # 提取的彩色图 │ ├── stamps/ # 时间戳文件 ├── associated/ # 对齐后的数据 └── output/ # 最终.sens输出2. 深度解析ROS bag文件
2.1 提取图像数据
使用以下Python脚本从bag中提取深度和彩色帧。关键点在于正确识别topic名称——D435i通常使用如下默认topic:
#!/usr/bin/env python import rosbag import cv2 from cv_bridge import CvBridge bag_path = 'input/your_data.bag' depth_topic = '/device_0/sensor_0/Depth_0/image/data' color_topic = '/device_0/sensor_1/Color_0/image/data' bridge = CvBridge() with rosbag.Bag(bag_path, 'r') as bag: for topic, msg, t in bag.read_messages(): if topic == depth_topic: cv_image = bridge.imgmsg_to_cv2(msg) timestamp = "%.6f" % msg.header.stamp.to_sec() cv2.imwrite(f'extracted/depth/{timestamp}.png', cv_image) if topic == color_topic: cv_image = bridge.imgmsg_to_cv2(msg, "bgr8") timestamp = "%.6f" % msg.header.stamp.to_sec() cv2.imwrite(f'extracted/rgb/{timestamp}.jpg', cv_image)2.2 时间戳同步难题破解
由于D435i的深度和彩色传感器物理上分离,两者的时间戳天然不同步。我们使用associate.py进行时间对齐:
python associate.py extracted/stamps/depth.txt extracted/stamps/rgb.txt > associated/associated.txt典型参数调整建议:
--max_difference:默认为0.02秒,室内场景可放宽至0.05--offset:当发现深度总是比彩色快/慢固定时间时可设置
3. 构建BundleFusion输入格式
3.1 文件命名规范转换
BundleFusion要求严格的命名约定:
frame-000000.color.jpg frame-000000.depth.png frame-000000.pose.txt使用以下脚本完成转换:
import shutil import os with open('associated/associated.txt') as f: for i, line in enumerate(f): depth_src, rgb_src = line.split()[1], line.split()[3] # 统一命名 frame_id = str(i).zfill(6) os.rename(depth_src, f'output/frame-{frame_id}.depth.png') os.rename(rgb_src, f'output/frame-{frame_id}.color.jpg') # 生成默认位姿文件 with open(f'output/frame-{frame_id}.pose.txt', 'w') as pose: pose.write("1 0 0 0\n0 1 0 0\n0 0 1 0\n0 0 0 1")3.2 info.txt关键参数配置
这个配置文件决定了数据的元信息,以下是D435i的典型参数:
m_versionNumber = 4 m_sensorName = RealSenseD435i m_colorWidth = 640 m_colorHeight = 480 m_depthWidth = 640 m_depthHeight = 480 m_depthShift = 1000 m_calibrationColorIntrinsic = 616.368 0 319.796 0 0 616.745 243.507 0 0 0 1 0 0 0 0 1 m_calibrationDepthIntrinsic = 386.144 0 320.548 0 0 386.144 247.582 0 0 0 1 0 0 0 0 1 m_frames.size = 1200 # 必须与实际帧数一致提示:使用
rostopic hz命令检查原始bag的帧率,确保与BundleFusion参数匹配
4. 生成.sens格式与质量验证
4.1 使用BundleFusion代码封装
修改BundleFusion的sensorData.cpp,添加以下入口函数:
void convertToSens(const std::string& inputDir, const std::string& outputFile) { SensorData sd; if (!sd.loadFromImages(inputDir, "frame-", "jpg")) { std::cerr << "Failed to load image sequence" << std::endl; return; } sd.saveToFile(outputFile); }编译后运行生成.sens文件,典型问题排查:
- 图像尺寸不匹配:检查depth和color的分辨率是否一致
- 时间戳错乱:重新运行associate.py减小max_difference
- 内存不足:分批处理大数据集
4.2 重建效果优化技巧
根据实际测试经验,提升重建质量的三个关键点:
运动轨迹规划:
- 保持匀速移动(约0.1m/s)
- 采用"蛇形"路径覆盖场景
- 对重点区域进行多次扫描
传感器参数调优:
# Realsense配置建议 config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) config["depth_module"]["visual_preset"] = "High Accuracy"后处理参数调整:
- 修改
zParametersDefault.txt中的voxelSize(建议0.01-0.03) - 调整
maxDepth避免远处噪声干扰
- 修改
5. 高级技巧与自动化方案
对于需要频繁转换的场景,建议建立自动化流程:
#!/bin/bash # 自动化转换脚本示例 python extract_images.py $1 python associate.py depth.txt rgb.txt > associated.txt python rename_frames.py generate_info_txt.py --frames $(wc -l associated.txt) --output info.txt ./build/SensConverter ./output ./result/output.sens常见错误代码速查表:
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 黑屏重建 | 深度值未缩放 | 检查m_depthShift参数 |
| 图像错位 | 时间戳未对齐 | 重新运行associate.py |
| 程序崩溃 | 帧数不符 | 核对info.txt的m_frames.size |
在多次项目实践中发现,转换过程中最易出错的是时间戳同步环节。一个实用的调试技巧是先用前100帧进行快速验证,确认流程无误后再处理完整数据集。
