我最近一年几乎都耗在“端侧推理”这四个字上。团队做的是一台巡检机器人,按最初的产品定义,一台设备要同时跑目标检测、语义分割、路径规划三路模型,整机功耗预算12W,从传感器采样到执行器动作的端到端延迟不能超过60ms,而且现场没有可靠的无线信号,云推理方案从立项第一天就被否掉了。这个场景其实非常典型——凡是需要在物理世界里干活的AI系统,几乎都绕不开端侧推理这道坎。
我在这套系统里同时负责算法选型和一部分嵌入式运行时的工作,也是我第一次比较完整地经历“模型设计—量化压缩—算子移植—整机联调”的全过程。过了大半年再回头看,项目文档里那些“通过深度学习实现缺陷识别”的开场白毫无意义,真正卡住进度的全是工程问题:算力够不够、内存有没有余量、延迟抖动能不能收住、掉电了怎么办。这篇文章想聊的,就是这份工程账应该怎么算,全栈自研到底该从哪一层开始,以及物理AI最后这一公里究竟难在哪里。
1. 端侧推理为什么是物理AI绕不开的坎
1.1 物理AI和数字AI的差别,本质是时间约束
大家聊AI时习惯性说的是大语言模型、文生图这类数字AI,这类应用有个共同特点:响应慢一点无所谓,100毫秒和3秒对用户体验的影响远没有到“系统崩溃”的程度。但物理AI完全不同——机械臂要用视觉引导抓取,视觉推理结果晚到20毫秒,工件可能已经错过抓取窗口;AGV的避障决策晚到哪怕50毫秒,车体可能已经撞上货架。物理AI的每个决策都直接作用在真实世界的运动物体上,这种硬实时的时间约束,是它和数字AI最本质的区别。
而当一个系统的延迟预算被压缩到几十毫秒以内,云端推理路线基本就走不通了。无线链路的往返延迟本身就占据了大头,再加网络拥塞造成的抖动,哪怕数据中心里的模型再强,物理世界这边的执行器也等不起。这是我在项目里最早得出的结论:物理AI想要落地,模型推理这一步必须发生在端侧,离传感器越近越好。
1.2 端侧推理的“贵”,贵在哪几个看不见的地方
很多团队从云端迁到端侧之后非常不适,因为端侧推理的难点根本不是“让模型跑起来”,而是“在极有限的预算里让模型可靠地跑”。这里有几个容易忽略的成本维度。
算力成本是最直观的。云端有A100、H100这类加速卡,算力多到可以堆;端侧的NPU通常只有几个TOPS的算力,要在这上面跑实时视觉模型,意味着模型要压缩、算子要融合、内存要反复复用。这些工作都是算法和工程交叉的细活,人力投入往往超出预期。
内存成本同样容易被低估。端侧设备的内存带宽和容量远小于服务器,动辄几百MB的权重文件根本放不下,放得下也扛不住带宽消耗。于是不得不做INT8量化、权重剪枝、知识蒸馏,每一步都是一套独立的工程流程。我们当时光是量化校准就做了三轮,中间还因为校准集选得不好重新标了一批数据,时间成本比预期翻了一倍。
还有一项隐性成本:可靠性。云端挂了可以重启、可以热迁移,端侧设备如果推理进程崩溃,机器人就在现场“睁眼瞎”。这意味着端侧要额外做看门狗、模型输出合法性检查、异常恢复机制。这些工作在算法Demo阶段完全看不到,却切切实实算在交付工期里。
1.3 什么场景下云端仍然是正确答案
讲完端侧的必要性,也得讲句公道话:不是所有AI都非得端侧推理。如果是离线分析类任务,比如设备历史数据的故障诊断、生产报表的图像复核,把数据传到云端用大模型慢慢跑,完全合理。我们这套巡检机器人,端侧只跑实时控制链路上需要的检测和分割,但每天产生的原始巡检图像仍然会批量上传云端,做二次分析建模。两套逻辑并行,端侧管实时,云端管深度,各干各的长处。
这个区分特别重要,它避免了一种危险倾向:为了“全栈自研”而把一切东西都塞进端侧。物理AI的工程账要想算得清,先得划清楚哪些计算必须在回路里,哪些可以容忍延迟出圈计算。
2. 全栈自研的技术栈拆解:每一层都在解决什么问题
2.1 从模型到硬件,中间隔着四层工程栈
很多人以为自研就是“自己训练一套模型然后部署上去”,这离全栈自研还差得很远。我和团队趟完这趟水之后,习惯把端侧推理的全栈自研拆成四层来看:模型层、图优化与编译层、算子与运行时层、硬件适配层。
模型层是大家最熟的,网络结构设计、训练策略、蒸馏和量化感知训练都发生在这层。这层自研的意义在于,你掌握了模型的“可塑性”——知道哪些层对精度敏感、哪些可以砍、哪些需要换成NPU更友好的结构。
第二层是图优化与编译,做的是算子融合、内存复用、计算图重排这些活,相当于编译器。这一层自研的深度决定了模型在端侧能榨出多少性能。举个具体例子,我们最初部署一个分割模型时,端侧跑一帧要45ms,后来在图优化阶段把卷积和BN层做了融合,再把相邻的三个小算子合成一个,单帧时间直接压到31ms。这些优化完全不用动模型参数,纯粹是工程层的价值。
第三层是算子与运行时,负责把图优化后仍然存在的算子映射到硬件指令上。这里的自研价值在于兼容性和可调试性——当你发现NPU上的某个算子数值不对时,能不能打开算子源码去查是硬件实现的边界问题,还是寄存器配置有偏差,这决定了排障的效率和深度。
最后一层是硬件适配层,包括芯片的驱动、任务调度、内存DMA搬运。这层通常不需要完全自研,但至少要做到可配置、可理解,尤其是任务优先级的中断机制和缓存一致性处理,必须自己心里有数。
2.2 什么值得自研,什么应该果断复用
全栈自研最容易犯的错误是“什么都想自己写”。我在项目初期也有这个冲动,后来被交付节点教育了一学期:工程资源是有限的,自研必须用在能形成长期壁垒的地方。我的取舍原则很简单——凡是会反复遇到、且直接影响性能的地方,自研;凡是标准化程度高、替换成本低的地方,复用。
拿算子库举例。卷积、ReLU、池化这类高频基础算子,如果硬件提供的NPU原生库性能已经达标,直接用就好。但像反卷积、GridSample这类不常用但又绕不开的算子,如果NPU支持不好,就必须自己实现一个优化版本跑在DSP或CPU上。我们项目里就为上位机的透视矫正写了一个高度优化的GridSample的ARM NEON实现,耗时两周,但换来了整机帧率15%的提升,这笔投入完全值得。
调度器和内存管理器则需要深度参与,有些情况下必须自研。量产阶段的整型模型内存碎片化曾让我们吃了大亏,推理跑到第四个小时频繁OOM,最后排查到原因是不同尺度的中间特征图反复申请释放,伙伴内存池被切碎。后来我们重写了帧级内存复用器,按模型拓扑预先分配好所有中间张量的内存块,彻底解决了这个问题。这种问题,用第三方运行时大概率会踢很久的皮球。
2.3 全栈自研的价值在于“可解释和可控制”
真正经历过一次端侧推理的完整上线,我越发觉得全栈自研直接带来的不是性能,而是“出了问题时你能快速定位问题在哪一层”。这在物理AI的现场运维中几乎是生死攸关的。
举个例子,线上设备某天凌晨突然频繁触发安全急停,查日志发现检测模型的输出置信度在夜间普遍比白天低了一截。如果是全栈自研的系统,你可以在几个小时内逐层排查:先看输入图像,发现夜间反光导致亮度分布异常;再看预处理,确认数值归一化范围没适配夜间分布;然后定位到模型层,发现训练数据中夜间样本比例不足。每一步都有明确的排查入口和责任人。
而如果系统里的模型是黑盒、运行时是闭源的,你大概率只能向供应商提工单,然后在“现场复现不了”“请抓日志”的来回拉扯中度过一周。物理AI的运维窗口是以天计的,设备宕机一天产线就损失一天,这种可解释性的价值,远不是几个算子库的授权费能比的。
3. 物理AI的工程账到底怎么算:算力、内存、功耗、实时性
3.1 算力需求的估算:从GMACs到TOPS的换算细节
项目立项时,硬件选型需要先回答一个问题:这套AI系统大概需要多少算力?我习惯的算法是先算出模型每帧的GMACs,再乘目标帧率,最后除以一个效率系数。
以我们用的一个改进型YOLOv8s检测模型为例,输入分辨率640×640,单帧计算量大约16.3 GMACs。目标是30fps,那么每秒需要 16.3 × 30 = 489 GMACs,折合约0.98 TOPS的计算量需求。但没有任何硬件能把MAC利用率跑到100%,端侧NPU通常要达到50%到70%就不错了,所以最终选型时会按0.98 / 0.6 ≈ 1.63 TOPS作为底线,再保守一点预留30%的余量,直接看2.5 TOPS以上算力的芯片。
这个效率系数不是拍脑袋定的,它和算法结构高度相关。全卷积网络在NPU上吃香,因为矩阵乘法规则整齐;如果网络里有大量深度的逐元素操作或分支结构,MAC利用率会明显下滑。我们后期把模型里的少量3×3卷积替换为1×1卷积后,同等算力下FPS整整提了12%,就是靠这会算账的工夫。
3.2 内存预算的三笔账:权重、特征图、运行时开销
内存规划做不好,设备运行三小时就重启,这在物理AI上是绝对不能接受的。我的估算方法是把内存分三笔来算。
第一笔是模型权重。目标检测模型INT8量化后,参数量11.2M对应约11.2MB权重;如果FP16跑就是22.4MB,FP32就是44.8MB。第二笔是中间特征图。这是最容易失控的部分,需要逐层叠加每个算子的输入输出张量。我们跑分割模型时,输入是512×512×16通道的INT8特征图,单层就是4MB,网络深度30层,中间峰值能到40多MB。第三笔是运行时开销,包括前处理和后处理的IO buffer、计算图的临时变量、调度器的元数据,这部分按前两笔总额的20%做预算是比较稳妥的。
将这三笔加起来再乘1.5的安全系数,基本就是你要选的内存容量下限。我们当时三个模型加起来权重约40MB,特征图峰值约90MB,预分配的帧内存池约120MB,加上系统和其他组件,整机最终选了2GB LPDDR4X,跑起来内存占用在65%左右。如果当时把内存算小了,后期加需求就得换板子重新做EMC测试,那个代价我都不敢想。
3.3 实时性预算:平均延迟远不够,P95才值得看
物理AI的延迟不仅要求低,更要求稳。我见过的很多团队,评估模型时只盯着平均推理时间,结果到了现场被延迟抖动坑惨了。逻辑很好理解:平均40ms不代表每帧都是40ms,如果P95是62ms,而控制器的安全阈值是60ms,那么100帧里就有5帧会让系统进入保护性急停,别小看这5%,一天跑下来就可能触发几百次急停,产线完全没法干活。
所以我们后来把实时性预算拆成了三份:感知链路30ms,控制链路20ms,通信和调度余量10ms。感知链路的30ms里面,图像采集2ms,前处理4ms,模型推理22ms,后处理2ms。每个环节都要求P95不超过该分段预算。一旦某段超了,立刻去看是算法侧问题还是系统侧问题,而不是笼统地说“今天这模型又卡了”。
一套在现场真正可靠运行的物理AI系统,其实时性评估必须有至少95%分位数的指标,而且要在持续运行两小时以上之后做统计,冷启动和热稳定是完全不同的两个状态。
4. 最后一公里的实操记录:一个端侧推理流水线的落地全过程
4.1 前处理与数据管线的细节
端侧推理的起点不是模型,而是数据管线。我们在开发阶段用的是公开数据集,图像质量干净、分辨率统一,结果部署到实机后发现推理结果飘忽不定。排查来排查去,问题出在前处理:实机摄像头输出的RAW图经过ISP后,颜色矩阵和训练时用的图像域差异很大。
这里的解决方案是建立一个固定的图像处理管线和参数校准流程:先在实验室用标准色卡标定ISP参数,然后在模型训练阶段就引入和端侧完全相同的前处理逻辑,包括BGR顺序、归一化系数、缩放算法。这样做之后,训练和推理的输入分布终于对齐,检测AP直接从0.78涨到0.84,什么网络结构都没改。很多团队忽略这个细节,然后在模型上疯狂调参,方向完全错了。
数据管线的时序也要处理好。我们用了双缓冲机制,摄像头采集新帧的同时,NPU在推理上一帧,DMA搬运和数据预处理并行流水。这样一来,从传感器曝光到推理结果产生的过程中,采集等待被彻底隐藏,整条链路的有效吞吐量大幅提升。
4.2 量化与编译的实战细节
从FP32到INT8的量化是整个流程里调试成本最高的环节。我们用的方案是先做PTQ,用500张现场图像做校准集,跑出来的初始模型在mAP上掉得不多,但个别难例类别掉得厉害。问题出在校准集的分布不够覆盖“暗光+近距离”的低频场景,补了300张夜间和强背光样本后,量化后精度恢复到FP32的97.8%。
如果你的精度掉点比较严重,我建议按顺序排查这几个原因:先检查是否有敏感层,一些输出通道数值范围特别大的层,INT8表示直接损失严重,需要在量化配置里把该层指定为FP16;再看是否启用了逐通道量化,尤其是深度可分离卷积,逐张量量化误差很大;最后确认是否有无法量化的自定义算子,这类算子最好在编译图里标记为CPU执行,不要硬翻译成NPU指令。
调试阶段还要和编译器建立“良好关系”。开源编译器的报错信息有时候很晦涩,常见坑包括reshape的维度推不动、动态shape不识别、某个常量变成了运行期张量导致无法静态内存规划。遇到这类问题,最快的办法是简化模型结构再单步编译,逐步二分定位是哪个子图导致的失败,比盯着日志硬猜高效得多。
4.3 多模型并发:NPU任务调度的优先级之争
我们的设备要同时跑三个模型,这就必须处理NPU资源的共享问题。团队最开始的做法是三个模型各建一个推理线程,让NPU自己调度,结果性能惨不忍睹——同时发起推理任务时,每个任务都被切得七零八落,单帧延迟飙升,控制链路一度超时。
后来改成了显式优先级调度:检测模型绑定最高优先级NPU任务,每帧强制抢占;分割模型跑低优先级,利用检测模型的空闲窗口执行;路径规划算力需求低,直接放CPU上算,用NEON向量指令也能轻松搞定。同时加了一个信号量机制,高优先级任务完成后主动释放NPU,低优先级任务只在无高优先级任务时才启动。改造后整机端到端延迟稳定在55ms以内,三路模型互不干扰。
这个案例给我的教训是:端侧推理不是“把模型塞进设备”,而是“让多个计算任务在有限的硬件资源里协同工作”。任务调度是系统层面的设计,算法工程师如果不跨出这一步,永远只能在单个模型上抠性能,整套系统永远无法达到量产水准。
5. 常见问题与排查技巧实录
5.1 模型在PC上跑得好好的,端侧又慢又卡
这是论坛里问烂了的问题,但答案往往不在模型本身。最快排查路径是:先看模型在端侧是否经历了大量反序列化和重新布局的操作,很多模型文件保存的权重排布是PC优先的,端侧加载时如果不做重排优化,内存访问效率极低。再看是否有大量小算子被翻译成了CPU执行而不是NPU,解决办法是把小算子合并到大算子中,或者手动改网络结构规避。最后确认你的输入分辨率是否和训练一致,端侧如果做了等比缩放后再padding,很可能引入了额外的访存开销。
我们有一个模型从PC迁到端侧时性能掉了60%,查到最后是上游框架在导出模型时把常量折叠进了一个动态shape节点,导致每次推理都要重新做内存分配,把这个节点静态化之后性能立刻恢复。这种问题不把工程栈打通,单看算法层根本找不到原因。
5.2 NPU推理结果和PC端不一致
浮点累加顺序不同导致端侧结果和PC端有个别数值偏差是正常的,但如果出现类别级别的差异,必须重视。排查思路是逐层对比:把PC端各层输出保存为二进制基准,端侧在同样位置打dump点,找出第一个出现显著偏差的层。找到后点后基本可以锁定两类原因:一是该层被编译器错误地做了重排或融合,二是该层本身的数值范围经过量化后误差过大。
我们实际碰到过的案例是LayerNorm层,NPU的实现把均值方差用FP16计算,而PC是FP32,导致中低频特征偏差被放大。解决方案是把该层强制回退到CPU用FP32跑,损失一点性能但换来精度一致性,完全可接受。
5.3 高温环境下推理延迟明显上升
这个问题特别容易被忽略,但现场设备在夏天暴晒后,整机推理速度会明显变慢。原理是芯片温升后触发降频保护,NPU频率掉了,延迟自然上去了。处理办法有两层:被动层是优化散热结构,保证风扇进风路径和散热片接触;主动层是结合温控曲线调整任务周期,比如温度超过75度时把低优先级的分割模型帧率从30fps降到15fps,把算力让给关键链路。
这类问题的排查需要你在固件里埋温度监测点,在日志里记录每个时间片的频率和延迟,拿到数据曲线后做回归分析。纯靠主观感觉调优,永远调不对。
5.4 常见问题速查表
| 现象 | 可能原因 | 快速排查方法 | 解决方向 |
|---|---|---|---|
| 单帧推理时间抖动大 | 内存碎片、DMA搬运冲突 | 拉取推理耗时曲线,观察周期性波动 | 预分配帧内存池,错开DMA调度 |
| 长时间运行后OOM | 中间特征图的动态内存泄漏 | 检查持续增长的内存分配日志 | 按拓扑静态规划内存,杜绝运行期分配 |
| 模型加载耗时过长 | 权重文件大+反序列化慢 | 统计加载各阶段耗时分布 | 权重重排预编译,改成内存映射加载 |
| NPU利用率低 | 算子碎片化、频繁上下文切换 | 查看NPU profiler的idle占比 | 算子融合重写子图,批处理小任务 |
| 高温下延迟飙升 | 芯片降频保护触发 | 查看温度监控日志和频率曲线 | 散热改进+任务降频策略 |
| 端侧结果和PC差异大 | 量化误差/特定算子精度损失 | 逐层dump,对比定位差异层 | 混合精度或CPU回退该层 |
写在最后的体会
端侧推理这件事,越做越觉得它是个“系统工程账”,不是某个单点技术的胜利。算力、内存、功耗、延迟、精度、可靠性,这些因素是一张互相牵连的网,你优化了某一项,很可能在另一项上付出代价。全栈自研的价值,不在于证明“我们不依赖外部”,而在于出了问题你有能力从硬件到算法逐层排查,有选择地在任何一层做调整。
我个人在实际操作中最大的体会是:一定要给端侧推理预留性能余量,别把算力和内存用到极限。产品需求的变动是必然的,模型升级后计算量上涨10%、现场新增一个数据源又吃掉一块内存,都是再正常不过的事。第二个体会是,量化校准、性能回归、内存泄漏检测这些流程,一定要在项目早期就自动化,靠人肉重复劳动撑不了几次迭代。最后想说的是,物理AI的“最后一公里”不是模型跑通的那一公里,而是设备无故障、无超时、无漂移地连续运转的那一公里,这需要算法工程师把自己当成系统工程师来思考,尽早走进硬件和运行时的世界。