历史快照
本专题页写于包拆分与 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: "miao 图优化库" description: >- 图优化框架的图元素(如 base_binary_edge)与数学、字符串、配置等公共工具,是 modules/optimization_miao 的底层依赖。
miao 图优化库
模块定位
src/asuka/thirdparty/miao 是仓库内置的图优化框架,命名空间为 lightning::miao。从类命名(BaseEdge / BaseFixedSizedEdge / BaseBinaryEdge / BlockSolver / SparseBlockMatrix)到目录划分,它与 g2o 高度同构,可视为一份针对本项目裁剪重写的 g2o 风格实现。它在 asuka 中的唯一上游消费者是 src/asuka/src/asuka/modules/optimization_miao.cpp(15KB 扩展模块):该模块构造时调用 ::lightning::miao::SetupOptimizer<6, 3>(opt_config) 建立优化器,用 SE(3) 位姿顶点与二元边维护一张增量位姿图,与 optimization_loam 构成可切换的配准/优化后端。
与 odometry 插件走 dlopen 的运行期边界不同,miao 是编译期依赖:图元素全部以头文件模板形式存在,仅 optimizer.cc、graph.cc、各 opti_algo_*.cc、block_solver.hpp、string_tools.cc 等少量实现文件参与编译。库本身只依赖 Eigen(各图元素头统一引入 common/eigen_types.h),不含任何 ROS/PCL 头,属于纯算法层。
目录分层
thirdparty/miao/
├── core/
│ ├── common/ config.h(优化器配置) math.h(旋转↔四元数及解析导数) string_tools macros
│ ├── graph/ vertex/edge 模板族 + graph(容器) + optimizer(迭代调度)
│ ├── opti_algo/ optimization_algorithm 基类 + GaussNewton/LM/Dogleg + algo_select
│ ├── solver/ block_solver(H、b 组装) + linear_solver(Dense/SparseEigen/PCG/CCS)
│ ├── sparse/ sparse_block_matrix 及 CCS/hashmap/diagonal 视图
│ ├── robust_kernel/ 基类 + huber/cauchy/tukey/welsch/dcs/fair/mc_clure/pseudo_huber/saturated
│ ├── math/ marginal_covariance_cholesky(边缘化协方差) matrix_operations misc
│ └── types/ 现成 SE2/SE3 顶点与边(vertex_se3/edge_se3/prior/height_prior/vertex_pointxyz)
└── utils/ tuple_tools.h(元编程小工具)
图元素模板体系
边一侧的继承链是整个库的核心:
Edge(edge.h):运行时抽象接口,维护vertices_容器与dimension_;BaseEdge<D, E>(base_edge.h):加入编译期维度Dimension、观测类型Measurement、误差向量ErrorVector与信息矩阵InformationType;Chi2()实现为error_·(information_·error_);RobustInformation(rho)直接返回rho[1] * information_——即鲁棒核只以一阶权重缩放信息矩阵,这是与 robust_kernel 目录的对接约定;BaseFixedSizedEdge<D, E, VertexTypes...>(base_fixed_sized_edge.h/hpp):注释明确"编译期确定 size 的 edge,unary/binary/multi 都可从这类定义……真正实用化的 edge"。顶点以 tuple 存储,VertexXn<N>()返回强类型顶点指针,VertexDimension<N>()编译期取维度。关键设计是HessianType<DN, DM>的双地址结构:本地hessian_矩阵 +hessian_in_solver_远程指针,边写雅可比/海森时可直接落进求解器预分配内存,实现零拷贝组装。internal命名空间里PairToIndex/IndexToPair把顶点两两组合的线性索引映射为编译期 对,配合std::make_index_sequence展开HessianTuple/HessianTupleTransposed(顶点组合数nr_of_vertex_pairs_),大量模板展开的具体实现沉在同名.hpp(8.7KB);BaseBinaryEdge<D, E, VertexXi, VertexXj>(base_binary_edge.h):二元边只是便捷封装——把 tuple 第 0/1 块雅可比绑定为引用jacobian_oplus_xi_/jacobian_oplus_xj_,子类只需向这两个矩阵填充解析导数。位姿图中的里程计约束、回环约束都直接落在这个类上;BaseUnaryEdge与顶点侧的vertex.h/base_vertex.h/base_vec_vertex.h构成同族对应物。
classDiagram
Edge <|-- BaseEdge
BaseEdge <|-- BaseFixedSizedEdge
BaseFixedSizedEdge <|-- BaseBinaryEdge
BaseFixedSizedEdge <|-- BaseUnaryEdge
class Edge {
运行时接口: vertices_ 与 dimension_
}
class BaseEdge {
+Dimension
+measurement_ 观测
+information_ 信息矩阵
+error_ 误差向量
+Chi2() double
+RobustInformation(rho)
}
class BaseFixedSizedEdge {
编译期定长边, 顶点以tuple存储
+VertexXn(N) 强类型顶点指针
+HessianTuple 编译期展开的H块元组
+hessian_in_solver_ 求解器远端指针
}
class BaseBinaryEdge {
+jacobian_oplus_xi_
+jacobian_oplus_xj_
}
继承链各层职责:BaseEdge 管"边自身"(观测/信息/卡方),BaseFixedSizedEdge 管"边与顶点的关系"(tuple 化顶点与成对海森块),BaseBinaryEdge 只做雅可比引用的绑定。上游自定义约束时继承最末层即可。
优化器与求解链
OptimizerConfig(common/config.h)是整条求解链的配置入口,字段与默认值本身就是行为说明书:
algo_type_:GAUSS_NEWTON / LEVENBERG_MARQUARDT(默认)/ DOGLEG,对应 opti_algo 目录的三个实现(optimization_algorithm.h 为公共基类,algo_select.h 按 config 分派;GN 实现仅 1.2KB,LM 4.2KB 含阻尼策略,Dogleg 5.9KB);linear_solver_type_:默认LINEAR_SOLVER_SPARSE_EIGEN,可选 DENSE 与 PCG(注释标注"快但是糙一些");CSPARSE 与 CHOLMOD 仅留注释,未移植,linear_solver_ccs.h 也只是接口壳;is_dense_:注释规定"稠密问题不允许有 marg;稀疏问题可有可无";incremental_mode_与max_vertex_size_:增量模式面向实时定位/实时 pose graph,max_vertex_size_ = -1不限制顶点数,为正时优化器用新顶点替换最旧顶点、保持 block solver 结构不变(滚动窗口);parallel_并发开关与eps_chi2_(默认 1e-4)卡方收敛退出阈值。
迭代调度由 Optimizer(optimizer.cc,10KB)承担,操作对象是 graph.h/graph.cc 提供的顶点/边容器;每次迭代把问题交给 BlockSolver(block_solver.hpp 21KB,全库最重的模板实现)组装 H 与 b,再交给选定的 LinearSolver 在 SparseBlockMatrix(sparse 目录,hpp 15KB 配 CCS/hashmap/diagonal 三种视图)上求解;math/marginal_covariance_cholesky 用于求解后从稀疏 H 提取边缘化协方差。
flowchart TD
OM["asuka 上游模块<br/>modules/optimization_miao.cpp"]
subgraph GRAPH["core/graph:图元素与迭代调度"]
OPT["Optimizer(optimizer.cc 10KB)"]
ALGO["OptimizationAlgorithm<br/>GaussNewton / LM / Dogleg"]
BFE["BaseEdge → BaseFixedSizedEdge<br/>→ BaseBinaryEdge / BaseUnaryEdge"]
end
subgraph SOLVER["core/solver"]
BS["BlockSolver:组装 H 与 b"]
LS["LinearSolver<br/>Dense / SparseEigen / PCG"]
end
subgraph SPARSE["core/sparse"]
SBM["SparseBlockMatrix<br/>+ CCS / hashmap / diagonal 视图"]
end
OM -->|"SetupOptimizer<6,3>(OptimizerConfig)"| OPT
OPT --> ALGO
ALGO -->|"每轮迭代取 H/b"| BS
BS --> LS
LS --> SBM
BFE -.->|"hessian_in_solver_ 远程指针直写"| BS
关键节点说明:Optimizer 只负责外层迭代收敛判据(eps_chi2_),数值核心全部在 BlockSolver + LinearSolver;虚线是本库最有特点的一条路径——边不经拷贝、通过 hessian_in_solver_ 指针直接把海森块写进求解器内存,这也是增量模式能"换顶点不换求解器结构"的前提。
数学与公共工具
- common/math.h(11KB):
_q2m按 Shepperd 分支法从旋转矩阵提取四元数(依迹与对角优势分四路,返回分支序号);compute_dq_dR_w/x/y/z输出四元数各分量对旋转矩阵 9 个元素的 3×9 解析导数。它们是 SE(3) 边解析雅可比的数学底座——顶点用四元数参数化旋转,边对位姿增量求导时先经这组函数换算; - robust_kernel/:
robust_kernel.h基类加 9 种核(Huber 最大 1.1KB,其余数百字节),经robust_kernel_all.h汇总,与BaseEdge::RobustInformation的rho[1]缩放约定配合; - types/:
vertex_se3/vertex_se2/vertex_pointxyz与edge_se3/edge_se2及 prior/height_prior 先验边,是图元素模板的现成实例化样例。上游SetupOptimizer<6, 3>的模板参数正与 6 维vertex_se3、3 维vertex_pointxyz的组合相对应; - common/string_tools、macros、utils/tuple_tools.h:字符串解析与元编程基础设施,服务于配置读取和模板展开。
上游接线:optimization_miao 如何使用本库
从 optimization_miao.cpp 前 53 行可见完整接线方式:
- 从
config_extensions读取回环间隔、NDT 分数阈值、运动/回环平移旋转噪声、rk_loop_th、高度先验等参数; - 构造
OptimizerConfig(LEVENBERG_MARQUARDT, LINEAR_SOLVER_SPARSE_EIGEN, is_dense=false)并打开incremental_mode_,调SetupOptimizer<6, 3>; - 用噪声倒数平方生成
info_motion/info_loops两个 6×6 信息矩阵,填入对应二元边的information_; - 订阅
Callbacks::on_new_frame,另起input_thread与worker_thread双线程——miao 自身不做任何线程管理,并发边界完全留在 asuka 侧,库内仅有OptimizerConfig::parallel_这一个并发开关。
关键状态与边界条件
- 海森块双地址的生命周期:边的本地
hessian_与求解器远端hessian_in_solver_成对存在;增量模式下顶点滚动替换时依赖 block solver 结构不变这一前提,两块内存的一致性由库内部维护; - 增量模式的硬约束(config.h 注释逐条写明):不支持带 Marg 的顶点(Hessian index 需重排);目前不支持
SetFixed节点(会跳过 fixed nodes 导致索引重排);稠密问题不允许 marg; - 未移植能力:CSPARSE、CHOLMOD 求解器与 CCS 线性求解器仅保留接口/注释位;
- 收敛与并发:
eps_chi2_决定迭代退出,parallel_决定求解是否走并发路径。
扩展点
- 新增约束类型:继承
BaseBinaryEdge(多顶点约束用BaseFixedSizedEdge),实现误差与雅可比填充;types/ 下 SE2/SE3 系列是最直接的参考样例; - 新增优化算法:继承
OptimizationAlgorithm并在algo_select.h登记分派; - 新增线性求解器:实现
LinearSolver接口,CCS 壳已预留位置; - 新增鲁棒核:按
robust_kernel_all.h的汇总方式追加,无需改动边侧代码。
Sources:
- src/asuka/thirdparty/miao/core/graph/base_binary_edge.h#L1-L28
- src/asuka/thirdparty/miao/core/graph/base_edge.h#L1-L64
- src/asuka/thirdparty/miao/core/graph/base_fixed_sized_edge.h#L1-L170
- src/asuka/thirdparty/miao/core/common/config.h#L1-L69
- src/asuka/thirdparty/miao/core/common/math.h#L1-L260
- src/asuka/src/asuka/modules/optimization_miao.cpp#L1-L200
关联源码
src/asuka/thirdparty/miao/core/graph/base_binary_edge.hsrc/asuka/thirdparty/miao/core/common/math.hsrc/asuka/thirdparty/miao/core/common/config.h