ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AVP-Loc地下车库定位方案:PCA、ICP与ESKF融合实战

AVP-Loc地下车库定位方案:PCA、ICP与ESKF融合实战

1. 从泊车场景说起:AVP-Loc到底在解决什么问题

自动代客泊车(Automated Valet Parking,AVP)这个场景,说白了就是让车自己去找车位、自己停进去,全程不需要人坐在驾驶位上。听起来很美好,但真正做过这个方向的人都知道,最难的其实不是“停进去”这个动作,而是“车怎么知道自己在哪”。地下车库没有GPS信号,光照条件差,车道线磨损严重,柱子长得都差不多,相邻车位之间的视觉特征高度重复——这些条件叠加在一起,对定位系统提出的要求非常苛刻。

前深动科技(后被小米收购,团队并入小米智驾体系)的AVP-Loc方案,就是专门针对这类场景设计的一套定位系统。它的核心任务是在地下车库这种结构化但视觉退化严重的环境中,提供稳定、实时、厘米级到分米级的车辆位姿估计。这套方案里涉及几个关键技术模块:PCA(主成分分析)用于点云或特征的主方向提取与降维,ICP(迭代最近点)用于帧间点云配准,ESKF(误差状态卡尔曼滤波)用于多传感器融合与状态估计,BEV(鸟瞰图)视角用于统一表达和感知融合。

如果你正在做泊车定位、低速自动驾驶或者地下车库建图相关的工作,这套方案里的很多设计思路是可以直接借鉴的。哪怕你不做AVP,只要涉及激光雷达或视觉在退化环境下的定位问题,PCA降维、ICP配准、ESKF融合这三板斧都是绕不开的基本功。接下来我会把这套方案的核心逻辑拆开,结合我自己的实操经验,把每个模块的关键细节和踩坑点讲清楚。

2. 整体方案架构与设计取舍

2.1 为什么选择“BEV+点云配准+滤波融合”这条路线

AVP-Loc的整体架构可以理解为三层:前端负责从传感器数据中提取可用于配准的特征表达,中端做帧间或帧与地图之间的配准来获得相对位姿增量,后端用滤波器把配准结果和IMU、轮速计等数据进行融合,输出平滑且鲁棒的位姿估计。

选择BEV视角作为统一表达,核心原因是地下车库的场景特点决定的。在地面道路上,前向视角的视觉特征丰富,车道线、交通标志、车辆轮廓都能提供很好的约束。但在地下车库,前向视角看到的往往是灰暗的墙壁、重复的柱子和地面反光,特征匹配极易出错。BEV视角把感知范围投影到地面平面上,柱子的位置、车位的边界、通道的走向在这个视角下反而变得清晰且稳定。你可以把它想象成从“站在车里往外看”变成“站在车顶正上方往下看”,后者对结构化环境的表达能力明显更强。

点云配准选择ICP及其变体而不是直接做端到端的位姿回归,是因为ICP在结构化场景下的精度和可解释性更好。端到端方法虽然在某些数据集上表现不错,但一旦场景分布发生变化,泛化能力就会打折扣。ICP的好处是你知道它在做什么——它就是在找两组点之间的最优刚体变换,出了问题也容易排查。

ESKF作为后端融合框架,相比EKF(扩展卡尔曼滤波)的优势在于误差状态参数化。简单说,ESKF把状态分成“名义状态”和“误差状态”两部分,名义状态用非线性模型传播,误差状态用线性模型更新。这样做的好处是误差状态始终在小量范围内,线性化误差小,数值稳定性好。对于AVP这种需要长时间稳定运行的系统来说,这一点很关键。

2.2 PCA在其中的角色:降维只是表象,主方向提取才是核心

很多人看到PCA第一反应就是“降维”,但在AVP-Loc里,PCA的作用远不止降维。它更重要的角色是提取点云或特征的主方向,为后续的配准和表达提供几何约束。

