跳到主要内容
YOLOv8 模型移植至高通 RB5 平台实战指南 | 极客日志
Python AI 算法
YOLOv8 模型移植至高通 RB5 平台实战指南 YOLOv8 模型部署至高通 RB5 平台涉及 PyTorch 转 ONNX、QNN 转换及量化流程。主要步骤包括环境配置、模型导出、使用 qnn-onnx-converter 转换、HTP 量化、生成模型库与上下文二进制缓存,最后通过 ADB 部署至设备运行。重点在于解决算子兼容性、量化精度损失及 HTP 运行时错误,并通过调整模型尺寸与量化策略优化性能,预期 YOLOv8n 在 640x640 分辨率下可达 15-25 FPS。
黑客 发布于 2026/4/5 更新于 2026/4/26 2 浏览前言
随着边缘端 AI 推理需求的激增,将深度学习模型部署到嵌入式平台已成为开发者的核心关注点。本文将分享如何将 Ultralytics YOLOv8 训练后的目标检测模型移植到高通机器人 RB5 平台上运行,涵盖从 PyTorch 模型到最终部署的完整流程,并提供常见问题的解决方案和性能优化建议。
1. 概述
1.1 背景介绍
RB5 平台是一款强大的机器人开发平台,搭载 Qualcomm QRB5165 处理器,支持 AI 加速和 5G 连接,非常适合边缘端 AI 推理任务。
1.2 模型移植流程
整个移植链路大致如下:
YOLOv8 PyTorch 模型 (.pt) → ONNX 格式 (.onnx)
ONNX 模型 → QNN 模型(通过 qnn-onnx-converter 转换并可选量化)
QNN 模型 → 模型库 (.so)(通过 qnn-model-lib-generator 编译)
模型库 → 上下文二进制 (.bin)(通过 qnn-context-binary-generator 生成缓存)
部署到 RB5 设备运行推理
1.3 硬件加速器选择
高通机器人 RB5 平台支持多种硬件加速器,根据场景灵活选择:
加速器 说明 适用场景 CPU Kryo 585 八核处理器 通用计算,调试验证 GPU Adreno 650 GPU FP16/FP32 推理,图形处理 HTP/DSP Hexagon Tensor Processor INT8 量化推理,最佳性能功耗比
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)。解压 SDK 并初始化环境时注意命令分隔:
unzip qairt_sdk_v2.x.x.zip -d ~/qnn
export QNN_SDK_ROOT=~/qnn/qairt/v2.x.x
source $QNN_SDK_ROOT /bin/envsetup.sh
2.3 配置 QNN 环境变量
在 ~/.bashrc 文件中添加以下配置,确保路径正确:
export QNN_SDK_ROOT=/path/to/qnn/qairt/v2.x.x
export PATH=$QNN_SDK_ROOT /bin/x86_64-linux-clang:
LD_LIBRARY_PATH= /lib/x86_64-linux-clang:
PYTHONPATH= /lib/python:
$PATH
export
$QNN_SDK_ROOT
$LD_LIBRARY_PATH
export
$QNN_SDK_ROOT
$PYTHONPATH
2.4 安装 Python 依赖 创建虚拟环境并安装必要的 Python 包,注意 pip install 后要有空格:
python3 -m venv qnn_env
source qnn_env/bin/activate
pip install ultralytics
pip install onnx==1.17.0
pip install onnxruntime==1.22.0
pip install numpy opencv-python
3. YOLOv8 模型导出为 ONNX
3.1 导出命令 使用 Ultralytics 提供的导出功能将 PyTorch 模型转换为 ONNX 格式。这里有个关键点,QNN 不支持动态输入,必须设为 False:
from ultralytics import YOLO
model = YOLO("path/to/your/best.pt" )
model.export(
format ="onnx" ,
imgsz=[640 , 640 ],
opset=12 ,
simplify=True ,
dynamic=False
)
3.2 关键导出参数说明 参数 说明 format 导出格式,设为 'onnx' imgsz 输入图像尺寸,如 [640,640],需与训练时保持一致 opset ONNX 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++ 模型格式。如果有多个输出节点,记得指定 --out_name:
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/
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
def preprocess_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
return img
calib_images = ["image1.jpg" , "image2.jpg" , ...]
for i, img_path in enumerate (calib_images):
data = preprocess_image(img_path)
raw_path = f"calib_data/input_{i} .raw"
data.tofile(raw_path)
with open ("input_list.txt" , "w" ) as f:
for i in range (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/
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 验证生成结果
7. 模型部署到 RB5 设备
7.1 连接设备
7.2 部署 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):
export PATH=$PATH :/data/qnn/
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH :/data/qnn/
export ADSP_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
qnn-net-run \
--backend libQnnHtp.so \
--retrieve_context /data/models/yolov8/model.bin \
--input_list /data/models/yolov8/test_list.txt
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>
typedef QNN_ErrorHandle_t (*QNNInterfaceGetProvidersFn_t) (const QNNInterface_t*** providerList, uint32_t * numProviders) ;
struct QNNRuntime {
void * backendHandle = nullptr ;
QNNInterface_t qnnInterface;
QNN_BackendHandle_t backendH = nullptr ;
QNN_ContextHandle_t contextH = nullptr ;
QNN_GraphHandle_t graphH = nullptr ;
};
bool initQNN (QNNRuntime& rt, const std::string& contextBinPath) {
rt.backendHandle = dlopen ("libQnnHtp.so" , RTLD_NOW | RTLD_LOCAL);
if (!rt.backendHandle) return false ;
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);
...
return true ;
}
void runInference (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 编译命令 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 数据集为例),需要进行后处理:
struct Detection {
float x, y, w, h;
float confidence;
int class_id;
};
std::vector<Detection> postprocess (float * output, float conf_thresh, float nms_thresh) {
std::vector<Detection> detections;
for (int i = 0 ; i < 8400 ; i++) {
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});
}
}
return applyNMS (detections, nms_thresh);
}
9. 常见问题与解决方案
9.1 ONNX 转换失败 问题 :qnn-onnx-converter 报告不支持的算子
使用较低的 opset 版本(如 opset=11)重新导出 ONNX
使用 onnx-simplifier 简化模型图
对于 5D 张量操作(如某些 Reshape),可能需要修改模型结构
考虑使用 QNN 自定义算子包(Custom Op Package)实现不支持的算子
9.2 量化后精度下降
增加校准数据集大小(建议 50-100 张代表性图像)
使用 FP16 激活值 + INT8 权重的混合量化
尝试 INT16 量化以获得更好精度
在 qnn-onnx-converter 中使用 --act_bw 16 提高激活值精度
9.3 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) YOLOv8n 640x640 INT8 ~15-25 YOLOv8n 320x320 INT8 ~40-60 YOLOv8s 640x640 INT8 ~8-12
注意 :实际性能受多种因素影响,包括模型复杂度、输入尺寸、量化精度和系统负载等。建议在目标平台上进行实际测试以获得准确的性能数据。
结语 本文详细介绍了将 YOLOv8 模型移植到高通机器人 RB5 平台的完整流程,包括环境搭建、模型转换、量化、部署以及应用开发。通过遵循本文步骤,开发者可以快速在边缘端实现高效的目标检测推理。如果在移植过程中遇到问题,可参考常见问题章节的解决方案,并结合性能优化建议进一步提升模型运行效率。希望本文能为您的嵌入式 AI 项目提供有价值的参考。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online