端侧视觉流水线
在内存、带宽与实时约束中把摄像头帧变成稳定事件
建议阅读:约 20 分钟
学习目标
本章关键词
| 关键词 | 解释 | ESP32 工程类比 |
|---|---|---|
| 峰值活跃内存 | 某一时刻同时需要保留的所有 buffer 总量。 | 像任务切换时同时占用的栈与 DMA 描述符。 |
| PSRAM | 通过外部接口连接的大容量 RAM。 | 像扩展仓库,容量大但访问和 DMA 规则不同。 |
| Stride | 同一行相邻像素数据在内存中的跨度。 | 像 DMA 行传输的实际步长,不总等于可见宽度。 |
| Letterbox | 保持比例缩放并填充边缘的 resize 方法。 | 像把不同长度 payload 装进固定帧但保留原比例。 |
承上:回顾与定位
Day 5 已把非 LLM 模型部署到 MCU;本章把资源账本与摄像头数据链合并,回答它能否在真实帧率、内存和热状态下长期稳定工作。
历史发展脉络
端侧视觉能力来自轻量网络、内存层级、DMA 与完整前后处理共同演进。峰值算力只有在数据能及时到达、buffer 生命周期正确且输出可还原时才有意义。
Roofline 把算力上限与带宽上限画在一起
Roofline 用运算强度连接峰值计算和可持续内存带宽,使“该优化算术还是减少搬运”成为可以测量和讨论的问题。
UC Berkeley Roofline 原始报告 ↗Eyeriss 量化了数据移动的能耗代价
Eyeriss 提出 row-stationary 数据流,利用局部存储和处理单元间通信复用权重、激活与部分和,强调少搬数据本身就是加速。
Eyeriss 原始论文页面 ↗MCUNet 把网络与推理引擎协同设计
MCUNet 用 TinyNAS 在设备约束内搜索网络,并让 TinyEngine 按整图生命周期调度内存,推动模型、runtime 和硬件联合优化。
MCUNet 原始论文 ↗深层 CNN 与 GPU 训练扩大视觉能力
AlexNet 在 ImageNet 上以深层卷积网络展示显著效果,推动视觉主干快速加深,也让部署侧开始面对更大的算力与内存压力。
AlexNet 原始论文 ↗类比图解
晚餐高峰期的一间小餐馆
冷库里食材很多,不代表厨房能同时做很多桌菜。切配台面积决定多少盘子能同时摊开,狭窄过道决定服务员搬菜的速度,专用烤炉虽然很快,却只接收合适的烤盘;每次把菜改盘、送进送出都会消耗时间。
类比的边界: 餐馆类比适合解释容量、吞吐和换盘成本,但真实存储层次并非互斥房间:cache 会自动替换,DMA 与 CPU 还可能并发访问同一物理内存。是否需要 flush、invalidate 或对齐,必须以芯片手册和实测为准。
本章讲解
峰值内存来自生命周期重叠,不是文件大小相加
为计算图列出每个 tensor 的产生节点、最后一次消费、大小和内存能力,画出活跃区间后才能求峰值。权重常可留在映射 Flash,激活、累加器和 kernel workspace 却可能在同一层同时存在;相机双缓冲、任务栈和网络包也要纳入系统峰值。规划器能复用生命周期不重叠的区域,但动态 shape、分支与 fallback 会改变计划,必须用运行时 high-water mark 校验静态估算。
用运算强度判断等待的是乘加器还是内存
粗算每个算子执行的 MAC 与从慢层级搬运的字节,得到“每字节做多少运算”的直觉;低运算强度路径更可能受带宽约束。提高主频或增加 NPU 单元只有在计算受限时才直接见效,带宽受限时应优先融合算子、分块复用、避免中间张量落地和改变布局。理论带宽是上限,连续读写、cache miss、总线争用与刷新协议决定可持续值,所以必须用接近真实访问模式的基准测量。
零拷贝是一份所有权协议,不是一处指针转换
让相机 DMA 写入模型输入可省一次复制,但前提是 buffer 的地址能力、对齐、stride、cache 一致性和生命周期都满足双方要求。CPU 在 DMA 完成前不能读取,DMA 复用前推理也必须释放;带 cache 的系统还需在正确方向做同步。建议用明确状态机标记 FREE、FILLING、READY、IN_USE,并把超时与丢帧计数写入日志。没有这些约束的“零拷贝”常变成偶发撕裂或陈旧数据。
先按业务需要选择输出粒度
只需判断“画面中是否有火焰”且目标占据稳定 ROI,可从分类开始;需要位置与数量才使用检测;需要像素级面积、边缘或可通行区域才考虑分割。输出越细,标签成本、后处理和内存通常越高。还要写明最小目标像素、允许遮挡和相机距离,因为输入分辨率决定信息上限。用业务事件回放比较候选方案,而不是拿不同数据集上的单一指标横向排名。
把 frame buffer 当成带描述符的二维存储
一帧不只有 width×height:还包含 RGB565、YUV 或 JPEG 等格式、每行 stride、平面排列、对齐和有效区域。JPEG 必须先解码,YUV 转 RGB 时要确认矩阵与范围;直接把有 padding 的行当连续像素会产生斜纹。相机驱动交出的 buffer 通常由池管理,推理完成前不得归还或覆盖。先用彩条、棋盘格和像素探针验证读取路径,再让模型介入,可快速隔离格式错误。
为每次几何变换保留可逆账本
预处理应记录原图尺寸、ROI 原点、缩放比例、填充宽度和模型输入尺寸。拉伸 resize 会改变物体形状;letterbox 保持比例却引入边缘填充,两者必须与训练配置一致。检测框或分割 mask 回到原图时,按变换逆序先去 padding、再除比例、再加 ROI 偏移,并裁剪到有效边界。用四角标记和已知矩形做单元测试,比肉眼看几个框更能发现半像素与取整偏差。
以分阶段回放验证实时流水线
保存一组可合法分享的原始帧,并为每帧留存模型输入、raw output、NMS 结果和回映射结果作为黄金记录。板端逐阶段计算摘要,与 PC 实现比较;同时记录采集、预处理、推理、后处理和排队耗时。压力测试要改变曝光、帧率和消费者速度,确认队列满时丢哪一帧、buffer 是否总能归还。最终 FPS 取决于最慢阶段和流水并发,并不等于模型单次推理的倒数。
动态过程演示
一帧图像到一个可信事件
播放器保留每次像素与坐标变换,直观看见候选如何被筛选并跨帧稳定。
DMA 采帧 · YUV/RGB/JPEG + stride + timestamp
相机填充池中 buffer 并移交所有权
一帧必须携带格式描述符
循环 / 返回条件: 下一帧回到 DMA 采帧;跟踪状态跨帧保留,而像素 buffer 在发布结果后立即归还相机池,二者生命周期不可混淆。
查看静态全景图
camera DMA → frame buffer → ROI/resize/letterbox → model partition
▲ ownership/stride SRAM↔PSRAM↔NPU │
└──────── release ◄── NMS + temporal policy ◄──┘代码或命令示例
frame = camera_acquire()
input, transform = preprocess(frame)
raw = infer(input)
event = postprocess(raw, transform)
camera_release(frame)
assert peak_live_bytes <= memory_budget动手实验
实验记录与导出
工程陷阱
核心测试
完成 3 道单选题后提交;提交前不会显示答案。
在 GitHub 上讨论本章
登录 GitHub 后提问、补充实测或分享你的实现;评论会保存在本课程的 GitHub Discussions 中。
评论区需要 JavaScript;也可以直接打开 GitHub 讨论区: 打开 GitHub 讨论区 ↗
延伸阅读
启下:下一章如何使用本章能力
下一章是课程分界点:从传统判别式端侧模型进入 LLM,建立语言模型发展脉络、核心术语和能力边界。
下一天: Day 7 · LLM 基础