车载360环视只是伪3D?CPU查表法 vs GPU碗状模型硬核拆解
导读
4 个鱼眼拼一张鸟瞰图,CPU 靠查表硬扛,GPU 用碗状模型渲染——但说真的,现在量产的 3D 环视全是伪 3D。CPU 方案为什么死活搞不定 3D 自由视角?GPU 碗状模型的硬伤在哪?从头拆一遍。
一、车载AVM环视系统:能干什么
AVM(Around View Monitor)要干的事就是:把装在车身四周的 4 个鱼眼摄像头拍到的画面,拼成一张从车顶往下看的完整鸟瞰图。
它解决什么问题?
驾驶员坐在车里,视野被车身遮挡,车头车尾、四个角落全是盲区。4 个鱼眼相机分别装在车头格栅、车尾牌照灯、左右后视镜下方,每个覆盖 180°+ 的视场角,四路拼接后做到 360° 无死角环视。
AVM 的应用场景:
| 场景 | 功能 | 依赖 |
|---|---|---|
| 倒车入库 | 鸟瞰图 + 轨迹线 | 方向盘转角 + 车身参数 |
| 窄路会车 | 左右两侧放大视图 | 转向灯信号触发 |
| 自动泊车 | 车位检测 + 泊车路径规划 | AVM + 超声波雷达 |
| 透明底盘 | 车底历史图像拼接 | 轮速脉冲 + 历史帧缓存 |
| 哨兵模式 | 停车监控 | 碰撞检测 + 录像 |
说白了:AVM 就是给驾驶员装了一双"上帝之眼",让你从车顶往下看,车身周围的一切尽收眼底。
二、AVM系统架构总览
整个系统里外分五层。采集层拿到 4 路 1280×720 的鱼眼图像,经过标定层拿到相机参数,处理层做畸变校正和视角变换,渲染层是分水岭——CPU 走 2D 查表,GPU 走 3D 渲染,输出层就是驾驶员看到的画面。
关键分水岭:CPU 方案只能走 2D 鸟瞰图一条路径,GPU 方案可以输出全部四种视图(2D 鸟瞰、3D 自由视角、放大视图、透明底盘)。
三、CPU版:2D俯视图是怎么做出来的
3.1 思路
把4个鱼眼相机拍到的、各自朝向不同方向的画面,统一"拍到一个水平地面上",然后拼成一张正上方看的鸟瞰图。
听起来简单,做起来全是细节。
第一步:去畸变
鱼眼镜头的畸变是"有意为之"——为了用一颗镜头覆盖180°+的视场,画面边缘必然严重弯曲。去畸变就是把弯曲的线条拉直,把扭曲的像素归位到针孔相机模型的投影位置。
第二步:透视变换
去畸变后的图像还是"从相机角度看的",需要变成"从正上方看的"。本质是求解一个单应性矩阵 H(3×3的变换矩阵),把路面上的点映射到俯视图的对应位置。
第三步:四路拼接
四路画面各自被变换到俯视图后,会有重叠区域。需要对重叠区域做融合(加权平均或线性混合),让拼接缝消失。
3.2 查表法(LUT):CPU方案的核心
如果每帧都实时计算透视变换——每个像素都要乘 3×3 矩阵、做一次除法、再插值——CPU 根本扛不住。
查表法的思路是:
标定完成后,相机和地面的位置关系就固定了。俯视图上每个像素点对应原始鱼眼图像的哪个坐标,是永远不变的。既然如此,标定完就把这个映射关系算好,存成一张表,以后每帧直接查表就行了。
说白了就是一张 "离线映射表":
俯视图坐标 (u, v) → 查表 → 原始图像坐标 (x, y) → 取像素 → 填到俯视图
怎么做:
- 标定阶段,根据相机的内参、外参、畸变系数,计算出俯视图上每个像素
(u, v)对应的世界坐标(X, Y, 0) - 把世界坐标反投影到相机坐标系,再投影到鱼眼图像坐标
(x, y) - 同时算好每个像素的融合权重(相邻相机重叠区域做渐变)
- 把
(x, y, weight)三件套存成一张查找表 - 运行时,遍历俯视图的每个像素,查表取坐标,从原始图像取像素值,乘以权重,填到输出画面
查表法为什么能跑在 CPU 上:
- 表算好了就不动了,运行时就是"读表 + 取像素 + 乘法",没有矩阵运算,CPU 轻松扛住
- 可以改成整型运算(定点数),不用浮点
- 可以配合 DMA 批量搬数据,减少 CPU 参与
3.3 图像融合:让拼接缝消失
四路画面拼在一起,重叠区域如果不处理,会看到明显的拼接缝。
为什么会有拼接缝?
- 四路相机曝光不同(车头对着太阳、车尾在阴影里,亮度差很多)
- 白平衡不一致(即使同一型号的相机,出厂微调也有差异)
- 拼接处几何对不齐(标定误差的累积)
融合方法:
重叠区域的像素值 = 左边相机的像素 × weight_left + 右边相机的像素 × weight_right
weight 在重叠区域从 1 渐变到 0,实现平滑过渡。这个权重也提前算好存在查表里。
本节要点:CPU 查表法的核心是用空间换时间——标定时把映射关系全算好,运行时只查表不计算。三个步骤:去畸变 → 透视变换 → 四路融合。缺点是只能输出固定视角的俯视图,换视角就得重新生成表。
3.4 CPU版完整流程
四、GPU版:3D环视是怎么做出来的
4.1 为什么需要3D
2D俯视图有个要命的问题:只有俯视一个视角。
倒车入库时俯视图很好用,但你想看车头前方3米的情况、想看右前轮旁边的马路牙子——俯视图帮不了你。2D方案只能靠"切换摄像头"来模拟(比如放大前视图、放大左右视图),但这些画面还是"从相机角度看的",不是"从驾驶员视角看的"。
3D环视的好处就是:你想从哪个角度看,就从哪个角度看。
4.2 思路:3D碗状模型 + 纹理映射
3D环视不是把图像"拍平"到地面上,而是把图像贴到一个3D曲面模型上,然后用虚拟相机在这个3D场景里漫游。
碗状模型是什么?
想象一个碗扣在地上,碗的内壁就是投影面。车身在地面中心,四路相机的画面被纹理映射到碗的内壁上——车头前方贴碗的前壁,车尾后方贴碗的后壁,左右两侧贴碗的左右壁。
为什么用碗状而不是平面?
- 平面投影:近处物体正常,远处物体被拉伸成面条(因为平面假设地面是无限远的水平面,但实际相机拍到的远景包含了天空和远处建筑物)
- 碗状投影:碗壁向上弯曲,远景贴在碗壁上,视角更自然,边缘不拉伸
除了碗状,还有哪些变种?
五平面投影(地面 + 四面斜坡)、半椭球投影。说穿了都是折中——在"地面平坦"和"远景不拉伸"之间找平衡,没有完美的解。
一句话:碗状模型 = 3D 曲面网格 + 纹理映射 + 虚拟相机。GPU 三阶段:离线建模 → 系统初始化 → 每帧渲染。
4.3 GPU具体怎么干(以OpenGL ES为例)
下面一步步拆开看 GPU 是怎么干的。
1. 初始化 OpenGL ES 环境
用 EGL 创建离屏渲染上下文(不显示在屏幕上,渲染到 FBO)。车载 SoC 通常支持 OpenGL ES 2.0/3.0。
技能点:
- eglCreateContext 创建 GL 上下文
- eglCreatePbufferSurface 创建离屏表面
- 加载顶点着色器和片段着色器(从文件或内嵌字符串)
2. 加载 3D 碗状模型
模型格式:顶点数组 (x, y, z) + 纹理坐标 (u, v) + 法线 (nx, ny, nz) + Alpha 值(用于融合)。
结构体大约是:
顶点属性交错存储:
position(3 float) + texCoord0(2 float) + texCoord1(2 float) + alpha(4 float)
两个纹理坐标对应两个相邻相机的纹理采样(用于重叠区域的 Alpha 混合)。
3. 上传 4 路鱼眼图像为纹理
每帧把 4 路鱼眼图像通过 glTexImage2D 上传到 GPU,绑定到 4 个纹理单元(GL_TEXTURE0 ~ GL_TEXTURE3)。
4. 顶点着色器:坐标变换
// 顶点着色器就是干这个的
gl_Position = matMVP * vec4(a_position, 1.0);
v_texCoord0 = a_texCoord0; // 相机A的纹理坐标
v_texCoord1 = a_texCoord1; // 相机B的纹理坐标
v_alpha = max(min(a_alpha, 1.0), 0.0); // 融合权重
matMVP = Projection × View × Model。Model矩阵把碗状模型放到世界坐标系,View矩阵是虚拟相机的位置和朝向,Projection矩阵是透视投影。
5. 片段着色器:纹理采样 + Alpha融合
// 片段着色器:两张纹理混合
color0 = texture(sTexture0, v_texCoord0); // 从相机A采样
color1 = texture(sTexture1, v_texCoord1); // 从相机B采样
FragColor = color0 * v_alpha + color1 * (1.0 - v_alpha); // Alpha混合
6. 虚拟相机控制
虚拟相机就是 View 矩阵的计算。控制虚拟相机的位置(eye position)和朝向(look-at point),就能切换视角:
| 视角 | 虚拟相机位置 | 朝向 |
|---|---|---|
| 鸟瞰(Top View) | 车顶正上方,高度约 5m | 垂直向下 |
| 3D 前视 | 车顶前上方,偏右 | 前方偏下 |
| 3D 后视 | 车顶后上方 | 后方偏下 |
| 3D 左前 | 车顶左前上方 | 左前轮方向 |
| 360° 旋转 | 围绕车顶圆周运动 | 始终指向车身中心 |
7. 车身模型渲染
在碗状场景之上,还要渲染一个 3D 车身模型(包括透明底盘效果)。车身模型和碗状模型共用同一个 MVP 矩阵,但材质不同。透明底盘需要额外处理——把前几帧已经渲染到地面的图像缓存起来,作为车底纹理。
8. 输出到屏幕
渲染完成后,从 FBO 读回像素数据,走 DRM/KMS(Linux)或 FrameBuffer 直接显示到屏幕。
GPU 渲染管线总结:初始化 OpenGL ES → 加载碗状模型 → 上传 4 路纹理 → 顶点着色器做 MVP 变换 → 片段着色器做纹理采样 + Alpha 混合 → 虚拟相机随意切换视角 → FBO 输出到屏幕。核心是 MVP 矩阵变换 + 纹理映射 + Alpha 混合,全部在 GPU 上并行完成。
4.4 GPU版的优缺点
优点:
| 优点 | 说明 |
|---|---|
| 自由视角 | 虚拟相机可以在3D场景中任意移动,想看哪个角度就看哪个角度 |
| 画面质量高 | 3D投影消除了2D方案的边缘拉伸问题,远景也清晰 |
| GPU并行加速 | 纹理采样、矩阵运算、Alpha混合全在GPU上并行完成,CPU几乎不参与 |
| 支持特效 | 透明底盘、雷达叠加、车模动画都能在GPU上自然实现 |
| 可扩展 | 增加视角只改View矩阵,不需要改渲染管线 |
缺点:
| 缺点 | 说明 |
|---|---|
| 需要GPU | 低端车机芯片上没有GPU或GPU太弱,3D渲染跑不起来 |
| 开发复杂 | 3D建模 + OpenGL着色器 + 纹理映射,比CPU查表复杂得多 |
| 调试困难 | CPU查表法出问题,打印几个像素值就能定位;GPU渲染出问题,得用RenderDoc之类的工具抓帧分析 |
| 功耗更高 | GPU一直开着比CPU查表耗电,对纯电车的续航有影响 |
| 纹理精度损失 | 鱼眼图像边缘分辨率低,贴到碗壁上会被放大,远处不够清晰 |
| 路面不平时拼缝明显 | 标定假设地面是完美平面,实际路面有坡度、坑洼、减速带,拼接处会出现错位和重影 |
| 碗状模型远近畸变 | 碗状模型是固定曲面,近处物体(< 0.5m)和远处物体(> 5m)投影到碗壁上会严重变形,不是真实光学投影 |
| 本质是伪3D | 碗状模型只是把2D纹理贴到3D曲面上,没有真实的深度信息,不是真正的3D重建 |
4.5 GPU 3D环视的本质缺陷——伪3D的真相
GPU 3D环视看着炫,但有三个绕不过去的硬伤:
1. 路面不平——拼缝原形毕露
AVM 标定是在平坦的标定间里完成的,假设地面是完美的 Z=0 平面。但实际驾驶中,路面有坡度、有坑洼、有减速带。一旦地面不是平的,单应性矩阵 H 的假设就失效了——四个相机交界处的路面点投影到俯视图上,会出现在不同的位置,拼缝就露出来了。
路面越不平,拼缝越明显。这也是为什么在越野、工地、烂路场景下,AVM 的环视画面看着"怪怪的"——地面标线在拼接处断开了,障碍物形状扭曲了。
2. 碗状模型——太近太远都畸变
碗状模型是一个固定曲面,不是根据实际场景动态生成的。问题出在两个极端:
- 太近(< 0.5m):碗底中心区域曲率变化剧烈,近处物体贴到碗底中心,投影关系在中心小范围内剧烈变化,物体会被"压扁"或"拉长"
- 太远(> 5m):碗壁向上弯曲,远处物体贴到碗壁上,投影关系变成曲线投影。碗壁的曲率是固定的,不会根据实际深度调整——一个 3 米外的路沿和一个 10 米外的建筑物,在碗壁上被"一视同仁",导致近处物体被拉伸、远处物体被压缩
这矛盾无解:你想要一个固定的碗同时适配 0.3m 和 30m 的场景,不可能。任何固定曲面模型都是折中。
3. 本质是伪3D——没有深度信息
现在市面上的"3D 环视"都是伪3D。说白了就是:把4张2D鱼眼图像当作纹理,贴到一个3D碗状模型上,然后渲染。碗状模型的顶点坐标是固定的,不管车前方是平地还是墙壁,碗状模型都不变——它只是"看起来"像3D,实际上没有深度信息。
真正的3D需要多目立体匹配:利用4路相机之间的视差,计算每个像素的深度值,生成3D点云,然后做三角网格重建。这才是真正的3D场景重建。但多目匹配的计算量巨大,在车机芯片上实时跑还很难。
伪3D的后果:
- 不能判断物体的真实距离(碗状模型上的顶点坐标是固定的3D位置,不是根据实际场景重建的,曲面上的"距离"和真实世界的距离对不上)
- 不能做碰撞检测(你不知道障碍物离车身有多远)
- 障碍物在碗壁上的投影位置和实际位置不一致(尤其是高处的物体,比如路灯杆、树枝)
总结:GPU 3D环视比CPU 2D方案强很多,但它不是"真3D"。它只是把2D纹理贴到3D曲面上,让你感觉像在看3D。路面不平、远近畸变、缺乏深度——这三个问题,要等真正的多目深度估计方案成熟后才能解决。
本节要点:GPU 3D 环视 = 碗状模型 + 纹理映射 + 虚拟相机。优点是自由视角、画面质量高、GPU 并行加速。缺点是伪 3D(无深度)、路面不平拼缝裂开、远近畸变、功耗高。记住:现在的"3D 环视"只是纹理贴曲面,不是真正的 3D 重建。
五、CPU 2D方案为什么搞不定3D
CPU查表法能跑2D俯视图,但3D自由视角是它永远跨不过去的坎:
1. 查表法天生是"固定视角"
查表法把"俯视图像素 → 鱼眼图像坐标"的映射固定死了。它的输出永远是俯视图,你想看3D左前方向?没办法,那张表只定义了俯视的映射关系。换一个视角就要重新生成一张表,而表的大小和视角数量成正比——8个视角就是8张表,内存爆炸。
2. 透视投影算不动
3D视角的本质是透视投影——近处的物体大、远处的物体小。CPU查表法用的是单应性变换(平面到平面的映射),无法表达透视效果。要模拟透视,每个像素的映射关系都不同,而且随着虚拟相机移动而实时变化,CPU根本算不过来。
3. 遮挡关系处理不了
3D场景中,碗壁上的一个面可能挡住另一个面。GPU可以用深度测试(Z-Buffer)自动处理遮挡,CPU方案要自己实现遮挡判断——在720p分辨率下,就是近百万次的深度比较,实时性没戏。
4. 纹理采样精度不够
查表法通常用双线性插值从鱼眼图像取像素。但3D环视需要多次纹理采样(比如鱼眼图像→3D曲面→虚拟相机视角,经过两次投影变换),双线性插值会产生明显的锯齿和模糊。GPU的mipmap和anisotropic filtering能解决这个问题,CPU没有这些硬件特性。
5. 融合计算量指数增长
2D俯视图只需要在重叠区域做一次融合(4个重叠区域)。3D环视中,每个三角面片都可能跨越两个相机的边界,需要逐面片做Alpha混合。CPU遍历每个面片的速度远不如GPU的并行着色器。
本节要点:CPU 搞不定 3D 的根因——查表法是固定映射,换视角就得重建表;单应性变换表达不了透视投影;没有 Z-Buffer 做遮挡判断;没有硬件纹理采样;融合计算量指数增长。CPU 方案对 3D 自由视角"没戏"。
六、车载环视的发展方向
1. 从4路到更多路
4路鱼眼是现在的主流,但已经有车厂在试6路甚至8路方案。侧前方和侧后方各加一个相机,减少死角,提高拼接精度。更多相机意味着更多的重叠区域,标定和融合的复杂度翻倍。
2. 从"看"到"理解"
现在的AVM只是"把画面拼出来给人看",下一步是"让系统理解画面里有什么"。用深度学习做语义分割,识别出车位线、路沿、障碍物、行人,把信息叠加到环视画面上。GPU不仅要有渲染能力,还得有推理能力(NPU/GPU Compute)。
3. 和传感器融合
环视只是视觉,但自动驾驶需要多传感器融合。AVM + 超声波雷达(短距)+ 毫米波雷达(中距)+ 激光雷达(可选)= 完整的感知系统。3D环视的碗状模型可以作为传感器融合的统一坐标系——所有传感器数据投影到同一个3D场景中。
4. 自适应3D投影
现在的3D环视用的是固定碗状模型,不能根据场景变化。前方有障碍物时,应该自动把虚拟相机抬高,让你看到障碍物后面有什么;高速行驶时,应该把视角拉远,扩大视野范围。用光流分析或语义信息,自适应调整投影模型。
5. 纯视觉深度估计
现在的3D环视只是"透视投影",不是真正的3D重建——它没有深度信息。如果能从4路图像中估计出每个像素的深度(类似双目立体匹配,但用4路多目),就可以实现真正的3D场景重建,不再依赖固定碗状模型。
6. 车机芯片升级
车载GPU从OpenGL ES 2.0升级到Vulkan/OpenGL ES 3.2,支持更复杂的着色器,可以做出更好的光照效果、阴影、反射。高通车机芯片(如SA8295)的GPU已经接近PC级,3D环视的画质还有很大提升空间。
发展关键词:更多路 → 语义理解 → 传感器融合 → 自适应投影 → 纯视觉深度估计 → 芯片升级。终极目标是:从"给人看"进化到"让系统理解",从"伪3D"进化到"真3D重建"。
七、总结
| 维度 | CPU 2D查表方案 | GPU 3D渲染方案 |
|---|---|---|
| 核心原理 | 离线查表:俯视图→鱼眼图像坐标映射 | 3D碗状模型 + OpenGL纹理映射 + 虚拟相机 |
| 视角 | 仅俯视 | 任意视角漫游 |
| 画面质量 | 边缘拉伸,远景模糊 | 远近均匀,边缘自然 |
| 算力 | CPU 密集,逐像素遍历 | GPU 并行,着色器一次处理全屏 |
| 开发难度 | 低(查表逻辑简单) | 高(3D建模+着色器+纹理管线) |
| 功耗 | 低 | 中高 |
| 适用平台 | 低端车机(无GPU) | 中高端车机(有GPU) |
| 代表场景 | 早期车载环视、后装市场 | 前装量产、自动泊车 |
| 路面适应性 | 同GPU方案,也怕不平 | 路面不平时拼缝明显 |
| 3D真实性 | 无3D | 伪3D(纹理贴曲面,无深度) |
记住这几点:
- AVM 就是拼 4 路鱼眼图像,给你 360° 上帝视角
- CPU 2D 方案的本质是查表法——标定阶段算好映射关系,运行时只查表,不计算
- 查表法简单直接,但只能输出固定视角的俯视图,没法做 3D 自由视角
- GPU 3D 方案用碗状模型 + 纹理映射,虚拟相机可以在 3D 场景里随便转
- 3D 环视的 GPU 实现涉及:OpenGL 初始化 → 模型加载 → 纹理上传 → MVP 矩阵 → 着色器渲染 → Alpha 混合 → FBO 输出
- CPU 方案搞不定 3D 的根因:查表法是固定映射、单应性变换无透视、无深度、无硬件纹理采样、融合计算量指数增长
- GPU 3D 方案的三个硬伤:路面不平拼缝裂开、碗状模型远近畸变、本质是伪 3D 没有深度
- 发展方向:更多相机 → 语义理解 → 传感器融合 → 自适应投影 → 纯视觉深度估计 → 芯片升级
AVM 从 2D 到 3D 的进化,说白了是从"算好了给你看"到"你想怎么看就怎么看"。GPU 让这件事变成了可能,车机芯片升级让 3D 环视从高端车走向普及。但别忘了,现在的"3D 环视"只是伪 3D——把纹理贴在碗上,不是真正的 3D 重建。真正的 3D,要等多目深度估计成熟之后。