【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论
文章目录
- 【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
- 前言
- 模型转换--pytorch转rknn
- pytorch转onnx
- onnx转rknn
- RKNPU2平台搭建依赖环境
- RKNPU2调用rknn模型
- RKNPU2推理核心流程
- RKNPU2推理代码
- 总结
前言
Orangepi RKNN(Rockchip Neural Network)是Rockchip公司推出的一种用于其处理器上的高效神经网络加速技术。它与Rockchip的处理器紧密结合,旨在通过硬件加速提升AI应用的运行效率,特别是在边缘计算设备上,为嵌入式设备和边缘计算场景提供了高性能、低功耗的深度学习解决方案。RKNN Toolkit 和 RKNPU2 是支持RKNN技术的两个重要工具集。RKNN Toolkit 是一套软件开发工具包,提供了模型转换、优化、测试和部署等功能,帮助开发者将训练好的深度学习模型轻松转换为适合Rockchip硬件的格式,以获得最佳性能。而RKNPU2 则是指Rockchip的神经网络处理单元(Neural Processing Unit, NPU)的第二代驱动程序和库文件,它为RKNN Toolkit 提供底层支持,确保了模型能够在Rockchip的硬件上高效地执行。
RKNN Toolkit 和 RKNPU2 是对 Rockchip NPU最原生的支持。
模型转换–pytorch转rknn
博主在RK3566开发板上进行部署演示: 模型转化可以是在Ubuntu环境的主机上或者虚拟机上,但是模型部署必须是在 Orangepi 的开发板子上。
Pytorch 模型转 RKNN 并推理的步骤如下:
- 将 PyTorch 预训练模型文件( .pth 或 .pt 格式)转换成 ONNX 格式的文件(.onnx格式),这一转换过程在 PyTorch 环境中进行。
- 将转换得到的 .onnx 文件再次转换成 .rknn 格式的文件,这一转换过程需要在安装有转换工具 rknn-toolkit2 的Ubuntu系统上运行。这里博主建议在 docker 的 Ubuntu 虚拟机上进行。
- 将转换得到的 .rknn 文件随后作为输入,在 RKNN 平台上调用 RKNPU2 的 C++ API 来执行模型的推理。
pytorch转onnx
博主使用AlexNet图像分类(五种花分类)进行演示,需要安装pytorch环境,对于该算法的基础知识,可以参考博主【AlexNet模型算法Pytorch版本详解】博文。
conda create --name AlexNet python==3.10
conda activate AlexNet
# 根据自己主机配置环境
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 假设模型转化出错则降级为指定1.16.1版本
pip install onnx==1.16.1
然后把训练模型好的AlexNet.pth模型转成AlexNet.onnx模型,pyorch2onnx.py转换代码如下:
【AlexNet.pth百度云链接,提取码:ktq5 】直接下载使用即可。
import torch
from model import AlexNet
model = AlexNet(num_classes=5)
weights_path = "./AlexNet.pth"
# 加载模型权重
model.load_state_dict(torch.load(weights_path))
# 模型推理模式
model.eval()
model.cpu()
# 虚拟输入数据
dummy_input1 = torch.randn(1, 3, 224, 224)
# 模型转化函数
torch.onnx.export(model, (dummy_input1), "AlexNet.onnx", verbose=True, opset_version=11)

onnx转rknn
【平台:x64 架构 windoes11 docker虚拟机 Ubuntu 系统】
1.安装dockers【参考】: Windows11系统下安装并配置阿里云镜像加速,并完成启动。

2.下载 RKNN Toolkit 转换工具【githup下载】: 博主这里没有下载master分支,而是下载 rknn-toolkit2-v1.5.2分支。

3.搭建docker镜像: 在doc/Rockchip_Quick_Start_RKNN_SDK_V1.5.2_CN.pdf中,博主参考该官方文档详细介绍如何构建docker的Ubuntu容器并安装转换工具 rknn-toolkit2,以及其详细的使用方式。

