YOLOv8 模型移植到高通机器人 RB5 平台详细指南
将 Ultralytics YOLOv8 目标检测模型部署到高通机器人 RB5 平台的完整流程。内容包括环境搭建(Ubuntu、QNN SDK)、PyTorch 模型导出为 ONNX 格式、使用 qnn-onnx-converter 转换为 QNN 模型及量化处理、生成模型库与上下文二进制缓存,以及通过 C++ 应用进行部署推理。此外还涵盖了常见问题排查与性能优化建议,旨在帮助开发者在边缘端实现高效的目标检测任务。

将 Ultralytics YOLOv8 目标检测模型部署到高通机器人 RB5 平台的完整流程。内容包括环境搭建(Ubuntu、QNN SDK)、PyTorch 模型导出为 ONNX 格式、使用 qnn-onnx-converter 转换为 QNN 模型及量化处理、生成模型库与上下文二进制缓存,以及通过 C++ 应用进行部署推理。此外还涵盖了常见问题排查与性能优化建议,旨在帮助开发者在边缘端实现高效的目标检测任务。

随着边缘端 AI 推理需求的增长,将深度学习模型部署到嵌入式平台成为许多开发者的关注焦点。本文将详细介绍如何将 Ultralytics YOLOv8 训练后的目标检测模型移植到高通机器人 RB5 平台上运行,涵盖从 PyTorch 模型到最终部署的完整流程,并提供常见问题的解决方案和性能优化建议。
本文档详细介绍如何将 Ultralytics YOLOv8 训练后的目标检测模型移植到高通机器人 RB5 平台(Robotics RB5)上运行。RB5 平台是一款强大的机器人开发平台,搭载 Qualcomm QRB5165 处理器,支持 AI 加速和 5G 连接,非常适合边缘端 AI 推理任务。
模型移植的完整流程如下:
高通机器人 RB5 平台支持多种硬件加速器:
| 加速器 | 说明 | 适用场景 |
|---|---|---|
| CPU | Kryo 585 八核处理器 | 通用计算,调试验证 |
| GPU | Adreno 650 GPU | FP16/FP32 推理,图形处理 |
| HTP/DSP | Hexagon Tensor Processor | INT8 量化推理,最佳性能功耗比 |
开发主机需满足以下要求:
从高通开发者网站下载高通® 神经处理 SDK(QNN SDK):
下载链接:Qualcomm AI Engine Direct 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
在 ~/.bashrc 文件中添加以下配置:
export QNN_SDK_ROOT=/path/to/qnn/qairt/v2.x.x
export PATH=$QNN_SDK_ROOT/bin/x86_64-linux-clang:$PATH
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang:$LD_LIBRARY_PATH
export PYTHONPATH=$QNN_SDK_ROOT/lib/python:$PYTHONPATH
使配置生效:
source ~/.bashrc
创建虚拟环境并安装必要的 Python 包:
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
使用 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)
| 参数 | 说明 |
|---|---|
| format | 导出格式,设为 'onnx' |
| imgsz | 输入图像尺寸,如 [640,640],需与训练时保持一致 |
| opset | ONNX opset 版本,推荐 11 或 12 以获得最佳 QNN 兼容性 |
| simplify | 是否简化模型图,建议设为 True |
| dynamic | 动态输入大小,QNN 不支持,必须设为 False |
导出后验证 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}")
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
| 参数 | 说明 |
|---|---|
--input_network | 输入 ONNX 模型文件路径 |
--output_path | 输出 QNN C++ 模型文件路径 |
--input_dim | 输入层名称和维度,格式:"name" N,C,H,W |
--out_name | 指定输出节点名称(可选) |
--input_list | 校准数据列表文件,提供后将在转换时同时完成量化 |
转换完成后会生成 .cpp 和 .bin 文件,可通过编译生成模型库来验证:
qnn-model-lib-generator \
-c best.cpp \
-b best.bin \
-o model_libs/
# 成功后会在 model_libs/ 下生成对应平台的 .so 模型库文件
要在 Hexagon Tensor Processor (HTP) 上运行模型,必须将模型量化为 INT8 或 INT16 格式。在 QNN SDK 中,量化可以在模型转换阶段通过 qnn-onnx-converter 的 --input_list 参数一步完成,也可以单独进行。量化过程包括两个步骤:权重和偏置量化(静态)以及激活层量化(需要校准数据)。
量化需要一组代表性的输入数据作为校准集。创建 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 # NHWC 格式
return img
# 准备校准图像列表(5-20 张代表性图像)
calib_images = ["image1.jpg","image2.jpg",...]
# 生成 raw 文件
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")
在 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
| 参数 | 说明 |
|---|---|
--input_list | 校准数据列表文件路径,提供后启用量化 |
--act_bw | 激活值位宽,默认 8,可选 16 |
--weight_bw | 权重位宽,默认 8,可选 16 |
--float_bw | 浮点位宽,可选 16 或 32 |
QNN SDK 采用两步流程将转换后的模型编译为设备可执行格式:先通过 qnn-model-lib-generator 生成目标平台的模型库(.so),再通过 qnn-context-binary-generator 生成上下文二进制缓存(.bin),这可以显著减少运行时的初始化时间。
将量化后的 QNN 模型编译为目标平台(aarch64)的模型库:
qnn-model-lib-generator \
-c best_quantized.cpp \
-b best_quantized.bin \
-o model_libs/ \
-t aarch64-ubuntu-gcc7.5
针对 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
检查生成的上下文二进制文件:
ls -la context_binary/
# 应生成 .bin 格式的上下文二进制文件,可直接用于设备端推理
通过 ADB 连接到 RB5 设备:
adb devices
# 进入设备 shell
adb shell
推送 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"
创建模型目录并推送文件:
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/
准备测试输入列表:
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/
以下是使用 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);
(!rt.backendHandle) ;
getProviders = (QNNInterfaceGetProvidersFn_t)(rt.backendHandle,);
QNNInterface_t** providerList = ;
numProviders = ;
(&providerList,&numProviders);
rt.qnnInterface = *providerList[];
rt.qnnInterface.QNN_INTERFACE_VER_NAME.(,,&rt.backendH);
;
}
{
QNN_Tensor_t inputTensor = {};
QNN_Tensor_t outputTensor = {};
rt.qnnInterface.QNN_INTERFACE_VER_NAME.(
rt.graphH,&inputTensor,,&outputTensor,,,);
}
在 RB5 设备上编译:
g++ -std=c++17 -o yolov8_inference main.cpp \
-I/data/qnn/include/ \
-L/data/qnn/ -lQNNHtp-ldl\
`pkg-config --cflags--libs opencv`
YOLOv8 输出格式为 [1, 84, 8400](以 80 类 COCO 数据集为例),需要进行后处理:
struct Detection{
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});
}
}
(detections, nms_thresh);
}
问题:qnn-onnx-converter 报告不支持的算子
解决方案:
onnx-simplifier 简化模型图问题:INT8 量化后检测精度明显下降
解决方案:
qnn-onnx-converter 中使用 --act_bw 16 提高激活值精度问题:在 DSP/HTP 上运行时报错
解决方案:
ADSP_LIBRARY_PATH 环境变量配置qnn-context-binary-generator 为目标 SoC 生成上下文二进制缓存qnn-context-binary-generator)减少初始化时间| 模型 | 输入尺寸 | 量化 | 预期 FPS (HTP) |
|---|---|---|---|
| YOLOv8n | 640x640 | INT8 | ~15-25 |
| YOLOv8n | 320x320 | INT8 | ~40-60 |
| YOLOv8s | 640x640 | INT8 | ~8-12 |
注意:实际性能受多种因素影响,包括模型复杂度、输入尺寸、量化精度和系统负载等。建议在目标平台上进行实际测试以获得准确的性能数据。
本文详细介绍了将 YOLOv8 模型移植到高通机器人 RB5 平台的完整流程,包括环境搭建、模型转换、量化、部署以及应用开发。通过遵循本文步骤,开发者可以快速在边缘端实现高效的目标检测推理。如果在移植过程中遇到问题,可参考常见问题章节的解决方案,并结合性能优化建议进一步提升模型运行效率。希望本文能为您的嵌入式 AI 项目提供有价值的参考。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online