YOLOv8 模型移植到高通机器人RB5 平台详细指南

YOLOv8 模型移植到高通机器人RB5 平台详细指南

💡前言

随着边缘端 AI 推理需求的增长,将深度学习模型部署到嵌入式平台成为许多开发者的关注焦点。本文将详细介绍如何将 Ultralytics YOLOv8 训练后的目标检测模型移植到高通机器人RB5平台设备上运行,涵盖从 PyTorch 模型到最终部署的完整流程,并提供常见问题的解决方案和性能优化建议。除了该设备外,如果你手上是一台Thundercomm EB5平台的设备,同样可以按照该步骤完成模型训练,快尝试下吧。

1. 概述

1.1 背景介绍

本文档详细介绍如何将 Ultralytics YOLOv8 训练后的目标检测模型移植到高通机器人 RB5平台(Robotics RB5)上运行。RB5 平台是一款强大的机器人开发平台,搭载 Qualcomm QRB5165 处理器,支持 AI 加速和 5G 连接,非常适合边缘端 AI 推理任务。

在这里插入图片描述

1.2 模型移植流程

模型移植的完整流程如下:

  1. YOLOv8 PyTorch 模型 (.pt) → ONNX 格式 (.onnx)
  2. ONNX 模型 → QNN 模型(通过 qnn-onnx-converter 转换并可选量化)
  3. QNN 模型 → 模型库 (.so)(通过 qnn-model-lib-generator 编译)
  4. 模型库 → 上下文二进制 (.bin)(通过 qnn-context-binary-generator 生成缓存)
  5. 部署到 RB5 设备运行推理

1.3 硬件加速器选择

高通机器人 RB5 平台支持多种硬件加速器:

加速器说明适用场景
CPUKryo 585 八核处理器通用计算,调试验证
GPUAdreno 650 GPUFP16/FP32 推理,图形处理
HTP/DSPHexagon Tensor ProcessorINT8 量化推理,最佳性能功耗比

2. 环境准备

2.1 主机开发环境要求

开发主机需满足以下要求:

  • 操作系统:Ubuntu 22.04 LTS(推荐)或 Ubuntu 20.04 LTS
  • Python 版本:Python 3.8 或 3.10
  • 内存:建议 16GB 或以上
  • 存储空间:至少 50GB 可用空间

2.2 安装 QNN SDK

从高通开发者网站下载高通® 神经处理SDK(QNN SDK):

下载链接:Qualcomm AI Engine Direct SDK

解压 SDK 并初始化环境:

unzip qairt_sdk_v2.x.x.zip -d ~/qnn exportQNN_SDK_ROOT=~/qnn/qairt/v2.x.x source$QNN_SDK_ROOT/bin/envsetup.sh 

2.3 配置 QNN 环境变量

~/.bashrc 文件中添加以下配置:

exportQNN_SDK_ROOT=/path/to/qnn/qairt/v2.x.x exportPATH=$QNN_SDK_ROOT/bin/x86_64-linux-clang:$PATHexportLD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang:$LD_LIBRARY_PATHexportPYTHONPATH=$QNN_SDK_ROOT/lib/python:$PYTHONPATH

使配置生效:

source ~/.bashrc 

2.4 安装 Python 依赖

创建虚拟环境并安装必要的 Python 包:

python3 -m venv qnn_env source qnn_env/bin/activate pip install ultralytics pip installonnx==1.17.0 pip installonnxruntime==1.22.0 pip install numpy opencv-python 

3. YOLOv8 模型导出为 ONNX

3.1 导出命令

使用 Ultralytics 提供的导出功能将 PyTorch 模型转换为 ONNX 格式:

from ultralytics import YOLO # 加载训练好的模型 model = YOLO("path/to/your/best.pt")# 导出为 ONNX 格式 model.export(format="onnx", imgsz=[640,640],# 输入尺寸 opset=12,# ONNX opset 版本,建议使用 11 或 12 simplify=True,# 简化模型 dynamic=False# QNN 不支持动态输入,必须设为 False)

3.2 关键导出参数说明

参数说明
format导出格式,设为 ‘onnx’
imgsz输入图像尺寸,如 [640,640],需与训练时保持一致
opsetONNX opset 版本,推荐 11 或 12 以获得最佳 QNN 兼容性
simplify是否简化模型图,建议设为 True
dynamic动态输入大小,QNN 不支持,必须设为 False

