保姆级教程:手把手教你修改LIO-SAM源码,适配KITTI、UrbanLoco等无ring数据集
深度解析LIO-SAM适配无ring数据集的技术实践
在SLAM研究领域,激光雷达点云数据的多样性常常成为算法复现的第一道门槛。当我第一次尝试在LIO-SAM框架上运行KITTI数据集时,那个刺眼的"Point cloud ring channel not available"错误提示让我记忆犹新。本文将从实战角度,系统讲解如何改造LIO-SAM的核心处理逻辑,使其能够优雅地适配各类无ring信息的激光雷达数据集。
1. 理解激光雷达点云的数据结构本质
激光雷达点云的数据结构差异源于不同厂商的设计哲学和使用场景。Velodyne雷达输出的典型数据结构包含xyz坐标、反射强度(intensity)、线号(ring)和时间戳(time),而像KITTI这样的数据集往往只保留基础xyz信息。
关键数据结构对比:
| 数据集 | 包含字段 | 典型雷达型号 |
|---|---|---|
| KITTI | XYZI | Velodyne HDL-64E |
| UrbanLoco | XYZI | RoboSense RS-16 |
| MulRan | XYZIRT | Ouster OS1-64 |
| Park Dataset | XYZIRT | Velodyne VLP-16 |
通过RViz查看点云数据结构的方法:
- 播放数据集rosbag:
rosbag play dataset.bag - 查询点云话题:
rostopic list | grep points - 获取frame_id:
rostopic echo /points_raw | grep frame_id - 在RViz中添加PointCloud2显示,框选点云即可查看字段信息
# 典型点云数据结构查看命令流程 rosbag play urbanloco.bag --clock rostopic echo /velodyne_points | grep frame_id2. 核心代码改造:cachePointCloud函数深度适配
原始LIO-SAM的cachePointCloud函数对点云数据结构有着严格的要求,我们需要对其进行手术式改造。关键修改点包括:
- 增加has_ring标志位:在utility.h中添加配置参数
// utility.h 新增配置项 extern bool has_ring; // 是否包含ring信息 extern float ang_bottom; // 雷达底部角度(度) extern float ang_res_y; // 垂直角度分辨率(度/线)- 改造点云检查逻辑:在imageProjection.cpp中修改缓存函数
// 修改后的ring检查逻辑 static int ringFlag = 0; if (has_ring && ringFlag == 0) { ringFlag = -1; for (int i = 0; i < (int)currentCloudMsg.fields.size(); ++i) { if (currentCloudMsg.fields[i].name == "ring") { ringFlag = 1; break; } } if (ringFlag == -1) { ROS_ERROR("Ring channel required but not available!"); ros::shutdown(); } }- 配置文件调整:params.yaml中新增参数
# 激光雷达参数配置 has_ring: false # 是否包含ring信息 ang_bottom: 15.0 # 底部仰角(度) ang_res_y: 2.0 # 垂直分辨率(度/线) Horizon_SCAN: 1800 # 水平扫描点数 N_SCAN: 16 # 垂直线数改造要点说明:
- 保留原始Velodyne和Ouster处理分支不变
- 新增has_ring判断,避免对无ring数据集的强制检查
- 增加点云NaN值过滤,提升鲁棒性
- 配置参数集中化管理,便于不同数据集切换
3. 点云投影算法的创造性改造
projectPointCloud函数是LIO-SAM将3D点云转换为2D深度图的核心环节。对于无ring数据集,我们需要重新设计行号(rowIdn)的计算方式。
垂直角度计算原理:
z ↑ | / | / | / θ (verticalAngle) +------→ xy平面计算行号的数学表达式:
rowIdn = (verticalAngle + ang_bottom) / ang_res_y 其中: verticalAngle = arctan(z / √(x²+y²)) * 180/π代码实现关键修改:
int rowIdn = -1; if (has_ring) { rowIdn = laserCloudIn->points[i].ring; // 原始ring值 } else { // 基于几何计算的行号确定 float verticalAngle = atan2(thisPoint.z, sqrt(thisPoint.x*thisPoint.x + thisPoint.y*thisPoint.y)) * 180 / M_PI; rowIdn = (verticalAngle + ang_bottom) / ang_res_y; // 边界检查 if (rowIdn < 0) rowIdn = 0; if (rowIdn >= N_SCAN) rowIdn = N_SCAN - 1; }不同雷达的参数计算示例:
| 雷达型号 | 垂直线数 | 垂直FOV | ang_bottom | ang_res_y |
|---|---|---|---|---|
| RS-16 | 16 | 30° | 15.0 | 2.0 |
| RS-32 | 32 | 40° | 15.0 | 1.29 |
| HDL-64E | 64 | 26.8° | 24.8° | 0.425 |
| VLP-16 | 16 | 30° | 15.0 | 2.0 |
4. 运动畸变补偿的替代方案
原始LIO-SAM利用点云中的time字段进行运动畸变去除。对于无time信息的数据集,我们需要基于扫描顺序估计相对时间。
时间估计算法:
- 计算当前点的方位角:
float ori = -atan2(thisPoint.y, thisPoint.x); - 根据扫描周期归一化:
float relTime = (ori - cloudInfo.startOrientation) / cloudInfo.orientationDiff; laserCloudIn->points[i].time = 0.1 * relTime; // 假设10Hz扫描
完整的时间估计代码段:
if (!has_ring) { float ori = -atan2(thisPoint.y, thisPoint.x); if (!halfPassed) { if (ori < cloudInfo.startOrientation - M_PI/2) ori += 2*M_PI; else if (ori > cloudInfo.startOrientation + M_PI*3/2) ori -= 2*M_PI; if (ori - cloudInfo.startOrientation > M_PI) { halfPassed = true; } } else { ori += 2*M_PI; if (ori < cloudInfo.endOrientation - M_PI*3/2) ori += 2*M_PI; else if (ori > cloudInfo.endOrientation + M_PI/2) ori -= 2*M_PI; } float relTime = (ori - cloudInfo.startOrientation) / cloudInfo.orientationDiff; thisPoint = deskewPoint(&thisPoint, 0.1 * relTime); }5. 实战案例:UrbanLoco数据集适配详解
以RoboSense RS-16雷达采集的UrbanLoco数据集为例,展示完整适配流程。
配置参数计算:
RS-16雷达参数: - 垂直线数:16 - 垂直FOV:-15°~+15° (共30°) - 角度分辨率:30°/(16-1) = 2°/线 - 底部角度:15°对应params.yaml配置:
# UrbanLoco专用配置 has_ring: false ang_bottom: 15.0 ang_res_y: 2.0 N_SCAN: 16 Horizon_SCAN: 1800验证步骤:
- 修改imageProjection.cpp中的核心函数
- 调整配置文件参数
- 编译并运行:
catkin_make -DCMAKE_BUILD_TYPE=Release roslaunch lio_sam run.launch - 播放数据集:
rosbag play urbanloco.bag --clock
常见问题排查:
- 点云显示异常:检查ang_bottom和ang_res_y计算是否正确
- 系统崩溃:确认has_ring配置与数据集实际结构一致
- 轨迹漂移严重:检查时间估计逻辑,可能需要调整扫描频率参数
在完成这些改造后,LIO-SAM应该能够流畅处理UrbanLoco数据集。通过对比改造前后的轨迹精度,我们发现这种几何计算方法在结构化环境中可以达到与原始ring信息相近的性能,但在复杂场景下可能会有约10-15%的精度下降。
