ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从Bird‘s Eye View到上帝视角:多相机BEV感知系统搭建全解析

从Bird‘s Eye View到上帝视角:多相机BEV感知系统搭建全解析 Gods Eye View我第一次看到这个词是在游戏里后来自己做多相机感知项目同事指着屏幕上拼接出来的俯视图随口说了句“这就是上帝视角。”这个名字就一直保留了下来。团队里管它叫GEV而技术圈更常见的叫法是BEVBirds Eye View鸟瞰视图。说白了BEV感知要解决的就是一个问题怎么让机器像站在二楼看停车场一样把车辆四周的障碍物、车道线、路沿都放进一张“俯拍图”里而不是费劲地从每路摄像头的2D画面里脑补出一个3D世界。这篇文章以“gods-eye-view”这个项目为主线讲一讲我从零搭一套多相机俯视感知系统的完整过程包括原理选型、坐标变换和标定、IPM投影、多相机融合以及一些代码和踩坑经验。如果你正在做智能驾驶、自动泊车、园区无人配送、监控拼接或者无人机建图这篇文章应该能帮你少走不少弯路。1. 为什么视觉感知需要“上帝视角”1.1 透视图像的“先天不足”普通摄像头装在车头或者车顶看到的是透视投影本质上是光心向外的一个锥形视域。这种图像符合人类的观察习惯但对感知算法并不友好。第一个问题是近大远小路边一个行人在10米外可能只有几十个像素到了2米内突然占满整个画面同一个物体在不同距离上的表观差异非常大。第二个问题是遮挡一个骑电动车的人被前方公交车挡住在透视图像里几乎完全不可见但如果你把视角提到空中俯视其实位置关系非常清楚。我在刚开始做这套系统时也想过“直接用单目检测不就行了”但很快就发现不行。检测框是轴对齐矩形只能告诉你图像上“这里有个东西”很难准确告诉你“这个东西在我的车体坐标系下到底在哪”。尤其是低速泊车、园区配送这类场景车辆和障碍物之间的距离往往只有几十厘米透视图像上的一两个像素误差反映到实际空间里就是致命的偏差。1.2 BEV视图带来的四个核心价值理解了透视问题再看BEV的价值就顺理成章了。我总结下来上帝视角至少带来四个直接好处。第一统一坐标系。车辆周围有六路、八路甚至更多摄像头每一路都有自己的成像平面目标在A相机里是一个形状在B相机里是另一个形状单独处理很难统一。但投影到BEV之后所有目标都落在同一个以自车为中心的平面上位置关系一目了然多相机融合天然就有了一个公共坐标系。第二跨相机目标跟踪更稳定。场景里的车从前方相机视野进入右侧相机视野时在透视图像里是瞬间消失再出现的你得靠复杂的轨迹关联逻辑去猜“这是不是同一个目标”。如果做BEV投影这个目标只是在俯视图里平移了一段距离连续性要好得多。第三下游任务直接消费BEV。规划和控制算法需要的不是图像坐标而是“障碍物在车的左前方1.2米”这种平面信息。BEV栅格可以直接作为规划算法的输入省掉了一堆坐标转换和语义解析的中间环节。第四多传感器融合更容易。激光雷达、毫米波雷达本身输出的就是三维空间点或目标列表把它们投影到同一个BEV坐标系下和视觉特征做对齐比在像素层面融合要简单得多。后面的章节会展开讲。这里也要泼一盆冷水不是所有场景都需要上帝视角。如果只是做室内入侵检测、人脸抓拍透视图像上的2D检测完全够用。一旦业务里涉及位置关系、距离判断、路径规划“上帝视角”基本就是必选项。2. 核心方案选型从逆透视到端到端BEV2.1 传统方案IPM逆透视映射先讲最传统的做法逆透视映射Inverse Perspective MappingIPM。它的基本假设是地面是一个理想平面。在这个前提下我们可以用一个单应矩阵H把图像平面上的像素直接映射到地面平面上。单应矩阵的推导并不复杂。假设地面是Z0的平面相机内参为K相机相对于车体坐标系的旋转和平移分别为R和t那么地面点X_ground和图像像素点x_img的关系可以写成x_img ~ K * [R的第1列, R的第2列, t] * X_ground换句话说H K * [r1, r2, t]其中r1和r2是旋转矩阵的前两列。这个3x3矩阵一旦求出来剩下的就是坐标变换。实际写代码时我强烈建议做反向映射也就是遍历输出BEV图的每个像素用H的逆矩阵算出它对应原图像的采样位置再用cv2.remap去采样。这样能避免正向映射带来的输出图空洞问题。下面是一段我常用的简化实现。import cv2 import numpy as np def ipm_warp(image, K, R, t, grid_size(600, 400), x_range(-10, 10), z_range(0, 20)): 将相机透视图像映射到车体BEV平面。 grid_size: (width, height)即BEV栅格的宽和高 x_range: 横向范围单位米 z_range: 纵向范围单位米 注意这里默认车体坐标系中x向右z向前y向上 h, w image.shape[:2] # 构造BEV栅格的车体坐标 xs np.linspace(x_range[0], x_range[1], grid_size[0]) zs np.linspace(z_range[0], z_range[1], grid_size[1]) xx, zz np.meshgrid(xs, zs) # 地面点y0 y np.zeros_like(xx) ones np.ones_like(xx) ground np.stack([xx, y, zz], axis-1).reshape(-1, 3).T # 转到相机坐标系 cam_pts R ground t[:, None] # 投影到像素平面 proj K cam_pts u proj[0] / proj[2] v proj[1] / proj[2] # 重映射采样 uv np.stack([u, v], axis-1).reshape( grid_size[1], grid_size[0], 2).astype(np.float32) bevs cv2.remap(image, uv[..., 0], uv[..., 1], interpolationcv2.INTER_LINEAR) return bevs这段代码写得很直白但有几个细节值得注意。第一R和t的方向要搞对是“车体坐标系到相机坐标系”还是反过来一旦弄反投影结果会乱成一团。第二remap的采样点如果超出图像边界默认会得到黑色或边缘值实际项目里记得用mask标记无效区域。第三IPM只对平面成立遇到坡道、减速带、路沿石平面假设直接失效远处会出现明显的拉伸和畸变。2.2 数据驱动方案深度估计加特征投影传统IPM在停车场这种相对平坦的场景里很稳但到了城市道路、上下坡、颠簸路面就顶不住了。最近几年业界的主流方案都是数据驱动的核心思路是用神经网络替代“地面是平面”这个硬假设。比较有代表性的思路是LSSLift-Splat-Shoot这一派。它先对每个像素预测一个深度分布而不是只给一个确定深度值然后把2D图像特征“提升”到3D体素空间再沿着高度维度压缩成BEV特征图。这种做法的好处是即使深度预测有一点点不确定性特征也不会被硬生生投到错误的位置而是以概率方式分布在多个深度区间里网络可以从数据中学会到底该信哪一层。另一派是BEVFormer这类基于Transformer的方案。它在BEV网格上放置一组可学习的query让每个BEV位置的query通过注意力机制去图像特征里找对应关系。这相当于把“投影坐标怎么算”这个问题也交给网络自己学精度上限更高但训练成本大推理速度也慢不少。如果只是做工程落地我的选型经验是先在传统IPM上把整套坐标变换和融合链路打通再根据实际效果决定要不要上数据驱动方案。因为BEV感知系统真正的难度往往不在网络结构而在数据标注、标定、同步和部署这些环节这些基础和方案选型无关但决定了系统能不能跑起来。2.3 方案选型建议速查方案适用场景优点缺点传统IPM平坦停车场、倒车影像、园区低速场景无需训练数据、部署成本低、实时性好依赖平面假设坡道和远处畸变大LSS类深度方案结构化道路、封闭园区、高速场景精度可控、工程化成熟、支持多相机需要大量BEV标注或3D标注数据BEVFormer类方案复杂城市道路、遮挡严重场景精度上限高、时序建模能力强训练和推理成本高数据门槛苛刻选型不是越复杂越好。我见过不少团队一上来就上Transformer结果连基础的标定都还没做对最后项目延期。反过来也有很多量产车用传统IPM拼出的环视图配合2D检测和雷达融合把泊车体验做得非常不错。3. 从零搭建一套BEV感知系统的实操步骤3.1 相机标定与坐标系统一搭建BEV系统的第一件事不是写代码而是把所有相机标定好把坐标系定义清楚。这里说的坐标系至少有四个图像像素坐标系、相机坐标系、车体坐标系、BEV输出坐标系。内参标定大家比较熟棋盘格或AprilTag标定板用OpenCV或Kalibr就能跑得到焦距fx、fy主点cx、cy以及畸变系数。内参不准后面一切都白搭所以标定完一定要看重投影误差通常平均误差要控制在0.5像素以内超过1像素就得重新拍。外参标定更关键它解决的是“这路相机相对于车体在什么位置、朝向哪里”的问题。常见做法是把车开到标定间利用地面上的标定板和多个相机共同看到的特征点求解每个相机到车体坐标系的旋转和平移。也可以用雷达点云辅助把相机图像和激光点云做交互标定。这一步我踩过的坑是不同同事定义的“车体坐标系原点”可能不一样有的放在后轴中心有的放在车辆几何中心有的放在GPS天线位置。坐标系不统一融合算法再好也白搭。坐标变换链路要非常清晰图像像素坐标 - 归一化相机坐标 - 相机坐标系三维点 - 车体坐标系三维点 - BEV栅格坐标。每一步用矩阵还是用齐次坐标建议写代码前先在纸上推一遍不要边写边试。3.2 IPM投影的代码实现解读接着前面那段代码说IPM最核心的就是H矩阵的构造和remap采样。实际项目中如果有多路相机每一路都要单独计算自己的H矩阵然后把多路BEV图放到同一个以自车为中心的大栅格里。这里有两个工程化细节。第一个是栅格尺寸和分辨率的匹配。假设车体坐标为x向右、z向前前方20米、两侧10米的范围分辨率取0.1米每像素那么BEV图尺寸就是200x400这个量级的计算量对嵌入式平台很友好。分辨率不要一味求高0.05米和0.1米之间的差异在最终的融合结果上人眼几乎分辨不出来但计算量差四倍。第二个是无效区域处理。图像边缘、天空区域、超出BEV范围的点在remap时都应该被标成无效。我习惯把采样坐标的mask一并输出后续在做多相机融合时mask可以帮助判断某个BEV位置到底有没有被某路相机覆盖到。3.3 多相机图像融合策略多路相机投影到同一个BEV栅格后重叠区域怎么融合是关键。最简单的做法是取平均值但效果通常不好因为不同相机的曝光和白平衡存在差异重叠区会出现明显的亮度跳变。更稳妥的做法是距离权重融合每个相机只对离自己较近的区域给高权重离得远就降低权重。这个权重的计算公式也很简单比如离相机越近权重越大可以用高斯函数来衰减。代码层面可以先为每路相机生成一个权重图权重图和BEV图一样大然后按这个公式融合bev_final sum(w_i * bev_i) / sum(w_i)其中w_i是第i路相机在当前位置的权重bev_i是第i路相机的BEV图。这样重叠区是两个相机的平滑过渡不会出现生硬的接缝。还要做一步曝光一致性校正。多相机硬件规格相同但装车位置不同朝向不同自动曝光出来的亮度可能差很多。如果说实测中经常遇到侧视相机明显偏暗那就要在融合前计算整幅图像的平均亮度做一次增益补偿。甚至可以更简单先记录每个相机的亮度统计值写死一个补偿系数避免自动曝光反复波动。3.4 时序同步与运动补偿多相机融合只是单个时刻的“上帝视角”但感知系统要给下游源源不断地输出就要考虑时序问题。最理想的情况是硬件同步。相机支持PPS或外触发就更好了同一时刻曝光所有图像对应同一个物理瞬间。如果硬件不支持就靠软件时间戳对齐用最接近的时间戳去取每一路相机画面但车载场景下运动一快几毫秒的偏差就可能带来明显错位。还有一个容易被忽略的问题是运动补偿。车辆在行驶过程中上一帧的BEV和当前帧的BEV描述的是不同自车位置下的空间。如果要做历史帧融合比如把10帧的BEV叠加起来提升稳定性就必须先把上一帧的BEV按自车的位移和转角变换到当前坐标系。这个变换就是一个二维刚体变换P_new R(Δθ) * P_old T其中Δθ是两帧之间的车辆航向变化T是车辆中心的位移。实现的时候可以把历史BEV先做一次仿射变换再和当前帧融合计算量不大但对稳定性提升非常明显。我在实际测试中发现加了运动补偿之后静态障碍物在BEV栅格里的闪烁大幅减少连续帧叠加出来的“占用热度图”也干净了很多。这个技术在自动泊车场景里尤其重要因为泊车时的车速虽然慢但转方向盘的动作频繁航向变化很快不做补偿的话历史信息基本是废的。3.5 数据驱动方案的工程化要点如果决定上深度方案几个工程参数需要提前定好。BEV栅格大小一般取0.2米到0.4米太细了模型参数量和推理耗时受不了太粗了又丢失位置精度。深度离散化一般是把相机前方4米到50米以及侧面近处范围划分成30到50个区间每个像素输出一个深度分布。高度范围通常取车体上下各几米比如-5米到3米划分成20个以上的体素层。训练数据的标注是最大难题。BEV标注不像2D检测框那样好画通常的做法是先用3D标注工具标出障碍物的三维包围框再投影到BEV平面生成mask。这样一个框在不同相机的透视图上投影出来就是完全一致的正好利用上了“上帝视角”这个统一坐标系的好处。4. 落地时最容易踩的坑与排查技巧4.1 标定误差引起的投影错位BEV系统最常见的现象就是拼接错位车道线在中间相机里是直的到了侧视相机和环视相机的交界处突然断成两截。遇到这种情况我的排查顺序是先看单路相机自己的IPM结果如果单路图上直线已经弯曲问题出在外参或内参如果单路正常、融合边界错位问题出在多个相机之间的外参相对关系。排查时可以用一个很简单的方法在场地里摆放几块边缘锐利的标定板或箱子看它们在BEV图上的边缘是否对齐。如果错位超过10厘米基本就是外参需要重新做。很多团队为了图省事只在出厂时标定一次但车辆在运输、使用过程中外参会因为震动发生偏移所以项目里最好设计一个“外参自检”流程定期用车道线或地面特征来评估偏差。提示外参标定的方向定义是最常见的坑。写代码前先明确“车体坐标系到相机坐标系”还是“相机坐标系到车体坐标系”并在代码注释里写清楚否则换人接手后很容易踩雷。4.2 远处区域拉伸与像素空洞IPM方案里远处区域拉伸是物理规律决定的同样的一个路灯杆在10米外可能占20个像素在40米外只剩五六个像素投影到BEV上远处的每个像素对应实际地面面积越来越大看起来就是一层模糊的拉丝。这个问题没有完美的解决方案工程上一般三种手段组合使用。第一限制BEV的有效范围比如只输出前方15米、后方8米的有效区域超出部分直接截断。第二提高相机的近处视野覆盖率多路相机重叠覆盖让远处区域由多路共同补充信息。第三适当做CLAHE或锐化后处理观感会好一些。实际项目里如果对远处障碍物的识别要求高单靠视觉IPM是不够的要么上深度方案要么融合毫米波雷达。4.3 动态目标的高度歧义IPM假设所有物体都贴在地面上所以站在地面上的行人还好卡车这种高度超过三四米的物体会被“压扁”在BEV图上出现重影。同一辆车的前部和顶部会投到完全不同的地面位置形成明显的拖尾。我在做园区配送车时遇到的典型场景是一辆三轮车从侧前方驶过BEV图上它的影像拉出一条长长的轨迹检测模块会误判为多个物体。解决思路有两种。一种是在透视图像上先做2D目标检测再把检测框底边中心点当作接触地面的点投影到BEV这样至少框的位置是准确的。另一种是融合激光雷达或毫米波雷达点云用雷达点给视觉目标赋予真实高度视觉负责分类雷达负责几何位置两个一结合重影问题基本消失。4.4 时序抖动与拼接闪烁时序抖动现象是车辆明明停着没动BEV图上的车道线却在几厘米范围内来回跳。这种问题往往不是算法原理造成的而是时间同步和标定稳定性问题。多路相机曝光时刻不一致车辆又在轻微晃动每一帧融合出来的画面自然有差异。我的排查思路是先把车辆完全静止看BEV是否稳定。如果静止也抖动优先检查两件事第一是相机是否存在卷帘快门效应高速运动场景下建议换全局快门相机第二是帧号和图像缓存是否真的一致有没有出现图像队列里混入旧帧的情况。如果静止稳定、运动时抖动那就是运动补偿参数的问题检查自车的速度和航向角来源是否延迟过大必要时对补偿量做一阶低通滤波。整理一个速查表方便现场排查现象可能原因排查建议解决方案车道线在拼接处断开外参不准或多相机相对位姿偏差用标定板原地验证边缘对齐重新外参标定设计定期自检BEV远处模糊拉丝透视分辨率随距离衰减对比不同距离的投影分辨率限制ROI增加重叠覆盖融合雷达动态目标拖影/重影平面假设失效、目标有高度检查单个相机的IPM结果与检测框底边用检测框底边投影或融合雷达点云静止画面仍抖动时间戳不同步或相机卷帘快门车辆静止测试检查缓存队列硬件同步曝光换全局快门相机5. 这套“上帝视角”还能用在哪些地方5.1 自动泊车与低速辅助自动泊车是BEV最早落地也最成熟的场景。鱼眼相机的环视图经过IPM展开后直接在俯视视角下做车位线检测、车位占用判断和路径规划整个流程都是在平面坐标系上完成的非常自然。我自己体验过一些量产车的自动泊车实际效果好的车型背后基本都是同一套思路多路鱼眼相机标定到车体坐标系再用一个俯视栅格做车位和障碍物感知。5.2 园区无人配送车园区配送车的速度不高但路况复杂有人、有外卖车、有减速带。BEV栅格可以直接作为局部代价地图的一部分融合激光雷达点云后无人车可以在栅格上做A*或者DWA路径规划。我在园区项目里的经验是BEV感知的引入让无人车避让行人的动作明显平滑很多因为它不再依赖单视角的检测结果而是真正知道每个目标和自车的相对位置。5.3 无人机正射影像拼接无人机挂载摄像头拍摄地面时拍到的同样是透视图像用于地图测量必须先做正射校正本质上也是“上帝视角”。无人机航拍的正射拼接就是把每一帧透视图像投影到地面平面坐标系然后通过特征匹配和全局优化把多帧拼成一张完整的地图。思路和车载BEV非常像只不过无人机多了GPS和IMU作为先验位姿。5.4 数字孪生与跨摄像头监控大型园区有几十路摄像头每路视角独立管理人员很难快速理解全局空间关系。如果把所有摄像头画面都投影到统一的园区平面图上形成一张动态更新的俯视大屏就相当于给监控系统也装上了“上帝视角”。跨摄像头目标跟踪在统一坐标系下也简单很多一个行人从中门走到东门在平面图上的运动轨迹是连续的一条线而不是在不同摄像头的预览画面里跳转。我在实际项目中体会最深的一点是很多人把BEV的难点理解成“网络结构够不够先进”但真正耗掉时间的反而是标定、坐标系和时序同步这些基础工作。把这几件基础事做扎实那个“上帝视角”只是数学上顺其自然的产物。最后分享一个小技巧如果只是给业务方演示概念不用急着上大模型先用传统IPM拼出一版稳定的底图再把检测和跟踪结果投影上去效果已经很能说明问题。等确认真有需要再评估要不要上数据驱动方案也不迟。
返回列表