LIO-SAM适配KITTI数据集实战:关键参数设置与避坑指南
简介为适配Kitti数据集修改的lio-sam项目压缩包内含完整源码与配置面向SLAM初学者、自动驾驶感知研究人员及机器人开发者。此版本针对Kitti数据特点对原始lio-sam进行定制涉及传感器数据同步、特征提取、滤波器参数、回环检测与地图构建等模块便于在城市道路场景下验证激光雷达惯性里程计。压缩包共44个文件打包约76MB主要包含C源码、ROS启动与参数配置、项目文档及演示GIF、图像等可视化材料目录组织清晰方便定位关键改动。已有1073人浏览学习。借助容器化构建配置与自定义消息与服务定义读者可复现编译运行流程、对比原版lio-sam的差异也能学习Kitti格式、SLAM多传感器融合及性能评估的实战经验为后续研究或部署提供参考。1. LIO-SAM 跑 KITTI不是解压就能跑这份 zip 改对了几处关键设置我是那种拿到 zip 先看 README、再决定动不动手的人。这份为适配 kitti 数据集修改的 lio-sam.zip解决的痛点很直接官方 LIO-SAM 直接跑 KITTI 原始数据不是点云话题对不上就是 IMU 频率和雷达线数不匹配编译过了也跑不出像样的轨迹。这个包把 KITTI 的 64 线 Velodyne、100Hz IMU 和坐标系偏差全部做了预配置还带了一个 kitti2bag 转换脚本算是比较省事的 KITTI 适配版。适合正在做 LiDAR 惯性融合、想在 KITTI 上复现 LIO-SAM 的人也被 ROS 话题、时间戳和坐标变换折腾过的人。2. 解压前先看懂改动目录结构、代码差异与依赖清单2.1 这份 zip 里装的是什么常见目录布局先别急着编译先把 zip 当资源包拆开看结构。我拿到的这份包虽然名为 lio-sam.zip但内部不是直接把官方仓库打了一个包而是把改动过的源码、KITTI 专用配置和一个数据转换工具都放到了一起。解压后大致是这样. ├── src/ │ └── lio-sam/ # 修改后的 LIO-SAM 源码 │ ├── config/ │ │ ├── kitti.yaml # KITTI 专用参数文件 │ │ └── parameters.yaml # 通用参数运行时会被 kitti.yaml 覆盖 │ ├── include/ │ │ └── lio-sam/ # 头文件包含点云预处理和 IMU 工具 │ ├── launch/ │ │ ├── lio_sam_kitty.launch # KITTI 专用启动文件 │ │ └── run.launch # 官方原版启动文件留作对比 │ └── CMakeLists.txt ├── tools/ │ └── kitti2bag.py # KITTI 原始数据转 ROS bag 的脚本 └── README.md # 作者写的适配说明和运行步骤这里最值得关注的是config/kitti.yaml和launch/lio_sam_kitty.launch这两个文件决定了整个系统是否能在 KITTI 数据上正常跑。tools/kitti2bag.py也不只是搬运代码它处理了 oxts 导航数据的时间同步这一点后面会详细说。另一个容易忽略的地方是src/lio-sam下面还保留了官方原版的run.launch。这个安排很有用当你在 KITTI 上调不出结果时可以切回原版参数做对照实验判断问题出在代码还是出在数据上。2.2 相对官方版改了什么核心代码与配置差异对照官方 LIO-SAM 仓库这份 zip 的主要改动集中在四处每一处都对应 KITTI 数据的特点不是随便加的。第一处是点云话题名。官方默认从/points_raw读点云KITTI 转换后的 bag 里也常用这个名但很多人在其他数据集上习惯了/velodyne_points。zip 里把话题名做成 launch 参数用arg传入params.yaml比官方写死的方式灵活。改法本身不复杂作用是让你不必再改源码才能切话题。第二处是 IMU 输出。LIO-SAM 前端对 IMU 的依赖非常重初始化、点云去畸变、里程计传播都要用 IMU。KITTI 的 oxts 数据里有 100Hz 的角速度和加速度但 LIO-SAM 官方版本默认还会判断磁力计数据如果没有磁力计就认为 IMU 无效。这份 zip 在imu_data.cpp里改了判断逻辑只使用角速度和加速度不再强制要求磁场数据这是 KITTI 上能跑通的一个重要前提。第三处是时间戳。官方代码很多地方用ros::Time(0)获取当前时间这在真实机器人上没什么问题但回放 bag 时必须使用 bag 里的时间否则点云和 IMU 时间轴会错开。zip 把所有关键回调改成了读消息头里的时间戳并在 launch 里强制开启了/use_sim_time。这一点不改回放 KITTI 时系统会一直认为没有新数据。第四处是坐标系。LIO-SAM 内部以 ENU 导航坐标系为基准而 KITTI 原始点云建立在雷达的 RFL 坐标系下oxts 真值又是相机坐标系。zip 在预处理的代码里统一做了外参旋转把雷达点云先转到车体坐标系再转到 ENU。你不需要理解每一步推导但要知道config/kitti.yaml里的extrinsicRPY和extrinsicTrans就是用来对齐这一步的。如果想知道具体改了哪些代码可以解压后在源码目录里跑一遍cd src/lio-sam git diff --stat HEAD 2/dev/null || diff -rq . 原始目录 2/dev/null | head -50第一行是如果你的 zip 里还保留了.git历史可以直接看改动统计第二行是拿解压目录和官方仓库目录做递归对比输出差异文件清单。实际用的时候我更喜欢直接搜索kitti关键字grep -rn kitti src/lio-sam --include*.yaml --include*.launch --include*.cpp这条命令能快速把改动过的文件和参数列干净比看一堆 diff 更直白。2.3 依赖环境Ubuntu / ROS / PCL / GTSAM 版本选择LIO-SAM 的编译依赖说多不多说少也不少最容易翻车的是 PCL 和 GTSAM 都能拉依赖库的 version 冲突。这份包在 README 里建议的环境如下按这个组合踩坑最少。依赖推荐版本说明Ubuntu18.04 或 20.042024 年之后新系统对旧依赖兼容差ROSMelodic 或 Noetic对应 Ubuntu 版本PCL1.8 / 1.10系统自带的即可不要自己编译新版GTSAM4.0.x不要用 4.2 以上版本会破坏部分接口Eigen3.3.x必须用系统 apt 源的版本如果你现在用的是 Ubuntu 20.04 ROS Noetic那么下面这段基本就能装齐sudo apt-get update sudo apt-get install -y \ ros-noetic-pcl-ros \ ros-noetic-velodyne-msgs \ ros-noetic-tf2-eigen \ ros-noetic-cv-bridge \ libgtsam-dev \ libeigen3-dev这里libgtsam-dev装的是系统 GTSAMNoetic 仓库里默认版本正好是 4.0.x和 LIO-SAM 的代码兼容。不要自己从源码编译 GTSAM除非你清楚知道自己在干什么。PCL 也不要额外安装ROS Noetic 自带的 PCL 1.10 和这套代码配合过够了。如果你跑的是 18.04 Melodic命令换成ros-melodic-前缀依赖逻辑一样。唯一要注意的是 Eigen很多人在源码编译 GTSAM 时会把/usr/local/include/eigen3装出一个新版等到编译 LIO-SAM 时系统找不到头文件或者找到了两个版本非常容易撞出Eigen/Core: No such file or directory这种问题。所以我的建议是所有依赖能走 apt 就走 apt不要走源码。3. 参数对齐KITTI 传感器模型与 LIO-SAM 配置映射3.1 KITTI 的 64 线雷达和 IMU 到底是什么规格KITTI 数据集用的雷达是 Velodyne HDL-64E这个名字里的 64 不是随便写的它意味着雷达在垂直方向上有 64 条扫描线。HDL-64E 的垂直视场从 2° 到 -24.9°水平视场是 360°水平角分辨率大约 0.09°。也就是说一帧完整扫描的水平点数在 360 / 0.09 左右大概 4000 到 4500 个点实际存储时按 4096 或 4500 处理都有。LIO-SAM 的核心预处理模块依赖N_SCAN和Horizon_SCAN这两个参数来组织点云。N_SCAN表示雷达的线数Horizon_SCAN表示一帧中每个线束上取多少个点。官方 LIO-SAM 默认值是 16 线雷达的配置N_SCAN16Horizon_SCAN1800。如果你直接用默认配置去跑 KITTI预处理会把 64 线的点云强行塞进 16 线的索引结构里特征提取和线束归属全部错乱最后出来的地图横竖都是问题。KITTI 的 IMU 来自 OXTS RT3000包含三个轴的角度、角速度和加速度输出频率是 100Hz。注意它没有磁力计输出。LIO-SAM 在做初始化时如果检测到没有磁力计会回退到一个相对弱的初始化逻辑。这也是之前说的为什么必须改掉官方强制判断磁力计的那段代码。如果你用的是原版 LIO-SAM即使话题和参数都对IMU 也会被判定为无效数据前端一直卡在等待状态。还有一个容易忽略的细节KITTI 的 oxts 数据是从导航系统融合出来的里面的加速度和角速度已经相对滤波噪声比现实中的原始 IMU 小很多。所以在配置imuAccNoise和imuGyrNoise时不要照抄官方对实车 IMU 的建议值适当调低系数可以让 LIO-SAM 更信任 IMU结果会稳定一些。3.2 修改params.yamlN_SCAN、Horizon_SCAN、外参和时间戳打开config/kitti.yaml最核心的一段配置长这样pointcloud_topic: /points_raw imu_topic: /imu/data sensor: velodyne N_SCAN: 64 Horizon_SCAN: 4096 downsampleRate: 1 lidarMinRange: 1.0 lidarMaxRange: 100.0 useImuHeadingInitialization: true imuAccNoise: 0.001 imuGyrNoise: 0.001 imuAccBiasN: 0.001 imuGyrBiasN: 0.001 imuGyrBiasW: 0.001 imuAccBiasW: 0.001 extrinsicTrans: [0.0, 0.0, 0.0] extrinsicRPY: [0.0, 0.0, 0.0]pointcloud_topic和imu_topic必须对应你转换出来的 bag 话题名。N_SCAN64是硬性要求Horizon_SCAN4096是相对合理的数值。如果你发现地图在同一个墙面上有横向错位可以把它调到 4500 试试但一般 4096 就是原版 HDL-64E 数据常见的选择。downsampleRate1表示不做降采样KITTI 每一帧约 12 万个点预处理耗时还能接受。如果你的 CPU 不够强可以改成 2也就是每两个点取一个跑起来快很多代价是精度稍有下降。lidarMinRange1.0是滤掉 1 米以内的近处点这个数值对 KITTI 很合适因为雷达装在车顶近处大多是车体自身留到后面会形成一层噪声云。lidarMaxRange100.0是限制最远距离KITTI 场景里 100 米足够太远会削掉特征点。useImuHeadingInitialization这个开关要打开。它让 LIO-SAM 在初始化时用 IMU 的航向来确定点云初始姿态比纯雷达里程计初始化更快。前面提到 KITTI 没有磁力计这里的航向来自角速度积分不是磁场。所以即使打开这个开关也不要期待它像真实机器人那样 10 秒内完成初始化KITTI 上我一般会让前 3 到 5 秒的点云都过一遍再观察输出。imuAccNoise和imuGyrNoise这几个噪声参数官方实车建议是0.01量级但我用的是0.001原因是 KITTI oxts 是导航级 IMU 融合结果噪声远低于消费级 IMU。嫌一个个调麻烦时可以先保持原值跑通一次再回来改这几个参数看轨迹误差变化后面第 6 章会讲怎么量化对比。extrinsicTrans和extrinsicRPY是雷达相对车体的外参。如果你直接用 kitti2bag 转换的数据雷达坐标系和车体坐标系的平移和旋转基本是零因为 kitti2bag 在转换时已经把雷达点云转到车体坐标下了。如果你的 bag 是自己写的转换脚本做的那就要根据实际外参填最常见的坑是车辆坐标系的 x 轴朝前、雷达点云的 x 轴朝下你不填会看到地图整个歪 90 度。3.3 用 kitti2bag 生成 rosbag话题名和时间轴怎么对上参数改完之后得先把 KITTI 原始数据变成 ROS bagLIO-SAM 才能消费。最常见的做法是使用 kitti2bag 命令行工具。pip install kitti2bag export KITTI_ROOT/path/to/kitti_raw kitti2bag -t 05 -r 2011_09_26 2011_09_26_drive_0005_sync $KITTI_ROOT这段命令把 KITTI 的2011_09_26_drive_0005_sync序列转成一个 bag-t 05指定是真值序列 05这样 bag 里会带上ground_truth真值话题。转换之后的 bag 里点云话题是/kitti/velodyne_pointsIMU 话题是/kitti/oxts/imu真实 GPS 话题是/kitti/oxts/gps/fix。这里有个强制的映射关系需要处理你的params.yaml里写的pointcloud_topic和imu_topic必须跟 bag 话题一致。如果你嫌话题名太长可以在lio_sam_kitty.launch里加两个 remapremap from/points_raw to/kitti/velodyne_points/ remap from/imu/data to/kitti/oxts/imu/我一般会在 launch 里直接 remap而不是改源码里的硬编码话题名这样切到其他数据集时不用重新编译。注意kitti2bag 输出的 IMU 话题类型是sensor_msgs/Imu这正是 LIO-SAM 需要的类型不需要额外转译。用rosbag info快速检查生成的 bagrosbag info 2011_09_26_drive_0005_sync.bag | grep -E topics|/kitti/velodyne|/kitti/oxts这条命令会打印 bag 时长、大小和话题列表。重点看两个话题的频率点云一般是 10HzIMU 是 100Hz。如果 IMU 频率只有 10Hz说明你下载的是 KITTI 的 10Hz 降采样版本这会让 LIO-SAM 的前端退化轨迹稳定性明显下降。遇到这种情况重新从原始sync数据转换不要用extract出来的降采样版本。4. 编译与运行从零跑通 KITTI 序列 054.1 编译修改后的 LIO-SAMcatkin 工作空间配置拿到 zip 之后第一步是把它放进 catkin 工作空间。我习惯专门为 KITTI 适配版本建一个干净的工作空间避免和别的包混在一起。mkdir -p ~/lio_sam_kitti_ws/src cd ~/lio_sam_kitti_ws/src unzip /path/to/为适配kitti数据集修改的lio-sam.zip mv 解压出来的目录名 lio-sam cd ~/lio_sam_kitti_ws catkin_make source ~/lio_sam_kitti_ws/devel/setup.bash注意catkin_make编译时如果报c: internal compiler error一般是内存不足可以限制一下并行度catkin_make -j2-j2是让编译器只开 2 个线程内存占用会小很多。编译完成后当前 shell 要 source 一下 setup 文件否则roslaunch lio_sam找不到包。如果你之前装过其他版本的 GTSAM或者系统里有多个 Eigen 导致编译时头文件冲突大概率在catkin_make阶段就会暴露。出现类似fatal error: gtsam/linear/NoiseModel.h: No such file or directory说明 GTSAM 没装好或版本太新。先检查版本再决定重装还是换机器。dpkg -l | grep gtsam pkg-config --modversion gtsam如果版本不是 4.0.x建议直接卸载重装系统自带的libgtsam-dev。版本这东西在 LIO-SAM 这里非常敏感我不建议硬刚。4.2 启动三个环节roscore、rosbag 播放、LIO-SAM 启动跑通 KITTI 序列 05最少需要三个终端。终端一先播放 bag并且一定要用--clock参数rosparam set /use_sim_time true rosbag play /path/to/2011_09_26_drive_0005_sync.bag --clock --pause --rate 1.0--clock让 bag 成为系统时间源--pause是让 bag 在一开始暂停在首帧等 LIO-SAM 节点起来后再手动空格开始。直接rosbag play而不设--pause的话LIO-SAM 还没初始化完bag 已经播放了两秒会白白丢掉前半段点云。终端二启动 LIO-SAMcd ~/lio_sam_kitti_ws source devel/setup.bash roslaunch lio_sam lio_sam_kitty.launch在这个 launch 文件里作者已经把/use_sim_time和 bag 的启动参数写在注释里了所以你只需要在终端二执行这一条命令。启动后你会看到lio_sam在等待点云和 IMU此时回到终端一按空格键让 bag 播放。终端三做可视化检查rviz -d ~/lio_sam_kitti_ws/src/lio-sam/rviz/lio_sam.rviz如果你在源码里的 rviz 配置文件路径不对也可以直接开 rviz 再手动订阅/lio_sam/mapping/cloud_registered和/lots/lio_sam/mapping/odometry话题。第一次启动时建议把 bag 暂停在 0.5 秒之后让 LIO-SAM 完成初始化再正常播观察/lio_sam/mapping/odometry话题的频率。用rostopic hz检查输出频率是一个很实用的习惯rostopic hz /lio_sam/mapping/odometry如果频率稳定在 8 到 10Hz说明前端和 IMU 时间同步都正常。如果只有 0.1Hz说明后端回环在拼命追赶点云CPU 已经吃不消了。4.3 观察输出如何判断收敛正常在 rviz 里最早出现的应当是当前激光帧和已构建的局部地图。正常状态下你会看到地面先出现然后两侧建筑立面陆续补上不会出现大面积的拖尾。如果地面在某个时间点之后开始扭曲成弧形说明 IMU 的角速度噪声参数过大系统过于信任点云匹配。另一个判断标准是看点云匹配的结果是否有跳变。在lio_sam_kitty.launch的启动日志里每隔一段时间会打印一帧关键帧数量。如果你发现关键帧数量增长很快说明每次点云匹配的误差都很大系统需要通过不断插入关键帧来补偿。反过来如果关键帧数量增长慢且匀速说明匹配稳定。KITTI 序列 05 整个长度大约 5 公里跑完后我会把params.yaml里的savePCD打开让它把拼接地图保存下来。通常这个序列能拼出不错的城市街区地图局部有错位也不用急着调参先看误差。等到第 6 章用 evo 把轨迹和真值对齐就会知道问题到底出在前端还是后端。5. 避坑手册KITTI LIO-SAM 的五个典型翻车现场5.1 编译报错PCL 和 GTSAM 的 Eigen 冲突现象catkin_make编译到一半报fatal error: Eigen/Core: No such file or directory或者 GTSAM 里Eigen is not defined。原因系统里存在两个 Eigen 版本一个来自 apt 安装位于/usr/include/eigen3另一个来自源码安装位于/usr/local/include/eigen3。CMake 搜索头文件时优先找到了/usr/local下的版本但 GTSAM 编译用的还是/usr/include下的版本两者接口不同直接冲突。解决先用apt-get remove libeigen3-dev移除系统包再重装一次把/usr/local/include/eigen3也清掉只留/usr/include/eigen3。或者反过来统一LIBRARY_PATH指向同一套。我更推荐的做法是卸载所有 Eigen然后只通过 apt 安装一次sudo apt-get remove -y libeigen3-dev sudo rm -rf /usr/local/include/eigen3 /usr/local/lib/cmake/Eigen3 sudo apt-get install -y libeigen3-dev这里要强调GTSAM 在编译时如果绑定了别的 Eigen重装后需要一并重装 GTSAM因为它是链接了 Eigen 的二进制库。5.2 启动后地图分层或点云扭曲现象明明 bag 播放正常、IMU 话题也有数据但 rviz 里的地图出现双层墙或者地面在车转弯后发生弯折。原因最常见的是N_SCAN和Horizon_SCAN设置错误。你如果用的是 KITTI 的 HDL-64E但params.yaml里N_SCAN16那么预处理会把 64 线点云每 4 线压缩成一个线束特征提取时线束索引错乱墙面会分成两层。解决把N_SCAN改成 64Horizon_SCAN改成 4096。改完要重启节点不要热加载。如果你用的是序列 00 到 10 中的某个且点云是经过自定义转换脚本的还需要检查Horizon_SCAN是否和转换后的水平角分辨率匹配。最简单的方式是打开rivz里原始点云话题数一个平面上是否有 64 条明显的线束如果是参数就不会有大问题。5.3 里程计不更新只有第一个点云现象启动后 /lio_sam/mapping/odometry 只发布了第一个消息之后就再也不发新帧rostopic hz显示 0。原因LIO-SAM 前端在初始化时会等待 IMU 的累计消息。如果 IMU 话题没有发布或者时间戳晚于点云很多系统会一直处于等待 IMU 状态。另一个可能原因是 bag 的/use_sim_time没有打开节点用系统时间读取 IMU但 bag 时间已经走远导致消息被当作过期数据丢弃。解决先检查 IMU 话题是否正常rostopic hz /imu/data rostopic delay /imu/data如果hz为 0说明 bag 没发 IMU 或话题名不对检查rosbag info和params.yaml的imu_topic。如果hz正常但delay很大说明时间戳问题把 bag 播放加--clock并把lio_sam_kitty.launch里的param name/use_sim_time valuetrue/打开。还有一个隐蔽情况是sensorvelodyne与你的点云类型不符如果点云是sensor_msgs/PointCloud2但配置写成了sensorlivox预处理会直接丢掉所有点。5.4 运行几分钟后内存爆掉现象跑 KITTI 序列 05 到第 3 分钟时LIO-SAM 节点进程被系统杀掉dmesg里能看到oom-killer相关信息。原因KITTI 序列 05 整个长度约 5 公里点云密度大关键帧数量积累很快后端因子图和地图占用的内存会持续增长。默认的降采样参数map_25m和map_global是给小型场景调的KITTI 这种长场景跑下来会指数级增长。解决先把降采样率调大downsampleRate2然后关闭回环或限制回环搜索范围。在params.yaml里loopClosureEnableFlag: false先关掉回环跑通一次看内存是否还在涨。如果关掉之后内存稳定说明问题出在回环检测不断累积约束。更优雅的方式是提高map_25m的降采样体素大小比如从 0.2 改到 0.4surfMapResolution: 0.4 mapDownsample: 0.8这不是官方原版参数名不同版本略有差别你需要根据自己 zip 里的 yaml 文件调整。核心思路是让后端地图的点数不要无限增加。如果只是想来KITTI 上验证算法关闭回环完全够用。5.5 轨迹与真值明显横滚翻转现象用 evo 或者 rviz 里对比真值轨迹LIO-SAM 输出的轨迹和 KITTI ground truth 的轨迹在 X 轴上差了 90 度或者 180 度地图整体是侧着或倒着的。原因KITTI ground truth 是在相机坐标系下定义的而 LIO-SAM 输出的是车体坐标系和 ENU 的组合。如果 kitti2bag 转换脚本没有把点云坐标系提前转到车体坐标系那么你在extrinsicRPY里的成本就会偏差很大。解决先在kitti.yaml里确认extrinsicRPY和extrinsicTrans。如果两者都是 0但地图依然翻转说明你的 bag 里点云坐标本身就是雷达坐标系没有转车体。常见做法是在tools/kitti2bag.py里预先左除雷达外参# tools/kitti2bag.py 中常见的处理段 import numpy as np T_lidar_to_body np.eye(4) T_lidar_to_body[:3, :3] np.array([ [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0] ]) pcl pcl.transform(T_lidar_to_body)这段代码里的旋转矩阵是 KITTI 雷达坐标与车体坐标之间的常见对准方式。如果你的extrinsicRPY里已经配置了对应角度就不要在脚本里再转一次否则会双重旋转。我的习惯是尽量把坐标系转换放在数据预处理阶段LIO-SAM 的外参全部设 0这样后续调参时更容易排查。6. 进阶验证用 evo 量化 KITTI 轨迹误差再决定要不要调参6.1 保存 LIO-SAM 轨迹并转成 TUM 坐标格式KITTI 序列 05 有官方 ground truth如果不把 LIO-SAM 输出和真值量化对比光在 rviz 里看地图是不够的。LIO-SAM 运行时里程计话题是/lio_sam/mapping/odometry类型是nav_msgs/Odometry。我通常会写一个小脚本订阅这个话题把位姿保存成 TUM 格式TUM 是 evo 工具的标准输入格式。#!/usr/bin/env python3 import rospy from nav_msgs.msg import Odometry traj_file open(lio_sam_traj.tum, w) def odom_cb(msg): t msg.header.stamp.to_sec() p msg.pose.pose.position q msg.pose.pose.orientation traj_file.write(f{t:.9f} {p.x:.6f} {p.y:.6f} {p.z:.6f} f{q.x:.6f} {q.y:.6f} {q.z:.6f} {q.w:.6f}\n) rospy.init_node(lio_sam_traj_writer, anonymousTrue) rospy.Subscriber(/lio_sam/mapping/odometry, Odometry, odom_cb) rospy.spin()保存下来的lio_sam_traj.tum每一行是时间戳、x、y、z、四元数 x、y、z、w。注意输出频率很高文件可能很大但 evo 会自动处理时间对齐不需要你在保存阶段降采样。接下来要把 KITTI 真值转换成 TUM 格式。KITTI 真值文件是 12 个数表示的 3x4 变换矩阵evo 自带读取器可以直接读evo_traj kitti 05.txt --save_as_tum这条命令会生成05.tum然后你可以对比两个轨迹。6.2 用 evo 计算 ATE / RPE有了两个 TUM 文件剩下就是标准操作。evo_ape tum 05.tum lio_sam_traj.tum -a --plot --plot_mode xz evo_rpe tum 05.tum lio_sam_traj.tum -a --plot-a是自动对齐让 evo 先对两个轨迹做 Umeyama 对齐再做误差统计这样消除掉坐标系原点不同带来的干扰。--plot_mode xz是只显示 x 和 z 轴KITTI 轨迹大多在水平面上xz 图能看清路径重合度。evo_rpe计算的是逐段相对位姿误差能反映局部畸变。我第一次跑 KITTI 05 时ATE 大概在 8 米左右看地图觉得已经不错了但 evo 一算还是太差。后来把imuAccNoise从 0.01 调到 0.001ATE 直接掉到 2 米左右。这个经验说明一个问题视觉判断地图“大致对”并不够定量误差才是调参的坐标轴。从那以后我每次拿到一个新的 LIO-SAM 适配包都会先把轨迹保存和 evo 对比跑一遍再决定要不要调参数。没有量化结果之前调参全凭玄学有了 evo 输出的 ATE 和 RPE你才知道手里的改动到底有没有让系统变好。这套流程不只适用于这份 KITTI 适配 zip你换成自己录的 bag、换成别的序列同样能用。希望帮到你。本文还有配套的精品资源点击获取