3.3 验证 ONNX 模型

导出后验证 ONNX 模型的正确性:

import onnx import onnxruntime as ort import numpy as np # 加载并验证模型 model = onnx.load("best.onnx") onnx.checker.check_model(model)print("ONNX 模型验证通过")# 检查输入输出信息print(f"输入: {[i.name for i in model.graph.input]}")print(f"输出: {[o.name for o in model.graph.output]}")# 测试推理 session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name test_input = np.random.rand(1,3,640,640).astype(np.float32) outputs = session.run(None,{input_name: test_input})print(f"输出形状: {outputs[0].shape}")

4. ONNX 模型转换为 QNN 模型

4.1 使用 qnn-onnx-converter 转换

QNN SDK 提供了 qnn-onnx-converter 工具将 ONNX 模型转换为 QNN C++ 模型格式:

基本转换命令:

qnn-onnx-converter \--input_network best.onnx \--output_path best.cpp \--input_dim"images"1,3,640,640 

如果有多个输出节点:

qnn-onnx-converter \--input_network best.onnx \--output_path best.cpp \--input_dim"images"1,3,640,640 \--out_name output0 

4.2 转换参数详解

参数说明
--input_network输入 ONNX 模型文件路径
--output_path输出 QNN C++ 模型文件路径
--input_dim输入层名称和维度,格式:“name” N,C,H,W
--out_name指定输出节点名称(可选)
--input_list校准数据列表文件,提供后将在转换时同时完成量化

4.3 验证转换结果

转换完成后会生成 .cpp.bin 文件,可通过编译生成模型库来验证:

qnn-model-lib-generator \-c best.cpp \-b best.bin \-o model_libs/ # 成功后会在 model_libs/ 下生成对应平台的 .so 模型库文件

5. 模型量化

5.1 量化概述

要在 Hexagon Tensor Processor (HTP) 上运行模型,必须将模型量化为 INT8 或 INT16 格式。在 QNN SDK 中,量化可以在模型转换阶段通过 qnn-onnx-converter--input_list 参数一步完成,也可以单独进行。量化过程包括两个步骤:权重和偏置量化(静态)以及激活层量化(需要校准数据)。

5.2 准备校准数据

量化需要一组代表性的输入数据作为校准集。创建 Python 脚本生成 .raw 格式的校准数据:

import numpy as np import cv2 import os defpreprocess_image(image_path, input_size=(640,640)):"""预处理图像为 QNN 所需格式""" img = cv2.imread(image_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, input_size) img = img.astype(np.float32)/255.0# NHWC 格式return img # 准备校准图像列表(5-20 张代表性图像) calib_images =["image1.jpg","image2.jpg",...]# 生成 raw 文件for i, img_path inenumerate(calib_images): data = preprocess_image(img_path) raw_path =f"calib_data/input_{i}.raw" data.tofile(raw_path)# 创建输入列表文件withopen("input_list.txt","w")as f:for i inrange(len(calib_images)): f.write(f"calib_data/input_{i}.raw\n")

5.3 执行量化

在 QNN SDK 中,量化集成在 qnn-onnx-converter 转换步骤中。通过提供 --input_list 参数指定校准数据,即可在转换时完成 INT8 量化:

qnn-onnx-converter \--input_network best.onnx \--output_path best_quantized.cpp \--input_dim"images"1,3,640,640 \--input_list input_list.txt \--act_bw8\--weight_bw8

5.4 量化参数说明

参数说明
--input_list校准数据列表文件路径,提供后启用量化
--act_bw激活值位宽,默认 8,可选 16
--weight_bw权重位宽,默认 8,可选 16
--float_bw浮点位宽,可选 16 或 32

6. 模型库生成与上下文二进制缓存

6.1 概述

QNN SDK 采用两步流程将转换后的模型编译为设备可执行格式:先通过 qnn-model-lib-generator 生成目标平台的模型库(.so),再通过 qnn-context-binary-generator 生成上下文二进制缓存(.bin),这可以显著减少运行时的初始化时间。

6.2 生成模型库

将量化后的 QNN 模型编译为目标平台(aarch64)的模型库:

qnn-model-lib-generator \-c best_quantized.cpp \-b best_quantized.bin \-o model_libs/ \-t aarch64-ubuntu-gcc7.5 

6.3 生成上下文二进制缓存

