历史快照
本专题页写于包拆分与 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: "五种算法总览与 so_name 动态选择" description: >- 通过 config_odometry 的 so_name 在 batchlio/fastlio/lightning/smallpointlio/superlio 五个动态库之间切换;每个实现目录的 create.cpp 导出 create_odometry_estimation 供 dlopen 后调用,config_batchlio.json 等提供各自参数边界。本页是算法矩阵的导航页。
五种算法总览与 so_name 动态选择
本页是算法矩阵的导航页。仓库内五种里程计实现(batchlio / fastlio / lightning / smallpointlio / superlio)各自编译为一个动态库,进程启动时由 config_odometry 的 so_name 决定装载哪一个。本页只讲这条"选择与装载"公共链路;各算法内部主流程请阅读各自的专页。
1. 模块职责划分
参与选择与装载的代码非常少,且各自职责单一:
| 文件 | 职责 |
|---|---|
core/odometry_estimation.cpp |
基类静态工厂,仅 11 行:load_module(so_name) 薄封装为 load_module_from_so<OdometryEstimation>(so_name, "create_odometry_estimation"),把"返回类型 + 导出符号名"这两个契约要素绑定在一起 |
utility/load_module.cpp |
dlopen/dlsym 原语(load_so/load_symbol)与进程级句柄注册表,是所有动态插件(含扩展模块)共用的装载通道 |
odometry/<impl>/create.cpp |
每个实现导出的 extern "C" 工厂,五个副本均只有 181–193 字节 |
config/odometry/config_*.json |
每算法一份配置,odometry.so_name 指定 so 文件名,其余键界定各自的参数边界 |
2. 调用链:构造期的一次性选择
asuka_ros.cpp 构造函数前 20 余行完成了整个选择过程:
GlobalConfig::instance(config_dir, true)定位包内 config 目录后,以get_config_path("config_odometry")打开里程计配置;odometry_config.param<std::string>("odometry", "so_name", "")读出 so 名(默认空串);OdometryEstimation::load_module(so_name)发起动态装载;- 若返回空指针,立即
throw std::runtime_error("failed to load odometry module: " + so_name),进程拒绝启动; - 成功后马上执行两条装配动作:
odometry->cloud_preprocess()把算法私有的预处理取出共享给 ROS 接入层(insert_frame中直接调用),再用odometry构造AsyncOdometryEstimation异步执行器。
顺序有讲究:模块装载发生在 ROS 话题/标定参数读取(L34 起)与扩展模块加载(L42)之前。算法选型失败会在任何数据通路建立之前终止进程——这是典型的 fail-fast 设计。装载完成后,运行期的 insert_imu/insert_frame → 异步队列 → process_once 调用全部落在被选中的那个实现上,对上层完全透明。
3. dlopen 细节与关键状态
load_module.cpp 里有三个值得记住的要点:
- 进程级注册表:
open_handles(std::map<std::string, void*>,互斥锁保护)按 so 名缓存句柄。同一次进程内重复装载同名 so 直接复用,不会重复dlopen。 - 句柄永不关闭:注释明确写出"handles are intentionally never closed today: plugin code must stay resident for the lifetime of the process",并把这个注册表标注为未来卸载/重载实现的接缝。这意味着当前不支持运行时热切换——换算法必须改配置后重启。
RTLD_LAZY懒绑定:函数符号延迟解析;工厂符号本身通过显式dlsym("create_odometry_estimation")查找,找不到时spdlog::warn输出dlerror并返回 nullptr。
4. create.cpp 插件契约
以 fastlio 的 6 行文件为样板:
extern "C" asuka::OdometryEstimation* create_odometry_estimation() {
return new asuka::fastlio::OdometryEstimation();
}
extern "C" 关闭 C++ 名字改编,保证 dlsym 能按字面符号名找到工厂;工厂内 new 具体实现并按基类指针返回。五个目录的 create.cpp 除类型名外完全一致,是"算法接入主链路"的最小接入样板。
5. 装载流程图
flowchart TD
CFG["config_odometry<br/>odometry.so_name"] -->|"param<string><br/>默认空串"| NAME["so_name<br/>例: libasuka_odometry_batchlio.so"]
NAME --> LOAD["AsukaROS 构造函数<br/>OdometryEstimation::load_module(so_name)"]
LOAD --> TMPL["load_module_from_so<OdometryEstimation><br/>符号: create_odometry_estimation"]
TMPL --> CACHE{"open_handles<br/>注册表命中?"}
CACHE -->|"是: 复用句柄"| DLSYM
CACHE -->|"否"| DLOPEN["dlopen(so_name, RTLD_LAZY)<br/>失败: warn + dlerror → nullptr"]
DLOPEN -->|"成功: 句柄登记"| DLSYM["dlsym(create_odometry_estimation)<br/>失败: warn + nullptr"]
DLSYM --> NULLCHK{"工厂结果为空?"}
NULLCHK -->|"是"| THROW["throw runtime_error<br/>failed to load odometry module"]
NULLCHK -->|"否"| FACTORY["create.cpp 工厂<br/>new <impl>::OdometryEstimation()"]
FACTORY --> WIRE["装配主链路:<br/>cloud_preprocess 共享给 ROS 层<br/>AsyncOdometryEstimation 接管计算"]
关键节点说明:
- so_name:唯一的算法选择旋钮,来自 JSON 配置而非编译期决定;
- open_handles 判断:进程级缓存分支,保证同名 so 只 dlopen 一次;
- dlsym → 工厂:基类与实现之间唯一的运行时绑定点,extern "C" 是这个绑定点成立的前提;
- throw 分支:空指针沿调用链上抛为异常,构造失败即进程失败;
- 装配节点:装载成功后有两个出口——预处理对象交给 ROS 层、估计器本体交给异步执行器,此后五种算法在框架眼中不再有区别。
6. 五实现文件矩阵
| 实现 | odometry_estimation.cpp | imu_preprocess.cpp | cloud_preprocess.cpp | create.cpp | 额外文件 | 配置体积 |
|---|---|---|---|---|---|---|
| batchlio | 43.8KB(最大) | 3.2KB | 5.3KB | 183B | common.cpp 4.2KB | 1294B(最详尽) |
| fastlio | 16.8KB | 8.7KB(最重) | 4.2KB | 181B | — | 512B(最精简) |
| lightning | 23.3KB | 2.9KB | 3.0KB | 185B | — | 794B |
| smallpointlio | 15.0KB | 0.7KB(最轻) | 6.4KB(最重) | 193B | — | 919B |
| superlio | 18.3KB | 1.2KB | 2.8KB | 183B | eskf.cpp 5.8KB、manifold.cpp 7.2KB | 690B |
体积分布透露了各实现的取舍方向:batchlio 把计算重心压在估计主循环(全仓库最大单文件);fastlio 的 IMU 预处理最重,前向传播与时间同步下沉在预处理里;smallpointlio 几乎不做 IMU 预处理却拥有最重的点云预处理(特征处理);superlio 自带 ESKF 与流形运算库,目录结构最完整;lightning 框架侧文件中等,核心逻辑在 thirdparty/lightning 库内。按矩阵挑体积异常项,是进入各专页前最省力的阅读线索。
7. 配置变体与参数边界
五份 config_*.json 同构而不同界:
- 公共骨架:
odometry节(so_name +imu_buffer_capacity/lidar_buffer_capacity,两份证据中均为 1000/50)+preprocess+mapping+filter;batchlio 额外多出batch(dt/deskew/enable_openmp)与publish节。 - odometry 节也承载算法私有键:fastlio 在其中放
max_iteration/laser_point_cov,batchlio 则只放缓冲容量——同一节的名字相同,语义边界并不通用。 - 预处理边界各自独立:
max_distance1000.0(batchlio)vs 100.0(fastlio)、point_filter_num1 vs 3、scan_line6 vs 4。切换算法即切换一整套预处理边界,不能假设参数跨实现可迁移。 - mapping 节差异最大:batchlio 收纳去饱和阈值、十余项协方差、ivox 网格参数、重力向量等 20+ 项;fastlio 只有
gravity_estimation与fov_degree两项。
8. 边界条件与失败路径
- 空 so_name:配置缺失时
param返回默认空串"",dlopen必然失败 → nullptr → 构造函数抛异常; - so 文件缺失或依赖断裂:
load_sowarn 两行(文件名 +dlerror()详情)后返回 nullptr; - 符号未导出:实现目录漏掉 create.cpp 或未加
extern "C"时,load_symbolwarn 后返回 nullptr,同样走到 throw; - 不可热切换:句柄常驻、无关闭路径,算法选择只在构造期发生一次;
- 注册表线程安全:查缓存与写入均在
handle_mutex保护下进行,但缓存命中仅免除dlopen,dlsym每次装载路径都会执行。
9. 扩展点
- 新增第六种算法:在
odometry/下新建目录,实现OdometryEstimation子类与定制的 cloud/imu 预处理,补一份 create.cpp 导出工厂,再新增config_<name>.json指向新 so——core 层与AsukaROS零改动。 - 卸载/重载:
load_module.cpp注释已把open_handles注册表预留为接缝,未来实现插件卸载只需在此收口。 - 同构机制:扩展模块(ExtensionModule)复用同一条 dlopen 契约,仅导出符号不同,两种插件共享同一装载边界。
10. 阅读入口建议
进入任一算法专页前,建议顺序为:该目录的 create.cpp(确认接入方式)→ 矩阵中的体积异常文件(定位该实现的计算重心)→ odometry_estimation.cpp 主流程 → 对应 config JSON(对照参数边界)。
Sources:
- src/asuka/src/asuka/core/odometry_estimation.cpp#L1-L11
- src/asuka/src/asuka/utility/load_module.cpp#L1-L61
- src/asuka/src/asuka/odometry/fastlio/create.cpp#L1-L6
- src/asuka/src/asuka/asuka_ros.cpp#L1-L130
- src/asuka/config/odometry/config_batchlio.json#L1-L60
- src/asuka/config/odometry/config_fastlio.json#L1-L25
关联源码
src/asuka/src/asuka/asuka_ros.cppsrc/asuka/src/asuka/utility/load_module.cppsrc/asuka/config/odometry/config_fastlio.jsonsrc/asuka/config/odometry/config_batchlio.json