前言:
无论是我们执行yolov5/yolov8模型,我们训练结果得到一个best.pt文件作为已经训练好的模型,我们都要将其部署到板端,本章节是笔者在部署瑞芯微RV1106时所整理的笔记。
主体思路:
best.pt -> best.onnx
best.onnx->best.rknn
将其部署到瑞芯微RV1106的板端
1.best.pt -> best.onnx
首先我找到了luckfox的官网
下载源码
git clone https://github.com/airockchip/yolov5.git
配置环境
conda pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple create -n yolov5_onnx python=3.8
将pt文件转换为onnx
python export.py --rknpu --weight best.pt
当前目录下就有best.onnx
2. best.onnx -> best.rknn
接着要进行best.onnx转换成best.rknn首先应该重新创建个环境
根据之前安装的
#切换环境配置并适配rknn_Toolskit2
git clone https://github.com/rockchip-linux/rknn-toolkit2
#下载rknn_model_zoo.git
git clone https://github.com/airockchip/rknn_model_zoo.git
#以防后面报错,提前安装好库
pip install tf-estimator-nightly==2.8.0.dev2021122109
#根据其python版本选择器package下合适的包
pip install -r rknn-toolkit2/packages/requirements_cp38-1.6.0.txt -i https://pypi.mirrors.ustc.edu.cn/simple/
#同样的在rknn-toolkit2下安装他的轮子
pip install rknn-toolkit2/packages/rknn_toolkit2-1.6.0+81f21f4d-cp38-cp38-linux_x86_64.whl
将best.onnx放到yolov5中的model中


python
python3 convert.py ../model/best.onnx rv1106

交叉编译:
python
#先克隆其目标文件
git clone https://gitee.com/LuckfoxTECH/luckfox-pico.git

适配其环境:

python
#配置完成环境后添加权限
chmod +x ./build-linux.sh
#在rknn_model_zoo目录下执行
./build-linux.sh -t rv1106 -a armv7l -d yolov5
执行成功后就会产生一个install文件,将其中的demo传输到板端
python
#给板端添加文件夹权限
chmod +x ./rknn_yolov5_demo
#板端本地执行
./rknn_yolov5_demo model/yolov5.rknn model/test.jpg
将生成的out.png传输到本地查看


如果要进行进一步的优化,比如说在推理代码中添加其运行时间,修改其main.cc文件