举个例子,地下车库的柱子点云投影到BEV平面后,大致是一个矩形或椭圆形的点簇。这个点簇的主方向(也就是PCA分解后的第一主成分方向)就代表了柱子的朝向。如果你能把每个柱子的朝向提取出来,那么在配准的时候就可以利用这些朝向信息做约束,而不是盲目地做点到点匹配。这就像你拼图的时候,先看每块拼图的边缘方向,再去匹配,效率比一块一块试要高得多。

PCA的计算过程本身不复杂:对点云去中心化,计算协方差矩阵,做特征值分解,取最大特征值对应的特征向量就是第一主方向。但在实际工程中,有几个细节需要注意。一是点云的数量和分布会影响PCA的稳定性,如果某个柱子只被扫到几个点,算出来的主方向可能完全不可靠。二是需要判断点云是否近似平面分布,如果是平面分布,那么第三主方向(法向量方向)才有意义;如果是线状分布,那么第一主方向才是稳定的。这些判断逻辑在代码里需要显式处理。

2.3 ICP的变体选择与配准策略

ICP的基本思想很简单:给定两组点云,找到一组旋转和平移,使得两组点云之间的距离最小。但标准ICP有几个众所周知的缺点:对初始位姿敏感、容易陷入局部最优、在点云密度不均匀时收敛慢。

在AVP-Loc里,实际使用的往往是ICP的变体。常见的选择包括点到面ICP(point-to-plane ICP)和基于特征的ICP。点到面ICP利用目标点云的法向量信息,收敛速度比点到点ICP快,在结构化场景下精度也更好。基于特征的ICP则是先提取关键点或特征描述子,再做匹配,适合点云数量大、需要降采样的场景。

配准策略上,通常采用“粗配准+精配准”的两级方案。粗配准用PCA提取的主方向做初始对齐,或者用轮速计和IMU的积分结果作为初始猜测。精配准再用ICP做迭代优化。这样做的好处是既保证了收敛速度,又保证了最终精度。

3. 核心模块拆解与实操要点

3.1 PCA主成分分析在点云处理中的具体实现

先讲PCA在点云处理中的落地细节。假设你从激光雷达或者深度相机拿到了一帧点云,已经投影到了BEV平面,现在要对某个局部区域做PCA分析。

第一步是确定分析的范围。你不能对整帧点云做PCA,那样算出来的主方向没有意义。通常的做法是按网格划分BEV平面,每个网格内的点云单独做PCA。网格大小的选择需要权衡:太小了点数不够,PCA不稳定;太大了会混合多个结构,主方向不明确。根据我的经验,在地下车库场景下,网格边长取0.5米到1米比较合适。

第二步是去中心化。计算网格内所有点的质心,然后每个点减去质心。这一步看起来简单,但如果你忘了做,后面算出来的协方差矩阵就是错的。

第三步是计算协方差矩阵。对于三维点云,协方差矩阵是3x3的对称矩阵。这里有个技巧:如果点云已经投影到BEV平面,你可以只计算2x2的协方差矩阵,减少计算量。

第四步是特征值分解。对于3x3对称矩阵,可以用解析法求特征值,比通用的数值方法快很多。特征值从大到小排列,对应的特征向量就是第一、第二、第三主方向。

第五步是判断点云的几何类型。如果三个特征值差不多大,说明点云是球状分布,没有明显主方向;如果第一特征值远大于另外两个,说明是线状分布,第一主方向可靠;如果第一和第二特征值差不多大且都远大于第三特征值,说明是面状分布,第三主方向(法向量)可靠。

import numpy as np def pca_analysis(points): # points: Nx3 array centroid = np.mean(points, axis=0) centered = points - centroid cov = np.cov(centered.T) eigenvalues, eigenvectors = np.linalg.eigh(cov) # eigh returns ascending order, reverse it idx = np.argsort(eigenvalues)[::-1] eigenvalues = eigenvalues[idx] eigenvectors = eigenvectors[:, idx] # classify geometry type ratio_12 = eigenvalues[1] / eigenvalues[0] if eigenvalues[0] > 1e-6 else 0 ratio_23 = eigenvalues[2] / eigenvalues[1] if eigenvalues[1] > 1e-6 else 0 if ratio_12 < 0.3: geom_type = 'linear' elif ratio_23 < 0.3: geom_type = 'planar' else: geom_type = 'spherical' return eigenvalues, eigenvectors, geom_type

