ESC
输入关键词搜索文章标题和内容

KISS-ICP激光里程计极简管道让Point-to-Point ICP重生

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

KISS-ICP激光里程计极简管道让Point-to-Point ICP重生

项目概览与核心定位

核心功能与设计哲学

KISS-ICP 是波恩大学 Cyrill Stachniss 教授团队开源的3D LiDAR里程计管道,名字取自工程界的经典原则——KISS(Keep It Simple, Stupid)。在SLAM领域普遍追求"加法"的今天,KISS-ICP反其道而行,用最简单的Point-to-Point ICP实现了与SOTA方法持平的精度,且零调参、即插即用。

项目核心只做四件事:体素降采样、自适应阈值、Point-to-Point ICP配准、运动补偿。没有特征提取,没有深度学习,没有IMU融合——但就是能跑,而且跑得比传感器帧率还快。

目标用户与适用场景

  • 需要LiDAR里程计的移动机器人开发者
  • 从事SLAM/里程计研究的学术人员
  • 仓库AGV/AMR的工程落地团队
  • 想快速验证LiDAR定位方案的集成商
  • 对ICP算法原理感兴趣的学习者

解决的核心问题

  • 现有LiDAR里程计需要针对不同传感器反复调参
  • 复杂系统(LOAM系列、LIO-SAM)依赖过多,部署困难
  • Point-to-Point ICP被认为"过时",但实际是被误用
  • 不同运动模式(车辆/无人机/手持/Segway)需要不同配置
  • 里程计系统运行速度低于传感器帧率,无法实时

目录结构与核心文件

kiss-icp/
├── cpp/                              # C++核心实现
│   └── kiss_icp/
│       ├── core/                     # 核心算法模块
│       │   ├── Registration.cpp      # ICP配准(Gauss-Newton优化)
│       │   ├── VoxelHashMap.cpp      # 体素哈希局部地图
│       │   ├── Threshold.cpp         # 自适应阈值
│       │   ├── Preprocessing.cpp     # 预处理(范围裁剪)
│       │   └── Deskew.cpp            # 运动补偿(去畸变)
│       ├── pipeline/                 # 管道封装
│       │   ├── KissICP.hpp           # 主类(89行,极简)
│       │   └── KissICP.cpp           # RegisterFrame实现
│       ├── metrics/                  # 评估指标
│       └── kiss_icp_pybind/         # pybind11 Python绑定
├── python/                           # Python接口与可视化
│   ├── kiss_icp/                    # Python包
│   │   ├── tools/                   # Kissualizer可视化器
│   │   ├── datasets/                # 数据集加载器
│   │   └── pipeline.py              # Python管道封装
│   └── README.md
├── ros/                              # ROS2封装
│   ├── kiss_icp/                    # ROS2节点
│   └── README.md
├── eval/                             # 评估脚本(EVO工具)
├── config/                           # 默认配置文件
└── Makefile                          # 开发快捷命令

核心文件说明:

文件 行数 用途
cpp/kiss_icp/pipeline/KissICP.hpp 89 主类定义,仅4个成员模块,极简设计
cpp/kiss_icp/core/Registration.cpp ~150 Point-to-Point ICP配准,Gauss-Newton+Robust Kernel
cpp/kiss_icp/core/VoxelHashMap.cpp ~200 体素哈希地图,O(1)近邻查询
cpp/kiss_icp/core/Threshold.cpp ~80 自适应阈值,基于运动先验动态调整
cpp/kiss_icp/core/Deskew.cpp ~60 恒速模型运动补偿
cpp/kiss_icp/core/Preprocessing.cpp ~30 范围裁剪(max_range/min_range)

架构设计与模块关系

整体架构与数据流

