SLAM后端图优化与边缘化:因子图、信息矩阵与工程实践
做SLAM后端优化这些年,我最大的一个感触就是:很多跑了好几年SLAM的工程师,前端里程计、回环检测都能说得头头是道,但一旦聊到后端图优化里的边缘化,就开始含糊其辞。这不奇怪,边缘化确实是整个SLAM后端里最抽象、最容易出问题、也最考验工程功底的一环——它牵涉概率论、数值优化、稀疏线性代数和实际工程妥协,任何一个环节理解不到位,写出的代码就可能在某个数据集上突然崩掉。
这篇文章我想把"图优化整理流程及边缘化"这件事从头到尾捋一遍,从为什么要用图优化、因子图怎么建、求解器到底在解什么,再到边缘化的数学本质和工程陷阱,最后把图优化和计算图优化里"算子融合、常量折叠"这些概念的相通之处也串一下。文中的所有推导和代码逻辑都来自我自己在实际项目中反复验证过的做法,适合正在入门SLAM后端、或者已经能跑通Demo但想搞清楚原理的读者。
1. 为什么SLAM后端要走向图优化:从滤波到因子图的真实转变
1.1 滤波方案撑不住大规模场景的底层原因
大家最早接触的SLAM后端基本都是滤波思路,尤其是EKF。EKF的思路很直接:维护一个当前状态向量(通常是当前机器人位姿加若干路标点),每来一帧观测就用卡尔曼更新公式修正状态。这个思路的问题是,它被马尔可夫假设锁死了——当前时刻的状态只依赖上一时刻的状态,之前所有的历史信息都被"压缩"进了当前状态的均值和协方差里。
这个压缩听起来很美好,但实际工程里会越来越难受。第一,EKF的协方差矩阵是稠密的,状态向量每一维之间都有相关性,更新一次的矩阵乘法复杂度是O(n²)到O(n³)(n是状态维度)。当年我做室内小场景的时候,几百个路标点还能勉强跑实时;一旦场景变大,路标点上千,EKF每帧更新直接变成性能灾难。第二,也是最致命的——EKF的线性化点(即Jacobian的计算点)是当前估计值,一旦估计值偏离真值,误差就沿着时间轴单向累积,没有回头修正的机会。这就是为什么EKF在建图后期常常出现"轨迹飘了但系统自我感觉良好"的诡异现象。
1.2 图优化的核心思路:用历史约束换全局一致性
图优化的思路完全不一样。我们不再把历史信息压缩成一个当前状态,而是把所有的历史状态(所有时刻的位姿、所有观察到的路标点)都保留下来,显式地构建成一张"图"——图中的节点是优化变量,图中的边是这些变量之间的约束关系。里程计给相邻位姿之间加约束,回环检测给两个相距很远的位姿之间加约束,视觉/激光观测给位姿和路标点之间加约束。
这样做的好处是革命性的:任何一条边都是一个独立的约束,优化时所有约束一起参与,哪怕早期的位姿估计已经偏了,后面来了一个回环约束,整个图会重新调整,历史错误可以被"修正"。代价也很明显——图中节点的数量会随时间增长,整个优化问题越来越大。这就是为什么图优化在工程上必须配合"整理流程":什么时候把节点加入图中、什么时候把节点从图中移除(边缘化)、怎么保证移除后不丢失信息,这些就是本文的核心内容。
对我来说,理解图优化最关键的一步是接受一个观点:图优化不是"每帧都在优化所有历史",而是"在合适的时间对合适的图做优化"。有了这个观点,你就会明白边缘化不是可有可无的优化技巧,而是图优化能持续运行的刚需。
2. 因子图到底是什么:节点、因子和信息矩阵的语言
2.1 因子图的物理直觉:绕了一圈的弹簧系统
先不急着上公式。想象一个物理系统:你有一堆铁环(节点,代表机器人位姿和路标点),铁环之间用弹簧连接(因子,代表各种观测约束)。每根弹簧的自然长度和刚度各不相同——里程计弹簧自然长度是两帧间的位移,刚度取决于里程计噪声;回环弹簧自然长度是回环检测给出的相对位姿,刚度取决于回环检测的置信度。整个弹簧系统放置在一个平面上,你的任务就是找到所有铁环的一个摆放位置,让所有弹簧的总弹性能量最小。
这个比喻几乎完美地契合了因子图的物理直觉。因子图就是一种二分图(bipartite graph):一侧是变量节点,另一侧是因子节点,因子节点只和它连接的变量节点相连。一个因子实际上是一个函数,它描述了连接在其上的变量应该满足的约束关系。因子图优化的目标,就是找到所有变量的取值,让整个图中所有因子的乘积(或者说负对数之和)最大化/最小化。
2.2 从概率角度理解信息矩阵
如果从概率角度再看一层,因子图本质上是在描述一个联合概率分布。假设我们有全部变量X,以及若干观测Z,那么后验概率P(X|Z)正比于P(X)·P(Z|X)。这里的P(Z|X)可以分解成所有独立观测的乘积,每一个观测就是一个因子。当我们假设所有噪声都是高斯分布时,这个后验概率的最大化就变成了一个非线性最小二乘问题。
这里必须引入一个极其重要的概念:信息矩阵(Information Matrix),它是协方差矩阵的逆。协方差矩阵的每个元素描述了变量之间的相关性;而信息矩阵的每个元素描述的是在消除其他变量影响之后,两个变量之间的"直接约束强度"。信息矩阵有一个非常好的性质:它天然是稀疏的。因为机器人位姿之间只存在局部约束(相邻位姿、回环对),所以信息矩阵中绝大多数元素是零。
理解信息矩阵的稀疏性是你理解后面所有内容(包括边缘化、边缘化为什么难、以及为什么需要稀疏求解器)的地基。举个例子,一个10000个位姿节点的图,协方差矩阵是10000×10000的稠密矩阵,但信息矩阵可能只有几万个非零元素——这是天壤之别。
2.3 因子图构建的工程步骤
在实际工程中,构建因子图通常分三步:
第一步,定义优化变量。 在视觉SLAM里通常是位姿(SE3,6自由度)加路标点(三维坐标,3自由度);在激光SLAM里可能纯位姿或位姿加子图位姿。组织变量时要考虑后续优化的雅可比计算,通常用四元数加平移的表示方式,但优化时四元数要做扰动模型处理。
第二步,插入因子节点。 每来一帧里程计,就在相邻位姿之间插入一个"里程计因子";每观测到一个路标点,就插入一个"观测因子";每检测到一次回环,就插入一个"回环因子"。这步的关键是每个因子要携带自己的信息矩阵(即观测噪声),它的值直接决定了这条约束在优化中的权重。
第三步,决定优化时机和频率。 可以每来一帧就优化(计算量大但响应快),也可以攒够一定数量的新节点再优化(计算量小但有延迟),还可以在回环触发时才优化。我实际项目的习惯是:平时匀速插入节点时用固定窗口优化,检测到回环候选后立刻做全局优化。这本质上就是一种"整理流程"的策略,而不是机械地"每帧必优化"。
3. 图优化求解的完整流程:从最小二乘到增量方程的每一步
3.1 误差函数与最小二乘的统一表达
图优化求解的过程,本质上是在反复求解一个非线性最小二乘问题。设所有优化变量为向量X,所有因子提供的误差函数为e_i(X),那么目标是:
F(X) = Σ eᵢ(X)ᵀ Ωᵢ eᵢ(X)
其中Ωᵢ是第i个因子的信息矩阵。这个式子其实就是把所有弹簧的弹性能量加起来。最小化F(X)等价于最大化后验概率(高斯假设下)。
非线性最小二乘没有解析解,必须迭代。最常用的方法是高斯牛顿法(Gauss-Newton)或列文伯格-马夸尔特法(Levenberg-Marquardt)。二者核心都在于:在当前估计值X₀附近把误差函数线性化,求解一个线性增量ΔX,然后更新X₀ = X₀ + ΔX,反复迭代直到收敛。
3.2 线性化与Jacobian组装
线性化是图优化里最机械但最容易出错的一步。每个误差项eᵢ(X)都是关于X的非线性函数,我们把它在X₀处做一阶泰勒展开:
eᵢ(X₀ + ΔX) ≈ eᵢ(X₀) + JᵢΔX
其中Jᵢ是误差关于所有变量的雅可比矩阵。注意:Jᵢ的维度是(误差维度 × 所有变量维度),但实际大部分列都是零,因为eᵢ只和它因子连接的少数变量有关。这就是为什么最终得到的H矩阵是稀疏的。
把线性化后的误差代入目标函数,求导等于零,可以得到所谓的正规方程(normal equation):
H ΔX = b
其中H = Σ Jᵢᵀ Ωᵢ Jᵢ,b = -Σ Jᵢᵀ Ωᵢ eᵢ(X₀)。这里的H就是近似Hessian矩阵(忽略二阶项),在SLAM社区里通常直接叫"H矩阵"或"信息矩阵"(虽然严格说它是线性化后信息矩阵的近似)。
真正工程实现的时候,组装H矩阵比公式看起来要麻烦得多。因为每个因子只连接少数变量,你需要一种机制把局部雅可比矩阵"塞进"大矩阵的对应位置。我见过太多新手在这里出错,最常见的问题是索引错位——两个不同的因子更新同一个变量时,累加到了H矩阵的同一个位置,结果互相覆盖而不是相加。正确的做法是先用哈希表或者固定索引数组把变量映射到全局索引,再按(全局索引,全局索引)坐标累加,这样才能保证所有约束都被计入。
3.3 增量方程的求解与稀疏性利用
增量方程HΔX = b是整个优化里计算量最大的部分。H是稀疏对称正定矩阵(LM算法里还要加上阻尼项λI,保证正定性),解这个方程可以利用稀疏Cholesky分解或共轭梯度法等。在g2o、Ceres、GTSAM这些库里,这一层通常封装好了,你只需要选择求解器类型。
这里我想强调一个很多人忽略的点:H矩阵的稀疏性不是"顺便"的,而是图优化得以实用的前提。如果忽略稀疏性,直接对稠密矩阵做Cholesky分解,复杂度是O(n³)(n是变量维度),几万个节点根本没法算。利用稀疏性做fill-reducing ordering(比如AMD、COLAMD排序)后,分解复杂度能降到接近O(n)到O(n^1.5)。所以图优化里一个关键的"整理流程"就是:在求解之前对变量做重排序,把H矩阵的非零元尽量往对角线附近聚拢,减少分解时的fill-in。
这里顺便点一下: 算子融合和常量折叠 ,这两个词虽然更多出现在编译器/深度学习框架里,但它们的核心思想和图优化的求解流程是相通的。算子融合就是把多个小计算步骤合成一个大步骤(比如把雅可比计算和H矩阵更新融合),减少中间数据的搬运;常量折叠就是提前把固定不变的计算结果缓存下来,避免重复计算。在SLAM图优化里,我们常做的"固定雅可比在回环前的预计算"、"因子图的稀疏模式复用",本质就是同样的思想。后面我会专门展开这一节。
4. 边缘化的本质:不是删除,而是信息压缩
4.1 边缘化的概率学含义与Schur补
现在终于到重头戏了。当图里的节点越来越多,优化规模越来越大,我们不可能无限地保留所有历史节点。但直接删除节点就丢掉了它携带的所有约束信息,这会导致后续优化缺失一部分"观测事实"。边缘化(Marginalization)提供了一种两全的做法:把要被移除的变量从联合概率分布中积分掉,把它的影响"压缩"到剩余变量的先验约束里。
在概率论里,这是很自然的一个操作。假设变量X = [X_m; X_r],其中X_m是要边缘化掉的变量(marginalized),X_r是保留变量(retained)。联合分布的协方差为Σ,信息矩阵为Λ = Σ⁻¹,两者按划分写成分块形式:
Λ = [[Λ_mm, Λ_mr], [Λ_rm, Λ_rr]]
我们要得到边际分布P(X_r),它的信息矩阵(即边际分布的信息矩阵)可以用 Schur补 计算:
Λ_rr_new = Λ_rr - Λ_rm · Λ_mm⁻¹ · Λ_mr
这个式子看起来简单,背后的含义却非常深刻:原本保留变量X_r之间的直接约束是Λ_rr,但通过X_m的"传导",X_r之间还隐藏着间接约束。边缘化操作把这两部分叠加在了一起,形成一个新的、更"稠密"的约束关系Λ_rr_new。
用一个生活中的类比:你有一个三人小组,A(要离职)和B、C(要留下)之间都有工作交接。A走了之后,不能就当没这个人,B和C之间会因为"共同知道A的一些事"而多出新的协作关系。Schur补就是把这个"新的协作关系"算出来,固化在B和C之间。
4.2 为什么滑动窗口必须边缘化而不是直接丢弃
在滑动窗口(Sliding Window)优化框架(比如VINS-Mono、OKVIS)里,我们会设定窗口大小,比如窗口内只保留最近10帧位姿和若干路标点。每当新帧进来,就要"踢出"一帧最老的。很多人一开始会想:直接把它从图中删掉不就行了吗?
不行。原因在于,被删掉的这一帧上挂着大量约束——它和周围帧的里程计约束、它观测到的路标约束、它带来的回环约束。如果直接删除,等于把这些约束从优化目标里抹除了。这会造成两个问题:一是信息丢失,系统的总约束变少,状态估计的确定性下降;二是更隐蔽但更严重的——可观测性问题,删掉某些关键约束后,系统的不可观方向(如绝对位姿、绝对尺度)会发生变化,导致状态在部分方向上"无约束漂移"。
边缘化则不同:它在删节点之前,先把这些约束的信息通过Schur补"投影"到剩余变量上。这样,虽然节点没了,但它带来的信息量被保留了。代价是,投影后的约束会在剩余变量之间产生新的稠密连接——这正是边缘化在工程上最棘手的地方:它破坏了H矩阵原来的稀疏模式。
4.3 边缘化在g2o/Ceres等框架中的落地方式
如果你用的是g2o,它并不直接提供边缘化接口——你需要手动操作H矩阵,或者在移除节点前自己算Schur补,再把得到的信息加入相关保留节点之间的边。Ceres也没有直接的高层API,但它提供了"边缘化"的概念——在求解时使用Schur补作为求解策略(即先消去路标点,求解位姿),这和在窗口移除时做信息保持不是一回事。
最实用的一套流程是我在自家SLAM系统中实现的,总结如下:
- 确定要边缘化的变量集合 :通常是滑窗里最老的一帧位姿,以及所有只在它和少数其他帧上被观测到的路标点。
- 收集所有和被边缘化变量相关的因子 :包括这些变量之间的因子、它们和保留变量之间的因子。
- 构建局部信息矩阵 :只考虑这些相关因子,构建一个局部H矩阵,并按"被边缘化变量/保留变量"重新分块。
- 计算Schur补 :得到新信息矩阵Λ_rr_new,关加到保留变量对应的H矩阵块上。
- 从图中物理删除被边缘化的变量和因子 ,同时为保留变量之间的新增稠密约束创建新的"先验因子"或"稠密边"。
这套流程每一步都藏着很多实现细节,比如Schur补计算时Λ_mm可能奇异(当被边缘化变量没有足够约束时),怎么加阻尼;再比如新生成的稠密边会让后续H矩阵的稀疏模式不再恒定,怎么通过提前预留空间来优化内存。这些细节我会在下一节逐一展开。
5. 边缘化工程实践里的四个深坑
5.1 第一个坑:FEJ(First Estimate Jacobian)与信息矩阵不一致
这个坑是所有做边缘化的人都躲不过去的,也是很多SLAM系统精度上不去的幕后黑手。Edge of the problem:当我们做完一次边缘化后,新的信息矩阵Λ_rr_new是在某个特定的线性化点(即被边缘化变量的当前估计值)上计算出来的。这个线性化点被固定下来了——因为变量已经被移除,之后没有办法再更新它。
问题在于:系统继续运行,保留变量在后续优化中被不断更新,它们的估计值偏离了边缘化时使用的线性化点。此时,边缘化带来的新约束(它本质上是一个先验项)和当前优化的其他约束,分别使用不同线性化点算出的雅可比矩阵。这两个信息矩阵的信息内容不再一致——相当于把"不同时间拍的快照"强行拼成了一张照片。
这个不一致会导致系统过信某些陈旧的约束,甚至引入虚假信息,严重时整个优化会被"锁死"在错误的地方。业界标准解法是**FEJ(First Estimate Jacobian)**原则:对所有和边缘化相关的变量,在后续优化中计算雅可比时,都固定在第一次被纳入边缘化因子时的线性化点上,而不是用当前估计值重新计算。
FEJ实现起来很简单——在因子图里存一份"首次估计值",计算雅可比时用这两个值之差,做完优化更新后再同步更新首次估计值。但踩过这个坑的人都知道,问题往往不在计算雅可比的地方,而在于你忘了存"首次估计值",或者某个变量在边缘化之后又被另一个因子间接约束,导致线性化点被悄悄更新了。
5.2 第二个坑:H矩阵稀疏结构被破坏后的性能灾难
边缘化带来的稠密连接是性能上的"慢性毒药"。前面说过,稀疏求解器的高效性依赖于H矩阵的稀疏结构。每做一次边缘化,被边缘化的变量和其它保留变量之间就会产生稠密的Schur补块。如果频繁边缘化,这些稠密块会越积越多,H矩阵的稀疏率急剧下降,Cholesky分解的fill-in也会爆炸式增长。
我在这方面吃过一次大亏:一个测试平台上跑视觉惯性融合定位,边缘化滑窗帧后,求解耗时从每轮8毫秒直接暴涨到200毫秒,并且随时间继续恶化。排查很久后发现,问题就在边缘化产生的稠密块没有做任何稀疏化处理——随着窗口不断滑动,新的稠密块不断叠加到H矩阵上,最后几乎变成稠密矩阵。
工程上可用的应对策略有三个:
- 限制稠密块的数量 :比如规定Schur补产生的新因子只连接到当前窗口内最近的N个帧,超出范围的直接丢弃(近似处理)。
- 定期做一次"分块稀疏化" :对信息矩阵做阈值裁剪,把绝对值小于阈值的项置零,恢复一部分稀疏性。这有精度损失,但控制得好影响极小。
- 使用能更好处理fill-in的求解器 :比如利用SuperLU或带AMD排序的Cholesky分解,可以在一定程度上缓解数值填充。
这三个策略没有一个是完美的,实际中往往是三者的组合——这也就是我理解的"整理流程"中非常核心的工程博弈部分。
5.3 第三个坑:可观测性与尺度漂移
边缘化后,系统的可观测方向和不可观测方向会发生微妙变化。在单目视觉惯性SLAM中,绝对尺度是(部分)可观的——通过IMU的加速度积分约束。但如果你在边缘化时把带有尺度信息的约束边缘化掉,而后续窗口内恰好又没有足够的视差/加速度激励,尺度就会不可控地漂移。
这个问题的微妙之处在于它不像前面两个坑那样立刻浮现,往往是跑长数据集时,前端看起来一切正常,但轨迹的绝对位置在慢慢漂移,且无论如何调权重都压不住。根源就是边缘化把系统的一部分"校准信息"给丢了——不是彻底消失,而是转换成了对剩余变量的先验约束,而剩余变量在后续优化中又不断被更新,先验的约束力逐渐被稀释。
应对方案是在边缘化策略上做文章:不要把带有尺度信息的约束(比如IMU预积分因子、重投影因子中具有足够视差的部分)全部丢进边缘化集合,而是要"保底"——在窗口中始终保留至少一个能约束尺度的结构(比如始终保留足够帧数的IMU预积分链,或一个固定路标点集)。这个策略在我的工程实践中被证明是非常有效的,代价是窗口管理的复杂度上升了一些。
5.4 第四个坑:工程实现里的"鬼故事"——我的踩坑复盘
具体的工程实现里还有无数小坑。我先说一个最典型的复现链路,希望能帮后来者省下至少一周时间。
某次我在测试一个自研的图优化后端时,突然发现只要滑窗超过8帧,优化结果就明显变差,而且是越跑越差。初步怀疑是边缘化代码写错了,但我反复检查了Cholesky分解和Schur补的公式,都没有发现明显问题。折腾了两天后,我把边缘化前后系统的总信息量(H矩阵的迹)打出来对比,才发现每次边缘化后总信息量不降反升——而且升得离谱。
这个异常说明我算的Schur补把信息"放大"了。追下去发现,问题出在一个很隐蔽的地方:我在计算局部H矩阵时,把同一条边(比如一个路标观测因子)既算在了"被边缘化变量集合"的内部约束里,又算在了"被边缘化变量与保留变量之间的约束"里,导致同一个观测被加了两次。修正之后,信息量正常了,优化结果也恢复正常。
这事的教训是: 调试边缘化问题,务必先写"信息量守恒"的单元测试 ——边缘化前后,被保留变量上的信息量只能不增加(在理想高斯线性情形下应该完全相等),一旦出现显著增加或减少,一定是代码里出bug了。这个测试写起来很简单:把局部H矩阵拆成四块,手动算一次Schur补,和接口输出对比即可。但它能帮你省掉的排查时间是真的非常多。
6. 图优化流程整理与计算图优化:算子融合和常量折叠的启发
6.1 从SLAM图优化到计算图优化:同一套抽象逻辑
聊完边缘化的具体工程实现,我想把视野拉高一点。近两年"算子融合"和"常量折叠"这两个词越来越多地出现在深度学习推理框架、编译器的讨论里,很多人可能觉得这和图优化是两个世界的概念。但如果你从抽象层面看,它们其实是同一个思想的两种体现: 对一张"图"做等价变换,让计算更高效 。
在SLAM中,我们的"图"是因子图,节点是状态变量,边是约束;"整理流程"就是决定节点何时加入、何时移除、如何保证信息不丢。在深度学习推理/编译器中,我们的"图"是计算图(Computational Graph),节点是算子(卷积、ReLU、矩阵乘法等),边是张量数据流;"整理流程"同样是把原图变成性能更好的等价图——这恰恰就是算子融合和常量折叠在做的事。
6.2 算子融合与常量折叠的核心思想
算子融合 的核心思想很简单:把多个可以合并的算子合并成一个算子,减少中间数据的内存读写和kernel启动开销。最经典的例子是把"卷积+批量归一化+ReLU"融合成一个复合算子。如果单独算,每一次操作都要把中间张量写回内存、再读出来传给下一个算子;融合后,中间张量完全不用落地,直接在一个kernel内部完成所有计算。
这和SLAM图优化里的什么对应?对应的是"H矩阵组装和求解的融合"——当你提前知道某些因子的雅可比更新模式时,可以先把这些因子的贡献直接累加到一个预分配的内存块里,避免反复遍历稀疏结构。还有就是"边缘化后的稠密先验块的复用"——这块Schur补的结果在窗口滑动中可以预计算好,而不是每次优化时重新组装。
常量折叠 的思想则更直接:如果在编译期能确定某个表达式的结果是常量,那就直接算出这个常量,把它替换掉所有使用它的地方,运行时就不需要再算了。在深度学习中,如果一个子图的输入都是静态已知的,完全可以在编译期完成前向计算,把结果固化下来。
在SLAM图优化里的对应场景也很常见:很多因子的噪声参数(信息矩阵)是固定不变的,我们可以在启动时就把雅可比的稀疏模式、因子索引映射表一次性构建好,之后每次迭代只更新数值而不更新结构。这本质上就是"常量折叠"——把结构信息当作常量,把数值信息当作变量。另外,某些固定设备对的标定结果、传感器延迟值,也可以折叠进因子的预计算中,避免每次构建因子图时的重复计算。
6.3 对SLAM后端的借鉴意义:先整理图,再优化图
如果用一个词来总结这部分的启发,我觉得是"整理"。无论是SLAM的因子图还是深度学习计算图,性能优化的大头从来不是单点算得快,而是 整个图的结构是否被精心整理过 。你有没有做变量重排序来降低fill-in?你有没有预计算固定的雅可比模式?你有没有把冗余的计算折叠掉?你有没有设计好边缘化策略来平衡信息保持和稀疏性?
这个思路完全可以反向借用到SLAM后端工程中:
- 优化前先"整理图" :检查有没有重复的因子、有没有可以合并的弱约束、有没有数值病态的条件数问题。这些整理不会改变优化结果的理论最优值,但能显著提升收敛速度和数值稳定性。
- 设计好"图的生命周期" :从节点出生(加入因子)、成长(更新约束)、衰亡(被边缘化)的整个生命周期,一次性规划清楚,而不是问题出现了再去补丁式处理。
- 多做基准测试和指标监控 :像信息量守恒测试、H矩阵稀疏率监控、单轮求解耗时监控,这些和深度学习里的profile工具是异曲同工——没有指标,你根本不知道整理是在优化还是在劣化。
最近一年我在设计新的后端框架时,已经完全把"算子融合、常量折叠"的编译器思维纳入进来:因子类型不再是一个个独立的类,而是根据其结构模式预编译成"无分支kernel";窗口滑动时的边缘化不再靠事后修正,而是在因子图构建时就预留好标准化的边缘化接口。实测下来,相同精度指标下,求解耗时的波动范围压缩了约60%,长序列运行也更加稳定。
最后再分享一点个人经验:如果你正在学习或者正在写自己的图优化后端,别急着上边缘化。先把不含边缘化的全局图优化搞得非常稳定,再逐步加入滑动窗口和边缘化;每加一步,都要配上对应的数值验证和耗时监控。图优化和边缘化这套东西,说难也难,说简单也简单——难在细节魔鬼太多,简单在只要你愿意从数学原理一层层往下验证,绝大多数坑都是可以提前排掉的。
更多推荐
所有评论(0)