这段代码是我在实际项目中常用的版本,阈值0.3是根据多个地下车库数据集调出来的经验值。不同场景可能需要微调,但大体思路是一致的。

注意:PCA对离群点非常敏感。如果网格里混入了几个噪声点,算出来的主方向可能完全偏掉。在做PCA之前,建议先做一遍统计滤波或者半径滤波,把明显的离群点去掉。

3.2 ICP配准的工程化实现与加速技巧

ICP的工程实现有几个关键点:对应点搜索、变换估计、迭代终止条件。

对应点搜索是ICP最耗时的环节。暴力搜索是O(N*M)的复杂度,对于大规模点云完全不可接受。实际工程中必须用KD-Tree或者Octree做加速。PCL库里的KDTreeFLANN是常用的选择,但如果你的点云是投影到BEV平面的2D点,用2D的KD-Tree会更快。

变换估计对于点到点ICP,就是求解一个最小二乘问题,可以用SVD分解直接得到闭式解。对于点到面ICP,需要求解一个非线性最小二乘问题,通常用高斯-牛顿法或者LM算法迭代求解。

迭代终止条件通常有三个:变换增量小于阈值、对应点距离均值小于阈值、达到最大迭代次数。这三个条件满足任意一个就停止。根据我的经验,变换增量阈值取1e-6,距离均值阈值取0.01米,最大迭代次数取50,这组参数在地下车库场景下比较平衡。

// 点到面ICP的核心迭代逻辑(伪代码) Eigen::Matrix4f icp_point_to_plane( const PointCloud& source, const PointCloud& target, const PointCloud& target_normals, Eigen::Matrix4f initial_guess, int max_iter = 50, float trans_eps = 1e-6, float dist_eps = 0.01) { Eigen::Matrix4f T = initial_guess; for (int iter = 0; iter < max_iter; ++iter) { // 1. transform source points PointCloud transformed = transformPointCloud(source, T); // 2. find correspondences using KD-Tree std::vector<Correspondence> correspondences; for (auto& pt : transformed) { int idx = kdtree.nearestSearch(pt); if (idx >= 0 && distance(pt, target[idx]) < max_correspondence_dist) { correspondences.push_back({pt, target[idx], target_normals[idx]}); } } // 3. build linear system and solve Eigen::Matrix<float, 6, 6> H = Eigen::Matrix<float, 6, 6>::Zero(); Eigen::Matrix<float, 6, 1> b = Eigen::Matrix<float, 6, 1>::Zero(); for (auto& corr : correspondences) { Eigen::Vector3f residual = corr.source - corr.target; float dist = residual.dot(corr.normal); Eigen::Matrix<float, 1, 6> J; J.block<1,3>(0,0) = corr.normal.transpose(); J.block<1,3>(0,3) = (corr.source.cross(corr.normal)).transpose(); H += J.transpose() * J; b += J.transpose() * (-dist); } Eigen::Matrix<float, 6, 1> delta = H.ldlt().solve(b); T = T * se3Exp(delta); // 4. check convergence if (delta.norm() < trans_eps) break; } return T; }

这段代码展示了点到面ICP的核心逻辑。实际工程中还需要处理很多边界情况,比如对应点数量太少时直接返回失败、H矩阵接近奇异时加阻尼等。

实操心得:ICP的初始位姿非常关键。如果初始位姿偏差太大,ICP很容易收敛到错误的局部最优。我的做法是用轮速计和IMU做短时间积分,给出一个初始猜测,再用PCA提取的主方向做一次粗对齐,最后才跑ICP。这样收敛率和精度都会好很多。

3.3 ESKF融合框架的搭建与调参