flowchart TB subgraph 输入["输入层"] direction TB I1["3D LiDAR点云"] I2["时间戳(可选)"] end subgraph 管道["KISS-ICP管道"] direction TB P1["Preprocessing<br/>范围裁剪"] P2["VoxelDownsampling<br/>体素降采样"] P3["AdaptiveThreshold<br/>自适应阈值"] P4["Registration<br/>Point-to-Point ICP"] P5["Deskew<br/>运动补偿"] P6["VoxelHashMap<br/>局部地图更新"] P1 --> P2 --> P3 --> P4 --> P5 --> P6 end subgraph 输出["输出层"] direction TB O1["SE3位姿估计"] O2["局部点云地图"] O3["去畸变点云"] end I1 --> P1 I2 --> P5 P4 --> O1 P6 --> O2 P5 --> O3 P6 -.->|下一帧| P3 style I1 fill:#E3F2FD,stroke:#1976D2 style I2 fill:#E3F2FD,stroke:#1976D2 style P1 fill:#E3F2FD,stroke:#1976D2 style P2 fill:#FFF8E1,stroke:#F9A825 style P3 fill:#FFF8E1,stroke:#F9A825 style P4 fill:#F3E5F5,stroke:#8E24AA style P5 fill:#F3E5F5,stroke:#8E24AA style P6 fill:#E8F5E9,stroke:#388E3C style O1 fill:#E8F5E9,stroke:#388E3C style O2 fill:#E8F5E9,stroke:#388E3C style O3 fill:#E8F5E9,stroke:#388E3C

核心类关系图

classDiagram class KissICP { -KISSConfig config_ -Registration registration_ -VoxelHashMap local_map_ -AdaptiveThreshold adaptive_threshold_ -SE3d last_pose_ -SE3d last_delta_ +RegisterFrame(frame) tuple +RegisterFrame(frame, timestamps) tuple +Voxelize(frame) tuple +LocalMap() vector +pose() SE3d +delta() SE3d } class Registration { +RegisterFrame(frame, map, threshold) SE3d -Align(frame, map,initial_guess) SE3d } class VoxelHashMap { -voxel_size_ double -max_range_ double -max_points_per_voxel_ int +AddPoints(points) +Pointcloud() vector +NeighborPoints(point) vector } class AdaptiveThreshold { -initial_threshold_ double -min_motion_th_ double +UpdateThreshold(delta) +ComputeThreshold() double } KissICP *-- Registration KissICP *-- VoxelHashMap KissICP *-- AdaptiveThreshold

技术栈与依赖选型

层级 技术选型 说明
核心算法 C++17 高性能Point-to-Point ICP实现
线性代数 Eigen3 矩阵运算与SVD分解
李群/李代数 Sophus SE3位姿表示与运算
并行计算 TBB (Intel Threading Building Blocks) 体素降采样与近邻查询并行化
哈希地图 tsl::robin_map 高性能体素哈希表
Python绑定 pybind11 C++核心→Python接口
可视化 Open3D / Kissualizer 点云与轨迹可视化
ROS集成 ROS2 (Humble/Iron/Jazzy/Kilted) 机器人系统即插即用
构建系统 CMake 跨平台编译
评估 EVO SLAM轨迹评估标准工具

系统流程与运行机制

单帧处理完整流程

flowchart TB A["🔴 原始3D点云"] --> B["范围裁剪+体素降采样"] B --> C{"首次帧?"} C -->|"是"| D["初始化局部地图"] D --> L C -->|"否"| E["计算自适应阈值"] E --> F["ICP配准+鲁棒核"] F --> G{"收敛?"} G -->|"否"| H["继续迭代 max 500"] H --> F G -->|"是"| I["输出SE3位姿"] I --> J["运动补偿/去畸变"] J --> K["更新局部地图"] K --> L["✅ 等待下一帧"] style A fill:#E3F2FD,stroke:#1976D2 style B fill:#E3F2FD,stroke:#1976D2 style C fill:#FFF8E1,stroke:#F9A825 style D fill:#E3F2FD,stroke:#1976D2 style E fill:#FFF8E1,stroke:#F9A825 style F fill:#F3E5F5,stroke:#8E24AA style G fill:#FFF8E1,stroke:#F9A825 style H fill:#FFEBEE,stroke:#D32F2F style I fill:#E8F5E9,stroke:#388E3C style J fill:#F3E5F5,stroke:#8E24AA style K fill:#E8F5E9,stroke:#388E3C style L fill:#E8F5E9,stroke:#388E3C

自适应阈值动态调整机制

