历史快照
本专题页写于包拆分与 FAST-LIVO2 / CustomMsg / 图像输入落地之前,反映当时的单包结构与
五前端状态。已知过时点:目录布局(apps/launch 现属 asuka_ros 包)、共享的
config_sensor.json(外参已改为各前端 odometry 配置的 sensor 块)、ROS 层 acc_scale
等描述、前端数量(现六种,新增 fastlivo)、CustomMsg 与图像输入链路。2026-09 下旬以来:lightning / superodom 前端与 optimization_miao 后端已移除
(现五前端:fastlio / batchlio / superlio / fastlivo / srlivo;smallpointlio 已于 2026-10-23 移除,odometry/10-11 为其历史快照);
freedom 扩展已整体迁出 asuka(移入 nagisa 包);KeyFrame::stamp 统一为扫描
起始时刻;执行器新增连续失败熔断与 wait_for() 排空检测、扩展与执行器有
ok() 健康查询。
现行事实以主文档 (index / architecture / parameters / odometry 等)与源码为准。
title: "IMU 前向预测模块" description: >- 利用 IMU 数据进行位姿前向预测(5.6KB 实现),为点云配准提供初值,是可被多种里程计复用的公共组件。
IMU 前向预测模块
模块定位与职责
ImuPrediction(src/asuka/src/asuka/modules/imu_prediction.cpp,约 5.6KB)是 modules/ 下的一个扩展模块(继承 ExtensionModule,见 extension_module.hpp),而非里程计插件。它的职责是:在两个前端关键帧之间,用纯 IMU 数据做中点法前向积分,以 IMU 频率持续发布预测位姿,为下游(如 RViz 可视化、点云配准初值)提供高频运动先验。
头文件注释明确说明其来源:这是对 Super-LIO 前向传播路径的忠实移植——原实现中 ROSWrapper::imuHandler 调用 ESKF::Predict 在前向状态 fw_R_/fw_p_/fw_v_ 上积分,每次扫描在 ESKF::Update() 内重锚定;本模块改为以前端 KeyFrame 作为锚点,预测状态仍按 IMU 频率发布。
装载与调用链
模块由 config_extensions.json 的 loading_extension_modules 列表激活(libasuka_imu_prediction.so 处于启用状态),经 ExtensionModule::load_module 的 dlopen 工厂创建。运行期链路完全通过全局回调 Callbacks 解耦:
- 输入 1:异步执行器在 worker 线程取出 IMU 事件、喂给算法之前发射
on_insert_imu→ImuPrediction::on_insert_imu入队; - 输入 2(锚点):里程计前端产出关键帧 →
on_new_frame→on_new_frame入队; - 输出:积分结果 →
emit_predicted_odometry→on_odometry_imu广播给订阅者。
三个链路槽共用前端 worker 线程,IMU 样本与锚点关键帧按算法程序序抵达模块的队列。
构造函数从 config_extensions 读取 imu_prediction.gravity(默认 9.81)与 imu_scale(默认 1.0),并固定 gravity_vec = (0, 0, -gravity)。设计上积分只跑在独立的 processing_thread 上,回调仅入队,ROS 订阅线程与里程计 worker 都不为积分付费。
数据流与锚定-预测循环
flowchart TD
A["异步执行器 worker<br/>(喂算法前发射)"] -->|on_insert_imu| Q
B["里程计前端 KeyFrame"] -->|on_new_frame| Q
Q["循环缓冲队列 容量4096<br/>满则丢最旧并节流告警"] --> T["processing_thread<br/>互斥锁+条件变量取item"]
T -->|item.imu| P1["process_imu"]
T -->|item.frame| P2["process_frame"]
P1 -->|"首样本 forward_time<0"| I1["仅记录 stamp 与 last_imu"]
P1 -->|"try_integrate<br/>dt∈[0,0.2]"| INT["中点积分传播<br/>fw_rot/fw_pos/fw_vel"]
P1 -->|"dt<0 或 dt>0.2 且超前"| PEN["pending_imus 暂存<br/>(如 rosbag 全速回放)"]
P2 --> N1["先积分帧前暂存样本"]
N1 --> RE["以 KeyFrame 重锚定:<br/>位姿/速度取 T_world_imu、v_world_imu<br/>bg/ba 取 imu_bias, forward_time=帧时戳<br/>initialized=true"]
RE --> N2["再积分帧后样本<br/>仍失败的留在 pending"]
INT --> E["emit_predicted_odometry<br/>id=-1, FrameId::IMU"]
E -->|on_odometry_imu| OUT["下游订阅者<br/>(如 rviz_viewer)"]
关键节点解释:
- 队列:
boost::circular_buffer<QueueItem>(容量 4096),满时覆盖最旧项,dropped_items每累计 100 打印一次 warn,避免日志风暴; - try_integrate(cpp L98-L121):拒绝
dt < 0(过期)与dt > 0.2(超前过多),对应原实现Predict的返回语义;积分采用中点加速度/角速度:acc = imu_scale * 0.5(a₁+a₀) - ba,gyr = 0.5(ω₁+ω₀) - bg,旋转用文件内自带的 Rodrigues 公式exp_so3(hpp 注明与 Super-LIOSO3::Exp一致); - process_frame 重锚定(cpp L123-L153):先补积分早于帧时戳的暂存样本,再用关键帧重置前向状态——注意
last_imu刻意不重置(注释:保持原始行为),随后继续积分时戳晚于帧的暂存样本,仍失败的保留在pending_imus; - 输出帧语义:预测位姿打包成
KeyFrame但id=-1、frame_id=FrameId::IMU、无 bias/点云,与前端关键帧语义“松散匹配”以复用轨迹输出通道(types.hpp中KeyFrame定义)。
关键状态
| 状态 | 含义 | 归属线程 |
|---|---|---|
fw_rot / fw_pos / fw_vel |
前向传播的世界系位姿与速度 | 仅 processing 线程触碰 |
bg / ba |
陀螺/加计零偏,来自最近关键帧的 imu_bias |
仅 processing 线程 |
forward_time / last_imu |
积分时间基准与上一 IMU 样本 | 仅 processing 线程 |
initialized |
未收到首个关键帧前为 false,所有 IMU 直接丢弃 | 仅 processing 线程 |
pending_imus(容量 4096) |
超前于当前锚点的样本暂存区 | 仅 processing 线程 |
queue / kill_switch |
输入队列与停止开关 | mutex+cv 保护 / atomic |
边界条件与容错
- 未初始化:首个关键帧到来前
process_imu直接返回,IMU 样本被丢弃; - 首样本分支:
forward_time < 0时只记录时戳与样本,不积分(对应 Super-LIO ESKF 的 first-sample 分支); - dt 校验失败:过期样本直接丢弃;超前样本进入
pending_imus等锚点——这覆盖了 rosbag 全速回放、IMU 先于前端到达 的典型场景;实时运行时该缓冲保持为空,流程与原始实现完全一致; - 双缓冲溢出:输入队列与 pending 均为环形缓冲,溢出丢旧并节流告警;
- 停机语义:
stop()用kill_switch.exchange(true)保证幂等,notify 后 join;worker 在kill_switch && queue.empty()时才退出,即停止时会排空剩余队列。按extension_module.hpp的契约,AsukaROS 先对所有模块stop()再销毁,因此析构中退订回调槽是安全的; - 有意的近似(头文件注释明确列出):无扫描观测的模块无法复现原实现的两项在线估计——重力方向固定为世界系
(0,0,-gravity)(原实现由点面观测估计)、imu_scale取配置值(原实现由初始化静止加速度均值估计);机器人系输出g_odom_robo属 ROS 封装层职责,未移植。
扩展点
- 锚点来源可换:当前订阅
on_new_frame(前端关键帧),可改为订阅on_odometry_opt等回调以优化后位姿重锚定; - 在线重力/尺度估计:若给模块引入扫描观测接口,可还原 Super-LIO 的原始在线估计行为;
- 公共组件复用:模块只依赖全局回调与
KeyFrame契约,与五种里程计实现零耦合——任何向on_insert_imu/on_new_frame发事件的上游都能驱动它,这正是“可被多种里程计复用”的结构基础; - 配置:仅需调整
config_extensions.json中imu_prediction段的gravity/imu_scale,加载与否由loading_extension_modules名单控制。
Sources:
- src/asuka/src/asuka/modules/imu_prediction.cpp#L1-L178
- src/asuka/include/asuka/modules/imu_prediction.hpp#L1-L124
- src/asuka/config/config_extensions.json#L1-L57
- src/asuka/include/asuka/core/callbacks.hpp#L1-L17
- src/asuka/include/asuka/core/types.hpp#L1-L96
- src/asuka/include/asuka/utility/extension_module.hpp#L1-L26
关联源码
src/asuka/src/asuka/modules/imu_prediction.cppsrc/asuka/include/asuka/modules/imu_prediction.hpp