ESKF的核心思想是把状态分成名义状态和误差状态。名义状态用非线性运动模型传播,误差状态用线性模型做卡尔曼更新。这样做的好处是误差状态始终在小量范围内,线性化误差小。

在AVP-Loc里,状态向量通常包括:位置、速度、姿态、陀螺仪零偏、加速度计零偏。如果做的是2D定位,可以简化为位置、航向角、速度。误差状态向量就是这些量的误差。

预测步骤用IMU和轮速计的数据做积分。这里需要注意的是,IMU的积分频率通常远高于轮速计,所以要做时间对齐。我的做法是用IMU做高频积分,轮速计做低频更新,中间用插值对齐时间戳。

更新步骤用ICP的配准结果作为观测。ICP输出的相对位姿可以转换成位置和姿态的观测,然后计算残差和雅可比矩阵,做卡尔曼更新。

// ESKF预测步骤的核心逻辑 void ESKF::predict(const IMUData& imu, double dt) { // nominal state propagation nominal_state.p += nominal_state.v * dt + 0.5 * (R * (imu.acc - ba)) * dt * dt; nominal_state.v += (R * (imu.acc - ba)) * dt; nominal_state.R = nominal_state.R * Exp((imu.gyro - bg) * dt); // error state transition matrix F = Eigen::Matrix<double, 15, 15>::Identity(); F.block<3,3>(0,3) = Eigen::Matrix3d::Identity() * dt; F.block<3,3>(3,6) = -R * skew(imu.acc - ba) * dt; F.block<3,3>(3,9) = -R * dt; F.block<3,3>(6,6) = Exp(-(imu.gyro - bg) * dt); F.block<3,3>(6,12) = -Eigen::Matrix3d::Identity() * dt; // covariance propagation P = F * P * F.transpose() + G * Q * G.transpose(); }

调参方面,Q矩阵(过程噪声)和R矩阵(观测噪声)的设置直接影响融合效果。Q矩阵太大,滤波器会过度信任观测,位姿输出会抖动;Q矩阵太小,滤波器会过度信任预测,位姿输出会滞后。我的经验是先用Allan方差分析IMU的噪声特性,给出Q矩阵的初始值,再根据实际跑车数据微调。R矩阵则根据ICP的配准残差统计来设定。

3.4 BEV视角下的特征表达与地图构建

BEV视角在AVP-Loc里承担两个角色:一是作为感知和配准的统一表达空间,二是作为地图构建的基础坐标系。

在BEV视角下,地面被划分成规则网格,每个网格存储该位置的点云统计信息,比如高度均值、高度方差、反射强度均值等。这些统计信息构成了BEV特征图。配准的时候,当前帧的BEV特征图和地图的BEV特征图做匹配,而不是直接做点云匹配。这样做的好处是数据量小、匹配速度快、对点云密度变化不敏感。

地图构建方面,通常采用“关键帧+子图”的结构。车辆行驶过程中,每隔一定距离或角度取一个关键帧,关键帧的BEV特征图存入地图。回环检测用词袋模型或者扫描上下文(Scan Context)方法,检测到回环后做全局优化,消除累积误差。

注意:BEV网格的分辨率选择需要权衡。分辨率太高,内存占用大,匹配慢;分辨率太低,特征表达粗糙,匹配精度差。地下车库场景下,5厘米到10厘米的分辨率是比较合适的选择。

4. 完整实操流程:从数据采集到定位输出

4.1 数据采集与预处理

实操的第一步是数据采集。需要的传感器包括:激光雷达(或深度相机)、IMU、轮速计。如果做视觉BEV,还需要环视相机。采集的时候要注意时间同步,硬件触发同步是最好的,软件同步需要做时间戳对齐。

预处理包括:点云去畸变、地面分割、ROI裁剪。点云去畸变是因为激光雷达在旋转过程中车辆在运动,每个点的采集时刻不同,需要用IMU数据做运动补偿。地面分割是为了把地面点和非地面点分开,地面点用于构建BEV特征图,非地面点用于提取柱子和墙壁等结构。ROI裁剪是只保留车辆周围一定范围内的点云,减少计算量。