【docker常用指令】
Dockerfile文件构建镜像镜像(不推荐):经常出现 failed with status code [manifests 18.04]: 403 Forbidden 的错误,博主暂时没有解决方案。
# 进入docker配置文件目录
cd XXX\rknn-toolkit2-1.x.x\docker\docker_file\ubuntu_xx_xx_cpxx
# eg: cd C:\Users\AYU\Downloads\rknn-toolkit2-1.5.2\docker\docker_file\ubuntu_18_04_cp36# 查询配资文件
ls
# 出现三个文件分别是:
# 1.Dockerfile_ubuntu_18_04_for_cp36:特定 Dockerfile,用于创建一个基于Ubuntu 18.04的Docker镜像,专门针对Python3.6(cp36)进行配置.
# 2.rknn_toolkit2-1.5.2+b642f30c-cp36-cp36m-linux_x86_64.whl:针对Linux x86_64架构的rknn-toolkit2 Python(wheel )安装包,支持Python3.6(cp36).
# 3.sources_bionic.list: APT源列表文件,用于Ubuntu系统中的软件包管理.# 构建 Docker 镜像
# -f 指定Dockerfile文件
# -t 镜像名:标签
docker build -f Dockerfile_ubuntu_xx_xx_for_cpxx -t rknn-toolkit2:x.x.x-cpxx .
# eg:docker build -f Dockerfile_ubuntu_18_04_for_cp36 -t rknn-toolkit2-env:1.5.2-cp36 .
Docker 镜像文件下载(推荐)【官方网盘,提取码:rknn】【个人网盘,提取码:rknn】
因为官方网盘没有保留一些旧版本的docker镜像,因此博主的个人网盘将旧的网盘补充完整了。
# 进入到docker镜像目录,加载镜像
docker load -i XXX\rknn-toolkit2-x.x.x-cpxx-docker.tar.gz
# eg: docker load -i E:\rknn-toolkit2-1.5.2-cp36-docker.tar.gz# 查看安装的镜像
docker images# 创建容器
docker run -it --name rknn_toolkit2_x.x.x_cpxx -d rknn-toolkit2:x.x.x-cpxx
# eg: docker run -it --name rknn_toolkit2_1.5.2_cp36 -d rknn-toolkit2:1.5.2-cp36# 查看运行的容器
docker ps

4.完成模型onnx到rknn的转化: convert_rknn文件拷贝至虚拟机,完成转化过程,并将rknn模型从虚拟机拷贝到主机。

参考下载的rknn-toolkit2-1.5.2\examples\onnx\resnet50v2中的内容
convert_rknn文件包括之前成功转化的AlexNet.onnx模型文件,一张验证图片,一个保存着验证图片相对路径的dataset.txt,以及转化rknn所需的简化版代码convert.py。
convert.py内容如下:
import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
from rknn.api import RKNNONNX_MODEL = 'AlexNet.onnx'
RKNN_MODEL = 'AlexNet.rknn'
DATASET = './dataset.txt'if __name__ == '__main__':# 创建RKNN对象rknn = RKNN(verbose=True)# 配置RKNN模型:标准化以及指定部署平台print('--> config model')# 注意target_platform='rk3566'要替换成自己的平台rknn.config(mean_values=[127.5, 127.5, 127.5], std_values=[127.5, 127.5, 127.5], target_platform='rk3566')print('done')# 加载对应的深度学习框架print('--> Loading model')ret = rknn.load_onnx(model=ONNX_MODEL)if ret != 0:print('Load model failed!')exit(ret)print('done')# 构造RKNN模型print('--> Building model')# 注意do_quantization 用于控制模型参数和输入数据的量化,即是否将浮点数(float32)转换为整数(int8或int16).ret = rknn.build(do_quantization=True, dataset=DATASET)if ret != 0:print('Build model failed!')exit(ret)print('done')# 导出RKNN模型print('--> Export rknn model')ret = rknn.export_rknn(RKNN_MODEL)if ret != 0:print('Export rknn model failed!')exit(ret)print('done')# 释放RKNN对象rknn.release()
转化流程的指令如下:
# 将转化代码拷贝到ubuntu
docker cp E:\convert_rknn container_id:/root
# eg: docker cp E:\convert_rknn 38097dad59cc:/root# 进入ubuntu容器
docker exec -it container_id bash
# eg: docker exec -it 38097dad59cc bash# 执行代码
cd /root/convert_rknn
python convert.py# 查看目录内容
ls# 退出ubuntu容器,并将模型从ubuntu中拷贝出来
exit
docker cp container_id:/root/convert_rknn/xxx.rknn E:\
# eg: docker cp 38097dad59cc:/root/convert_rknn/AlexNet.rknn E:\