flowchart TB A["🔴 上一帧delta"] --> B{"运动量>min_motion_th?"} B -->|"是"| C["阈值=运动量×系数"] B -->|"否"| D["阈值=initial_threshold"] C --> E["用于当前帧ICP对应点搜索"] D --> E E --> F["✅ 约束匹配范围<br/>避免错误对应"] style A fill:#E3F2FD,stroke:#1976D2 style B fill:#FFF8E1,stroke:#F9A825 style C fill:#E3F2FD,stroke:#1976D2 style D fill:#E3F2FD,stroke:#1976D2 style E fill:#F3E5F5,stroke:#8E24AA style F fill:#E8F5E9,stroke:#388E3C

VoxelHashMap查询流程

sequenceDiagram participant F as 当前帧点云 participant V as VoxelHashMap participant M as 局部地图 F->>V: 逐点查询近邻 V->>V: 计算体素坐标(x,y,z) V->>V: 哈希查找robin_map V->>M: 返回该体素及邻域内所有点 M-->>V: 近邻点集 V-->>F: 对应点对 F->>V: 配准完成后,添加新点到地图 V->>V: 更新体素(max_points_per_voxel=20)

技术特点与差异化优势

核心创新点

  • 为Point-to-Point ICP正名:论文核心论点——Point-to-Point不是不行,而是之前没人用对。关键在于自适应阈值+鲁棒核函数+体素降采样的组合,而非Point-to-Plane等"高级"变体
  • 自适应阈值(Adaptive Threshold):根据上一帧的运动量动态调整ICP对应点搜索范围。运动大→阈值大,运动小→阈值小。无需手动设置correspondence distance
  • VoxelHashMap局部地图:用体素哈希替代KD-Tree,O(1)近邻查询,且自动控制地图密度(max_points_per_voxel=20),避免内存膨胀
  • Geman-McClure鲁棒核:在ICP优化中嵌入鲁棒核函数,自动剔除动态障碍物等外点,无需额外滤波
  • 恒速模型运动补偿:假设匀速运动,对LiDAR扫描中的运动畸变进行补偿,支持非重复扫描模式(如Livox)

与主流方案差异化对比

特点 KISS-ICP LOAM系列 LIO-SAM Cartographer
调参需求 零调参 多参数 多参数 多参数
IMU依赖 不需要 可选 必须 可选
核心算法 Point-to-Point ICP 特征提取+ICP 因子图优化 子地图+回环
代码复杂度 ~500行C++核心 数千行 数千行 数万行
运行速度 >传感器帧率 接近实时 接近实时 依赖配置
传感器适配 通用(任意3D LiDAR) 需适配 需适配 需适配
ROS依赖 可选 必须 必须 必须

代码质量与工程实践

  • C++核心仅约500行,每个模块职责单一
  • 89行头文件定义整个管道,4个成员变量清晰明了
  • MIT开源协议,商业友好
  • 276 commits,49 贡献者,37 个release
  • 完整的pre-commit hooks(clang-format + cmake-format)
  • 2026年4月26日刚发布 v1.3.0,持续活跃维护

工业落地案例

KISS-ICP已被Dexory公司部署在其全球仓库机器人车队中。Dexory开发了12米高的自主移动机器人,在仓库中实时扫描库存并构建数字孪生。同团队还基于KISS-ICP开发了Kinematic-ICP,引入轮式机器人运动学约束,在仓库走廊等退化场景中表现更优,同样部署在Dexory车队中。

适用场景与部署要求

主要适用场景

  1. 移动机器人LiDAR里程计:AGV/AMR的实时定位,零调参即用
  2. SLAM前端:作为SLAM系统的里程计前端,输出高质量位姿初值
  3. 3D建图:增量式构建点云地图,支持任意LiDAR传感器
  4. 里程计评估基准:标准化的评估脚本,基于EVO工具
  5. ICP算法教学:代码极简,适合理解ICP配准的每个细节

典型应用案例

案例 平台 LiDAR 说明
仓库AGV定位 Dexory AMR 3D LiDAR 全球仓库部署,工业级验证
无人车里程计 汽车平台 旋转式LiDAR MulRan数据集验证
无人机定位 UAV 3D LiDAR Voxgraph数据集,高加速度
手持扫描 Segway/手持 Livox LiDAR 非重复扫描模式,运动补偿
室内服务机器人 轮式平台 3D LiDAR NCLT数据集验证

支持的数据集格式

