当前位置: 首页 > news >正文

手把手教程:使用Vitis部署YOLOv5到边缘设备

手把手教程:把 YOLOv5 部署到 Xilinx 边缘设备上,实现实时目标检测

你有没有遇到过这样的场景?训练好一个精度很高的 YOLOv5 模型,兴冲冲地想把它部署到现场的工业相机或边缘盒子上,结果发现 CPU 推理慢得像“幻灯片”,功耗还高得吓人——30fps 的视频流只能跑出 2~3 帧?这显然没法用。

别急。如果你手头用的是Xilinx Zynq UltraScale+ MPSoCKria KV260这类异构架构的边缘平台,其实有一条更高效的路可走:借助Vitis AI 工具链 + DPU 硬件加速单元,让 YOLOv5 在低功耗下轻松跑出30+ FPS,而且几乎不掉点!

本文不是泛泛而谈的技术综述,而是一份从模型导出到板端运行的全流程实战笔记。我会带你一步步走过每一个关键环节,告诉你哪些坑必须绕开、哪些配置直接影响性能,甚至包括那些官方文档里一笔带过的“潜规则”。


为什么非要用 Vitis AI?直接 PyTorch 不行吗?

当然可以,但代价太大。

在嵌入式 Linux 上直接跑 PyTorch 的.pt模型,意味着所有计算都压在 ARM Cortex-A53/A72 核心上。对于像 YOLOv5s 这样的网络,其卷积层密集、参数量不小(约 700 万),FP32 计算对 CPU 来说简直是“重载列车”。实测表明,在 ZCU104 上纯软件推理仅能维持2~3 FPS,延迟高达 300ms 以上。

而 Vitis AI 的核心思路是:把最耗时的神经网络前向传播卸载到 FPGA 可编程逻辑(PL)中的 DPU 上。DPU 是专为深度学习设计的硬件加速器,擅长并行处理 Conv、BN、ReLU 等操作,支持 INT8 定点运算,能效比远超通用处理器。

更重要的是,Vitis AI 提供了一整套工具链,让你无需写一行 Verilog 就能完成模型编译与部署——这才是真正意义上的“软硬协同”。


第一步:准备好你的 YOLOv5 模型

我们以 Ultralytics 官方发布的yolov5s.pt为例,这是最适合边缘部署的轻量版本。

✅ 建议使用 Ultralytics/yolov5 的 v6.1 或 v7.0 版本,这些版本对 ONNX 导出支持更好。

修改代码,确保导出兼容性

原始 YOLOv5 的forward()函数中包含一些动态操作,比如自动 resize 输入图像,这会导致 ONNX 导出失败或生成不稳定的图结构。我们需要固定输入尺寸,并移除不必要的控制流。

打开models/common.py,找到Detect类的forward方法,修改如下:

def forward(self, x): # 移除训练相关的分支 shape = x[0].shape for i in range(self.nl): x[i] = self.m[i](x[i]) # 卷积输出 bs, _, ny, nx = x[i].shape x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() return x # 返回三个特征图列表

这样导出时就能保留 P3/P4/P5 三层输出,而不是被拼接成一个大张量。


第二步:导出为 ONNX 模型 —— 别小看这一步,90% 的问题出在这儿

ONNX 是连接 PyTorch 和 Vitis AI 的桥梁。但不是随便导出一个.onnx文件就能用,很多细节决定成败。

正确的导出脚本

import torch from models.experimental import attempt_load # 加载模型 weights = 'yolov5s.pt' model = attempt_load(weights, map_location='cpu') model.eval() # 构造 dummy input dummy_input = torch.randn(1, 3, 640, 640) # 导出 ONNX torch.onnx.export( model, dummy_input, "yolov5s.onnx", input_names=["input"], output_names=["output_0", "output_1", "output_2"], # 显式命名三层输出 dynamic_axes=None, # 关闭动态维度!否则 Vitis 不认 opset_version=13, # 必须用 Opset 13 do_constant_folding=True, verbose=False ) print("✅ ONNX 模型导出成功")

⚠️ 关键点提醒:
-dynamic_axes=None:强制静态 shape,Vitis AI 目前不支持动态 batch 或分辨率;
-opset_version=13:YOLOv5 使用了 SiLU 激活函数(即 Swish),只有 Opset 13 才能正确映射;
- 输出名称要和forward返回一致,方便后续调试。

导出完成后建议用 Netron 打开.onnx文件检查结构是否正常,特别是确认有三个独立输出节点。


第三步:启动 Vitis AI Docker 环境

Xilinx 提供了预配置的 Docker 镜像,省去环境搭建的麻烦。

docker pull xilinx/vitis-ai:latest docker run -it --gpus all --shm-size=8g \ -v $(pwd):/workspace \ xilinx/vitis-ai:latest

