VPU与NPU协同优化:边缘AI视觉处理的算力融合实践 1. 项目概述边缘计算时代的算力融合新范式最近和几个做嵌入式AI和边缘设备的老朋友聊天大家不约而同地都在讨论一个话题在资源受限的边缘端如何把有限的算力“榨干”让模型跑得更快、更省电。聊着聊着焦点就落在了VPU和NPU这两个核心处理单元上。过去我们可能习惯于把它们看作是独立的、甚至是对立的加速器——一个专攻视觉一个专攻神经网络。但现在的趋势尤其是在智能摄像头、机器人、AR/VR眼镜这些对实时性和功耗都极其苛刻的场景里VPU与NPU的协同工作已经从一个“可选项”变成了“必选项”。简单来说VPU更像是视觉处理的“前道工序专家”它擅长处理摄像头进来的原始像素流完成诸如去噪、畸变校正、色彩转换、特征点提取、光流计算等任务。而NPU则是“推理决策专家”专门针对卷积、矩阵乘加等神经网络算子进行极致优化。如果把一个智能视觉任务比作一条流水线VPU负责把粗糙的原材料原始图像进行清洗、分拣、初步加工变成规格统一、质量上乘的半成品如图像金字塔、光流场、特征描述子NPU则负责在这个高质量的半成品基础上进行复杂的“深加工”目标检测、识别、分割得出最终结果。这种协同的核心价值在于效率。让VPU去干它最擅长的、高度并行且规则化的像素级处理解放NPU让它专注于更复杂的张量计算。两者通过高效的内存共享和任务调度避免了数据在系统内存中的来回搬运从而大幅降低延迟和功耗。我经手过的一个车载DMS项目最初只用NPU跑完整的检测模型帧率勉强到15fps功耗却居高不下。后来引入VPU进行图像预处理和ROI区域提取NPU只处理关键区域最终在功耗几乎不变的情况下帧率提升到了30fps以上这就是协同带来的实实在在的收益。2. 核心架构与分工原理深度拆解要理解协同必须先吃透两者各自的设计哲学和擅长领域。这绝不是简单的“11”而是基于任务特性的深度耦合。2.1 VPU视觉流水线的“硬件加速器”VPU的设计目标非常明确高效处理二维像素阵列。它的核心能力可以概括为几个方面固定功能硬件单元这是VPU效率的基石。比如内部会集成专门的图像信号处理器硬件模块用于执行白平衡、去马赛克、伽马校正等操作这些操作有非常固定的算法流程用硬件实现比通用处理器或可编程单元快几个数量级功耗也更低。可编程向量/SIMD单元用于处理一些需要灵活性的视觉算法比如特定的特征提取ORB、SIFT的某些阶段、图像滤波双边滤波、导向滤波等。这些单元支持单指令多数据流操作能同时对图像中多个像素进行相同的计算非常适合图像处理的高度并行性。高效的内存访问模式图像数据量大且访问具有强烈的空间局部性相邻像素常被一起使用。VPU的存储器子系统通常针对这种二维数据访问进行优化支持高效的块传输、二维DMA等能最大限度地减少数据搬运开销。在实际项目中我们通常把以下任务卸载给VPU图像预处理分辨率缩放下采样/上采样、色彩空间转换YUV2RGB/RGB2BGR、直方图均衡化。这些操作非常规整VPU处理能极大减轻主CPU和NPU的负担。几何变换与校正镜头畸变校正、透视变换用于鸟瞰图生成。这些涉及像素坐标的重映射计算密集但规则是VPU的强项。前端视觉处理光流计算、稀疏特征点提取与跟踪。例如在VSLAM或视觉里程计中VPU可以实时计算出帧间特征点的运动矢量为后续的位姿估计提供输入。注意不是所有“视觉算法”都适合VPU。那些控制流复杂、分支众多的算法比如一些复杂的决策逻辑在VPU上效率可能反而不高更适合CPU处理。VPU的强项在于数据流规整、计算密集型任务。2.2 NPU张量计算的“定域化武器”NPU则是为神经网络尤其是深度学习模型推理而生的。它的设计围绕张量展开乘积累加运算阵列这是NPU最核心的部分一个巨大的、二维的MAC阵列。它可以一次性完成一个卷积核与输入特征图一片区域的所有乘加操作。阵列越大并行度越高计算吞吐量越大。例如一个128x128的MAC阵列单周期就能完成16384次乘加运算。权重/激活缓存为了喂饱巨大的计算阵列NPU内部设计了多级缓存体系。权重通常被预先加载到片上缓存中避免频繁访问外部低速内存。激活数据中间特征图的复用也会被精心优化。专用数据流架构如脉动阵列、NVDLA等架构通过精心设计数据在计算单元间的流动方式使得在完成一次计算的同时就能为下一次计算准备好数据实现极高的计算效率和能效比。NPU擅长的是模型中那些可表示为大规模矩阵乘加或卷积的操作例如卷积层、全连接层。池化层虽然简单但NPU集成后效率很高。一些常见的激活函数如ReLU、Sigmoid的硬件实现。2.3 协同的本质数据流与任务流的重新编排VPU和NPU的协同本质上是对传统视觉AI处理流水线的一次硬件级重构。我们来看一个典型的人脸识别门禁系统的例子传统方式NPU为主传感器采集原始图像Bayer格式 - 系统内存。CPU调用软件库进行ISP处理耗时 - 输出RGB图像到系统内存。CPU将RGB图像预处理缩放、归一化 - 系统内存。NPU从系统内存读取数据进行人脸检测推理 - 结果写回系统内存。CPU处理结果如需识别再裁剪人脸区域进行第二次预处理 - 系统内存。NPU再次读取进行人脸特征提取推理 - 系统内存。CPU进行特征比对。这个过程中数据在传感器、系统内存、CPU、NPU之间经历了多次“旅行”每一次搬运都是时间和能量的消耗。协同优化后VPUNPU传感器数据直接进入VPU的专用输入接口。VPU硬件ISP管线实时处理输出高质量RGB图像并直接写入VPU与NPU共享的片上或紧耦合内存。VPU继续对RGB图像进行缩放和归一化结果仍在共享内存中。NPU直接从共享内存读取预处理好的张量数据进行人脸检测推理输出人脸框坐标可写回共享内存。VPU根据人脸框坐标直接从其内部流水线上“截取”对应区域的图像数据可能只需指针偏移无需物理拷贝进行第二次针对识别模型的预处理如对齐、标准化。处理后的数据直接提供给NPU进行特征提取。特征值被CPU读取进行比对。这个优化后的流程其提升主要体现在零拷贝或极低拷贝数据在VPU和NPU间通过共享内存或直接数据通路传递避免了经过系统内存的冗余搬运。流水线化VPU的预处理和NPU的推理可以部分重叠。当NPU在处理第N帧的推理时VPU已经在处理第N1帧的图像了。任务卸载CPU被彻底解放出来只负责最上层的业务逻辑和调度整个视觉流水线由VPU和NPU自主完成。3. 协同创新的关键技术实现路径理解了原理我们来看看在实际芯片和系统设计中是如何实现这种协同的。这里面有几个关键的技术节点。3.1 内存架构与数据共享机制这是协同的物理基础。通常有以下几种模式片上共享SRAM这是效率最高的方式。VPU和NPU作为同一个SoC上的IP核共享一块物理上的高速片上内存。双方通过统一的内存控制器和地址空间访问这块内存。数据生产方如VPU写完通过硬件信号如门铃、中断通知消费方如NPU消费方直接读取。延迟极低通常在几十纳秒级别。但这种内存容量有限几MB到几十MB需要精心管理数据生命周期。一致性互联与共享DDR当数据量较大时会使用片外DDR内存作为共享池。VPU和NPU通过支持缓存一致性的片上网络连接到系统互联总线上。这样VPU处理完的数据写入DDRNPU的DMA引擎可以直接从DDR中读取。由于有硬件一致性保证无需软件进行缓存刷新操作效率也比较高。但访问延迟比片上SRAM高一个数量级。直接数据通路更激进的设计是在VPU和NPU之间建立一条专用的、点对点的数据通道。例如VPU的输出FIFO可以直接连接到NPU的输入FIFO。数据像流水一样直接从VPU“流”向NPU完全绕过任何形式的内存。这适用于处理流程固定、数据实时性要求极高的场景但灵活性较差。在编程模型上开发者面对的是一个统一的内存视图。芯片厂商会提供相应的驱动和中间件将物理上可能分散的内存在逻辑上抽象成连续的、可分区的缓冲区。开发者只需申请一块内存指定其用途VPU输入、VPU输出、NPU输入等底层驱动会自动将其分配到最合适的物理位置片上或片外并处理好同步问题。3.2 任务调度与同步策略硬件有了如何让VPU和NPU“步调一致”地工作就是软件栈的任务了。核心是事件驱动的流水线调度。基于硬件信号量的同步这是最底层、最高效的方式。VPU完成一个阶段的任务如ISP处理后会向一个硬件寄存器写入一个值释放信号量。NPU的控制器持续轮询或通过中断感知到这个信号量变化随即开始自己的工作。这种方式延迟极低但需要开发者对硬件非常了解编程复杂度高。基于中间件/运行时系统的调度这是更主流的方式。芯片厂商会提供一个任务图编译器或调度器。开发者以描述性的语言如JSON、Python定义整个处理流水线节点代表任务VPU任务、NPU任务边代表数据依赖关系。{ graph: [ {id: isp, type: vpu, input: sensor, output: rgb_image}, {id: preprocess, type: vpu, input: rgb_image, output: tensor_4det}, {id: face_detection, type: npu, model: detect.kmodel, input: tensor_4det, output: bboxes}, {id: crop_and_align, type: vpu, input: [rgb_image, bboxes], output: face_patch}, {id: face_recognition, type: npu, model: recog.kmodel, input: face_patch, output: feature} ] }运行时系统会解析这个图自动为每个任务分配硬件资源VPU核、NPU核在数据就绪时触发任务执行并处理所有同步。开发者只需关注算法逻辑本身。动态负载均衡在更复杂的系统中可能有多个VPU核和NPU核。调度器还需要根据任务的计算量、当前各核心的负载情况动态决定将下一个任务派发给哪个空闲的或负载轻的核心以最大化整体吞吐量避免“忙的忙死闲的闲死”。3.3 软件栈与工具链支持再好的硬件没有易用的软件也是空中楼阁。协同创新的落地强烈依赖于完整的软件生态。统一的编程框架理想的状况是开发者使用一套API就能同时操作VPU和NPU。例如OpenVINO™工具套件就能在一定程度上实现这一点它提供了一个统一的运行时可以部署模型到CPU、GPU、VPU等多种硬件上并优化它们之间的数据流动。在嵌入式领域一些芯片厂商也提供了自己的统一推理框架将VPU的功能封装成“预处理算子”与NPU的模型推理算子无缝衔接。模型编译与优化工具链需要能够理解整个处理图。不仅要将神经网络模型编译成NPU能执行的指令还要能将其中一些层如某些简单的预处理层、后处理层自动识别出来并尝试将其“下沉”到VPU执行如果VPU有更高效的实现。这个过程称为“图优化”或“算子下沉”。性能分析与调试工具这是开发者的“眼睛”。工具需要提供时间线视图能清晰展示每一帧数据在VPU和NPU上的执行时间、空闲时间、数据搬运时间。当性能不达预期时开发者可以快速定位瓶颈是在VPU处理太慢还是在NPU等待数据亦或是在内存带宽上。4. 典型应用场景与实战优化案例理论说再多不如看实战。下面我结合几个具体的场景聊聊协同设计是如何解决实际痛点的。4.1 场景一高分辨率智能视频监控痛点4K甚至8K的监控视频流数据量巨大。如果直接送给NPU做全图检测计算量爆炸帧率上不去功耗也无法接受。协同方案VPU作为“侦察兵”VPU首先对全分辨率视频流进行下采样生成一个低分辨率版本如1080p。同时VPU可以运行一个非常轻量级的、计算密集型的场景变化检测或运动区域检测算法。这个算法可能只包含几层卷积或者直接使用背景减除等传统视觉方法在VPU上可以极高效率运行。NPU聚焦“重点区域”VPU将检测到的运动区域坐标ROI传递给NPU。NPU不再处理整张图而是根据这些坐标从原始高分辨率帧中或VPU预处理后的高质量帧中只裁剪出对应的区域块对这些小块进行高精度的目标检测如人、车、包裹识别。结果融合NPU识别出小区域内的目标后再将坐标映射回原始全景画面中。收益NPU的计算负载可能降低80%以上系统整体功耗和延迟大幅下降同时保证了关键区域的分析精度。VPU做的运动检测其功耗远低于NPU做一次全图检测。4.2 场景二移动端实时AR应用痛点AR应用需要同时完成SLAM实时定位与地图构建和物体识别/渲染对延迟20ms和功耗手机发热要求极其苛刻。协同方案VPU负责SLAM前端摄像头图像输入后VPU并行完成多项任务硬件ISP处理、提取FAST或ORB特征点、计算光流追踪特征点、构建稀疏点云。这些都是规则且并行的计算完美契合VPU。NPU负责语义理解在同一帧图像上NPU并行运行轻量级的语义分割模型识别出地面、墙壁、可放置物体的平面等。数据汇合与决策VPU输出的相机位姿估计6-DoF和NPU输出的语义信息被送到CPU或一个专用的融合处理器。CPU结合两者信息决定虚拟物体应该放置在哪个语义平面如桌面上并且位姿是稳定的。VPU辅助渲染可选一些高级的VPU还能辅助进行简单的几何变换帮助实现虚拟物体的透视遮挡等效果。收益SLAM和识别两大高负载任务被分流到最合适的硬件上并行执行满足了AR应用极高的实时性要求。如果全部用NPU或CPU软件实现要么延迟太高要么手机瞬间变成“暖手宝”。4.3 场景三工业视觉质检痛点生产线速度极快检测算法复杂可能包含传统视觉深度学习且对稳定性要求极高。协同方案VPU完成标准化产品经过摄像头VPU负责进行光照归一化消除反光、图像锐化、对比度增强确保无论环境光如何变化输入到后续算法的图像质量是稳定的。同时VPU通过模板匹配或边缘检测对产品进行粗定位和纠偏。NPU进行缺陷分类将标准化和定位后的产品图像区域送入NPU运行一个深度学习分类模型判断是否存在划痕、污渍、装配错误等缺陷。VPU执行精确测量对于某些需要亚像素级精度的尺寸测量NPU可能并不擅长。此时可以由VPU在NPU给出的疑似缺陷区域使用灰度重心法、边缘亚像素拟合等传统算法进行精确测量给出量化数据。收益将鲁棒性高的传统视觉算法预处理、定位、测量放在VPU将需要“认知”能力的复杂缺陷分类交给NPU形成了优势互补。系统既利用了深度学习的高准确性又保留了传统视觉的稳定性和高精度整体检出率和效率都得到提升。5. 开发挑战与避坑指南在实际开发中从“知道协同好”到“实现协同好”中间有不少坑。这里分享一些我踩过的坑和总结的经验。5.1 内存带宽成为隐形瓶颈这是最容易忽视的问题。你以为VPU和NPU都在疯狂计算性能应该很好但实际帧率就是上不去。用性能分析工具一看发现硬件计算单元利用率很低大部分时间在等待数据。问题根源VPU处理高分辨率图像产生中间数据如多尺度图像金字塔、光流场NPU的模型也有巨大的输入输出张量。当VPU和NPU通过共享的DDR内存交换数据时巨大的数据吞吐量可能会瞬间占满内存控制器的带宽导致访问排队所有人都慢下来。解决方案精简数据仔细评估VPU传递给NPU的数据是否都是必需的。例如NPU做目标检测真的需要完整的RGB三个通道吗也许YUV的Y亮度通道就够了。数据格式能从float32降到int8吗利用片上缓存尽可能利用VPU和NPU内部的私有缓存减少对共享内存的访问次数。优化数据复用模式。数据压缩在VPU和NPU之间传输数据时如果支持可以考虑使用轻量级的无损或有损压缩。例如一些芯片的VPU输出支持Tile-based的压缩格式。错峰调度如果可能让VPU和NPU对内存的访问高峰尽量错开。但这需要非常精细的调度控制。5.2 任务划分与负载不均另一个常见问题是VPU和NPU一个忙死一个闲死流水线无法饱和。问题根源任务划分不合理。例如你把一个非常轻量的颜色转换放在VPU而把一个极其庞大的检测模型放在NPU。VPU瞬间干完活就等NPUNPU则需要很长时间整体帧率被NPU拖慢。解决方案性能剖析务必使用 profiling 工具精确测量流水线中每个阶段VPU任务A、VPU任务B、NPU任务的耗时。找到那个最长的“短板”关键路径。动态调整如果VPU是短板考虑能否将部分预处理任务简化或者看看NPU是否有能力分担一点但通常NPU做像素处理效率不高。如果NPU是短板就是前面提到的想方设法为NPU减负让VPU做ROI提取、让模型量化得更轻量、使用更高效的网络结构。流水线深度增加流水线的深度。如果处理一帧需要VPU(5ms) NPU(15ms)20ms那么理想帧率是50fps。但如果我们能让VPU处理第2帧时NPU处理第1帧那么虽然单帧延迟还是20ms但系统的吞吐量可以达到66fps1/15ms。这需要中间有足够的缓冲区来存储处理中的帧。5.3 软硬件耦合过紧移植性差早期为了追求极致性能我们可能会大量使用芯片厂商提供的底层硬件指令、特殊内存操作。代码里充满了#ifdef (CHIP_A)这样的条件编译。问题根源代码严重依赖特定芯片的特定硬件特性一旦更换硬件平台需要大量重写甚至重设计维护成本极高。解决方案抽象中间层在业务逻辑和硬件加速库之间定义一层清晰的硬件抽象层。这层API是稳定的例如ImageProcessor.process(),NeuralEngine.infer()。底层针对不同芯片实现不同的适配器。业务代码只调用HAL接口。拥抱开源或行业标准运行时优先选择支持多种后端硬件的运行时框架如TVM、Apache MXNet、TensorRT等。它们已经做了大量的硬件适配工作你的模型和预处理流程可以用相对统一的方式描述由运行时去负责在特定硬件上的高效执行。配置化非代码化将任务流水线、硬件资源分配等用配置文件如YAML、JSON来描述而不是写死在代码里。这样换一个平台可能只需要修改配置文件而不是C/Python代码。5.4 调试与诊断困难当系统行为不符合预期时由于涉及两个异构计算单元和复杂的数据流调试起来非常头疼。日志打印可能会破坏实时性而线上问题又难以复现。实操心得内置性能计数器选择那些提供丰富性能计数器的芯片。不仅要看VPU和NPU的利用率还要看内存带宽、缓存命中率、任务队列深度等。这些计数器是定位性能瓶颈的黄金指标。非侵入式跟踪利用芯片的跟踪调试单元它可以以极低的开销将关键事件任务开始/结束、数据生产/消费信号量变化和时间戳记录到一个循环缓冲区中。事后可以将这些数据导出在PC端工具上重现出完整的时间线图像看视频回放一样分析每一帧的处理过程。设计“降级模式”在软件中设计开关可以动态关闭VPU加速全部用CPU软件模拟或者关闭NPU用CPU推理。通过对比可以快速定位问题是出在VPU的预处理结果不对还是NPU的推理出了问题或者是协同接口的数据格式有误。可视化中间结果在开发阶段一定要有机制能将VPU处理后的图像、NPU的输入张量、输出结果导出来在PC上用PythonOpenCV进行可视化检查。很多时候问题就是简单的颜色通道顺序搞反了、归一化范围不对、图像对齐有偏差。肉眼一看便知。