数据集 传感器类型 支持方式
KITTI Velodyne HDL-64 内置加载器
MulRan Ouster OS1-64 内置加载器
NCLT Velodyne PUCK 内置加载器
Newer College Ouster OS0-128 内置加载器
Voxgraph 3D LiDAR 内置加载器
自定义rosbag 任意3D LiDAR ROS2节点
自定义PCD/BIN 任意3D LiDAR Python API

部署环境要求

要求 最低配置 推荐配置
C++编译器 GCC 9+ / MSVC 2022 GCC 11+
Python 3.8+ 3.10+
CMake 3.16+ 3.24+
内存 4GB 16GB+
CPU 4核 8核+(TBB并行加速)
操作系统 Linux/macOS/Windows Ubuntu 22.04

不适用场景

  • 需要2D LiDAR里程计(仅支持3D LiDAR)
  • 需要回环检测与全局优化(KISS-ICP仅做里程计,不做SLAM)
  • 需要IMU紧耦合(可用Kinematic-ICP替代)
  • 需要实时重定位(无地图匹配功能)

快速上手与运行示例

环境要求清单

# 检查Python版本(需要3.8+)
python3 --version

# 检查C++编译器
g++ --version

# 检查CMake(源码编译需要)
cmake --version

三种安装方式

方式一:pip安装(30秒上手)

pip install kiss-icp

# 如需可视化功能
pip install "kiss-icp[all]"

# 验证安装
kiss_icp_pipeline --help

方式二:源码编译(开发者)

来自 linuxros.cn · linuxROS
# 克隆仓库
git clone https://github.com/PRBonn/kiss-icp.git
cd kiss-icp

# 安装开发依赖(仅需g++和pip)
sudo apt install g++ python3-pip

# 开发模式安装
make editable

方式三:ROS2集成

cd ~/ros2_ws/src/
git clone https://github.com/PRBonn/kiss-icp
cd ~/ros2_ws/
colcon build --packages-select kiss_icp

五种运行示例

1. 处理离线数据集

# 下载KITTI数据集后直接运行
kiss_icp_pipeline /path/to/kitti/sequences/00

# 使用自定义配置
kiss_icp_dump_config > my_config.yaml
kiss_icp_pipeline /path/to/dataset --config my_config.yaml

2. ROS2实时运行

# 启动KISS-ICP节点
ros2 launch kiss_icp kiss_icp.launch.py

# 或直接指定话题
ros2 run kiss_icp kiss_icp_node --ros-args \
  -p pointcloud_topic:=/velodyne_points

3. Python API调用

from kiss_icp.pipeline import KissICPPipeline
from kiss_icp.datasets import dataset_factory

dataset = dataset_factory(datalist=["/path/to/pointclouds"])
pipeline = KissICPPipeline(dataset=dataset)
pipeline.run()

4. C++核心API调用

#include "kiss_icp/pipeline/KissICP.hpp"

kiss_icp::pipeline::KISSConfig config;
config.voxel_size = 1.0;
config.max_range = 100.0;
config.deskew = true;

kiss_icp::pipeline::KissICP odometry(config);

for (const auto& frame : pointcloud_frames) {
    auto [source, map] = odometry.RegisterFrame(frame);
    Sophus::SE3d pose = odometry.pose();
}

5. 评估里程计精度

# 使用内置评估脚本
evo_ape kitti /path/to/gt.txt /path/to/pred.txt -va --align --correct_scale

推荐学习路径

顺序 内容 难度 预计时间
1 pip install + 运行demo数据 ⭐ 10分钟
2 阅读KissICP.hpp(89行) ⭐⭐ 20分钟
3 理解Registration.cpp(ICP核心) ⭐⭐⭐ 1小时
4 理解VoxelHashMap(地图表示) ⭐⭐⭐ 1小时
5 理解AdaptiveThreshold(自适应阈值) ⭐⭐ 30分钟
6 ROS2集成到自己的机器人 ⭐⭐⭐ 2小时
7 阅读论文(IEEE RA-L 2023) ⭐⭐⭐⭐ 2小时
8 尝试Kinematic-ICP扩展 ⭐⭐⭐⭐ 半天

在SLAM领域追求"更多特征、更复杂模型"的潮流中,KISS-ICP用89行头文件和500行C++核心证明了一个道理——简单,如果做对了,就是最强的复杂。零调参、全传感器适配、超传感器帧率——这就是KISS的力量。

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页