【FPAI开发】超详细!YOLO26适配FPAI芯片部署过程详解!
1. 背景知识1.1 FPAI概述FPAI是一款异构融合可重构智能芯片是国内首个将FPGA和AI加速核心集成于单芯片内部的产品。FPAI单芯片集成高性能处理器系统Processing SystemPS、神经网络处理器Neural Processing UnitNPU和可编程逻辑Programmable LogicPL三部分实现了“通用计算硬件可编程 AI 加速”的深度融合与高效协同。人工智能主要分为“训练”和“推理”两大场景训练端通过海量数据和算法让模型从无到有掌握规律、优化参数最终实现“精准识别、精准决策”核心目标是提升模型的准确率与泛化能力推理端利用训练好的模型对新的输入数据进行快速计算、输出结果核心目标是在保证模型精度基本达标的前提下实现高效部署、快速响应满足实际应用的落地需求。面向多元化人工智能推理应用场景FPAI单芯片满足端侧智能应用全流程计算需求涵盖数据采集与预处理、AI 推理计算、后处理及业务功能三大阶段。具备高集成度、小型化、高能效、高可靠、灵活可扩展等显著优势搭配全自主研发的 ICraft 软件工具链可快速赋能多场景智能应用落地。1.2 FPAI开发流程作为一款智能推理芯片FPAI开发本质上是将上位机的智能推理应用前后处理代码AI模型推理迁移部署至FPAI平台运行开发过程中要建立系统整体概念、明确数据流以“数据”为核心清晰把握“数据-预处理-AI推理-后处理-结果”每个阶段中数据格式、存储位置、计算后端、意义与去向。1.3 FPAI支持哪些模型FPAI 异构融合可编程智能芯片采用算子级模块化支持机制以算子Op为神经网络部署的最小执行单元。诸葛架构 NPU 原生支持 Conv2d、Linear、MaxPool、GRU、LSTM、LayerNorm、Softmax、multiply、sqrt、log、Sigmoid、SiLU 等百余种主流算子完整覆盖 CV、NLP、时序预测等多类神经网络的核心计算需求。基于标准化算子集只要神经网络结构所包含的算子均在官方支持清单ICraft Docs算子支持清单内即可直接在FPAI平台完成编译部署与推理运行。同时依托 FPAI 平台的 FPGA 可编程逻辑与 CPU 异构算力可对 NPU 暂不支持的算子进行快速扩展与自定义实现无需等待硬件迭代即可适配神经网络的升级与新算子的快速落地兼具部署兼容性与架构灵活性。快速验证方式将框架模型导出ONNX格式 toml配置文件通过ICraft-Compiler进行编译转换转换成功即说明支持该模型此外可参考ICraft-ModelZoo已适配模型清单https://www.modelscope.cn/models/AIBS/modelzoo_index。1.4 Toml文件如何配置教程https://www.modelscope.cn/models/AIBS/icraft_tutorial/file/view/v3.33.1/ZHUGE%2FPart%202_1%20compile.md?status12. 开发环境硬件FMQL30TAI-悟净开发板诸葛架构NPU软件ICraft_V3.37.1ICraft-ModelZoo网址https://www.modelscope.cn/collections/icraft_modelzoo-18b52923d4854f基础教程https://blog.csdn.net/qq_36840004?spm1000.2115.3001.53433. YOLO适配FPAI详解3.1 YOLO26适配30TAI开发思路本文目的是将YOLO26模型部署于30TAI平台运行该过程是一个算法迁移的过程将YOLO26从“上位机开发环境”迁移到“嵌入式运行环境”中并利用30TAI的硬件实现算法运行因此第一步需要在上位机完成YOLO26的部署运行并明确从图像输入到目标坐标结果的全部计算流程以及每一环节的数据格式。YOLO26在上位机运行的流程如下图所示。部署过程是将上图所示流程迁移至30TAI平台实现为了快速上手首先对YOLOv5的运行流程进行梳理其流程如下图所示。结合上述两个流程图可以看出对于智能算法的部署主要工作集中在前处理、AI推理和后处理三部分的迁移开发以及精度对齐对于30TAI而言前后处理可以基于CPU/NPU/FPGA来实现AI推理基于NPU来实现其中NPU只能运行ICraft编译生成的模型文件jsonraw/snapshot。YOLO26适配30TAI有以下几个问题需要明确前后处理模块是否能复用ICraft-ModelZoo已经支持的YOLO模型避免重复开发需要对YOLO系列模型结构进行分析。YOLO26主干网络部分是否有不支持算子需要导出ONNX模型通过ICraft进行编译验证。如何适配DetPost模块模型结构需要进行相应调整DetPost参数需要正确配置。3.2 DetPost 模块DetPost是针对YOLO系列目标检测模型基于硬件实现的网络后处理加速模块读取 PLDDR 中的NPU计算结果完成硬件筛选加速然后将数据写回至PSDDR。在ICraft 中通过自定义硬算子的方式进行调用ICraft 3.0 及以上版本。对于30TAI诸葛架构NPU内集成了DetPost IP但功能尚未正式开放目前30TAI中基于FPGA外挂实现了DetPost模块仅支持INT8数据格式。工作原理对于YOLOv5输出的每个预选框向量由85个数组成x,y,w,h,s,类别数量80第五个数据是scorescore 值与DetPost设置的阈值进行比较大于阈值的筛选出来把整个向量回传到 PS。调用方式如下图所示编译时需要在相应的阶段打开 customop的pass。DetPost算子加入网络是在adapt阶段所以在adapt阶段的pass_on参数中配置 customop.DetPostZGPass。custom_config参数用来指定custom_config.toml的路径。配置 customop 的参数3.3 YOLO家族模型结构对比YOLO26是Ultralytics在2026年发布的最新版本专注边缘与低功耗设备优化设计其主要特点有端到端无NMS推理无需后处理中的非极大值抑制简化部署流程。去除分布焦点损失DFL模块模型更易适配到各类终端设备。支持多任务统一架构覆盖检测、分割、分类、姿态估计、旋转框检测五大任务。由于嵌入式部署主要关注模型的算子、前后处理因此对YOLO系列经典模型的结构进行分析其特点如表格所示其中attention/Softmax代表该模型的部署需要softmax算子在100TAI平台softmax算子是通过FPGA扩展实现30TAI的NPU支持softmax算子。对于YOLO系列模型在30TAI平台部署ICraft-ModelZoo已经提供了示例工程建议先根据教程运行YOLOv5示例工程其中对于YOLOv5源码模型导出ONNX或者Torchscript格式ICraft要求对结构进行修改只导出到卷积为止目的是适配DetPost算子实现后处理FPGA硬件加速。YOLOv5结构可视化分析YOLOv8结构可视化分析YOLOv10结构可视化分析YOLO26结构可视化分析通过对经典YOLO系列模型的结构分析可以看出YOLO26结构跟YOLOv10相比差异最小因此可以复用YOLOv10的部分代码。4. YOLO26适配FPAI4.1 验证YOLO26模型Pytorch框架下精度YOLO26源码下载地址https://github.com/ultralytics/ultralyticsYOLO26权重下载地址https://github.com/ultralytics/assets/releases/download/v8.4.0/yolo26n.pt为了避免图像缩放和PAD操作影响对齐精度所以准备一张640*640图片用于推理验证编写推理脚本infer.py并运行得到以下结果作为golden结果infer.pyfrom ultralytics import YOLO # Load a pretrained YOLO26n model model YOLO(yolo26n.pt) # Perform object detection on an image results model(./bus_640x640.png) # Predict on an image # 获取第一个图片的结果最关键 result results[0] # # 打印所有目标坐标 # print( 检测到的目标坐标信息 ) for box in result.boxes: # 坐标 (x1, y1, x2, y2) 左上角 右下角 x1, y1, x2, y2 box.xyxy[0].cpu().numpy() # 置信度 conf box.conf[0].cpu().numpy() # 类别编号 类别名 cls_id int(box.cls[0].cpu().numpy()) cls_name result.names[cls_id] # 打印 print(f类别{cls_name} \t置信度{conf:.2f}) print(f坐标 (x1,y1,x2,y2)({x1:.1f}, {y1:.1f}, {x2:.1f}, {y2:.1f})) print(- * 50) results[0].show() # Display results4.2 保存ONNX模型并修改ONNX结构适配DetPost要求编写导出ONNX脚本export.py并运行得到yolo26n.onnxfrom ultralytics import YOLO # Load a pretrained YOLO26n model model YOLO(yolo26n.pt) # Export the model to ONNX format for deployment path model.export(formatonnx) # Returns the path to the exported model通过netron查看yolo26.onnx输入输出维度以及结构从结构图可以看出其包含了后处理及TopK模块由于DetPost加速模块要求接在conv2d算子之后所以需要将图中红色箭头箭头之后的算子全部从ONNX中删掉有两种方式方式1通过修改模型源码类似于ICraft_ModelZoo中的1_save.py方式2通过脚本对ONNX进行算子的删除该方法属于通用做法注意输出的顺序要符合要求编写get_onnx_graph.py用于获取ONNX计算图中指定输入到输出节点的全部算子输入节点名字第一个输出节点名字get_onnx_graph.pyimport onnx ## 注意为了适配DetPost ## 输出层顺序: 1x80x80x80,1x4x80x80;1x80x40x40,1x4x40x40;1x80x20x20,1x4x20x20; onnx.utils.extract_model(./yolo26n.onnx, # 输入ONNX模型路径 yolo26n-icraft.onnx, # 输出ONNX模型路径 [images], # 模型输入节点名对应1x3x640x640 [/model.23/one2one_cv3.0/one2one_cv3.0.2/Conv_output_0, # 1x80x80x80 /model.23/one2one_cv2.0/one2one_cv2.0.2/Conv_output_0, # 1x4x80x80 /model.23/one2one_cv3.1/one2one_cv3.1.2/Conv_output_0, # 1x80x40x40 /model.23/one2one_cv2.1/one2one_cv2.1.2/Conv_output_0, # 1x4x40x40 /model.23/one2one_cv3.2/one2one_cv3.2.2/Conv_output_0, # 1x80x20x20 /model.23/one2one_cv2.2/one2one_cv2.2.2/Conv_output_0 # 1x4x20x20 ] )运行get_onnx_graph.py脚本得到yolo26n-icraft.onnx其结构如下接下来的工作准备复用大部分yolov10的代码4.3 下载ICraft_ModelZoo_yolov10下载地址https://www.modelscope.cn/models/AIBS/yolov10/files?versionv3.33.1下载4.4 验证yolo26-icraft.onnx的正确性该步骤目的有两点1. 验证ONNX模型是正确的2. 掌握ONNX 输入和输出数据的维度以及含义对齐前后处理脚本便于迁移至嵌入式平台。从3.3章节可以看出yolo26输出两部分cls部分维度1x80x8400box部分1x4x8400yolov10输出的1x64x8400经过DFL模块后变成1x4x8400。对2_save_infer.py脚本进行修改得到yolo26_save_infer.pyimport argparse import os import cv2 import torch import numpy as np import sys sys.path.append(R..) sys.path.insert(0,R..) from ultralytics.nn.modules.block import DFL from ultralytics.utils.tal import dist2bbox,make_anchors from ultralytics.data.augment import LetterBox from ultralytics.utils import ops from visualize import vis,COCO_CLASSES import onnxruntime def pred_one_image(img_path,model_path,test_size): img_raw cv2.imread(img_path) print(img_path,img_path) # 前处理 letterbox LetterBox(test_size, autoFalse, stride32) im np.stack([letterbox(imagex) for x in [img_raw]]) print(******im ,im.shape) im im[..., ::-1].transpose((0, 3, 1, 2)) im np.ascontiguousarray(im) im torch.from_numpy(im) im im.float() im / 255 # 加载 ONNX 模型你之前导出的 yolo26n-icraft.onnx onnx_model onnxruntime.InferenceSession(../2_compile/fmodel/yolo26n-icraft.onnx) ## 前向推理计算 onnx_input im.numpy() # 输入预处理后的图像数据1x3x640x640 onnx_output onnx_model.run(None, {onnx_model.get_inputs()[0].name: onnx_input}) # 1. 拼接 3 个尺度的类别输出cls # onnx_output[0]/[2]/[4] 分别对应 80x80、40x40、20x20 的类别特征图 cls torch.cat([torch.tensor(onnx_output[0]).reshape(1,80,-1), torch.tensor(onnx_output[2]).reshape(1,80,-1), torch.tensor(onnx_output[4]).reshape(1,80,-1)], dim2) # 2. 拼接 3 个尺度的坐标输出box # onnx_output[1]/[3]/[5] 分别对应 80x80、40x40、20x20 的坐标特征图 box torch.cat([torch.tensor(onnx_output[1]).reshape(1,4,-1), torch.tensor(onnx_output[3]).reshape(1,4,-1), torch.tensor(onnx_output[5]).reshape(1,4,-1)], dim2) print(cls.size()) # 输出应为 [1, 80, 80*80 40*40 20*20] [1, 80, 8400] print(box.size()) # 输出应为 [1, 4, 8400] # 3. 生成锚点anchors并获取步长strides outputs [torch.tensor(onnx_output[0]),torch.tensor(onnx_output[2]),torch.tensor(onnx_output[4])] anchors, strides (x.transpose(0, 1) for x in make_anchors(outputs, torch.from_numpy(np.array([8, 16, 32],dtypenp.float32)), 0.5)) # 4. 将模型输出的分布值转换为实际框坐标乘以步长还原到原图尺度 dbox dist2bbox(box, anchors.unsqueeze(0), xywhTrue, dim1) * strides y torch.cat((dbox, cls.sigmoid()), 1) #[1,84,8400] # yolov10 postprocess - NMS free preds y.transpose(-1, -2) conf_thres 0.25 max_det 300 bboxes, scores, labels ops.v10postprocess(preds,max_det, preds.shape[-1]-4)# bbox - [1,max_det,4] scores - [1,max_det] labels - [1,300] bboxes ops.xywh2xyxy(bboxes) preds torch.cat([bboxes, scores.unsqueeze(-1), labels.unsqueeze(-1)], dim-1) #[1,max_det,6] [1,max_det, bboxscoreslabel] mask preds[..., 4] conf_thres b, _, c preds.shape preds preds.view(-1, preds.shape[-1])[mask.view(-1)]# 取mask True的结果即scoreconf的结果 pred preds.view(b, -1, c)#[1,res_num,6] _,res_num,_ pred.shape pred pred[0] # rescale coords to img_raw size print(im.shape[2:]: ,im.shape[2:],img_raw.shape) pred[:, :4] ops.scale_boxes(im.shape[2:], pred[:, :4], img_raw.shape) # show results # 假设你的 pred 是推理后的结果 (N, 6) [x1,y1,x2,y2, score, cls] print( 检测到的目标坐标 (x1y1x2y2 格式) ) for i in range(len(pred)): # 取出每个目标 x1, y1, x2, y2, conf, cls_id pred[i] # 转成普通数值方便打印 x1, y1, x2, y2 float(x1), float(y1), float(x2), float(y2) conf float(conf) cls_id int(cls_id) cls_name COCO_CLASSES[cls_id] # 类别名称 # 格式对齐打印和你上面 YOLO 输出格式一模一样 print(f类别{cls_name:10s} \t置信度{conf:.2f}) print(f坐标 (x1,y1,x2,y2)({x1:6.1f}, {y1:6.1f}, {x2:6.1f}, {y2:6.1f})) print(- * 60) result_image vis(img_raw, boxespred[:,:4], scorespred[:,4], cls_idspred[:,5], confconf_thres, class_namesCOCO_CLASSES) cv2.imshow( , result_image) cv2.waitKey(0) cv2.imwrite(yolo26_infer_result.jpg,result_image) print(Detect ,res_num, objects!) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--model, typestr, default../2_compile/fmodel/yolo26n-icraft.onnx, helptorchscript model path) parser.add_argument(--source, typestr, defaultbus_640x640.png, helpimage path) parser.add_argument(--imgsz, nargs, typeint, default[640], helpimage size) opt parser.parse_args() opt.imgsz * 2 if len(opt.imgsz) 1 else 1 test_size tuple(opt.imgsz) if os.path.isfile(opt.source): pred_one_image(opt.source, opt.model, test_size) elif os.path.isdir(opt.source): image_list os.listdir(opt.source) for image_file in image_list: image_path opt.source // image_file pred_one_image(image_path, opt.model, test_size)运行yolo26_save_infer.py脚本得到以下结果对比4.1章节的推理结果可以看到二者一致说明ONNX正确以及前后处理脚本对齐为下一步FPAI平台部署打下良好基础。注意输入图像640x640和网络要求一致未涉及缩放前处理对比。4.5 ICraft编译yolo26模型toml文件配置30TAIToml配置参考yolov10_int8即可修改以下三处DetPost模块参数配置toml文件将DetPost阈值设置为0.14.6 ICraft编译yolo26模型toml文件配置100TAI将编译目标后端target改为buyi增加imagemake模块配置4.7 ICraft编译yolo26生成多阶段JSONRAW运行编译命令icraft compile .\config\ZG\yolo26_int8.toml通过ICraft-Show查看网络生成的模型结构是选中JSONRAW文件右键选择ICraft-Show打开。JSON文件描述了AI模型的结构和算子维度等RAW文件记录了AI模型的参数以及驱动NPU运行的指令。Parsed和Optimized阶段的JSON和RAW为FP32精度理论上无精度损失如果精度异常说明算子解析异常。Quantized阶段及之后的JSONRAW文件经过了量化适配存在一定程度的精度损失。所有阶段的JSONRAW文件均支持在上位机WIN系统仿真其中仅有BY/ZG/WL阶段的JSONRAW支持在FPAI芯片上运行。BY/ZG/WL阶段的JSONRAW仿真结果与芯片推理结果完全一致。ICraft-Show展示ZG阶段JSON和RAW结构4.8 ICraft编译yolo26生成多阶段JSONRAW100TAI运行编译命令icraft compile .\config\BY\yolo26_int8.tomlICraft-Show展示BY阶段JSON和RAW结构5. YOLO26-Runtime APP代码开发5.1 C运行时代码开发支持WIN仿真/SOCKET模式/PSIN模式复用大部分yolov10.cpp代码对前后处理部分进行修改。修改bbox_info_channel 4yolo26直接输出xywhyolov10该参数配置为64。对于yolo系列模型ICraft_ModelZoo提供的示例runtime代码中包含两套后处理参数post_detpost_hard函数仅支持带有DetPost算子模型的仿真与芯片端运行post_detpost_soft函数支持不带DetPost算子模型的后处理包括各阶段JSONRAW的仿真与芯片端运行。修改post_detpost_soft函数适配yolo26后处理参数要求3处修改。修改post_detpost_hard进行参数修改适配yolo26后处理参数要求2处修改。5.2 WIN系统编译C工程运行# cmake ..# cmake --build . --config Release5.3 WIN系统运行C仿真仿真parsed阶段将yaml参数修改为parsed阶段以及host仿真模式PARSED阶段仿真结果从上述结果可以看出PARSED结果完全正确说明PARSED阶段JSON和RAW文件解析完全正确以及前后处理函数已实现精度对齐。同理可对optimized、quantized、adapted阶段进行仿真对比验证。仿真ZG阶段模型带DetPost的结果存在微小程度的精度误差属于正常现象5.4 WIN系统运行C仿真100TAI修改输入模型路径以及stagePARSED阶段仿真结果修改输入模型路径以及stageBY阶段仿真结果5.5 WIN系统运行Socket模式30TAI开发板启动icraft-serve修改yaml文件为socket模式运行socket模式