历史快照
本专题页写于包拆分与 FAST-LIVO2 / CustomMsg / 图像输入落地之前,反映当时的单包结构与
五前端状态。已知过时点:目录布局(apps/launch 现属 asuka_ros 包)、共享的
config_sensor.json(外参已改为各前端 odometry 配置的 sensor 块)、ROS 层 acc_scale
等描述、前端数量(现六种,新增 fastlivo)、CustomMsg 与图像输入链路、thirdparty 收纳范围(2026-09-17 起 faster_ivox / small_ivox / tsl 已移至各算法模块的 include 目录)。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: "ivox 增量局部地图" description: >- faster_ivox 提供基于体素节点的三维增量局部地图(ivox3d/ivox3d_node/hilbert 索引);thirdparty/lightning/core/ivox3d 内含同源副本供 Lightning 算法使用,两者可对照阅读。
ivox 增量局部地图
模块定位
ivox(incremental voxel)是一套基于体素哈希的三维增量局部地图,为 LIO 算法的"扫描到地图"配准提供近邻查询能力。仓库中存在两份同源副本:
thirdparty/faster_ivox/:上游原始实现,代码位于fasterlio命名空间,由ivox3d.h、ivox3d_node.hpp、hilbert.hpp与eigen_types.h四个头文件组成;thirdparty/lightning/core/ivox3d/:同源副本,供 Lightning 算法使用,三个同名头文件体量均略小(如 hilbert.hpp 28.3KB vs 18.1KB),说明分叉后各自演化,适合对照阅读差异。
同为局部地图数据结构,FastLIO 走的是 ikd_tree 路线(另页详述),而 ivox 以"哈希体素 + LRU 缓存"换取 O(1) 的网格定位与有界的内存占用。
文件构成与职责
| 文件 | 职责 |
|---|---|
ivox3d.h |
IVox 主类:体素哈希表 grids_map_ + LRU 链 grids_cache_ + 近邻偏移表 nearby_grids_;提供 AddPoints / GetClosestPoint / Pos2Grid |
ivox3d_node.hpp |
体素内节点的两种实现(线性 IVoxNode 与 PHC IVoxNodePhc),以及 distance2 / ToEigen 工具与点类型特化 |
hilbert.hpp |
希尔伯特曲线编码,被 ivox3d_node.hpp 引入,支撑 CalculatePhcIndex 把体素内子立方坐标映射为一维索引 |
eigen_types.h |
提供 hash_vec<dim>(grids_map_ 整型键的哈希器,见 ivox3d.h 中 grids_map_ 的声明用法) |
IVox 主类:哈希体素 + LRU 缓存
IVox<dim=3, node_type=DEFAULT, PointType=pcl::PointXYZ> 是三参数模板(ivox3d.h L38):
- 键与网格化:
KeyType是Eigen::Matrix<int, dim, 1>整型体素坐标;Pos2Grid对点坐标乘inv_resolution_后取 floor。构造函数从Options(分辨率默认 0.2m、邻域默认 NEARBY6、容量默认 100 万体素)反算inv_resolution_ = 1/resolution并调用GenerateNearbyGrids()预生成 NEARBY6/18/26 三种偏移表。 - 核心状态:
grids_map_是unordered_map<KeyType, list迭代器, hash_vec<dim>>,grids_cache_是list<pair<KeyType, NodeType>>。存迭代器而非指针是关键设计:std::list::splice移动节点时不使迭代器失效,因此 LRU 搬移后哈希表无需重建,节点地址(DistPoint持有裸指针)也保持稳定。 - 对外接口面:
AddPoints、三个GetClosestPoint重载(单点 1-NN / 单点 KNN / 整云批量)、NumPoints/NumValidGrids/StatGridPoints统计。值得注意的是 faster 副本把grids_map_、Pos2Grid与Pos2Grid_放在了 public 区,其中Pos2Grid_支持传入自定义分辨率(按除法而非乘逆分辨率),这是为算法侧访问与多分辨率实验留出的接缝。
体素节点:线性与 PHC 两种实现
IVoxNodeType 枚举给出 DEFAULT(linear)与 PHC 两个选项,IVoxNodeTypeTraits 特化把选项映射为 IVoxNode 或 IVoxNodePhc——新增节点类型只需补充 traits 特化。
IVoxNode(线性):points_裸 vector 直接emplace_back,不去重。KNNPointByCondition线性扫点,距离平方小于max_range²才加入候选,并对本节点新增段做nth_element只保留 K 个,保证候选总量有界。可见代码中该节点没有ErasePoint。IVoxNodePhc(伪希尔伯特曲线):体素内部再按2^phc_order(断言 ≤8)细分为子立方;PhcCube只保存pcl::CentroidPoint质心——重复插入同一子立方时做均值合并而非堆点;phc_cubes_按 Hilbert 索引有序存放,插入、删除、查询均用lower_bound定位。NNPoint在定位点前后各取一个立方比较距离,属近似最近邻;KNNPointByCondition以查询点索引为起点向前后双向迭代,扩展规模由max_range换算的子立方边长与8 × side³的索引阈值共同约束。
关键流程
classDiagram
class IVox {
+Options options_
+grids_map_ unordered_map_KeyType_list_iter
-grids_cache_ list_pair_KeyType_NodeType
-nearby_grids_ vector_KeyType
+AddPoints(points_to_add)
+GetClosestPoint(pt, closest_pt, max_num, max_range) bool
+Pos2Grid(pt) KeyType
+StatGridPoints() vector_float
}
class IVoxNode {
-points_ vector_PointT
+InsertPoint(pt)
+KNNPointByCondition(dis_points, point, K, max_range) int
}
class IVoxNodePhc {
-phc_cubes_ vector_PhcCube
-phc_order_ int
+InsertPoint(pt)
+ErasePoint(pt, th)
+NNPoint(cur_pt, dist_point) bool
+KNNPointByCondition(cur_pt, K, max_range) int
-CalculatePhcIndex(pt) uint32_t
}
class PhcCube {
+idx uint32_t
+mean CentroidPoint
+AddPoint(pt)
+GetPoint() PointT
}
class IVoxNodeTypeTraits {
<<traits>>
DEFAULT 映射 IVoxNode
PHC 映射 IVoxNodePhc
}
IVox ..> IVoxNodeTypeTraits : 按 node_type 选择
IVoxNodeTypeTraits ..> IVoxNode
IVoxNodeTypeTraits ..> IVoxNodePhc
IVox o-- IVoxNode : grids_cache_ 持有
IVox o-- IVoxNodePhc : grids_cache_ 持有
IVoxNodePhc o-- PhcCube : 按 idx 有序
图中关键节点:IVox 只依赖 traits 产出的 NodeType 统一契约(InsertPoint / NNPoint / KNNPointByCondition / DistPoint),两种节点可互换;PhcCube 的质心合并使 PHC 节点以少量内存表达稠密体素。
flowchart TD
subgraph W["写入路径 AddPoints"]
A["点集 points_to_add"] --> B["Pos2Grid:floor(pt × inv_resolution)"]
B --> C{"grids_map_ 命中?"}
C -- "否" --> D["以 key×resolution 为中心构造 NodeType<br/>push_front 进 grids_cache_ 并登记 grids_map_"]
D --> E["InsertPoint"]
E --> F{"体素数 ≥ capacity?"}
F -- "是" --> G["淘汰 grids_cache_ 尾部体素<br/>map 与 list 同步删除"]
C -- "是" --> H["InsertPoint 后 splice 到链表头<br/>LRU 刷新,list 迭代器仍有效"]
end
subgraph Q["查询路径 GetClosestPoint(KNN 版)"]
P["查询点 pt"] --> R["key = Pos2Grid(pt)"]
R --> S["遍历 nearby_grids_ 偏移<br/>查 grids_map_(key+delta)"]
S --> T["各命中节点 KNNPointByCondition<br/>产出 DistPoint 候选"]
T --> U{"候选为空?"}
U -- "是" --> V["返回 false"]
U -- "否" --> X["nth_element 截断至 max_num<br/>回填最近点集"]
end
图中关键节点:
- 写入路径:LRU 淘汰只发生在"新体素插入且超容量"时,一次淘汰整个体素(含其全部点),内存上界由体素数决定;命中路径的
splice+ 更新grids_map_是 LRU 语义的核心三行。 - 查询路径:先网格定位,再按偏移表扫周边体素,候选收集交给节点;
nth_element做部分选择而非全排序,最后再排一次把最近点放到首位。 - 重载差异:单点 1-NN 版本内部走
NodeType::NNPoint,可见代码中只有IVoxNodePhc定义了它;批量版又转调单点 1-NN 版。因此KNN 重载(依赖KNNPointByCondition)才是两种节点类型通用的查询路径。
边界条件与阅读提示
- 批量查询对找不到近邻的点填默认构造的
PointType()(全零坐标),上层必须自行过滤,否则会把原点噪声引入配准。 nearby_type非法时GenerateNearbyGrids的报错日志被注释掉,nearby_grids_保持为空,查询会静默失败。IVoxNodePhc::ErasePoint中erase_distance_th_分支为空实现,属占位代码。- 距离比较全程使用平方距离(
d < max_range * max_range),避免开方。 INNER_TIMER统计宏默认关闭,开启后按每 10 万次打印一次体素内耗时分布。StatGridPoints返回{valid_num, ave, max, min, stddev},stddev对num <= 1做了除零保护。- 可见代码中没有任何互斥锁(头文件虽引入
<thread>),增删查须在单线程上下文进行——与 asuka 单 worker 的里程计消费模型天然契合。
扩展点
- 新点类型:为
ToEigen补特化即可接入(现有pcl::PointXYZ/PointXYZI/PointXYZINormal三个特化可作样例)。 - 新节点实现:特化
IVoxNodeTypeTraits,并满足DistPoint/NNPoint/KNNPointByCondition契约。 - 邻域扩展:在
GenerateNearbyGrids中追加新的NearbyType分支。 - 调参面:
Options的resolution/nearby_type/capacity直接权衡查询精度、召回与内存上界。
与上层算法的衔接
Lightning 算法使用 thirdparty/lightning/core/ivox3d 副本:配准迭代中 GetClosestPoint 提供点面/点点的对应关系,状态收敛后 AddPoints 把新扫描并入局部地图;容量淘汰机制使地图随运动自然"遗忘"远处的体素。faster_ivox 一份保留上游形态,两相对照可以看出 Lightning 侧的裁剪与演化痕迹(hilbert.hpp 少约 10KB、ivox3d 两个文件各小数百字节),是理解"同一数据结构在不同算法中如何被取舍"的最佳对照组。
Sources:
- src/asuka/thirdparty/faster_ivox/ivox3d.h#L1-L316
- src/asuka/thirdparty/faster_ivox/ivox3d_node.hpp#L1-L412
- src/asuka/thirdparty/faster_ivox/hilbert.hpp#L1
- src/asuka/thirdparty/faster_ivox/eigen_types.h#L1
- src/asuka/thirdparty/lightning/core/ivox3d/ivox3d.h#L1
- src/asuka/thirdparty/lightning/core/ivox3d/ivox3d_node.hpp#L1
- src/asuka/thirdparty/lightning/core/ivox3d/hilbert.hpp#L1
关联源码
src/asuka/thirdparty/faster_ivox/ivox3d.hsrc/asuka/thirdparty/faster_ivox/ivox3d_node.hppsrc/asuka/thirdparty/faster_ivox/eigen_types.hsrc/asuka/thirdparty/lightning/core/ivox3d/ivox3d.h