ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

hyperframe超帧:解决长时SLAM因子图爆炸与退化漂移

2026/9/10 9:03:35 拓冰建站 浏览量
hyperframe超帧:解决长时SLAM因子图爆炸与退化漂移 先讲一个我上个月真实遇到的问题。项目里一台移动机器人要在室内长走廊环境里连续跑两个小时传感器配置是激光雷达加IMU加轮速计后端走的是因子图优化。单看前端定位短时间之内效果很漂亮帧间匹配误差能控制在厘米级可是一旦把运行时长拉到以小时为单位问题就全冒出来了普通帧直接作为优化节点塞进因子图节点数量涨得飞快优化器越来越慢地图维护成本也直线上升更麻烦的是长走廊里激光扫到两堵平行的墙帧与地图之间的约束在横向自由度上几乎是退化的单帧观测根本压不住累积漂移。最后让我把整条链路从底层改掉的不是换一个更强的前端匹配算法而是把“帧”这个基本单位升级成了“hyperframe”。如果你以前没接触过“hyperframe”这个词我先用一句话把它说明白hyperframe超帧把一段时间窗口内、一个局部里程范围内产生的所有传感器帧打包成一个整体单元作为后端优化和地图维护的基本对象。它既不是单纯的关键帧抽稀也不是把每一帧都当作独立优化节点参与全局优化而是取了一个折中超帧内部保留帧与帧之间的密集相对约束超帧对外则以一个整体身份参与回环检测、地图匹配和全局优化。这套思路在很多多传感器融合和SLAM系统里都出现过只是不同团队叫法不同有的叫submap group有的叫frame block有的直接叫bundle但核心思想是一致的。这篇内容我会从建模逻辑、工程实现、参数选择、实测踩坑几个角度把hyperframe这条路讲透。适合谁看呢如果你正在做激光SLAM或多传感器融合发现自己的因子图规模一跑长就爆炸或者长期在退化环境里漂移压不住那这篇文章应该能给你一个非常实用的中间层设计思路。1. 为什么单帧位姿当优化节点跑长了必然出事1.1 从一次真实的定位翻车现场说起先说那次让我下决心改架构的翻车现场。当时机器人从A区穿过一条大概40米长、两侧全是玻璃墙的通道进B区。玻璃墙对激光来说基本是透明的前半段扫描还能打回来一些稀疏点云后半段几乎彻底退化只剩下走廊尽头的一扇防火门能提供有效约束。结果机器人在通道中段开始出现横向漂移走到一半时位姿已经偏了差不多30厘米等重新匹配上防火门时后端一次性把轨迹“拉”回去画面上出现一条明显的折线地图也裂了一道缝。我复盘的时候第一反应是前端匹配参数有问题把扫描匹配的阈值调严、把IMU权重调高试了一圈改善有限。后来把优化后端的数据拉出来看发现问题不在前端的帧间估计而在后端维护的约束结构所有帧都平等地挂在因子图里帧与帧之间只有局部的里程约束当环境出现大段退化时远端没有任何一个“锚点”能把这段轨迹钉在地图上于是所有误差都在退化的自由度上自由游走直到重新观测到强特征才一次性爆发。1.2 单帧位姿为什么不够用帧与地图之间的“债务问题”你可以把整个优化过程想象成记账。每一帧位姿相对上一帧的估计相当于一笔“小额贷款”单独看都没问题债权人地图和债务人当前帧距离很近信任度很高。但问题是当机器人跑了很长时间前端会产生成千上万笔这样的贷款而后端手里的本金强特征观测、回环约束并没有同步增加。欠条太多、现金太少一旦遇到某个环节估计错了整个系统就得靠后续的强约束来“还债”。更具体一点说在经典的关键帧方案里后端为了控制规模会按固定距离或时间间隔抽帧比如每隔1米抽一个关键帧进优化器。抽帧的问题是它把中间的细节全部丢掉如果机器人在这1米里发生过一次微小侧滑或者IMU的bias在短时间内跳了一下这些信息再也不会出现在后端里只能被当作噪声消化掉。而高频全帧方案虽然保住了细节但优化规模会随运行时间线性甚至超线性膨胀跑半小时以后一次全局优化要好几秒系统实时性直接崩掉。1.3 hyperframe与关键帧、子图的边界hyperframe想解决的正是“既要细节、又要规模可控”这个矛盾。它把一段时间内的所有帧当作一个刚性偏弹性的整体从超帧外部看它是一个单独节点可以在全局优化里被整体搬动从超帧内部看它保留了一整套帧与帧之间的相对位姿约束这些约束在优化时仍然存在只是不会把整个图撑爆。这个思路和子图submap有一点像但有一个本质区别子图通常是把一批点云拼成一个局部地图之后对外只保留这个局部地图的位姿内部帧的约束被抹平了而hyperframe对外是整体、对内保留约束内部帧的相对关系依然可优化、可更新。也就是说子图是“快照”hyperframe是“账本”。当回环约束落到超帧内部的某一帧上时子图方案只更新子图整体位姿内部细节无法修正hyperframe方案则能把修正量通过内部约束传播到整个超帧内部的每个帧上地图细节也跟着被修正。2. hyperframe的建模逻辑它到底是在优化什么2.1 内部约束超帧内各帧的相对位姿是硬约束既然超帧内部保留了一堆帧第一步就要明确这些帧之间的约束从哪来。以我项目里的传感器配置为例激光雷达10HzIMU 100Hz轮速计50Hz。每个激光帧进来的时候我会用IMU预积分加上轮速计推算出一个相对上一激光帧的相对位姿同时做一次激光扫描匹配得出观测位姿。这两者进入后端就构成了一个帧间相对约束。假设一个超帧包含50帧激光5秒数据内部就有49个相邻帧间约束这些约束的噪声模型不是随便拍的IMU预积分的协方差可以通过预积分公式逐步递推得到扫描匹配的协方差可以用匹配时的信息矩阵近似。实际操作里我会给每个相对约束配一个鲁棒核Huber核因为扫描匹配偶尔会出离谱结果不加核的话一个野值就能把整个超帧的内部结构带歪。2.2 跨超帧约束外部解算、回环与观测如何挂进来跨超帧约束是hyperframe方案里最容易想歪的地方。很多人会惯性思维地把超帧整体当作一个刚体节点回环检测到某两帧之间的匹配后直接把这两个超帧的锚点位姿连一条边。这个做法在子图方案里没问题但在hyperframe里会浪费掉内部约束的弹性。正确的做法是一条回环约束应该“穿”进超帧内部连接具体的帧对而不是连接超帧锚点。比如超帧A的第17帧和超帧B的第33帧是同一个物理位置的重复观测那就在这两帧之间建立一条相对位姿约束。优化时这条约束的残差先作用到内部帧上再通过内部相对约束传播到整组超帧位姿。这样等于在全局图中形成了一种“约束穿透”效应——回环不只是把两个超帧的距离拉近还会让超帧内部的局部形变也一起被修正。2.3 位姿图视角下的信息矩阵长什么样用位姿图的语言来说hyperframe方案的信息矩阵天然带有块状结构。每个超帧的内部帧互相连接形成一个稠密的块超帧之间通过稀疏的外部约束相连比如回环约束、跨超帧的里程约束等。从矩阵结构上看它既不是全稠密也不是稀疏到极致而是一种“块内稠密、块间稀疏”的模式。这种结构的优势在于现代稀疏优化库比如g2o、GTSAM、ceres对这种块结构非常友好可以在消元的时候把超帧内部帧先边缘化掉只在超帧锚点层面做全局优化。等全局优化收敛后再通过内部约束把锚点的修正量“投影”回内部帧。这种两阶段策略能把一个十万帧级别的优化问题压缩成一个几千超帧级别的优化问题求解时间从秒级降到百毫秒级精度损失却可以控制得很小。3. 落地实现时要回答的几个工程问题3.1 超帧的触发条件按里程、按时间、还是按退化检测建模思路清楚了接下来最直接的工程问题就是什么时候打包一个超帧我见过三种方案各有适用场景。第一种是按里程触发机器人每走过固定距离比如5米就封一个新超帧。好处是超帧大小均匀适合开阔环境坏处是原地旋转或缓慢移动时长时间不产生新超帧内部帧堆积过多。第二种是按时间触发固定每隔几秒封一个。好处是数据率稳定适合快速运动场景坏处是机器人静止时会产生大量重复帧浪费存储。第三种是按退化检测触发当前端匹配的信息矩阵条件数超过阈值时立刻封帧、开新超帧。这个方案最智能能保证每个超帧内部的观测质量相对一致但实现复杂度高需要一套可靠的退化检测逻辑。我最后用的是里程触发 时间上限双重保险走过5米或者超过5秒没有封帧就强制封一个。同时把退化检测作为一个“提前封帧”的信号检测到退化时主动缩小超帧长度让之后的强观测尽快形成一个新锚点。参数上最初我直接用固定5米/5秒跑了一周后根据走廊环境调到3.5米/3秒大家抄作业的时候建议从自己的场地实际调试。3.2 数据结构怎么设计从帧容器到位姿节点的映射实现层面很多第一次写hyperframe的人会踩一个坑把所有数据塞进一个数组然后后端优化时遍历每个内部帧去建节点结果发现超帧边界处的约束断开了。这里的关键在于设计时要区分两层身份。我给内部帧设计了两个ID一个是内部帧自身的唯一ID一个是它所属的超帧ID。全局位姿图中只给超帧锚点建优化节点内部帧节点只在“需要时”才被物化出来平时它们作为超帧的成员存在。伪代码大概是struct InnerFrame { uint64_t frame_id; uint64_t hyperframe_id; SE3 pose_in_hyperframe; // 相对超帧锚点的位姿 SE3 T_world_anchor; // 锚点世界位姿优化变量 // 内部相对约束、观测、协方差 std::vectorRelativeConstraint constraints; }; struct Hyperframe { uint64_t id; SE3 anchor_pose; // 优化的节点状态 std::vectorInnerFrame inner_frames; // 跨超帧约束列表 std::vectorExternalConstraint external_edges; };注意pose_in_hyperframe一旦通过相对约束锁定全局优化时不是独立变量而是锚点位姿加上内部相对位姿推导出来的。当内部帧位姿需要变化时靠的是优化内部相对约束的修正量而不是直接把内部帧当独立节点扔进求解器。3.3 滑动窗口与边界超帧的淘汰策略长时间运行中超帧数量还是会持续增长所以必须引入淘汰机制。常见的做法是把滑动窗口放到超帧层级窗口内保留最近N个超帧参与优化窗口之外的老超帧通过边缘化marginalization转成先验因子。边缘化有个特别容易踩的坑如果把老超帧强制固定变成恒定先验而它内部帧和激活窗口内的新超帧之间还有跨超帧约束那么这些约束就变成了“一边自由、一边钉死”的状态优化结果很容易被老超帧的残差扯得僵硬。正确的做法是在边缘化时把老超帧内部帧与窗口内新超帧之间的约束通过舒尔补转成窗口内新超帧之间或者新超帧与锚点之间的先验约束然后才可以把老超帧从图中删除。我团队里一位同学最开始图省事直接对所有老超帧加了强先验固定住结果机器人每次从老区域回到新区域轨迹会出现一个台阶状的跳变。后来改成先边缘化再淘汰台阶就消失了。这一步值得在整个系统联调前就早做规划。4. 实战中用到的核心算法模块4.1 超帧内约束的构建IMU预积分与激光扫描匹配内部约束构建是hyperframe方案的地基地基本身不能松。我的流程是每来一帧激光先用IMU预积分推算帧间相对运动这个预积分结果会输出一个相对位姿和对应的协方差矩阵同时用上一帧的激光点云做scan-to-scan匹配得到第二个相对位姿估计。两者在信息层面做一个融合相当于加权平均得到最终进入超帧内部的那个相对约束。这个融合要特别注意IMU与激光时间戳对齐的问题。IMU频率100Hz激光频率10Hz如果时间戳对不齐预积分结果就会平白带上一块未知偏差。我的做法是给每个激光帧打上精确的到达时间戳用两帧激光时间戳之间的IMU数据做预积分绝不用“最近的一包IMU数据”这种粗暴方式。就这一个细节曾经让我的内部约束精度提高了大概15%。4.2 跨超帧回环检测匹配失败时如何利用内部约束兜底跨超帧回环检测是hyperframe方案里收益最大的部分。传统回环是“当前帧对历史关键帧”做匹配匹配成功就加一条约束。在hyperframe方案里我一般先用当前超帧内最近几帧的累计点云作为query去历史超帧集合里做基于描述子的检索找到候选超帧后再在候选超帧内部逐帧做scan-to-submap匹配。匹配失败时的兜底策略才是体现hyperframe价值的地方。如果当前帧和候选超帧的某一帧匹配分数不够但附近几帧的匹配分数都不错那就可以把这几条弱约束全部挂到超帧上用数量换质量。由于超帧内部帧之间有强相对约束单条弱约束也许证据不足但多条弱约束经内部结构一传播就能形成足够强的合力。实测下来这种策略在长走廊和重复纹理环境里很有效回环召回率比单纯逐帧匹配高了不少。4.3 协方差估计与不确定性传播超帧变形的根源超帧内部弹性是个好东西但也带来一个新问题内部帧的相对不确定度会随时间传播、累积如果把超帧内部的形变估计错了全局优化反而会给地图引入新的畸变。所以内部约束的协方差不能简单给一个固定值。我的做法是给每条内部相对约束的协方差设一个下限比如平移分量不能小于5mm旋转分量不能小于0.05度。这个下限能防止IMU预积分在零运动时把协方差估得过小导致超帧内部被“过度信任”。同时设置一个协方差上限当IMU长期没有有效观测时内部约束的信息权重不会无限膨胀。经验之谈超帧内最后一帧和第一帧之间的协方差大小大致反映了这个超帧在退化环境中的“可信弹性”。我在可视化调试时会把所有超帧按内部总协方差排序颜色从绿到红一眼就能看出哪些超帧是风险段然后针对性地去补回环或调参数比瞎调阈值有用得多。5. 踩坑实录我在这套方案里翻过的车5.1 第一坑超帧边界把回环约束切成两半第一次把hyperframe方案跑起来时我碰到一个特别隐蔽的bug回环检测明明成功了精度却没提升。查了两天最后发现是超帧边界搞的鬼。机器人第二次路过同一个位置的时候它的位姿刚好落在超帧A的最后一帧和超帧B的第一帧之间回环检测模块把相邻的两次观测分别匹配到了两个不同的超帧里结果一条完整的回环约束被切成了两条而且每条都不完整权重都打了折扣。这个问题解决起来不算复杂但思路要变一下当检测到当前帧与候选超帧的历史帧成功匹配时不只在当前帧与匹配帧之间加约束还要在当前超帧的锚点和候选超帧的锚点之间加一条“辅助约束”这条约束的信息矩阵要适当缩小权重。表面上看加了一条冗余约束其实它保证了回环信息能传导到超帧的整体位姿上而不是只在内部帧之间打转。5.2 第二坑内部约束权重过大超帧整体被“焊死”另一个翻车点更容易被忽略。一开始我为了让内部结构“稳”把内部相对约束的权重调得很大。确实小范围局部优化看起来很稳超帧内部的点云拼接质量很高。但一旦遇到全局回环权重过大的内部约束会把超帧焊成一个不可变形的刚体全局优化的修正量完全无法传播到超帧内部帧上相当于hyperframe退化成了一个普通的关键帧节点。这事的本质是“内聚”和“弹塑性”之间的平衡。内部约束的权重应该足够高让超帧内部在局部时间尺度上保持一致性但又不能高到让超帧在长距离回环时完全失去形变能力。我最后的经验值是内部约束的标准差大约是扫描匹配不确定度的2到3倍权重上大概比跨超帧回环约束低一个数量级。这个比例不是拍脑袋定的而是对着一组长走廊数据反复试出来的平衡点。5.3 第三坑高频帧全部塞进超帧内存直接爆炸第三个坑比较新手向但我确实见过不少人栽在上面以为hyperframe把所有帧都塞进内部是理所当然于是保留了全部100Hz的IMU数据和10Hz的激光数据。一个超帧50帧激光内部约束也就49条其实没什么。但如果把IMU原始数据全部堆进内存再加上描述子、点云副本、中间缓存跑两个小时就占了接近30GB内存直接把工控机干到OOM。解决方式很朴素超帧内部的相对约束一旦被加入图里原始点云就可以降采样甚至丢掉。保留一份轻量级的摘要帧比如抽稀后的点云用于后续回环匹配即可。IMU原始数据在预积分完成后也只需要保留预积分结果和对应的协方差矩阵原始加速度和角速度可以直接清掉。我把这块做完之后内存占用直接降到原来的五分之一优化速度也快了不少。6. hyperframe与相邻方案的取舍和一些经验6.1 对比把所有帧扔进全局优化、子图式管理、hyperframe我把三种方案放到一张表里方便大家根据自己的场景选型。方案规模控制细节保留实现复杂度长走廊/退化环境下表现全帧全局优化极差几千帧后求解变慢最好低容易累积漂移依赖回环关键帧抽稀较好差中间细节丢失低抽帧后细节缺失漂移难修复子图式管理好差内部帧被抹平中漂移能修但内部细节不跟着修hyperframe好好内部帧细节保留中高能修漂移也能修内部形变我自己的感受是如果你的项目只有短时间运行10分钟以内、小地图、少量回环那么经典的关键帧方案完全够用没必要上hyperframe的复杂度。但如果你的机器人要在同一个环境里反复跑数小时甚至几天或者经常经过走廊、隧道这类退化环境hyperframe的“整体刚体局部弹性”结构带来的收益就会非常明显。6.2 什么场景下真的该上hyperframe方案从我的实践看下面三类场景最值得考虑hyperframe第一类是多传感器异步融合。激光10Hz、相机30Hz、IMU 200Hz这些帧率完全不成比例如果全部进优化器光时间戳对齐就能累死人。hyperframe天然适合按时间窗口打包每个超帧内部做好多传感器的时间对齐和预积分外部只维护超帧锚点系统复杂度会清爽很多。第二类是长期运行的持久化建图。机器人每天在同一区域跑地图需要一天比一天准。每秒产生的大量帧如果全保留地图数据会膨胀到无法管理。用hyperframe做单位每天结束时做一次跨超帧的全局优化和地图合并既能利用当天新增的观测来修正历史地图又不会把数据规模搞爆炸。第三类是退化环境中的可靠性提升。长走廊、地下车库、室外空旷广场这类环境里单帧匹配的信息量不够必须依赖一段时间的观测累积才能形成有效约束。hyperframe把一段时间的观测打包成一个整体等于把“单帧弱观测”升级成“一段强观测”在退化检测、匹配兜底方面都有天然优势。6.3 参数起点和调参顺序基于我的项目数据最后给一套可以直接当起点的参数都是我在真实项目里调过、验证过的初始值。一个超帧覆盖50个激光帧约5秒或者累计里程超过5米先到先封。内部相对约束的协方差平移分量标准差设5mm旋转分量标准差设0.05度再在这个基础上乘以2倍作为内部约束权重。跨超帧回环约束的权重大约是内部约束的5到10倍。滑动窗口保留最近的40个超帧窗口外旧超帧用边缘化转成先验因子。优化频率控制在1Hz左右每次优化持续时间不超过200毫秒如果超过了优先检查是不是回环约束过多或内部帧过于冗长。调参顺序上我建议先调内部约束权重再调超帧大小最后才动回环约束权重。因为内部约束决定超帧的基本行为这一步不调稳后面所有参数都会受影响。我的经验是先把内部权重调“软”一点观察超帧是否在全局优化后出现明显形变如果没有再把内部权重逐级加大找到精度提升和形变风险之间的平衡点。跑hyperframe方案这几个月最大的体会是它不是一个“调参解决一切”的方案而是一种重新思考“帧”和“地图”之间关系的思路。刚上手的人容易把它当成一个复杂版的子图结果写出来的代码又慢又难调真正把它用顺的人会发现它的价值在于让后端优化既保得住细节、又控得住规模。这中间的过程没有捷径多跑数据、多看信息矩阵、多把超帧内部的协方差可视化出来比什么参数表都管用。