进入容器后激活 conda 环境:

conda activate vitis-ai-onnxruntime

第四步:量化 —— 把 FP32 转成 INT8,提速降耗的关键一步

FPGA 更适合整数运算。我们将使用校准量化(Calibration-based Quantization)方法,将浮点模型压缩为 INT8 模型,同时尽可能保持精度。

准备校准数据集

找大约100~500 张代表性图片(不需要标注),最好是来自你实际应用场景的数据分布。例如,如果是做工业质检,就用产线上的样本图。

mkdir -p calibration/images cp /your/dataset/path/*.jpg calibration/images/

创建量化配置文件config.json

{ "dataset_list": "calibration/images", "input_shape": "3,640,640", "preprocess_function": "inference_onnx_yolov5", "preprocess_layout": "NCHW", "batch_size": 1, "output_dir": "quantized" }

其中preprocess_function使用内置的inference_onnx_yolov5,它会自动处理归一化(/255)、BGR→RGB、HWC→CHW 等预处理步骤。

开始量化

vai_q_onnx quantize \ --model yolov5s.onnx \ --calib_dataset calibration/images \ --quant_mode calib \ --config config.json

第一次运行会执行校准阶段,收集各层激活值的最大值用于确定量化缩放因子。完成后会在quantized/下生成yolov5s_int.onnx

🔍 小技巧:如果发现量化后精度下降明显,可以尝试增加校准图像数量,或者改用量化感知训练(QAT)模型。


第五步:编译模型 —— 生成 .xmodel 文件,DPU 的“可执行程序”

.xmodel是 Vitis AI 编译器生成的目标文件,相当于 DPU 的“二进制可执行程序”。

你需要根据目标硬件选择对应的arch.json文件:

平台arch.json 路径
ZCU104/opt/vitis_ai/compiler/arch/DPUCZDX8G/ZCU104/arch.json
KV260/opt/vitis_ai/compiler/arch/DPUCZDX8G/KV260/arch.json

执行编译命令:

vai_c_onnx \ --arch /opt/vitis_ai/compiler/arch/DPUCZDX8G/ZCU104/arch.json \ --model quantized/yolov5s_int.onnx \ --output_dir compiled

成功后你会看到类似输出:

Total Kernel Number: 28 First Stage Kernel Number: 27 Second Stage Kernel Number: 1 ... Compile Success! Output: compiled/yolov5s.xmodel

这个.xmodel文件就可以拷贝到开发板上了。


第六步:开发板部署 —— 图像流水线全打通

现在我们切换到目标设备(如 ZCU104 或 KV260),假设已经烧录好官方 Petalinux 镜像,并安装了 VART 运行时库。

安装依赖

sudo apt update sudo apt install python3-opencv libopencv-dev

编写推理主程序(Python 示例)

import cv2 import numpy as np from vai.dpu.runner import Runner # 初始化 DPU runner runner = Runner("compiled/yolov5s.xmodel") def preprocess(img): resized = cv2.resize(img, (640, 640)) rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB) normalized = rgb.astype(np.float32) / 255.0 transposed = np.transpose(normalized, (2, 0, 1)) # HWC -> CHW batched = np.expand_dims(transposed, axis=0) # NCHW return batched def postprocess(outputs): # outputs 是 list of numpy arrays: [P3, P4, P5] # 每个 shape 为 (1, num_boxes, 85) import ultralytics.utils.ops as ops from ultralytics.utils.torch_utils import non_max_suppression # 合并三个尺度的输出 preds = [torch.from_numpy(o).float() for o in outputs] det = non_max_suppression(preds, conf_thres=0.25, iou_thres=0.45)[0] return det.numpy() if len(det) else [] cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break # 预处理 input_data = preprocess(frame) # 推理 outputs = runner(input_data) # 返回三个 ndarray # 后处理 detections = postprocess(outputs) # 绘制结果 for *box, conf, cls in detections: x1, y1, x2, y2 = map(int, box) label = f"Class {int(cls)}: {conf:.2f}" cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow("YOLOv5 + DPU", frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows() runner.close()

💡 注意事项:
- 使用vai.dpu.runner是最简单的调用方式,适用于 Python 应用;
- 若追求极致性能,可用 C++ + VART API 实现零拷贝流水线;
- 后处理部分仍由 CPU 完成(NMS、解码等),但耗时通常小于 10ms。


实测性能表现如何?

ZCU104上测试结果如下:

项目数值
输入分辨率640×640
模型类型YOLOv5s INT8
推理帧率32 FPS
CPU 占用率~40%
功耗<10W
mAP@0.5 下降<1.5%

相比纯 CPU 推理,性能提升了10 倍以上,且功耗更低,非常适合长时间运行的边缘设备。


常见坑点与避坑指南

问题原因解决方案
ONNX 导出失败使用了 unsupported op(如Resizewith dynamic size)固定输入尺寸,关闭自动 resize
量化后精度暴跌校准数据不具代表性换成真实场景子集做校准
编译报错 “unsupported layer”Opset 版本太低或模型结构异常升级到 opset 13,检查 ONNX 结构
板端加载 .xmodel 失败arch.json 不匹配确保使用对应平台的 DPU 配置文件
输出全是 background后处理未适配多尺度输出正确解析 P3/P4/P5 并合并

进阶优化建议

  1. 预处理卸载到 PL
    当前 OpenCV 的 resize 和 normalize 仍在 CPU 上执行。可通过添加 VIP(Video IP)模块或将这部分逻辑固化到 FPGA 中,进一步降低 CPU 负载。

  2. 启用 QAT(Quantization-Aware Training)
    在训练阶段模拟量化误差,可显著提升 INT8 模型鲁棒性。Ultralytics 最新版已支持 QAT,值得尝试。

  3. 利用 Kria KV260 的加速应用商店
    KV260 支持即插即用的 AI 应用包(.kar),你可以打包整个推理流程,实现“插入摄像头 → 自动运行检测”的极简体验。

  4. 结合 Vitis Vision Library(VVAS)
    对于多路视频流场景,VVAS 提供了完整的 pipeline 管理能力,支持 GStreamer 插件化部署,适合复杂系统集成。


写在最后:这不是终点,而是起点

当你第一次看到 YOLOv5 在 ZCU104 上流畅跑出 30fps 视频流时,那种“终于通了”的成就感是难以言喻的。但这只是一个开始。

Vitis 不只是用来跑模型的工具,它是打通算法、软件、硬件之间的关键桥梁。掌握这套方法论后,你可以轻松迁移其他主流模型(如 YOLOv8、EfficientDet、DeepSORT)到边缘端。

更重要的是,这种“模型量化 → 编译加速 → 板端部署”的范式,已经成为边缘 AI 落地的标准路径。无论你是做工业视觉、智慧交通还是机器人感知,这套技能都会成为你手中的利器。

如果你在实践过程中遇到了别的问题,欢迎留言交流。也别忘了给项目点个 star —— 毕竟,让 AI 真正落地,靠的不只是技术,还有社区的力量。

http://www.jsqmd.com/news/223285/

相关文章:

  • Qwen2.5-7B实战案例:搭建多语言客服系统,成本降低60%
  • Windbg与LiveKd对比:内核调试工具选型建议
  • Qwen2.5-7B怎么调优?系统提示适应性增强部署入门必看
  • 游戏性能大升级:DLSS Swapper让你的游戏帧率飞起来!
  • DLSS版本切换终极指南:快速提升游戏画质的完整教程
  • Qwen2.5-7B编程能力提升:代码生成与调试教程
  • Qwen2.5-7B模型解释:可解释AI技术应用
  • 数据库触发器助力实时审计日志采集的方法论
  • Qwen2.5-7B品牌命名:产品名称生成器
  • Qwen2.5-7B部署提速300%:FlashAttention集成实战案例
  • NVIDIA DLSS版本管理终极指南:解锁游戏图形性能新高度
  • Qwen2.5-7B免配置镜像测评:网页服务一键启动实操体验
  • DownKyi深度解析:B站视频高效下载的完整方案
  • Qwen2.5-7B部署教程:GQA注意力机制下的显存优化策略
  • 通俗解释MOSFET基本工作原理中的表面反型现象
  • Qwen2.5-7B模型融合:多专家系统集成方案
  • Qwen2.5-7B显存优化方案:使用FlashAttention提升效率
  • Qwen2.5-7B智能合约:区块链应用案例
  • Qwen2.5-7B实战:基于系统提示的个性化AI开发
  • DownKyi高效下载指南:B站视频批量下载与画质优化完整方案
  • 如何快速上手Qwen2.5-7B?网页推理部署实战教程入门必看
  • Qwen2.5-7B架构特点解析:SwiGLU与RMSNorm部署影响
  • Altium Designer导出Gerber文件新手教程
  • vivado2018.3安装步骤超详细版:涵盖Xilinx Artix-7配置
  • Qwen2.5-7B部署遇阻?多语言支持场景下的算力优化解决方案
  • Qwen2.5-7B持续集成:模型更新后的自动化部署流程
  • Qwen2.5-7B高性能部署:利用Tensor Parallelism提升吞吐量
  • Qwen2.5-7B智能写作助手:从大纲到完整文章
  • DownKyi完全攻略:轻松下载B站高清视频的终极指南
  • Qwen2.5-7B知识图谱:与结构化数据结合应用