Co-authored-by: Cursor <cursoragent@cursor.com>
LiDAR–IMU 外参标定
用车上的**激光雷达(LiDAR)与惯性测量单元(IMU)**在连续行驶中的相对运动,估计二者之间的安装关系(外参),并估计两路传感器的时间差。
p_IMU = T_IMU_lidar · p_lidar
实现包:[imu_lidar/](imu_lidar/)。文件职责说明:[imu_lidar/文件职责说明.md](imu_lidar/文件职责说明.md)。
本仓库只有这一条标定路径(连续运动关键帧 + IMU 预积分)。不直接解析原始 .rscap / dlog,需先导出为中间格式。
0. 现状一览(先读)
| 项 | 说明 |
|---|---|
| 仓库做什么 | 输入标准中间格式(IMU CSV + 雷达会话),输出 LiDAR–IMU 正式安装外参 T_IMU_lidar 与时间偏置 δt。合格数据且过质量门 → 可交付(常规先交旋转 + δt;激励够再交可观平移);数据不合格或未过门 → blocked,不交外参 |
| 当前阶段 | 算法链路已闭环,合成自检通过;尚缺合格实车数据验收,故正式外参尚未对实车落盘交付(生产级原始录制→中间格式导出仍待完善) |
| 合成数据测什么 | 证明链路能跑通,并能收回已知旋转(yaw)与 δt;不证明实车安装精度或平移交付 |
| 旧车 S2 测什么 | 仅线下烟测:主机时间旧数据上流水线能否跑完;结果预期为 blocked,不当作外参真值 |
| 合格数据 | 见下方定义;拿到后即可按正式标定流程跑通,期望得到可交付外参(先旋转 + δt) |
什么叫合格数据
同时满足下列条件,才视为可用于正式标定(与旧车主机时间烟测数据区分):
| 类别 | 要求 |
|---|---|
| 时间戳 | IMU、雷达均用设备时间写入中间格式的 t / t_start/t_end(可另留主机接收时间作排查,但标定主轴不用主机时) |
| 会话结构 | 单趟:静止 20–30 s → 低速连续运动 3–8 min → 再静止 10–20 s;运动含明显左右转(如「8」字 / 圆)及适量加减速 |
| 场景 | 有墙、柱、路缘等结构,避免空旷无特征;雷达帧率与点数正常、无明显大面积丢帧 |
| 覆盖 | 两路传感器覆盖同一段物理行驶;安装全程不得改动 |
| 格式 | 已导出本仓库中间格式:imu.csv + lidar_session/(见 docs/V1_数据格式.md) |
| 验证会话(建议) | 另录一段独立路线 B,只做叠点/一致性检查,不参与求外参 |
不算合格(举例): 仅主机接收时间;只有静止、无转弯;IMU 与雷达不是同一趟;未导出中间格式;时间相关极弱仍强行当安装参数。
完整现场清单见 docs/标定流程与采集清单.md。
合格数据拿到后 → 正式外参与预期效果
仓库目标产物就是可安装使用的外参(再加 δt)。合格数据过门后:
| 模式 | 跑法 | 正式交付内容 | 较现实预期 | 成功标志 |
|---|---|---|---|---|
rotation_only(第一步,必做) |
--mode rotation_only |
旋转外参 + δt | 旋转约 0.5°–2°;δt 随同步与激励质量变化 | summary.json → rotation_only_accepted |
full_se3(激励够再试) |
--mode full_se3 |
上一项 + 可观的平移 | 水平平移约数厘米~十几厘米;竖直常更差或不可观(需缓坡或垂直先验) | full_se3_accepted;否则旋转仍可交付、平移拒绝 |
交付前须叠点云 / 跨会话独立验证;blocked 一律不可用作安装参数。
当前尚未完成的是「用合格实车数据跑出并验收这份外参」,不是「合格数据也交不出外参」。
两类试验对照
| 合成数据(默认 pytest / 一键复现) | 旧车 S2(线下,非默认测试) | |
|---|---|---|
| 目的 | 回归算法正确性 | 验证实车 IO/链路可跑 |
| 时间戳 | 可控、带已知 δt | 主机接收时间(非设备时间) |
| 是否合格数据 | 否(合成,仅对照) | 否(主机时间,预期拒绝) |
| 期望结果 | rotation_only_accepted;δt / yaw 接近真值 |
blocked(质量门拒绝) |
| 能否当安装参数 | 否 | 否 |
细节见 tests/README.md。实现状态细表见 docs/IMU-LiDAR标定.md §8。
目录
1. 系统输入 / 输出
输入
| 输入 | 说明 |
|---|---|
imu.csv |
列:t,gx,gy,gz,ax,ay,az(秒;陀螺 rad/s;加速度 m/s²) |
| 雷达点云 | frames_index.csv + frames/frame_XXXXX.npz(点云米制) |
| 车辆配置 YAML | 轴向与时间语义;不必事先填外参真值(模板:config/vehicle_installation.template.yaml) |
中间格式细节见 docs/V1_数据格式.md。原始 .rscap 等需先导出。
输出(--output 目录)
| 文件 | 内容 |
|---|---|
T_IMU_lidar.json |
外参矩阵、平移、四元数、欧拉角 |
time_offset.json |
δt,定义 t_imu = t_lidar + δt |
summary.json |
状态、残差、可观性、各阶段细节 |
report_preview.png |
一键复现时生成的简易指标图(可选) |
2. 一键复现(合成数据)
不需要实车数据。脚本会:生成合成 IMU/雷达 → 跑标定 → 打印报告并出预览图 → 跑 pytest。
这一趟证明什么: 全流程可复现;能收回合成真值中的旋转(yaw)与时间偏置。
不证明什么: 实车安装精度、平移外参可交付、主机时间旧数据可用。
推荐(Windows)
cd <本仓库根目录>
python -m pip install -e ".[dev]"
python -m pip install -e ".[open3d]" # 可选,配准更稳;未装则用内置 ICP
# 预览图需要 matplotlib(没有则跳过绑图,不影响标定)
python -m pip install matplotlib
powershell -File tools\reproduce_synthetic.ps1
只要标定、跳过测试:
powershell -File tools\reproduce_synthetic.ps1 -SkipPytest
尝试完整六自由度模式:
powershell -File tools\reproduce_synthetic.ps1 -Mode full_se3 -SkipPytest
等价 Python 入口
python tools\reproduce_synthetic.py
python tools\reproduce_synthetic.py --mode full_se3 --skip-pytest
这一趟用了什么、产出什么
INPUT
examples/synthetic_session/imu.csv
examples/synthetic_session/lidar/
config/vehicle_installation.template.yaml
examples/synthetic_session/meta.json # 合成真值(δt、yaw),仅用于对照
OUTPUT
examples/synthetic_session/out/T_IMU_lidar.json
examples/synthetic_session/out/time_offset.json
examples/synthetic_session/out/summary.json
examples/synthetic_session/out/report_preview.png
单独查看已有结果:
python tools\show_calibration_report.py `
--summary examples\synthetic_session\out\summary.json `
--truth-meta examples\synthetic_session\meta.json `
--plot examples\synthetic_session\out\report_preview.png
配准结果 3D / 俯视可视化
蓝=目标帧,橙=源帧。
| 按键 | 作用 |
|---|---|
1 / 2 / 3 / 4 |
原始 / IMU(X=I) / 雷达配准 B / 标定预测 X⁻¹AX |
N 或 ] |
下一运动对 |
P 或 [ |
上一运动对 |
python tools\visualize_pair_3d.py `
--lidar examples\synthetic_session\lidar `
--imu examples\synthetic_session\imu.csv `
--summary examples\synthetic_session\out\summary.json `
--pair-index 0 `
--save-png examples\synthetic_session\out\pair0_overlay.png
--pair-index 只决定起始对;窗口内可继续按 N/P 切换。无 GUI 时加 --no-gui。封装:tools\view_pair.ps1。
3. 能求出什么
| 结果 | 含义 | 说明 |
|---|---|---|
| 旋转外参 | 雷达相对 IMU 的朝向差 | 常规主交付 |
| 时间偏置 δt | 两路时钟差(秒) | 与旋转一并给出 |
| 平移外参 | 安装位置差(米) | 激励足够且质量门通过才接受,否则置零 |
| 零偏等 | 辅助量 | 改善积分/优化,一般不当安装外参交付 |
4. 方法概要
- 低速多转弯行驶(如「8」字),首尾各留静止段。
- 雷达关键帧配准得相对运动 B。
- 同一时段 IMU 预积分得 A(先用旋转)。
- 手眼关系:
R_A · R_X ≈ R_X · R_B。 - 时间与旋转稳定后,若
--mode full_se3且可观,再估计平移(用加速度预积分、重力、速度与时变零偏)。
5. 分步求值
| 步骤 | 交付 | 典型要求 |
|---|---|---|
| 第一步 | 旋转 + δt | 设备时间戳;静止 + 低速转弯;有结构的场景 |
| 第二步 | + 水平平移 | 更多转弯半径与加减速 |
| 第三步 | 完整六自由度 | 缓坡等俯仰激励,或垂直安装尺寸先验 |
--mode rotation_only:第一步--mode full_se3:尝试完整刚体;平移仍可能被拒绝
质量门未通过时,不能当正式安装参数。
6. 程序流程
读入 → 时间/IMU 质检 → 粗估 δt
→ 关键帧配准 B + IMU 预积分 A → 旋转手眼 R
→ 用 R 精修 δt(可交替组对)
→ 联合精修 R / 常值陀螺零偏
→(full_se3 且可观)重力、速度、时变零偏、平移 t
→ 写出 JSON
「可观」:程序检查转轴是否够多样、相对位移是否够丰富;不够则只保留旋转。
7. 真实数据怎么跑
python -m pip install -e ".[dev]"
python -m pip install -e ".[open3d]"
python -m imu_lidar.cli plan --mode rotation_only
python -m imu_lidar.cli run `
--vehicle-config config\vehicle_installation.template.yaml `
--imu path\to\imu.csv `
--lidar path\to\lidar_session `
--output path\to\output `
--mode rotation_only `
--time-offset-search-s 2.0
采集与现场清单:docs/标定流程与采集清单.md。
旧车 S2 主机时间烟测(预期 blocked,不当交付):配置见 config/s2_old_smoke.yaml,步骤见 [tests/README.md](tests/README.md)。
8. 结果判读
summary.json 状态 |
含义 |
|---|---|
rotation_only_accepted |
旋转可用(该模式下平移置零) |
full_se3_accepted |
含平移的完整外参通过 |
full_se3_rejected_due_to_observability |
旋转可用,平移未接受 |
blocked |
前序失败,不可用作安装参数 |
请同时看手眼残差、时间相关、可观性说明,并做点云叠加等独立验证。
9. 仓库结构与文档
imu_lidar/ 标定算法与 CLI
config/ 车辆配置模板
docs/ 流程、数据格式、方法说明
tools/ 合成数据、一键复现、报告查看
tests/ 自动化测试
| 文档 | 内容 |
|---|---|
| imu_lidar/文件职责说明.md | 各源文件职责 |
| imu_lidar/CHANGELOG.md | 算法改动记录 |
| docs/标定流程与采集清单.md | 现场采集与分步流程(与现行实现对齐) |
| docs/V1_数据格式.md | 中间数据格式 |
| docs/IMU-LiDAR标定.md | 方法约定与实现状态 |
| tests/README.md | 自动化测试说明 + S2 旧数据线下试验结果 |
改算法请同步更新 imu_lidar/文件职责说明.md 与 CHANGELOG.md;改安装/运行/对外说明时更新本文件。