# 点云去畸变的简化实现 def undistort_pointcloud(points, timestamps, imu_poses): undistorted = [] for pt, ts in zip(points, timestamps): # find nearest imu pose pose = interpolate_pose(imu_poses, ts) # transform point to reference frame pt_ref = pose.inverse() * pt undistorted.append(pt_ref) return np.array(undistorted)

4.2 特征提取与BEV投影

预处理完成后,下一步是特征提取和BEV投影。对于每个BEV网格,计算网格内点云的高度统计量(最大高度、最小高度、高度差、高度均值、高度方差)和反射强度统计量。这些统计量组成该网格的特征向量。

如果使用PCA做特征增强,还需要对每个网格内的点云做PCA分析,提取主方向和几何类型,作为额外的特征维度。比如,如果某个网格被判定为“线状分布”,那么它的主方向就可以作为一个强特征,用于后续的配准约束。

BEV投影的代码实现需要注意坐标系的定义。通常以车辆后轴中心在地面的投影为原点,x轴指向车辆前方,y轴指向车辆左侧,z轴指向上方。BEV网格的索引通过坐标除以分辨率再取整得到。

4.3 帧间配准与地图匹配

帧间配准用ICP,输入是当前帧和上一帧的BEV特征图或点云。初始位姿用IMU和轮速计的积分结果。配准输出相对位姿,送入ESKF做更新。

地图匹配是在有先验地图的情况下,把当前帧和地图做配准。地图匹配的初始位姿用ESKF的预测位姿。地图匹配的精度通常比帧间配准高,因为地图是多帧累积的结果,噪声更小。但地图匹配的计算量也更大,需要做降采样或者特征提取来加速。

实际系统中,帧间配准和地图匹配通常是并行的,或者根据场景切换。比如,在进入新区域时以帧间配准为主,在已知区域以地图匹配为主。

4.4 ESKF更新与位姿输出

ESKF的更新步骤把ICP的配准结果作为观测。观测模型可以写成:z = h(x) + v,其中z是ICP输出的相对位姿,h(x)是根据当前状态预测的相对位姿,v是观测噪声。

计算残差和雅可比矩阵后,做卡尔曼更新,得到误差状态的估计,再把误差状态注入名义状态,完成位姿更新。

// ESKF更新步骤 void ESKF::update(const Eigen::Matrix4d& icp_pose, const Eigen::Matrix<double, 6, 6>& R) { // compute residual Eigen::Matrix4d predicted_pose = getPoseFromState(); Eigen::Matrix4d residual_pose = predicted_pose.inverse() * icp_pose; Eigen::Matrix<double, 6, 1> residual = se3Log(residual_pose); // compute Jacobian Eigen::Matrix<double, 6, 15> H = Eigen::Matrix<double, 6, 15>::Zero(); H.block<3,3>(0,0) = Eigen::Matrix3d::Identity(); H.block<3,3>(3,6) = Eigen::Matrix3d::Identity(); // Kalman gain Eigen::Matrix<double, 15, 6> K = P * H.transpose() * (H * P * H.transpose() + R).inverse(); // update error state Eigen::Matrix<double, 15, 1> dx = K * residual; // inject error into nominal state injectError(dx); // update covariance P = (Eigen::Matrix<double, 15, 15>::Identity() - K * H) * P; }

位姿输出的频率通常和IMU频率一致,保证输出的平滑性。ICP的更新频率可以低一些,比如10Hz,因为ICP的计算量较大。

5. 常见问题与排查技巧实录

5.1 ICP配准失败或精度差的排查思路

ICP配准失败是实际中最常见的问题。表现通常是配准后的位姿跳变、残差很大、或者直接不收敛。

排查的第一步是检查初始位姿。如果初始位姿偏差超过一定范围,ICP基本不可能收敛到正确结果。我的做法是打印初始位姿和真值的偏差,如果偏差超过1米或10度,就要检查IMU和轮速计的积分逻辑。

