☰
hyperframe超帧:解决长时SLAM因子图爆炸与退化漂移
2026/10/13 14:44:44 网站建设 项目流程

先讲一个我上个月真实遇到的问题。项目里一台移动机器人要在室内长走廊环境里连续跑两个小时,传感器配置是激光雷达加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 内部约束:超帧内各帧的相对位姿是硬约束

既然超帧内部保留了一堆帧,第一步就要明确这些帧之间的约束从哪来。以我项目里的传感器配置为例:激光雷达10Hz,IMU 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::vector<RelativeConstraint> constraints; }; struct Hyperframe { uint64_t id; SE3 anchor_pose; // 优化的节点状态 std::vector<InnerFrame> inner_frames; // 跨超帧约束列表 std::vector<ExternalConstraint> 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方案这几个月,最大的体会是:它不是一个“调参解决一切”的方案,而是一种重新思考“帧”和“地图”之间关系的思路。刚上手的人容易把它当成一个复杂版的子图,结果写出来的代码又慢又难调;真正把它用顺的人,会发现它的价值在于让后端优化既保得住细节、又控得住规模。这中间的过程没有捷径,多跑数据、多看信息矩阵、多把超帧内部的协方差可视化出来,比什么参数表都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询