简介:本资源是面向SLAM算法研究者与自动驾驶方向开发者的Kitti数据集专用LIO-SAM改进版本,解决原始LIO-SAM在Kitti真实城市场景中因传感器标定差异、点云密度变化及IMU同步偏差导致的建图漂移与定位不稳定问题。压缩包共44个文件,含5个launch启动脚本(适配Kitti序列加载)、4个核心cpp源码(含LiDAR-IMU时间对齐与特征提取优化模块)、3个conf/yaml配置文件(预设Kitti雷达型号与运动先验参数)、8个png/gif可视化结果图(含轨迹对比与地图重建效果),以及Dockerfile、rviz配置与完整README说明文档,整体76.06MB。已有1059人学习下载,提供可直接编译运行的完整工程结构(MY-LIO-SAM-main为主目录),涵盖数据预处理接口、城市道路特征匹配策略改进、EKF滤波器参数调优方案及标准Kitti序列(如00/05/10)的评估结果对比,助读者快速复现、调试并深入理解多传感器融合SLAM在真实数据上的适配逻辑。
1. 项目概述:当LIO-SAM遇上KITTI
如果你正在做自动驾驶或者机器人定位与建图(SLAM)的研究,那么“LIO-SAM”和“KITTI”这两个词对你来说一定不陌生。LIO-SAM是一个基于因子图优化的紧耦合激光惯性里程计框架,以其在复杂场景下的高精度和鲁棒性而闻名。而KITTI数据集,则是这个领域里最经典、最权威的公开基准测试集之一,无数算法都在它的排行榜上证明过自己。但当你兴冲冲地下载了LIO-SAM的代码,准备用KITTI数据来跑一跑,验证一下效果时,很可能会发现程序直接报错,或者跑出来的轨迹和真值对不上。这就像你拿到了一把精密的瑞士军刀(LIO-SAM),却发现它没法直接开你手边这个特定牌子的罐头(KITTI数据集)。
这个名为“为适配kitti数据集修改的lio-sam.zip”的项目,就是为了解决这个“刀”与“罐头”不匹配的问题。它不是一个全新的轮子,而是对原始LIO-SAM仓库进行了一系列针对性的“外科手术式”修改,使其能够无缝读取、解析并处理KITTI数据集提供的传感器数据格式,最终输出可与KITTI官方真值进行对比的轨迹结果。对于研究者而言,这意味着你可以快速在公认的基准上评估LIO-SAM的性能;对于学习者来说,这提供了一个绝佳的、可运行的实例,让你能深入理解激光-惯性里程计如何处理真实世界中的标准化数据。接下来,我将详细拆解这个适配工作的核心思路、具体修改点、实操步骤以及我踩过的那些坑。
2. 核心需求与适配思路拆解
2.1 原始LIO-SAM与KITTI的“代沟”
要理解为什么需要修改,首先得看清两者之间的不匹配点。原始的LIO-SAM代码仓库预设了特定的数据输入接口,主要体现在以下几个方面:
- 传感器话题与消息类型:LIO-SAM默认订阅ROS(Robot Operating System)中的特定话题,例如
/velodyne_points(激光雷达点云)和/imu/data(IMU数据)。它期望接收的是ROS标准消息类型sensor_msgs/PointCloud2和sensor_msgs/Imu。而KITTI数据集提供的是.bin格式的激光雷达文件和.txt格式的IMU/Odometry数据,并非ROS消息。 - 坐标系与时间戳:LIO-SAM内部有复杂的坐标系变换树(TF tree),包括
map,odom,base_link,velodyne,imu_link等。它假设传感器之间的外参(Extrinsics)是已知并通过参数文件配置的。KITTI数据集虽然提供了标定文件,但其坐标系定义(例如相机坐标系cam0)和LIO-SAM的预设可能不同。此外,KITTI数据的时间戳是独立的文件,需要被正确解析并赋予到每条数据上,以维持时间同步,这是多传感器融合的命脉。 - 数据播放与同步:LIO-SAM是一个在线系统,设计为实时处理ROS话题流。我们需要一个“播放器”来将KITTI的离线数据,按照正确的时间顺序,以ROS话题的形式“模拟实时”地发布出来。这个播放器还需要处理不同传感器数据流之间的时间同步问题。
- 真值格式与评估:KITTI提供
poses.txt作为真值轨迹(以第一个相机坐标系为参考)。我们需要将LIO-SAM输出的轨迹(通常是odom坐标系下的位姿)转换到与真值相同的坐标系下,才能进行ATE(绝对轨迹误差)等定量评估。原始LIO-SAM不包含这个评估流程。
2.2 适配工作的整体架构
基于以上分析,适配工作的核心思路可以概括为“搭建一座桥”,这座桥由几个关键组件构成:
- KITTI数据转ROS包工具:我们需要一个工具,将原始的KITTI数据集(
data_odometry_velodyne和data_odometry_calib)转换为ROS支持的.bag文件。这样,我们就可以使用rosbag play命令来“回放”数据,模拟在线传感器输入。常用的工具是kitti2bag。 - 修改LIO-SAM的启动与参数配置:
- 话题重映射:修改LIO-SAM的启动文件(
.launch),使其订阅的话题名称与kitti2bag生成的话题名称一致。例如,KITTI的激光雷达数据可能发布在/kitti/velo/pointcloud。 - 参数调整:根据KITTI传感器(Velodyne HDL-64E激光雷达和OXTS IMU/GPS)的实际参数,调整LIO-SAM配置文件中的关键参数。这包括:
pointCloudTopic: 激光雷达点云话题名。imuTopic: IMU话题名。extrinsicTrans,extrinsicRot: 激光雷达到IMU的外参矩阵(需要从KITTI标定文件计算得出)。imuAccNoise,imuGyrNoise,imuAccBiasN,imuGyrBiasN: IMU噪声参数,这些参数对滤波和优化效果影响巨大,通常需要根据传感器手册或经验调整。timeOffset: 如果发现激光雷达和IMU时间戳存在固定偏移,需要在此处补偿。
- 话题重映射:修改LIO-SAM的启动文件(
- 坐标系对齐与轨迹输出:修改LIO-SAM的代码,使其在保存轨迹时,能够输出与KITTI真值坐标系对齐的位姿。这通常涉及在因子图优化后,将位姿从
odom或map坐标系转换到第一个激光雷达帧(或第一个相机帧)坐标系。同时,需要将位姿保存为KITTIposes.txt格式(每行12个数字,代表3x4的变换矩阵的前三行)。 - (可选)轨迹评估脚本:编写或集成一个Python脚本,用于读取LIO-SAM输出的轨迹文件和KITTI的真值文件,计算ATE、RPE等指标,并绘制轨迹对比图。
这个“为适配kitti数据集修改的lio-sam.zip”项目,本质上就是完成了上述第2步和第3步的核心修改,并可能提供了第4步的脚本,打包成了一个开箱即用的版本。
3. 关键修改点与代码解析
拿到一个适配好的lio-sam.zip,我们不应该只满足于能跑起来,更要理解作者改了哪里,为什么这么改。这里我结合自己的经验,剖析几个最关键的修改部位。
3.1 启动文件与参数配置的调整
通常,修改会从config/params.yaml和launch/module.launch开始。
params.yaml关键参数修改示例:
# 原始LIO-SAM可能为 pointCloudTopic: “/velodyne_points” imuTopic: “/imu/data” # 适配KITTI后需改为(具体话题名取决于kitti2bag的版本和设置) pointCloudTopic: “/kitti/velo/pointcloud” imuTopic: “/kitti/oxts/imu” # 外参参数 - 这是重中之重! # KITTI的标定文件提供了相机0到相机N,以及相机0到Velodyne的变换矩阵。 # LIO-SAM需要的是Velodyne到IMU的变换。但KITTI的IMU/GPS数据是OXTS单元,其坐标系与相机0有固定的变换关系(通常记录在calib.txt中)。 # 假设我们从标定文件中得到了从Velodyne到相机0的变换矩阵 T_v2c0,以及从相机0到IMU(OXTS)的变换矩阵 T_c02i。 # 那么 Velodyne 到 IMU 的变换为:T_v2i = T_c02i * T_v2c0 # 需要将这个矩阵的平移和旋转部分分解,填入以下参数: extrinsicTrans: [x, y, z] # T_v2i 的平移部分 extrinsicRot: [r11, r12, r13, r21, r22, r23, r31, r32, r33] # T_v2i 的旋转矩阵(行优先展开)注意:外参的计算很容易出错。务必仔细阅读KITTI的
readme.txt,理解每个标定矩阵的含义(是从目标坐标系到源坐标系,还是反过来)。一个常见的错误是矩阵乘法顺序或求逆搞反,这会导致建图完全错乱。我个人的习惯是,先用一个小脚本,用标定数据验证一下变换链是否正确,比如将一个在激光雷达坐标系下的点,通过变换链转换到IMU坐标系,看看是否合理。
module.launch修改:这里主要是确保所有节点订阅的话题与params.yaml中的设置,以及实际rosbag发布的话题一致。有时kitti2bag生成的话题带有命名空间,比如/kitti/velo/pointcloud,那么launch文件中对应的<remap>标签就需要做相应调整。
3.2 核心代码修改:轨迹输出与坐标系对齐
这是适配工作的核心技术部分,通常需要修改src/mapOptmization.cpp或相关的保存轨迹的代码段。
原始LIO-SAM的输出:可能是在map或odom坐标系下的位姿,保存格式可能是TUM(时间戳 + 位置 + 四元数)或自定义格式。
目标:输出在KITTI第一个相机(或第一个激光雷达帧)坐标系下的位姿,格式为poses.txt(每行12个值,代表3x4变换矩阵的前三行,即R|t)。
修改逻辑:
- 获取初始变换:在程序开始时(例如第一帧激光雷达数据到来时),记录下此时
odom(或map)坐标系到base_link(或velodyne)坐标系的变换T_odom2velo_init。这个变换可以通过查询TF树获得。 - 定义目标坐标系:我们的目标是将所有位姿表示在第一个激光雷达帧坐标系下。设第一个激光雷达帧为
velo_init。 - 在线计算与保存:对于后续优化得到的每一个关键帧位姿
T_odom2velo_current,我们计算它相对于初始帧的变换:T_velo_init2velo_current = T_odom2velo_current * inverse(T_odom2velo_init)这个T_velo_init2velo_current就是当前帧在初始激光雷达坐标系下的位姿。 - 格式转换与输出:将
T_velo_init2velo_current这个4x4齐次变换矩阵的前三行(3x4,即旋转矩阵R和平移向量t)按行优先顺序写入文件。同时,KITTI的poses.txt通常不需要时间戳,但我们可以选择同时保存时间戳以备其他用途。
代码片段示意(关键思想):
// 假设在某个回调函数或保存函数中 // 获取当前关键帧在odom下的位姿 (来自因子图优化结果) Eigen::Isometry3d pose_odom2velo_current = ...; // 计算相对于初始帧的位姿 Eigen::Isometry3d pose_velo_init2velo_current = pose_odom2velo_current * pose_odom2velo_init.inverse(); // 提取旋转矩阵和平移向量 Eigen::Matrix3d R = pose_velo_init2velo_current.rotation(); Eigen::Vector3d t = pose_velo_init2velo_current.translation(); // 按KITTI格式写入文件 (行优先,3x4矩阵) std::ofstream out_file(“trajectory_kitti.txt”, std::ios::app); out_file << R(0,0) << “ “ << R(0,1) << “ “ << R(0,2) << “ “ << t(0) << “ “ << R(1,0) << “ “ << R(1,1) << “ “ << R(1,2) << “ “ << t(1) << “ “ << R(2,0) << “ “ << R(2,1) << “ “ << R(2,2) << “ “ << t(2) << std::endl;实操心得:这里有一个细节需要注意,就是
T_odom2velo_init的获取时机。必须在LIO-SAM完成初始化(例如IMU预积分稳定、收到第一帧有效的激光雷达数据并完成初始化位姿估计)之后获取,否则初始位姿不准,会导致整个轨迹发生旋转或平移。我通常会在收到第一个“关键帧”并成功插入到因子图之后,才记录这个初始变换。
3.3 时间同步与传感器频率处理
KITTI数据集中,激光雷达频率是10Hz,而OXTS IMU/GPS数据频率是100Hz。在rosbag play时,如果播放速度过快,可能会导致LIO-SAM的缓存队列溢出或处理不及时。如果播放速度设为真实时间,那么处理速度会很慢。
常见处理方式:
- 使用
--clock和-r参数:rosbag play --clock -r 2 kitti.bag。--clock发布模拟时间,-r 2表示以2倍速播放。这可以加快处理速度,同时保持传感器数据间的时间关系。 - 调整LIO-SAM队列大小:如果出现数据丢失警告,可以适当增大
params.yaml中的imuQueueSize和pointCloudQueueSize。 - 检查时间戳:确保
kitti2bag正确地将KITTI的时间戳(通常是微秒或纳秒)转换成了ROS时间戳。有时时间戳错误会导致TF查找失败。可以在rviz中查看/tf和/tf_static来诊断。
4. 完整实操流程:从数据准备到轨迹评估
假设你已经有一个修改好的lio-sam-kitti.zip,以下是详细的复现步骤。
4.1 环境准备与依赖安装
- 系统与ROS:推荐Ubuntu 18.04 + ROS Melodic,或Ubuntu 20.04 + ROS Noetic。确保ROS基础环境安装完整。
- 安装LIO-SAM依赖:
sudo apt-get install -y ros-$ROS_DISTRO-navigation ros-$ROS_DISTRO-robot-localization ros-$ROS_DISTRO-robot-state-publisher sudo apt-get install -y libsuitesparse-dev libpcl-dev libeigen3-dev - 安装
kitti2bag:pip install kitti2bag # 如果pip安装失败,可以尝试从源码安装 # git clone https://github.com/tomas789/kitti2bag.git # cd kitti2bag && pip install -e . - 编译修改后的LIO-SAM:
unzip lio-sam-kitti.zip cd lio-sam-kitti mkdir build && cd build cmake .. make -j4 # 根据你的CPU核心数调整
4.2 KITTI数据下载与转换
- 下载KITTI Odometry数据集:从KITTI官网下载
data_odometry_velodyne.zip(激光雷达数据)和data_odometry_calib.zip(标定文件)。选择你想要的序列,比如序列00。 - 解压并组织数据:解压后,目录结构应类似:
kitti_data/ ├── dataset/ │ └── sequences/ │ └── 00/ │ ├── velodyne/ # .bin文件 │ ├── calib.txt │ └── times.txt - 转换为ROS Bag:
这个命令会生成一个名为cd path/to/kitti_data kitti2bag -t 2011_10_03 -r 00 raw_synced .kitti_2011_10_03_drive_00xx_synced.bag的文件。-t指定日期,-r指定行程号,raw_synced是数据类型,.表示在当前目录查找数据集。注意:kitti2bag对目录结构有严格要求,务必确保calib.txt和times.txt在序列文件夹内。
4.3 运行LIO-SAM处理Bag文件
- 启动ROS核心:
roscore - 在新的终端中启动LIO-SAM:
确保launch文件中的参数路径正确,特别是cd lio-sam-kitti source devel/setup.bash roslaunch lio_sam run.launchparams.yaml。 - 播放KITTI Bag文件:
使用cd path/to/your/bag rosbag play --clock -r 2 kitti_2011_10_03_drive_00xx_synced.bag--clock发布模拟时间至关重要,这样LIO-SAM才能使用bag文件中的时间戳。-r 2是2倍速,可以根据你的电脑性能调整。 - 观察与记录:
- 打开
rviz,添加PointCloud2显示,话题选择/lio_sam/feature/cloud_full或/lio_sam/mapping/odometry,可以看到实时建图效果。 - LIO-SAM会在指定目录(通常在
~/catkin_ws/src/LIO-SAM/下)输出轨迹文件,如trajectory_kitti.txt。
- 打开
4.4 轨迹评估与可视化
- 安装评估工具:推荐使用
evo,一个强大的轨迹评估工具。pip install evo --upgrade --no-binary evo - 准备真值文件:KITTI Odometry数据集的
poses文件夹下提供了00.txt等真值文件。每行已经是3x4的变换矩阵。 - 使用
evo进行评估:- 格式转换:如果LIO-SAM输出的格式不是标准的
tum或kitti,可能需要写个小脚本转换一下。假设输出已经是KITTI格式。 - 计算绝对轨迹误差(ATE):
这个命令会计算ATE,并给出最大、最小、均方根误差等统计量,同时生成轨迹对比图和误差图。evo_ape kitti ground_truth_00.txt lio_sam_trajectory.txt -va --plot --plot_mode xz - 计算相对位姿误差(RPE):
RPE反映了局部精度,对于里程计来说也很重要。evo_rpe kitti ground_truth_00.txt lio_sam_trajectory.txt -va --plot --plot_mode xz
- 格式转换:如果LIO-SAM输出的格式不是标准的
- 结果分析:观察生成的图表。理想情况下,两条轨迹应该基本重合。如果出现明显的漂移、旋转或尺度错误,就需要回头检查外参、时间同步或IMU参数。
5. 常见问题排查与调优心得
在实际操作中,你几乎一定会遇到各种问题。下面是我总结的一些典型问题及其解决方法。
5.1 问题排查速查表
| 问题现象 | 可能原因 | 排查步骤与解决方法 |
|---|---|---|
| 启动LIO-SAM后无点云显示 | 1. 话题不匹配。 2. Bag文件未播放或播放错误。 3. TF树断裂。 | 1. 使用rostopic list确认Bag发布的话题名,与params.yaml中的pointCloudTopic对比。2. 使用`rostopic echo /kitti/velo/pointcloud |
| 轨迹严重漂移或旋转 | 1. 激光雷达-IMU外参错误。 2. IMU噪声参数设置不当。 3. 时间戳不同步。 | 1.重点检查:重新计算extrinsicTrans和extrinsicRot,用可视化工具验证(如将点云用外参变换后叠加)。2. 调整 imuAccNoise,imuGyrNoise等参数,尝试将噪声值适当调大(更保守的滤波)。3. 检查 times.txt和IMU数据时间戳,确保rosbag play使用了--clock。 |
| 程序运行缓慢或卡顿 | 1. 点云过于稠密。 2. 回环检测计算量大。 3. 系统资源不足。 | 1. 在params.yaml中启用downsampleRate,对输入点云进行降采样。2. 增大 keyframeResolution(关键帧距离阈值),减少关键帧数量。3. 关闭 rviz或降低其刷新率。考虑使用-r参数降低bag播放速度。 |
| 保存的轨迹文件为空 | 1. 轨迹保存路径错误或不可写。 2. 保存轨迹的代码逻辑未触发。 | 1. 检查代码中指定的保存路径,确保有写入权限。 2. 在保存轨迹的代码处添加打印信息,确认程序执行到了那里。检查是否有关键帧成功生成的标志。 |
evo评估时报格式错误 | 1. 轨迹文件格式与evo命令指定的格式不匹配。2. 文件中有空行或格式错误。 | 1. 确认evo_ape kitti ...中的kitti格式是否正确。用evo_traj先查看轨迹:evo_traj kitti your_file.txt --plot。2. 检查轨迹文件,确保每行都有正确数量的数值,且为纯数字。 |
5.2 参数调优经验分享
LIO-SAM的性能很大程度上依赖于参数配置。对于KITTI数据集,以下参数需要特别关注:
imuAccNoise和imuGyrNoise:这是IMU的随机游走噪声。KITTI使用的OXTS IMU精度相对较高。如果轨迹出现“抖动”或高频噪声,可以尝试稍微增大这些值(例如从0.01调到0.02),让滤波器更信任激光雷达。如果轨迹过于平滑但延迟大,可以尝试调小。imuAccBiasN和imuGyrBiasN:这是IMU偏置的随机游走噪声。通常保持默认值即可,除非你观察到明显的偏置不稳定。edgeFeatureMinValidNum和surfFeatureMinValidNum:提取特征点的最小数量。对于Velodyne 64线雷达,点云很稠密,可以适当提高这些值(如从10调到30),以提取更稳定、更少的特征,提高计算速度和鲁棒性。loopClosureFrequency:回环检测频率。KITTI序列较长,开启回环对消除累积漂移至关重要。但回环检测很耗时。可以设置为1.0(1Hz)或更低,在精度和速度间取得平衡。keyframeResolution:关键帧距离阈值。设置为1.0或2.0(米)对于KITTI的城市场景通常比较合适。太小会导致关键帧过多,计算量剧增;太大会导致匹配约束不足,精度下降。
一个实用的调优流程:先用序列00(城市场景,有回环)进行调试。首先保证外参绝对正确(这是基础)。然后关闭回环,只调里程计部分的参数(IMU噪声、特征提取),使开环轨迹的形状基本正确。最后打开回环,调整回环相关参数,观察轨迹末端是否能正确闭合。
5.3 关于“KITTI 3D专用迷你数据集”和“KITTI转nuScenes”
- KITTI 3D专用迷你数据集:这通常指的是从完整的KITTI 3D Object Detection或Tracking数据集中提取的小规模子集,用于快速算法验证。在LIO-SAM的适配中,我们主要使用Odometry数据集。但如果你手头只有这种迷你检测数据集,需要注意它可能不包含连续的全序列点云和IMU数据,可能无法直接用于里程计任务。
- KITTI转nuScenes:nuScenes是另一个大型自动驾驶数据集,其数据格式(特别是标注和传感器布局)与KITTI不同。存在一些工具可以将KITTI格式的数据(主要是检测框)转换到nuScenes格式,以便在nuScenes的评估框架下测试。但这与LIO-SAM的适配是两回事。LIO-SAM适配关注的是原始传感器数据(点云、IMU)的读取和预处理,而不是高层级的标注信息。所以“KITTI转nuScenes”这个热词,对于本项目而言,更可能是一个平行的数据格式转换工具领域,除非你需要在一个统一的nuScenes格式管道中处理KITTI的传感器数据流,那将涉及更底层的数据驱动层改写。
最后,我想说的是,成功适配并跑通LIO-SAM on KITTI,只是一个开始。通过这个过程,你不仅得到了一个可用的工具,更重要的是深入理解了激光-惯性里程计系统如何与具体的数据格式、坐标系、时间戳打交道。这些经验对于你将来处理其他传感器、其他数据集,甚至调试自己SLAM系统中的类似问题,都是极其宝贵的。当你看到evo输出的ATE曲线完美贴合,两条轨迹几乎重叠时,那种成就感就是对所有调试工作最好的回报。如果在操作中遇到任何问题,不妨多看看终端输出的INFO和WARN信息,那往往是解决问题的第一线索。
本文还有配套的精品资源,点击获取