python
// Copyright (c) 2023 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*-------------------------------------------
Includes
-------------------------------------------*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 新增:时间统计相关头文件
#include <sys/time.h>
#include "yolov5.h"
#include "image_utils.h"
#include "file_utils.h"
#include "image_drawing.h"
#if defined(RV1106_1103)
#include "dma_alloc.hpp"
#endif
/*-------------------------------------------
新增:时间统计工具函数
-------------------------------------------*/
// 获取当前时间(微秒)
static inline uint64_t get_current_time_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}
// 计算耗时并打印(单位:毫秒,保留3位小数)
static inline void print_elapsed_time(const char* step_name, uint64_t start_us, uint64_t end_us) {
double elapsed_ms = (end_us - start_us) / 1000.0;
printf("[TIME] %s: %.3f ms\n", step_name, elapsed_ms);
}
/*-------------------------------------------
Main Function
-------------------------------------------*/
int main(int argc, char **argv)
{
// ==============================================
// 核心修复:提前声明所有时间变量,避免goto跳过初始化
// ==============================================
uint64_t total_start_us, total_end_us;
uint64_t step1_start, step1_end;
uint64_t step2_start, step2_end;
uint64_t step3_start, step3_end, step3_end_all;
uint64_t step4_start, step4_end;
uint64_t step5_start, step5_end;
uint64_t step6_start, step6_end;
uint64_t step7_start, step7_end;
uint64_t step8_start, step8_end;
uint64_t step9_start, step9_end;
// 总耗时统计
total_start_us = get_current_time_us();
if (argc != 3)
{
printf("%s <model_path> <image_path>\n", argv[0]);
return -1;
}
const char *model_path = argv[1];
const char *image_path = argv[2];
int ret;
rknn_app_context_t rknn_app_ctx;
memset(&rknn_app_ctx, 0, sizeof(rknn_app_context_t));
// 步骤1:初始化后处理(统计耗时)
step1_start = get_current_time_us();
init_post_process();
step1_end = get_current_time_us();
print_elapsed_time("Init post process", step1_start, step1_end);
// 步骤2:初始化YOLOv5模型(核心步骤,统计耗时)
step2_start = get_current_time_us();
ret = init_yolov5_model(model_path, &rknn_app_ctx);
step2_end = get_current_time_us();
print_elapsed_time("Init YOLOv5 model", step2_start, step2_end);
if (ret != 0)
{
printf("init_yolov5_model fail! ret=%d model_path=%s\n", ret, model_path);
goto out;
}
// 步骤3:读取图片(统计耗时)
image_buffer_t src_image;
memset(&src_image, 0, sizeof(image_buffer_t));
step3_start = get_current_time_us();
ret = read_image(image_path, &src_image);
step3_end = get_current_time_us();
#if defined(RV1106_1103)
//RV1106 rga requires that input and output bufs are memory allocated by dma
ret = dma_buf_alloc(RV1106_CMA_HEAP_PATH, src_image.size, &rknn_app_ctx.img_dma_buf.dma_buf_fd,
(void **) & (rknn_app_ctx.img_dma_buf.dma_buf_virt_addr));
memcpy(rknn_app_ctx.img_dma_buf.dma_buf_virt_addr, src_image.virt_addr, src_image.size);
dma_sync_cpu_to_device(rknn_app_ctx.img_dma_buf.dma_buf_fd);
free(src_image.virt_addr);
src_image.virt_addr = (unsigned char *)rknn_app_ctx.img_dma_buf.dma_buf_virt_addr;
src_image.fd = rknn_app_ctx.img_dma_buf.dma_buf_fd;
rknn_app_ctx.img_dma_buf.size = src_image.size;
#endif
// 补充:包含RV1106的DMA处理耗时
step3_end_all = get_current_time_us();
print_elapsed_time("Read image (include DMA)", step3_start, step3_end_all);
if (ret != 0)
{
printf("read image fail! ret=%d image_path=%s\n", ret, image_path);
goto out;
}
object_detect_result_list od_results;
// 步骤4:模型推理(核心步骤,统计耗时)
step4_start = get_current_time_us();
ret = inference_yolov5_model(&rknn_app_ctx, &src_image, &od_results);
step4_end = get_current_time_us();
print_elapsed_time("YOLOv5 inference", step4_start, step4_end);
if (ret != 0)
{
printf("inference_yolov5_model fail! ret=%d\n", ret);
goto out;
}
// 步骤5:结果处理(画框+打印+绘图,统计耗时)
step5_start = get_current_time_us();
// 画框和概率
char text[256];
for (int i = 0; i < od_results.count; i++)
{
object_detect_result *det_result = &(od_results.results[i]);
printf("%s @ (%d %d %d %d) %.3f\n", coco_cls_to_name(det_result->cls_id),
det_result->box.left, det_result->box.top,
det_result->box.right, det_result->box.bottom,
det_result->prop);
int x1 = det_result->box.left;
int y1 = det_result->box.top;
int x2 = det_result->box.right;
int y2 = det_result->box.bottom;
draw_rectangle(&src_image, x1, y1, x2 - x1, y2 - y1, COLOR_BLUE, 3);
sprintf(text, "%s %.1f%%", coco_cls_to_name(det_result->cls_id), det_result->prop * 100);
draw_text(&src_image, text, x1, y1 - 20, COLOR_RED, 10);
}
step5_end = get_current_time_us();
print_elapsed_time("Result process (draw box/text)", step5_start, step5_end);
// 步骤6:保存输出图片(统计耗时)
step6_start = get_current_time_us();
write_image("out.png", &src_image);
step6_end = get_current_time_us();
print_elapsed_time("Write output image", step6_start, step6_end);
out:
// 步骤7:释放后处理(统计耗时)
step7_start = get_current_time_us();
deinit_post_process();
step7_end = get_current_time_us();
print_elapsed_time("Deinit post process", step7_start, step7_end);
// 步骤8:释放模型资源(统计耗时)
step8_start = get_current_time_us();
ret = release_yolov5_model(&rknn_app_ctx);
step8_end = get_current_time_us();
print_elapsed_time("Release YOLOv5 model", step8_start, step8_end);
if (ret != 0)
{
printf("release_yolov5_model fail! ret=%d\n", ret);
}
// 步骤9:释放图片内存(统计耗时)
step9_start = get_current_time_us();
if (src_image.virt_addr != NULL)
{
#if defined(RV1106_1103)
dma_buf_free(rknn_app_ctx.img_dma_buf.size, &rknn_app_ctx.img_dma_buf.dma_buf_fd,
rknn_app_ctx.img_dma_buf.dma_buf_virt_addr);
#else
free(src_image.virt_addr);
#endif
}
step9_end = get_current_time_us();
print_elapsed_time("Release image memory", step9_start, step9_end);
// 输出总耗时
total_end_us = get_current_time_us();
print_elapsed_time("Total runtime", total_start_us, total_end_us);
return 0;
}
重新编译程序并部署到板端
python
# 激活你的 Conda 环境(可选,确保依赖一致)
conda activate yolov5_rknn
#设置编译器前缀(核心:指向去掉 -gcc 的部分)
export GCC_COMPILER=/root/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf
# 验证变量是否生效
echo $GCC_COMPILER # 应输出上面的路径
#重新编译程序
./build-linux.sh -t rv1106 -a armv7l -d yolov5
#推送新程序到板子
adb push 编译后的rknn_yolov5_demo /root/rknn_yolov5_demo/
#在板子上运行
cd /root/rknn_yolov5_demo
chmod +x ./rknn_yolov5_demo
./rknn_yolov5_demo model/yolov5.rknn model/test.jpg
彻底解决GCC_COMPILER
python
# 1. 编辑 .bashrc 文件
vim ~/.bashrc
# 2. 在文件末尾添加以下内容(按 i 进入编辑模式)
export GCC_COMPILER=/root/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf
# 3. 保存退出(按 Esc → 输入 :wq → 回车)
# 4. 使配置生效
source ~/.bashrc
# 5. 验证生效
echo $GCC_COMPILER # 应输出正确路径
# 6. 重新编译
./build-linux.sh -t rv1106 -a armv7l -d yolov5

总结:
本章是笔者在适配RV1106所整理的笔记,仅供记录和学习使用,希望能给大家带来帮助。