RKNPU2平台搭建依赖环境
【平台:aarch64 架构 Orange Pi 3B (RK3566) 的 Ubuntu 系统】
VNC可视化控制RK3566参考
常用的rknpu1、rknpu2用于端侧内容的开发和编译,对应python模型转换环境分别为:rknn-toolkit、rknn-toolkit2。
参考Rockchip NPU C++推理示例工程【githup下载】,构建AlexNet C++ 图像分类推理工程:
- rknpu2-master\examples\3rdparty\opencv\opencv-linux-aarch64拷贝到AlexNet;
- rknpu2-master\runtime\RK356X\Linux\librknn_api拷贝到AlexNet;
- 在AlexN目录下新建weights目录将rknn权重文件放到里面;
- 在AlexN目录下新建src目录放置推理代码用于执行c++推理(后面会提供);
- 构建CMakeLists.txt核心配置文件(后面会提供)。
AlexNet └── 3rdparty├── opencv| ├── opencv-linux-aarch64└── librknn_api├── aarch64| ├── vlibrknnrt.so├── include| ├── rknn_api.h| ├── rknn_matmul_api.h└── src├── AlexNet.cpp└── weights├── AlexNet.rknn└── CMakeLists.txt
RKNPU2调用rknn模型
RKNPU2推理核心流程
初始化RKNN模型
用于初始化一个 RKNN 上下文,并加载指定的模型。
ret = rknn_init(&ctx, model, model_len, 0, NULL);
| rknn_init参数 | ctx | model | model_size | flags | config |
|---|---|---|---|---|---|
| 作用 | 指向一个 rknn_context 类型的指针,用于存储初始化后的上下文。 | 指向模型数据的指针,通常是经过编译和优化的二进制文件。 | 以字节为单位模型数据的大小。 | 用于指定一些特殊初始化选项的标志位,通常设置为 0。 | 指向一个 rknn_sdk_config 结构体的指针,用于配置 SDK 的行为,不需要特殊配置通常设置为 NULL。 |
获取模型输入输出信息
用于查询 RKNN 上下文中的各种属性,包括输入和输出的详细信息(数量、名称和形状)、性能统计等信息。
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
| rknn_query参数 | ctx | cmd | *param | param_size |
|---|---|---|---|---|
| 作用 | 调用 rknn_init 函数时已经初始化的 RKNN 上下文,标识当前的 RKNN 模型实例。 | 查询命令,用于指定要查询的信息类型。 | 指向一个缓冲区的指针,用于存储查询结果,缓冲区的类型和大小取决于查询命令。 | 以字节为单位的缓冲区大小。 |
| 内容 | – | RKNN_QUERY_IN_OUT_COUNT:查询模型的输入和输出张量的数量。RKNN_QUERY_INPUT_ATTR:查询输入张量的属性。RKNN_QUERY_OUTPUT_ATTR:查询输出张量的属性。RKNN_QUERY_PERF_DETAIL:查询性能详细信息,包括每个层的执行时间。RKNN_QUERY_PERF_STAT:查询性能统计信息,包括总的执行时间和平均执行时间。RKNN_QUERY_TARGET_PROCESSOR:查询目标处理器信息。RKNN_QUERY_MODEL_INFO:查询模型信息,包括模型的版本、输入输出张量的数量等。 | – | – |
预处理输入数据
对输入数据进行颜色空间转换,尺寸缩放操作。
cv::cvtColor(orig_img, orig_img_rgb, cv::COLOR_BGR2RGB);
cv::resize(orig_img, img, cv::Size(MODEL_IN_WIDTH, MODEL_IN_HEIGHT), 0, 0, cv::INTER_LINEAR);
这部分不是 RKNPU2 核心部分,根据任务需求不同,代码略微不同。
设置输入
设置 RKNN 模型的输入数据。
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
| cudaMalloc参数 | ctx | n_inputs | inputs |
|---|---|---|---|
| 作用 | 标识当前的 RKNN 模型实例。 | 输入张量的数量,与模型的输入张量数量必须一致。 | rknn_tensor_attr 结构体数组的指针,每个结构体描述一个输入张量的属性和数据。 |
执行推理
用于执行神经网络模型的推理,触发模型的前向传播过程,将输入数据传递给模型,并生成输出结果。
ret = rknn_run(ctx, nullptr);
| 函数 | ctx | mem |
|---|---|---|
| 作用 | 标识当前的 RKNN 模型实例。 | 指向 rknn_input_output_mem 结构体数组的指针,用于指定输入和输出数据的内存地址;设置为 nullptr,则表示使用默认的输入和输出内存。 |
获取输出
设置 RKNN 模型的输入数据。
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
| cudaMalloc参数 | ctx | n_inputs | inputs |
|---|---|---|---|
| 作用 | 标识当前的 RKNN 模型实例。 | 输入张量的数量,与模型的输入张量数量必须一致。 | rknn_tensor_attr 结构体数组的指针,每个结构体描述一个输入张量的属性和数据。 |
后处理推理结果
推理完成后,从输出张量中获取结果数据,根据需要对结果进行后处理,以获得最终的预测结果。
cv::Mat prob(output_attrs[i].dims[0], output_attrs[i].dims[1], CV_32F, (float*)buffer);cv::minMaxLoc(prob, &minv, &maxv, &minL, &maxL);
这部分不是 RKNPU2 核心部分,根据任务需求不同,代码基本不同。
RKNPU2推理代码
需要配置flower_classes.txt文件存储五种花的分类标签,并将其放置到工程目录的src路径下(推荐)。
daisy
dandelion
roses
sunflowers
tulips
这里需要将AlexNet.rknn放置到工程目录的weight路径下(推荐),并且将以下推理代码拷贝到src路径下的AlexNet.cpp文件中:
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "rknn_api.h"#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>#include <fstream>
#include <iostream>using namespace std;
using namespace cv;std::string labels_txt_file = "src/flower_classes.txt";
std::vector<std::string> readClassNames()
{std::vector<std::string> classNames;std::ifstream fp(labels_txt_file);if (!fp.is_open()){printf("could not open file...\n");exit(-1);}std::string name;while (!fp.eof()){std::getline(fp, name);if (name.length())classNames.push_back(name);}fp.close();return classNames;
}// 从文件中读取二进制模型数据
// 参数:filename:模型文件名,model_size:模型大小 返回值:模型数据指针
static unsigned char *load_model(const char *filename, int *model_size)
{FILE *fp = fopen(filename, "rb");if (fp == nullptr){printf("fopen %s fail!\n", filename);return NULL;}fseek(fp, 0, SEEK_END);int model_len = ftell(fp);unsigned char *model = (unsigned char *)malloc(model_len); // 申请模型大小的内存,返回指针fseek(fp, 0, SEEK_SET);if (model_len != fread(model, 1, model_len, fp)){printf("fread %s fail!\n", filename);free(model);return NULL;}*model_size = model_len;if (fp){fclose(fp);}return model;
}int main(int argc, char **argv)
{rknn_context ctx = 0; // Rockchip NPU 的上下文句柄,用于标识和管理当前的模型实例.int ret; // 用于检查函数调用是否成功.int model_len = 0; // 用于存储模型文件的长度(以字节为单位).unsigned char *model; // 指向模型数据的指针.int MODEL_IN_WIDTH; // 输入模型图像的宽.int MODEL_IN_HEIGHT; // 输入模型图像的高.// const char *model_path = "weights/AlexNet.rknn";// const char *img_path = "images/40410963_3ac280f23a_n.jpg";const char *model_path = argv[1];const char *img_path = argv[2];if (argc != 3){printf("Usage: %s <rknn model> <image_path> \n", argv[0]);return -1;}std::vector<std::string> labels = readClassNames(); // 预测的目标标签数// ======================= 读取图片 ===================cv::Mat orig_img = imread(img_path, cv::IMREAD_COLOR);if (!orig_img.data){printf("cv::imread %s fail!\n", img_path);return -1;}// ======================= 初始化RKNN模型 ===================model = load_model(model_path, &model_len); // 获取模型指针ret = rknn_init(&ctx, model, model_len, 0, NULL); // 初始化RKNN模型if (ret < 0){printf("rknn_init fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输入输出信息 ===================// ********** 输入输出数量 **********rknn_input_output_num io_num;ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); // 使用rknn_query函数获取模型输入输出数量if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); // 打印模型输入输出数量// ********** 输入输出属性 **********rknn_tensor_attr input_attrs[io_num.n_input]; // 使用rknn_tensor_attr结构体存储模型输入属性memset(input_attrs, 0, sizeof(input_attrs)); // 将input_attrs用0初始化for (int i = 0; i < io_num.n_input; i++) // 网络可能有多个输入,遍历模型所有输入{input_attrs[i].index = i; // 设置模型输入索引ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); // 使用rknn_query函数获取模型输入信息,存储在input_attrsif (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}MODEL_IN_WIDTH = input_attrs[i].dims[1]; // 获取模型输入的具体宽MODEL_IN_HEIGHT = input_attrs[i].dims[2]; // 获取模型输入的具体高// 打印模型输入信息printf("input tensors: index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, ""zp=%d, scale=%f\n",input_attrs[i].index, input_attrs[i].name, input_attrs[i].n_dims, input_attrs[i].dims[0], input_attrs[i].dims[1], input_attrs[i].dims[2], input_attrs[i].dims[3],input_attrs[i].n_elems, input_attrs[i].size, get_format_string(input_attrs[i].fmt), get_type_string(input_attrs[i].type),get_qnt_type_string(input_attrs[i].qnt_type), input_attrs[i].zp, input_attrs[i].scale);}rknn_tensor_attr output_attrs[io_num.n_output]; // 使用rknn_tensor_attr结构体存储模型输出信息memset(output_attrs, 0, sizeof(output_attrs)); // 将output_attrs用0初始化for (int i = 0; i < io_num.n_output; i++) // 网络可能有多个输出,遍历模型所有输出{output_attrs[i].index = i; // 设置模型输入索引ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}// 打印模型输出信息printf("output tensors: index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, ""zp=%d, scale=%f\n",output_attrs[i].index, output_attrs[i].name, output_attrs[i].n_dims, output_attrs[i].dims[0], output_attrs[i].dims[1], output_attrs[i].dims[2], output_attrs[i].dims[3],output_attrs[i].n_elems, output_attrs[i].size, get_format_string(output_attrs[i].fmt), get_type_string(output_attrs[i].type),get_qnt_type_string(output_attrs[i].qnt_type), output_attrs[i].zp, output_attrs[i].scale);}// ======================= 前处理 ===================cv::Mat orig_img_rgb;cv::cvtColor(orig_img, orig_img_rgb, cv::COLOR_BGR2RGB); // 默认是BGR需要转化成RGBcv::Mat img = orig_img_rgb.clone();if (orig_img.cols != MODEL_IN_WIDTH || orig_img.rows != MODEL_IN_HEIGHT){cv::resize(orig_img, img, cv::Size(MODEL_IN_WIDTH, MODEL_IN_HEIGHT), 0, 0, cv::INTER_LINEAR); // 对图像尺寸进行缩放}// ======================= 设置模型输入 ===================rknn_input inputs[io_num.n_input]; // 使用rknn_input结构体存储模型输入信息memset(inputs, 0, sizeof(inputs)); // 将inputs用0初始化for (int i = 0; i < io_num.n_input; i++){ inputs[i].index = input_attrs[i].index; // 设置模型输入索引 inputs[i].type = RKNN_TENSOR_UINT8; // 设置模型输入类型 inputs[i].size = input_attrs[i].dims[1] * input_attrs[i].dims[2] * input_attrs[i].dims[3] * sizeof(uint8_t); // 设置模型输入大小inputs[i].fmt = input_attrs[i].fmt; // 设置模型输入格式:NHWC inputs[i].buf = img.data; // 设置模型输入数据 }ret = rknn_inputs_set(ctx, io_num.n_input, inputs); // 使用rknn_inputs_set函数设置模型输入if (ret < 0){printf("rknn_input_set fail! ret=%d\n", ret);return -1;}// ======================= 推理 ===================ret = rknn_run(ctx, nullptr); // 使用rknn_run函数运行RKNN模型if (ret < 0){printf("rknn_run fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输出 ===================rknn_output outputs[io_num.n_output]; // 使用rknn_output结构体存储模型输出信息memset(outputs, 0, sizeof(outputs)); // 将outputs用0初始化for (int i = 0; i < io_num.n_output; i++){ outputs[i].want_float = 1; // 设置模型输出类型为float}ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); // 使用rknn_outputs_get函数获取模型输出if (ret < 0){printf("rknn_outputs_get fail! ret=%d\n", ret);return -1;}// ======================= 后处理 ===================for (int i = 0; i < io_num.n_output; i++) // 遍历模型所有输出{float *buffer = (float *)outputs[i].buf; // 模型输出数据// 1x5 获取输出数据并包装成一个cv::Mat对象,为了方便后处理cv::Mat prob(output_attrs[i].dims[0], output_attrs[i].dims[1], CV_32F, (float*)buffer);std::cout << "prob: " << prob << std::endl;// 后处理推理结果cv::Point maxL, minL; // 用于存储图像分类中的得分最小值索引和最大值索引(坐标)double maxv, minv; // 用于存储图像分类中的得分最小值和最大值cv::minMaxLoc(prob, &minv, &maxv, &minL, &maxL); int max_index = maxL.x; // 获得最大值的索引,只有一行所以列坐标既为索引std::cout << "label id: " << max_index << std::endl;cv::putText(orig_img, labels[max_index], cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, 8);cv::imwrite("./output.jpg", orig_img);}// ======================= 释放输出缓冲区 ===================rknn_outputs_release(ctx, 1, outputs); // 释放rknn_outputs_get获取的输出if (ret < 0){printf("rknn_outputs_release fail! ret=%d\n", ret);return -1;}else if (ctx > 0){// ======================= 释放RKNN模型 ===================rknn_destroy(ctx);}// ======================= 释放模型数据 ===================if (model){free(model);}return 0;
}
CMakeLists.txt核心配置文件:
# 设置最低版本号
cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
# 设置项目名称,博主的平台是3566
project(rk3566-demo VERSION 0.0.1 LANGUAGES CXX)# 输出系统信息
message(STATUS "System: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")# 设置编译器
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# rknn_api 文件夹路径
set(RKNN_API_PATH ${CMAKE_CURRENT_SOURCE_DIR}/librknn_api)
# rknn_api include 路径
set(RKNN_API_INCLUDE_PATH ${RKNN_API_PATH}/include)
# rknn_api lib 路径
set(RKNN_API_LIB_PATH ${RKNN_API_PATH}/aarch64/librknnrt.so)# 寻找OpenCV库,使用自定义的OpenCV_DIR
set(3RDPARTY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty)
set(OpenCV_DIR ${3RDPARTY_PATH}/opencv/opencv-linux-${LIB_ARCH}/share/OpenCV)
find_package(OpenCV 3.4.5 REQUIRED) # 输出OpenCV信息
message(STATUS "include path: ${OpenCV_INCLUDE_DIRS}")# 用来搜索头文件的目录
include_directories(${OpenCV_INCLUDE_DIRS}${RKNN_API_INCLUDE_PATH}
)# 测试NPU:rknn alexnet
add_executable(alexnet src/AlexNet.cpp)# 链接库
target_link_libraries(alexnet${RKNN_API_LIB_PATH}${OpenCV_LIBS}
)
编译和链接,完成推理,查看结果:
# 用于配置 CMake 项目的命令
# -S .: 指定了源代码目录,.当前目录
# -B build: 指定了构建目录,当前目录下创建build子目录
cmake -S . -B build# 使用先前配置好的构建系统来编译和链接项目
cmake --build build# 执行推理
./build/alexnet ./weights/AlexNet.rknn ./images/sunflowers.jpg
向日葵图片预测不准确:

不知道为什么,可能是在模型转化过程中造成了精度损失,在五种花分类这种类别相近的任务中,分类准确度超级低。
总结
尽可能简单、详细的介绍了pytorch模型到rknn模型的转化,C++下 RKNN Toolkit 和 RKNPU2 环境的搭建以及 rknn 模型的 RKNPU2 部署。
相关文章:
【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转rknnpytorch转onnxonnx转rkn…...
CentOS环境上离线安装python3及相关包
0. 准备操作系统及安装包 准备操作系统环境: 首先安装依赖包,安装相应的编译工具 [rootbigdatahost bin]# yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-d…...
学习threejs,使用设置bumpMap凹凸贴图创建褶皱,实现贴图厚度效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.MeshPhongMaterial高…...
React表单联动
Ant Design 1、dependencies Form.Item 可以通过 dependencies 属性,设置关联字段。当关联字段的值发生变化时,会触发校验与更新。 一种常见的场景:注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段&#x…...
408数据结构:栈、队列和数组选择题做题笔记
408数据结构 第一章 绪论 第二章 线性表 绪论、线性表选择题做题笔记 第三章 栈、队列和数组 栈、队列和数组选择题做题笔记 文章目录 408数据结构前言 一、队列二、栈和队列的应用总结 前言 本篇文章为针对王道25数据结构课后习题的栈、队列和数组的做题笔记,后续…...
sql工具!好用!爱用!
SQLynx的界面设计简洁明了,操作逻辑清晰易懂,没有复杂的图标和按钮,想对哪部分操作就在哪里点击右键,即使你是数据库小白也能轻松上手。 尽管SQLynx是一款免费的工具,但是它的功能却丝毫不逊色于其他付费产品ÿ…...
嵌入式驱动开发详解3(pinctrl和gpio子系统)
文章目录 前言pinctrl子系统pin引脚配置pinctrl驱动详解 gpio子系统gpio属性配置gpio子系统驱动gpio子系统API函数与gpio子系统相关的of函数 pinctrl和gpio子系统的使用设备树配置驱动层部分用户层部分 前言 如果不用pinctrl和gpio子系统的话,我们开发驱动时需要先…...
【C++】IO库(一):IO类
IO 库 C 不直接处理输入输出,而是通过定义一族定义在标准库当中的类型来处理IO。 8.1 IO 类 为了支持不同种类的 IO 处理操作,除了 istream 和 ostream 之外,标准库还定义了其它 IO 类型。这些类型分别定义在三个独立的头文件当中…...
uniapp介入极光推送教程 超级详细
直接按照下面教程操作 一步一步来 很快就能 完成 下面的文章非常详细 ,我就不班门弄斧了 直接上原文链接 https://blog.csdn.net/weixin_52830464/article/details/143823231...
阿里云整理(一)
阿里云整理 1. 介绍规模 2. 专业名词2.1 专有网络VPC2.2 安全组SG2.3 云服务器ECS2.4 资源组2.5 部署集2.5 web测试 1. 介绍 阿里云是一家提供云计算和人工智能服务的科技公司,成立于2009年,总部位于杭州。它为全球客户提供全方位的云服务ÿ…...
论文笔记 网络安全图谱以及溯源算法
本文提出了一种网络攻击溯源框架,以及一种网络安全知识图谱,该图由六个部分组成,G <H,V,A,E,L,S,R>。 1|11.知识图 网络知识图由六个部分组成,…...
室内定位论文速递(11.23-11.25)
多传感器姿态估计的Delta滤波器和卡尔曼滤波器设计在球形移动测绘系统中的应用 关键词 球形机器人;姿态估计;传感器融合;卡尔曼滤波器;Delta滤波器;移动测绘;LiDAR 研究问题 球形移动测绘系统中的惯性姿态估计过滤技术尚未得到充分研究。由于其内在的滚动运动,该系统…...
英伟达推出了全新的小型语言模型家族——Hymba 1.5B
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
云网络基础- TCP/IP 协议
文章目录 典型服务模式TCP/IP 协议设置和查看IPIP地址的分类:IP地址组成: 网络位主机位组成克隆:产生一台新的虚拟机win2008 典型服务模式 • C/S,Client/Server架构 – 由服务器提供资源或某种功能 – 客户机使用资源或功能 TCP/IP 协议 • TCP/IP是最广泛支持的通信协议集合…...
android 音效可视化--Visualizer
Visualizer 是使应用程序能够检索当前播放音频的一部分以进行可视化。它不是录音接口,仅返回部分低质量的音频内容。但是,为了保护某些音频数据的隐私,使用 Visualizer 需要 android.permission.RECORD_AUDIO权限。传递给构造函数的音频会话 …...
Python人工智能项目报告
一、实践概述 1、实践计划和目的 在现代社会,计算机技术已成为支撑社会发展的核心力量,渗透到生活的各个领域,应关注人类福祉,确保自己的工作成果能够造福社会,同时维护安全、健康的自然环境,设计出具有包…...
DockerFile 构建基础镜像
1.准备东西 DockerFile 文件 以及安装docker环境 文件内容如下: # 使用Alpine Linux作为基础镜像 FROM --platformlinux/amd64 nginx:1.27.2-alpine # 维护者信息 LABEL maintainer"xu_yhao163.com" ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV …...
卷积神经网络学习记录
目录 神经网络基础定义: 基本组成部分 工作流程 卷积层(卷积定义)【CONV】: 卷积层(Convolutional Layer) 特征提取:卷积层的主要作用是通过卷积核(或滤波器)运算提…...
5种常见的k8s云原生数据管理方案详解
Kubernetes(K8s)是云原生架构的核心组件,提供高效的容器编排和管理功能。在数据存储方面,K8s通过PersistentVolumes(PV)和PersistentVolumeClaims(PVC)机制实现数据持久化࿰…...
[C++]了解内置类型升级
内置类型升级 1.调用模板T时,为什么可以使用T()类型的匿名对象来传参2.内置类型被升级成为类后的使用事项 1.调用模板T时,为什么可以使用T()类型的匿名对象来传参 当我们在定义或声明一个函数时,如果想使用模板T类型的默认构造(例…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