第二步是检查对应点数量。如果对应点太少,说明两组点云的重叠区域太小,或者KD-Tree的搜索半径设置不合理。可以适当增大搜索半径,或者降低对应点距离阈值。

第三步是检查点云质量。如果点云噪声大、离群点多,ICP的配准结果会很不稳定。这时候需要加强预处理,比如增加统计滤波的强度。

第四步是检查场景退化。如果车辆行驶在长直通道中,通道两侧的墙壁提供了很强的横向约束,但纵向约束很弱,ICP在纵向上的估计会漂移。这时候需要引入其他约束,比如轮速计的纵向速度、地面纹理的纵向特征等。

问题表现可能原因排查方法解决方案
配准位姿跳变初始位姿偏差大打印初始位姿与预测位姿的偏差检查IMU积分逻辑,增加粗配准步骤
残差始终很大对应点数量不足统计对应点数量和距离分布增大搜索半径,降低距离阈值
配准结果抖动点云噪声大可视化点云,检查离群点比例加强统计滤波,增加降采样
纵向漂移场景退化分析点云在纵向上的分布引入轮速计约束,使用地面纹理特征
收敛速度慢点云数量大统计ICP迭代次数和耗时降采样,使用点到面ICP

5.2 PCA主方向提取不稳定的处理

PCA主方向提取不稳定的表现是:同一个柱子在相邻帧中算出的主方向差异很大,导致配准约束不一致。

根本原因通常是点云数量不足或分布不均匀。解决办法有几个:一是增大PCA分析的网格尺寸,让更多点参与计算;二是对点云做降采样,让点分布更均匀;三是引入时间上的平滑,比如用滑动窗口对多帧的主方向做平均。

还有一个容易被忽略的问题是坐标系定义。如果点云的坐标系和BEV网格的坐标系不一致,算出来的主方向也会有问题。建议在做PCA之前,先把点云统一到BEV坐标系下。

5.3 ESKF滤波器发散的调试方法

ESKF发散的表現是位姿估计越来越偏离真值,协方差矩阵越来越大。调试的时候可以按以下步骤来。

先检查预测步骤。把IMU和轮速计的数据单独积分,和真值对比,看漂移速度是否合理。如果漂移太快,说明IMU的零偏没标定好,或者轮速计的尺度因子不对。

再检查更新步骤。把ICP的观测单独拿出来,和真值对比,看观测本身的精度如何。如果观测本身噪声就很大,那滤波器发散是正常的,需要先提高ICP的精度。

然后检查Q和R矩阵的设置。可以做一个简单的实验:固定Q矩阵,逐渐增大R矩阵,看滤波器的表现。如果R太大,滤波器会过度信任预测,位姿输出滞后;如果R太小,滤波器会过度信任观测,位姿输出抖动。找到那个平衡点。

最后检查时间同步。如果IMU、轮速计、ICP的时间戳没有对齐,滤波器的预测和更新就会错位,导致发散。建议用硬件触发同步,或者用软件做精确的时间戳对齐。

避坑技巧:ESKF的协方差矩阵P在长时间运行后可能会失去正定性,导致滤波器崩溃。我的做法是每次更新后检查P的特征值,如果出现负特征值,就把P重置为一个合理的初始值。这个操作虽然粗暴,但在实际系统中很有效。

5.4 BEV特征匹配中的典型问题

BEV特征匹配的问题通常表现为匹配得分低、匹配位置偏移、或者匹配结果不稳定。

匹配得分低可能是因为BEV特征图的表达能力不够。可以尝试增加特征维度,比如加入PCA主方向、几何类型等特征。也可能是分辨率设置不合理,可以尝试不同的分辨率看效果。

匹配位置偏移可能是因为BEV特征图的原点定义不一致。当前帧和地图的BEV特征图必须用同一个原点定义,否则匹配结果会有固定偏移。