针对 RB5 平台(QRB5165 SoC)生成 HTP 上下文二进制缓存:

qnn-context-binary-generator \--model model_libs/aarch64-ubuntu-gcc7.5/libmodel.so \--backend libQnnHtp.so \--output_dir context_binary/ 

可选:指定目标 SoC 进行离线编译优化

qnn-context-binary-generator \--model model_libs/aarch64-ubuntu-gcc7.5/libmodel.so \--backend libQnnHtp.so \--output_dir context_binary/ \--config_file htp_config.json 

6.4 验证生成结果

检查生成的上下文二进制文件:

ls-la context_binary/ # 应生成 .bin 格式的上下文二进制文件,可直接用于设备端推理

7. 模型部署到 RB5 设备

7.1 连接设备

通过 ADB 连接到 RB5 设备:

adb devices # 进入设备 shell adb shell 

7.2 部署 QNN 运行时库

推送 QNN 运行时库到设备:

adb push $QNN_SDK_ROOT/lib/aarch64-ubuntu-gcc7.5/* /data/qnn/ adb push $QNN_SDK_ROOT/lib/hexagon-v68/unsigned/* /data/qnn/ 

在设备上配置环境变量(添加到 ~/.bashrc):

exportPATH=$PATH:/data/qnn/ exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/qnn/ exportADSP_LIBRARY_PATH="/data/qnn:/system/lib/rfsa/adsp:/system/vendor/lib/rfsa/adsp:/dsp"

7.3 部署模型文件

创建模型目录并推送文件:

adb shell mkdir-p /data/models/yolov8 adb push context_binary/model.bin /data/models/yolov8/ adb push test_images/ /data/models/yolov8/test_images/ 

7.4 使用 qnn-net-run 测试

准备测试输入列表:

echo"/data/models/yolov8/test_images/test.raw"> /data/models/yolov8/test_list.txt 

在 HTP 上运行推理:

qnn-net-run \--backend libQnnHtp.so \--retrieve_context /data/models/yolov8/model.bin \--input_list /data/models/yolov8/test_list.txt 

查看输出结果:

ls output/ 

8. 推理应用开发

8.1 C++ 应用开发

以下是使用 QNN C API 进行推理的示例代码框架:

#include"QNN/QnnInterface.h"#include"QNN/QnnCommon.h"#include"QNN/QnnTypes.h"#include"QNN/QnnContext.h"#include"QNN/QnnGraph.h"#include"QNN/QnnTensor.h"#include"QNN/HTP/QnnHtpDevice.h"#include<dlfcn.h>#include<vector>#include<string>#include<cstring>typedefQnn_ErrorHandle_t(*QnnInterfaceGetProvidersFn_t)(const QnnInterface_t*** providerList,uint32_t* numProviders);structQnnRuntime{void* backendHandle =nullptr; QnnInterface_t qnnInterface; Qnn_BackendHandle_t backendH =nullptr; Qnn_ContextHandle_t contextH =nullptr; Qnn_GraphHandle_t graphH =nullptr;};boolinitQNN(QnnRuntime& rt,const std::string& contextBinPath){ rt.backendHandle =dlopen("libQnnHtp.so", RTLD_NOW | RTLD_LOCAL);if(!rt.backendHandle)returnfalse;auto getProviders =(QnnInterfaceGetProvidersFn_t)dlsym(rt.backendHandle,"QnnInterface_getProviders");const QnnInterface_t** providerList =nullptr;uint32_t numProviders =0;getProviders(&providerList,&numProviders); rt.qnnInterface =*providerList[0]; rt.qnnInterface.QNN_INTERFACE_VER_NAME.backendCreate(nullptr,nullptr,&rt.backendH);// 从上下文二进制文件加载模型// 读取 contextBinPath 到内存 buffer,然后调用 contextCreateFromBinary// ...returntrue;}voidrunInference(QnnRuntime& rt,float* inputData, size_t inputSize){// 配置输入输出张量并执行图推理 Qnn_Tensor_t inputTensor ={}; Qnn_Tensor_t outputTensor ={};// 设置张量维度、数据指针等// ... rt.qnnInterface.QNN_INTERFACE_VER_NAME.graphExecute( rt.graphH,&inputTensor,1,&outputTensor,1,nullptr,nullptr);}

8.2 编译命令

在 RB5 设备上编译:

g++ -std=c++17 -o yolov8_inference main.cpp \ -I/data/qnn/include/ \ -L/data/qnn/ -lQnnHtp-ldl\`pkg-config --cflags--libs opencv`

8.3 后处理逻辑

YOLOv8 输出格式为 [1, 84, 8400](以 80 类 COCO 数据集为例),需要进行后处理:

structDetection{float x, y, w, h;// 边界框float confidence;// 置信度int class_id;// 类别ID}; std::vector<Detection>postprocess(float* output,// 模型输出 [1,84,8400]float conf_thresh,float nms_thresh){ std::vector<Detection> detections;// 遍历 8400 个预测框for(int i =0; i <8400; i++){// 提取边界框坐标(前4个值)float x = output[0*8400+ i];float y = output[1*8400+ i];float w = output[2*8400+ i];float h = output[3*8400+ i];// 找到最大类别置信度float max_conf =0;int max_class =0;for(int c =0; c <80; c++){float conf = output[(4+ c)*8400+ i];if(conf > max_conf){ max_conf = conf; max_class = c;}}// 过滤低置信度检测if(max_conf > conf_thresh){ detections.push_back({x, y, w, h, max_conf, max_class});}}// 应用 NMSreturnapplyNMS(detections, nms_thresh);}

9. 常见问题与解决方案

9.1 ONNX 转换失败

问题qnn-onnx-converter 报告不支持的算子

解决方案

  • 使用较低的 opset 版本(如 opset=11)重新导出 ONNX
  • 使用 onnx-simplifier 简化模型图
  • 对于 5D 张量操作(如某些 Reshape),可能需要修改模型结构
  • 考虑使用 QNN 自定义算子包(Custom Op Package)实现不支持的算子

9.2 量化后精度下降

问题:INT8 量化后检测精度明显下降

解决方案

  • 增加校准数据集大小(建议 50-100 张代表性图像)
  • 使用 FP16 激活值 + INT8 权重的混合量化
  • 尝试 INT16 量化以获得更好精度
  • qnn-onnx-converter 中使用 --act_bw 16 提高激活值精度

9.3 HTP 运行时错误

问题:在 DSP/HTP 上运行时报错

解决方案

  • 确保模型已正确量化(非量化模型无法在 HTP 上运行)
  • 检查 ADSP_LIBRARY_PATH 环境变量配置
  • 确认 DSP 签名库已正确部署
  • 使用 qnn-context-binary-generator 为目标 SoC 生成上下文二进制缓存

10. 性能优化建议

10.1 模型优化

  • 使用 YOLOv8n(nano)或 YOLOv8s(small)变体以获得更快推理速度
  • 降低输入分辨率(如 320x320 或 416x416)在可接受精度损失范围内
  • 减少检测类别数量以降低输出处理开销

10.2 运行时优化

  • 优先使用 HTP 加速器以获得最佳性能功耗比
  • 使用离线上下文二进制缓存(qnn-context-binary-generator)减少初始化时间
  • 使用 QNN 共享内存缓冲区减少数据拷贝
  • 实现多线程流水线:图像采集、预处理、推理、后处理并行

10.3 预期性能参考

模型输入尺寸量化预期 FPS (HTP)
YOLOv8n640x640INT8~15-25
YOLOv8n320x320INT8~40-60
YOLOv8s640x640INT8~8-12

注意:实际性能受多种因素影响,包括模型复杂度、输入尺寸、量化精度和系统负载等。建议在目标平台上进行实际测试以获得准确的性能数据。

📢 结语

本文详细介绍了将 YOLOv8 模型移植到高通机器人RB5平台的完整流程,包括环境搭建、模型转换、量化、部署以及应用开发。通过遵循本文步骤,开发者可以快速在边缘端实现高效的目标检测推理。如果在移植过程中遇到问题,可参考常见问题章节的解决方案,并结合性能优化建议进一步提升模型运行效率。希望本文能为您的嵌入式 AI 项目提供有价值的参考。

Read more

蓝耘 × 通义万相 2.1,AIGC 双雄合璧,点燃数字艺术新引擎

蓝耘 × 通义万相 2.1,AIGC 双雄合璧,点燃数字艺术新引擎

目录 一、本篇背景: 二、蓝耘与通义万相 2.1 概述: 2.1蓝耘简介: 2.2通义万相 2.1 简介: 注册并使用蓝耘元生代智算平台: 完成通义万相 2.1部署并调用:  个人代码调用过程及感受: 环境准备: 代码实现: 保存生成的图像: 三、蓝耘与通义万相 2.1 结合的优势: 3.1强大的计算力支撑: 3.2高效的数据处理与传输: 3.3定制化与优化: 四、蓝耘调用通义万相 2.1 API 的实际代码演示: 4.1环境搭建: 4.2图像生成代码示例: 4.3文本生成代码示例: 五、蓝耘与通义万相 2.1

硕士论文盲审前降AI率:盲审评委到底会不会看AIGC报告?

硕士论文盲审前降AI率:盲审评委到底会不会看AIGC报告? 最近收到不少同学私信问我:"学长,我硕士论文马上要送盲审了,学校说要做AIGC检测,但盲审评委真的会看这个报告吗?"说实话,这个问题我当初也纠结过。今天就把我了解到的情况和大家详细聊聊,希望能帮到正在准备盲审的同学。 盲审流程中AIGC检测处于什么位置? 盲审前的"关卡"越来越多 以前硕士论文盲审,学校主要关注的就是查重率。但从2025年下半年开始,越来越多的高校在盲审前增加了AIGC检测环节。根据我收集到的信息,目前的盲审流程大致是这样的: 环节时间节点负责方是否涉及AI检测论文提交盲审前2-4周研究生院部分学校要求提交检测报告查重检测盲审前1-2周学院/研究生院与AIGC检测同步进行AIGC检测盲审前1-2周学院/研究生院是,多数用知网系统送审盲审开始研究生院统一安排部分学校附带检测报告评审盲审期间(2-4周)外校评委评委可能收到报告 三种常见的学校处理方式 经过调研,我发现不同学校对盲审中AIGC检测的处理方式主要分三种: 第一种:检测不通过直接不送审。 这是最严格的情况。如果AIGC检测率超过

IDEA 中的 AI 编程插件怎么选?Copilot / 灵码 / TRAE 实际使用对比

IDEA 中的 AI 编程插件怎么选?Copilot / 灵码 / TRAE 实际使用对比

# 【不吹不黑】Java 开发者真实体验:IDEA 三大 AI 编程插件深度对比(Copilot / TRAE / 灵码) > 本文是一篇**技术交流与使用体验记录**,仅用于分享 Java 开发过程中使用 AI 插件的真实感受与效率提升方式,不涉及任何商业推广或广告行为。 *** ## 一、写在前面:为什么要写这篇文章 过去一年,大模型能力的跃迁,直接改变了开发者的工作方式。**AI 已经不再是“写 Demo 的玩具”,而是逐渐演变为 IDE 中的“第二大脑”** 。 本文的目的非常明确: *   记录一名 **Java 后端开发者** 在真实项目中使用 AI 插件的体验 *   对比不同插件在 **补全、对话、Agent 工作流** 等方面的差异 *   帮助开发者根据自身场景选择合适的工具,而不是盲目跟风 本文所有结论,

Optimus狂奔充电,特斯拉选了哪种方式破解机器人能源困局?

Optimus狂奔充电,特斯拉选了哪种方式破解机器人能源困局?

一台人形机器人安静地跑向充电站,熟练地为自己插上充电枪——这不是科幻电影,而是特斯拉实验室里的日常。当行业还在争论哪种充电技术是未来时,特斯拉的Optimus似乎已经给出了它的答案。 在特斯拉近期新发布的视频中,Optimus人形机器人展示了流畅的慢跑能力,并在电量不足时自主导航到充电站,通过后置摄像头引导,独立完成充电枪对接。 这一场景直观地回答了特斯拉当前的充电方案选择。那么,这一选择背后是怎样的考量?随着Optimus即将大规模量产,未来的能源补给技术又会如何演变? 01 特斯拉的当前答案:自主接触式充电 根据近期发布的视频和报道,特斯拉为Optimus配备的是具备高度自主能力的接触式充电系统。 这项技术的核心流程是:当机器人电量低于设定阈值时,它会启动内置的视觉感知系统(后置摄像头),识别并导航至最近的充电站。 抵达后,通过力控传感器,准确地将充电接口与机身端口对接,整个过程无需人工干预。 特斯拉选择这一方案并非偶然,而是基于多重考量。直接高效的能源传输方式能够满足快速补能的需求,且能充分利用特斯拉在电动汽车领域积累的成熟技术。 更重要的是,高度自主