HyperFrames超平面激光雷达里程计:解决退化场景漂移的关键技术 做激光雷达里程计的人大概率都经历过这种场景一条笔直的长走廊两边是白墙GPS 没有UWB 也没有IMU 还在慢慢飘看着点云匹配好像没问题可跑出去 50 米再回头看轨迹已经斜到墙里面了。传统方案里FAST-LIO2 这类紧耦合 LiDAR-Inertial 系统已经非常能打但它本质上还是在用“点”去描述世界——点数量大信息却稀疏遇到大平面、重复结构、空旷场景点级配准的可观测性就会明显下降。HyperFrames 给我的感觉是把世界描述从“点”升级成了“框架”从原始点云里把平面结构提取出来用平面的法向量、质心、不确定度定义成一个有机的整体观测再把这种超平面观测塞进状态估计框架里。这篇文章不打算复述论文我想从一个实践者的角度把 HyperFrames 到底解决什么问题、核心原理怎么做、实际跑起来有哪些坑一次讲透。1. 为什么需要 HyperFrames点级特征的天花板1.1 激光雷达配准的三种观测形态做 SLAM 的人都知道激光里程计最底层的动作就是“配准”把当前帧和地图/上一帧对齐求出一个相对位姿。按照观测元的不同大致有三种形态。第一种是点对点point-to-point经典 ICP 干的事。它对每个点找最近对应点最小化欧氏距离。这种做法的好处是通用不需要额外特征提取坏处是对初值敏感而且在平面场景里点到点的最近邻关系往往不稳定一个点的噪声会直接拉偏结果。第二种是点对面point-to-planeFAST-LIO、LOAM 这类系统用的就是它。当前帧的每个点去地图里找近邻面片然后残差取“点到面”的距离。相比点对点它允许点在切平面内自由滑动相当于把平面给齐了而不是把点焊死所以精度和收敛性都更好。但它仍然有个隐形前提地图以点云形式存储每个约束都来自一个具体点。第三种是面对面plane-to-plane也就是 HyperFrames 这类超平面方案走的路线。它不再关心某个点而是先从点云中提取出完整的平面模型然后用“面”作为配准主体。面与面之间的匹配比点和面的匹配更稳定因为一个平面观测本身包含了上百个点的统计压缩随机噪声被平均掉了。我刚开始也觉得直接用 FAST-LIO2 就挺好了没必要折腾平面提取。但后来在真实环境里跑多了才意识到点级方案的瓶颈不在精度而在“退化场景下的鲁棒性”。这个问题单纯靠调参是绕不过去的。1.2 退化场景到底难在哪退化场景里最典型的就是长直走廊和地下车库。以走廊为例激光扫到两侧墙壁和地面约束基本集中在垂直于墙壁的方向沿着走廊方向几乎没有几何起伏点云怎么平移都对得上。从数学上看这等价于信息矩阵在某些方向上的特征值趋近于零。状态估计里我们最后要解一个最小二乘问题它的正规方程系数是 H^T H。如果一个方向没有有效约束H^T H 在该方向上的特征值会非常小微小的噪声就会被放大导致沿走廊方向的位置漂移。传统做法是加 IMU 约束但 IMU 本身有零偏和积分误差如果长时间缺少几何约束IMU 的漂移照样会把轨迹带歪。点级方案还有一个隐藏问题点云中的平面在近距离下是“局部”的。比如你站在走廊里每一帧点云只覆盖身边两三米的范围这些局部点拟合出来的平面方向可能一致但平面对应的“砧板”是碎的——东一块、西一块。一个平面被拆成几十个小面片匹配关系就容易乱。HyperFrames 的思路则是先把这些碎片合并成完整的大平面形成全局一致的结构观测再用这些大平面去约束位姿。这相当于从“看见几棵树”升级成“看见整片森林”。1.3 超平面框架想解决什么问题简单说HyperFrames 把环境中的平面结构提升为“一等的观测对象”。一片墙不再是一堆点而是一个带有几何参数的实体法向量、到原点距离、平面范围、协方差。这带来几个直接好处。第一噪声被明显抑制。一片墙上有一万多个点直接用点云配准每个点的噪声都会进残差但拟合出一个平面后一万个点的噪声会被最小二乘平均掉法向量和 offset 的估计通常非常干净。第二约束强度更好控制。点级配准给不出每个约束的“可信度”但平面拟合能给出协方差。一个拟合残差很大的平面权重可以自动降低一个法向量估计很准的大平面权重可以给高。这种不确定性建模正是提升退化场景可观测性的关键。第三匹配关系更鲁棒。平面特征数量少但辨识度高几何重复的场景里只要法向量和位置足够接近匹配的成功率远高于点的最近邻搜索。做重定位或者闭环检测的时候超平面还天然适合做数据关联。所以 HyperFrames 本质上不是把 FAST-LIO2 推翻重来而是在观测模型这一层做了升级把“点云”变成“超平面集合”然后基于超平面构建残差。它能跑在滤波框架里也能跑在滑动窗口优化里重点是那个“超平面”的表示和残差设计。2. HyperFrames 核心原理拆解2.1 从点云中把平面“立”起来HyperFrames 的第一步一定是从原始点云里提取平面。这一步的质量直接决定了后面状态估计的上限。最常见的提取方法是“区域增长 主成分分析”。先把一帧点云体素化比如用 0.2m 的体素降采样对每个体素内的点做 PCA计算协方差矩阵的三个特征值。如果最小特征值明显小于另外两个说明这些点近似在一个平面上最小特征值对应的特征向量就是法向量。然后以这个体素为种子向邻域体素增长判断条件是两个邻域点和当前拟合平面的距离足够小比如小于 0.05m邻域点的法向量与当前平面法向量的夹角足够小比如小于 10 度。满足条件就并入并重新拟合平面。这里有个容易踩的坑法向量方向不是唯一的。同一个平面法向量可以是 n也可以是 -n如果不统一后面匹配和残差计算全乱套。我习惯的做法是把所有法向量统一指向传感器位置的方向也就是让法向量和“平面中心到传感器原点”的向量点积为正。这样至少在一帧内方向的符号是一致的。大平面的提取质量还受扫描密度影响。激光雷达越远点越稀疏远处的平面拟合噪声越大。所以我会对每个平面保留一个“可信范围”超过一定距离的平面点不参与拟合只用来判断平面是否存在。2.2 为什么叫“Frame”而不叫“Plane”名字里的“Frame”不是随便起的。一个平面如果只用法向量 n 和距离 d 表示在很多情况下是不够的——你还需要知道这个平面属于哪个局部区域它的边界在哪它的不确定性分布是什么。在 HyperFrames 的表示里每个超平面会构成一个局部坐标系法向量方向作为 z 轴平面上选两个正交方向作为 x、y 轴平面质心作为原点。这样一个平面就不再是孤立的参数而是一个携带方向、位置、范围的“框架”。两个超平面之间的约束可以是法向量夹角约束也可以是质心相对位置的约束甚至可以定义整个“框架”之间的变换。这种表示还有一个很大的好处方便多传感器融合。平面的法向量对旋转非常敏感质心和 offset 对平移敏感正好和视觉、IMU 的观测形成互补。比如视觉可以提供丰富的纹理特征约束平移IMU 可以提供短期相对运动而超平面则是稳定的大尺度几何约束三者在滑动窗口里可以统一描述。我不建议把平面的内部点都存下来做状态那样内存和算力都扛不住。HyperFrames 的实用做法是存平面参数、协方差距阵以及一个轻量的点云索引。匹配的时候用当前帧的平面参数去地图里找相近的超平面而不是去点云里找最近点。2.3 残差模型与雅可比配准问题最终要落到残差和求导。HyperFrames 的核心残差有两类。第一类是法向量残差。假设当前帧有个平面法向量为 n_i地图里匹配到了某个超平面法向量为 n_j理想情况下旋转 R 把 n_i 变换到全局系后应当和 n_j 平行。于是残差可以写成error_normal R * n_i - n_j这个残差是三维的但要注意法向量是单位向量实际自由度只有两个所以直接套三维残差会导致雅可比矩阵奇异。实践里通常会把其中一个分量约束掉或改用反对称矩阵形式保证雅可比行满秩。第二类是距离残差。用平面质心 p_i 和法向量 n_j可以写出点到面的距离error_dist n_j^T (R * p_i t - p_j)其中 p_j 是地图平面上任意一点通常取全局平面质心。这个残差对平移 t 的雅可比就是 n_j^T很简单对旋转 R 的雅可比则涉及 p_i 的叉乘项需要用右扰动模型推。如果手推雅可比嫌烦至少可以用数值微分做一次验证。我之前调试时发现很多人法向量残差写对了但旋转雅可比的正负号搞反结果优化半天不收敛。我的习惯是先在仿真数据里用随机初始位姿做配准如果误差能单调收敛到 0再上真机。2.4 与状态估计框架怎么融合HyperFrames 既能在滤波框架里用也能在优化框架里用。我自己更推荐的是把它放进类似 FAST-LIO 的迭代卡尔曼框架里理由很简单滤波框架天然适合和 IMU 紧耦合计算量可控超平面的数量也比点少得多实时性压力小。在这个框架里系统的预测过程由 IMU 驱动更新过程则把超平面残差当作量测。每次收到一帧激光点云先做平面提取然后和当前局部地图里的超平面做关联再带入迭代卡尔曼更新。地图维护也很自然新提取的超平面如果不重复就插入地图如果和已有超平面足够接近就融合更新法向量和 offset 各按协方差加权。如果你更习惯滑动窗口优化也一样。把超平面参数作为待优化变量放进因子图IMU 预积分作为相对约束超平面残差作为绝对约束。关键是要注意超平面的参数化避免过参数化导致的秩亏。最常见的是 4 参数表示法向量取前两个分量参数化第三维用归一化算出来再加一个距离 d。这样状态维度刚好是 3不会有冗余。3. 实操如何把 HyperFrames 用起来3.1 环境准备与数据来源实操的时候不建议一开始就上自己的真机数据那样变量太多出了问题很难定位。我的路线是先用公开数据集跑通流程再用自采数据验证鲁棒性。环境方面HyperFrames 这类的 LiDAR 状态估计系统底层基本逃不开 ROS、PCL、Eigen。我是基于 Ubuntu 20.04 ROS Noetic 来搭的编译前把 PCL 和 Eigen 都确认好版本。Velodyne 或 Ouster 这类机械式雷达的数据结构相对规整适合第一个跑通Livox 雷达是旋转多棱镜式点云分布不同平面提取参数要重新调。公开数据集我建议用带 IMU、带真实轨迹的室内场景比如自建的小型走廊环境也行。关键是环境里要有足够多的人造平面结构墙面、天花板、地面、柜子。纯粹的野外林地反而不是 HyperFrames 的优势场景因为自然环境中平面结构太少。跑通流程时我会先开一个 RViz订阅原始点云和平面提取的可视化话题确认每一帧里平面数量足够、法向量方向统一。这一步看似初级其实是整个系统最容易出错的地方。如果平面提取得都是碎块后面状态估计再漂亮也是空中楼阁。3.2 参数配置逐项说明不同系统里的参数名可能不一样但核心参数就那几个。我整理了一份自己常用的配置模板你可以照着关键项去对lidar_type: velodyne # velodyne / ouster / livox scan_period: 0.1 # 雷达扫描周期单位秒 extrinsic_R: [1, 0, 0, 0, 1, 0, 0, 0, 1] # IMU 到 LiDAR 的外参旋转 extrinsic_t: [0, 0, 0] # IMU 到 LiDAR 的外参平移 plane_voxel_size: 0.4 # 体素越大平面越完整但细节丢失 plane_min_points: 30 # 少于 30 个点的簇不当作平面 plane_max_radius: 1.0 # 平面可信范围超过 1m 的点不参与拟合 normal_angle_threshold: 0.15 # 区域增长的角度阈值单位弧度 plane_dist_threshold: 0.08 # 点到拟合平面的距离阈值单位米 plane_min_area: 0.5 # 最小平面面积过滤小碎块 kd_tree_update_every: 2 # 地图 KD 树更新频率 keyframe_angle_threshold: 0.3 # 旋转超过 0.3 弧度建新关键帧 keyframe_distance_threshold: 0.5 # 平移超过 0.5m 建新关键帧 degenerate_ratio_threshold: 0.1 # 退化检测阈值特征值占比小于 0.1 视为退化拿plane_voxel_size来说这个参数直接决定平面提取粒度。设得太小一面墙会被切成很多小块匹配时平面数量多且不稳定设得太大小物体和小结构会被吞掉而且拟合出的平面可能跨越真正的不连续区域。我通常从 0.3m 起步对标配的 Velodyne 16 线雷达这个值在室内足够用。keyframe_*也很关键。HyperFrames 的优势是平面特征稳定所以关键帧不需要太密。太密会导致地图里平面冗余配准计算量大太稀又会在转弯时丢失中间约束。我的经验是平移阈值 0.5m、旋转阈值 0.3 弧度基本能在精度和性能之间取到一个比较舒服的平衡。如果你的环境转弯很急可以把旋转阈值降到 0.15 弧度。3.3 在退化场景下做对比实验为了验证这套方案到底有没有用我专门设计了一个对照实验。场景选在一条长度约 80 米的室内通道宽度约 3 米两侧和地面都是混凝土平面全程几乎没有明显特征物。这是一个典型的退化场景GPS 无效传统的点云匹配很容易在通道方向产生漂移。实验组是“点级基线”对照组是“超平面方案”。为了让对比有意义两者共用同一个 IMU 数据、同一条轨迹真值只在观测模型上做区分。实验流程分四步第一步录制 rosbag。从通道一头开始加速到正常步行速度走到另一头再折返动作尽量自然不要刻意急停急转。第二步把 rosbag 分别灌给两个方案跑离线建图。第三步把输出的轨迹和 RTK/全站仪测出来的真值对齐比较终点误差。第四步用误差曲线看全过程漂移而不只是看终点。结果其实和我预期差不多点级基线在通道前 20 米还能跟上真值越往后沿通道方向的误差单调增长到折返点附近已经横向偏出四五十厘米。超平面方案在同样条件下法向量约束和平面距离约束把通道方向的累积误差压住了终点偏差基本控制在 15 厘米以内。这个实验给我最大的启发是退化场景里单纯增加点云采样密度是没有用的。技术上要做的是把每个约束的“几何信息量”变大。一个超平面观测在垂直于法向量的方向上约束弱但在法向量方向上约束极强。多个不同朝向的超平面互相咬合整体的可观测性就被撑起来了。3.4 调参顺序建议很多人拿到手就乱调我觉得调参要有顺序否则根本定位不了问题。第一步先只调平面提取把系统挂到可视化工具里盯着平面看。目标是让每一帧的关键结构墙、地、天花板都能被稳定提取且没有明显错切。这个阶段不要看轨迹精度只看“面”的质量。第二步关掉 IMU只用纯激光超平面配准跑一小段短距离。如果这一小段都飘说明残差或雅可比有问题别急着上 IMU 去遮羞。第三步打开 IMU 融合跑长距离场景。这阶段主要看退化方向有没有被真正约束住而不是看整体轨迹漂不漂。第四步最后的精调才碰权重和阈值。调权重时一次只改一个变量改完重新跑同一段 bag做好每一次的实验记录。我以前为了省事同时改了三四个参数结果出了问题完全不知道是谁的锅后来老老实实地每次只改一个参数效率反而更高。4. 常见问题与排查技巧实录4.1 平面跳变导致优化跳动这是我遇到最频繁的问题平面提取在相邻帧之间不稳定这一帧提取到了整面墙下一帧墙断成了两块优化输出就开始跳。原因通常有两个。一是体素尺寸太小或者平面合并阈值太紧导致同一面墙在不同扫描角度下被切碎。二是点云畸变没补偿载体运动速度快时一帧内点云被拉伸平面拟合出来就是弯的。排查方法也很直接在 RViz 里同时显示上一帧和当前帧提取的平面看是不是同一个物理平面被重复提取。如果是适当调大plane_max_radius和体素尺寸让平面提取更“粗”一些。记住HyperFrames 的定位是抓大结构不是做精细建模小碎平面反而会引入噪声。另一个容易被忽略的问题是时间戳同步。雷达点云和 IMU 数据的时间戳如果差了几十毫秒姿态变化快的场景会把平面拟合搞得一塌糊涂。我一般会在数据预处理阶段做时间戳插值对齐确保提取平面用的位姿是点云时间戳对应的位姿而不是最近一帧 IMU 的位姿。4.2 法向量方向不一致超平面匹配特别依赖法向量方向一致。法向量指向反了看起来就差一个负号但残差公式里夹角直接就偏了 180 度匹配全乱。我的通用规则是对所有法向量做一次“重定向”让它们朝向传感器所在的一侧。因为激光雷达只能看到物体的外表面平面中心到传感器原点的方向和法向量之间的夹角一定小于 90 度利用这个几何事实非常可靠。但这个方法在地图里保存历史平面时会失效。因为地图里的平面传感器位置一直在变你没法简单判断法向量应该朝哪。我的解决办法是地图在插入新平面时统一用法向量与全局坐标系的某个基准方向做对齐比如让法向量的 z 分量始终为正。如果平面本来就是天花板z 分量天然为正如果墙面是垂直的z 分量接近零这时再用平面中心到地图原点的方向做二次判断。如果平面法向量方向还是乱那多半是拟合时 PCA 的输入点太少特征值区分度不够。这时候增加plane_min_points或者提高体素分辨率通常能解决。4.3 退化方向还是有漂移即使用了超平面在某些环境里还是会漂。比如一条特别长、宽度特别窄的通道两侧墙的法向量几乎平行地面又平得像镜面整体约束在通道方向和绕前进轴的旋转上依旧偏弱。这种场景下我的建议有三条。第一条引入额外的先验约束。提前知道通道是直线就把走廊的中轴线作为弱约束加进去约束通道方向的位置漂移。这类似给优化加了“天窗”让它在退化方向上不至于完全自由。第二条利用超平面的时序关联。把前后两帧同一平面的匹配次数记录下来如果一个平面连续十几帧都能被可靠观测到说明它特别稳定在退化检测中给它更高的权重让有效约束集中在稳定结构上。第三条加退化检测。实时监测信息矩阵或协方差矩阵的最小特征值一旦某个方向的特征值占比低于阈值就降低这个方向上的更新权重宁可不更新也不要被噪声带偏。这个思想和某些 GNSS 组合导航里的“弱观测方向自动抑制”很像实际效果很明显。我自己最常用的是第三条因为它不需要额外传感器也不会改变系统结构。用它做一层保护退化方向就算漂也只漂一小段整体轨迹不会发散。4.4 参数速查表最后整理一张速查表是我在实践中总结的相对稳妥的起始值。不同雷达和场景肯定要调但至少能帮你少走一些弯路。参数推荐起始值调整方向问题表现plane_voxel_size0.3 - 0.5m平面碎则调大平面串则调小平面不连续 / 拟合错误plane_min_points20 - 30噪声多则调大小碎块太多normal_angle_threshold0.10 - 0.15 rad平面合并不足则调大同一平面被拆开plane_dist_threshold0.05 - 0.10m合并过急则调小不同平面被串一起degenerate_ratio_threshold0.05 - 0.15发散频繁则调大退化方向更新过强keyframe_distance_threshold0.5 - 1.0m轨迹卡顿则调大计算量过大这里面最容易忽略的是degenerate_ratio_threshold。它在状态估计里承担安全网的功能调得太小等于没有退化检测调得太大又会让系统在正常转弯时也缩手缩脚。我习惯先保持默认等真机实验出现明显发散时再一档一档往上加每次加 0.02直到漂移被压住。回过头说我做这套东西最深的体会是不要迷信任何一种单一特征也不要觉得“更先进的算法”能自动解决所有问题。HyperFrames 的价值在于它逼着你去思考环境里到底有什么样的几何结构可用以及如何把结构的不确定性明确地表达出来。哪怕你最后还是用点云配准这一套分析问题的方法也完全能迁移过去。如果后续有时间我打算把超平面闭环检测也加上去。平面特征数量少、辨识度高用来做重定位候选其实比点云全局描述子更干净。这个话题拆开又能写不少等我把实验数据整理完再和大家细聊。