匹配结果不稳定可能是因为特征图对视角变化太敏感。BEV视角虽然对旋转有一定的鲁棒性,但如果车辆俯仰角变化大,BEV投影的结果会有变形。这时候需要做俯仰角补偿,或者用更鲁棒的特征描述子。

6. 工程落地中的经验与建议

6.1 传感器标定与时间同步的实操细节

传感器标定是整套系统的基础。激光雷达和IMU之间的外参标定,我推荐用基于平面的方法:把车辆停在一个有明确平面的地方,比如墙边,采集多帧数据,用平面约束求解外参。轮速计的标定主要是尺度因子,可以直线行驶一段已知距离,对比轮速计积分和实际距离。

时间同步方面,如果传感器支持硬件触发,一定要用硬件触发。硬件触发同步的精度可以做到微秒级,软件同步通常只能做到毫秒级。毫秒级的误差在低速场景下可能影响不大,但在高速或者剧烈运动场景下会导致明显的位姿误差。

6.2 计算资源的分配与优化

AVP-Loc的计算量主要来自ICP配准和ESKF更新。ICP配准通常占70%以上的计算时间。优化ICP的方法包括:降采样、使用点到面ICP、用GPU加速KD-Tree搜索、限制最大迭代次数。

在实际系统中,我通常把ICP放在单独的线程里跑,和ESKF的预测步骤并行。ESKF的预测用IMU数据,频率可以做到100Hz以上;ICP的更新频率做到10Hz左右就够了。这样可以在保证精度的同时,降低计算资源的占用。

6.3 不同车库场景的适配策略

地下车库的场景差异很大:有的车库柱子密集,有的车库通道很长,有的车库地面反光严重。一套参数打天下是不现实的。

我的做法是维护多套参数配置,根据场景特征自动切换。场景特征可以用点云的统计量来表征,比如柱子的密度、通道的宽度、地面的反射强度均值等。系统启动时先做一次场景识别,然后加载对应的参数配置。

另外,对于特别长的通道,纵向退化问题严重,可以考虑引入地面纹理特征或者反光柱特征作为额外的纵向约束。对于柱子密集的车库,PCA主方向提取会比较稳定,可以加大PCA在配准中的权重。

6.4 系统集成与测试验证

系统集成的时候,建议先做离线测试,用录制的数据包回放,验证每个模块的功能和精度。离线测试通过后,再做在线测试,在真实车辆上跑。

测试验证的指标包括:定位精度(和真值对比)、定位可用率(输出有效位姿的时间占比)、计算耗时(单帧处理时间)、内存占用。这些指标需要在实际场景中反复测试和调优。

我个人的经验是,离线测试能发现80%的问题,但剩下的20%问题只有在在线测试中才会暴露,比如时间同步的抖动、计算资源的竞争、传感器的偶发丢帧等。所以在线测试的时间要留够,不要指望一次就能跑通。

6.5 后续扩展方向

这套方案的扩展方向有几个。一是加入视觉信息,用视觉BEV特征和激光BEV特征做融合,提高在激光雷达退化场景下的鲁棒性。二是加入语义信息,把柱子、车位、通道等语义标签融入BEV特征图,提高匹配的区分度。三是做多车协同,多辆车的地图数据可以共享和融合,加快地图构建速度,提高地图覆盖范围。

这些扩展方向我在实际项目中都尝试过,视觉融合的效果最明显,但计算量也增加最多。语义融合的性价比最高,实现难度不大,但对匹配精度的提升很显著。多车协同适合车队场景,单车场景下意义不大。

最后分享一个小技巧:在做BEV特征图的时候,除了高度和强度统计量,可以加入一个“点云数量”通道。这个通道看起来不起眼,但在点云稀疏的区域,它能帮助判断该区域的特征是否可靠。如果某个网格的点云数量太少,匹配的时候可以降低该区域的权重,避免不可靠特征误导配准结果。这个技巧是我在调试一个地下车库场景时偶然发现的,后来成了标配。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进