【RK3588 第一篇】Firefly 瑞芯微板子入门知识、和环境篇汇总

公司买了块瑞芯微的移动开发板,准备将公司的主营业务的AI模型,从服务器主机,移动到开发板上面。所以,就选择了瑞芯微的RK3588的板子。

从目前市面上出现的板子来看,主要的还是以瑞芯微的板子为主,比如鸣辰1号等等。接着,也就有了我学习的内容。

这里将我学习的过程记录和分享到这里,一是帮助新手快速入手,二是帮助自己进行记录,以便在我忘记之前如何弄的时候,及时的可以快速查看。参考资料主要是来自于官方给的文档,还有网上热心网友的分享,在此感谢。

其中,RK3588的主要参考链接集合到这里,如下:

  • ROC-RK3588S-PC网页教程:https://wiki.t-firefly.com/zh_CN/ROC-RK3588S-PC/index.html
  • Core-3588J资料下载:https://www.t-firefly.com/doc/download/161.html
  • 技术文档:https://wiki.t-firefly.com/zh_CN/ROC-RK3588S-PC/index.html
  • 官方论坛首页:https://dev.t-firefly.com/portal.php?mod=topic&topicid=11
  • 发邮件,请教官方问题

下面的内容,也是基于上述资料进行展开的,只是对自己感兴趣和遇到的一些问题做了些记录,整体上是大差不差。

一、RK3588基本内容

板子的相关资料,官方和网络开放的一些资料,对其中对了很详尽的描述和介绍,我在这里就不做进一步的赘述了。贴一个功能和输入、输出接口的官方介绍如下图这样:

在这里插入图片描述
下文主要参照文档:RKNN SDK 快速上手指南,这个文件是官方的文档,自行查找即可,建议直接参照这个文档,会描述的比较详尽,接近于手把手教学。配合网页教程,会更佳。

RKNN SDK的下载地方,包括了NPU Demo和配置所需要的文件。有1.3.01.2.0两个版本,我选择的是1.3.0的版本进行实验。文档包括:

  • RKNN Toolkit2 快速上手指南(Rockchip_Quick_Start_RKNN_Toolkit2_CN-1.3.0.pdf
  • RKNN-Toolkit2 用户使用指南(Rockchip_User_Guide_RKNN_Toolkit2_CN-1.3.0.pdf

都存储在:RK_NPU_SDK_1.3.0\rknn-toolkit2-1.3.0\doc文件夹下,这两个文档比较重要,能够帮助我们快速部署和快速转换自己的模型,后面会主要用到这两个文档。

rknn sdk

二、入门上手

2.1、安装ADB

ADB的安装与使用,也可以参考这里:ADB 使用

2.1.1、ADB是个什么东西?

ADB全称Android Debug Bridge,中文翻安卓调试桥。专业术语就不讲解释了,简单来说就是可以通过这个命令用电脑控制手机,或者是其他的安卓设备,比如这个板子,就是安卓的系统。

所以,为了控制这个板子,就要在自己电脑上面,搞个ADB,有了这个东西,才能与自己的板子通信,给他发指令,控制他去干活。下面就按照官方教程,在自己的电脑上,进行安装吧。

拿到时候,我也没搞明白,为啥不直接把板子脸上屏蔽,直接操作。刷Linux系统是可以的,我这里还是用出厂自带的安卓系统,就通过这个安卓调试桥,用PC作为媒介,给板子发命令和传送和查看板子内的文件

adb常用命令如下,这个也是后面常用到的命令

adb root 			以root权限重启adb服务
adb reboot 			重启设备
adb kill-server		终止adb服务进程

adb remount 		将system分区重新挂载为可读写分区
adb push <local> <remote> 	将本地文件复制到设备
adb pull <remote> <local> 	从设备复制文件到本地
adb shell 			进入板子系统

更多adb命令,参考这里:adb命令大全

2.1.2、window下安装ADB

就按照他说的来,先去下载驱动,把驱动安装好。然后再来下载这个adb.zip,解压后进行安装。没有遇到什么问题,测试发现安装成功,就完事了。

我也没搞明白,为啥去用window去首先做了,可能就是因为不了解,先按教程说的做得了。

这块内容有很详尽的介绍,并且我也是参照这块做的,没有发现什么问题,就不做过多解释了。

adb

下面是在 ROC-RK3588S-PC 上运行demo的展示结果:

在这里插入图片描述

ROC-RK3588S-PC网页教程NPU使用一节的步骤,可以实现对官方demo的复现,打印结果如下:(根据你自己运行的demo的不同,打印的内容也不同)

在这里插入图片描述
这是rknn_ssd_demo,也就是目标检测算法SSD的RKNN的版本实现方法。其中

  • 加载的模型是.rknn
  • rknn_ssd_demo是经过编译后的c语言版本,速度有所提升

在下一节中,主要就是仿照官方的方法:

  • 将自己pytorch训练的yolov5的模型,给转成.rknn版本
  • 生成yolov5的c语言版本,进行加速

2.2、更新板子的 rknn_server 和 librknnrt.so

在这里插入图片描述

正常输出运行结果:

在这里插入图片描述

2.3、创建和激活的rknn虚拟环境

前面的环境按照,都是为了这一刻,能够激活环境,进行操作。进入激活的python环境,需要这样操作:

root@zyyl-Lenovo:~# adb devices
List of devices attached
c21389c12d9b5c0b	device

root@zyyl-Lenovo:~# adb root && adb remount
adbd is already running as root
remount succeeded

root@zyyl-Lenovo:~# virtualenv -p /usr/bin/python3 venv      #  选择一个python解释器来创建虚拟化环境
created virtual environment CPython3.8.10.final.0-64 in 749ms
  creator CPython3Posix(dest=/root/venv, clear=False, global=False)
  seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, pkg_resources=latest, via=copy, app_data_dir=/root/.local/share/virtualenv/seed-app-data/v1.0.1.debian.1)
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
  
root@zyyl-Lenovo:~# source venv/bin/activate			# 激活虚拟化环境
(venv) root@zyyl-Lenovo:~# 

root@zyyl-Lenovo:~# deactive 		# 退出虚拟化环境

虚拟环境的部分,更多可参考这里:virtualenv介绍及基本使用

创建虚拟环境的一些记录:假设我在某个位置创建了一个虚拟环境,那么就会创建一个 venv 的文件夹,如下所示:

创建虚拟环境的地址
那么,我在下次采用source venv/bin/activate 激活虚拟环境的时候,就需要在文件夹下面进行操作。

激活虚拟环境

下面是运行官方example时候,打印的结果。还好官方已经给出了yolov5的案例,我们就可以先参照这里,对自己的模型,进行转换了。

在这里插入图片描述

三、模型转换

下面以paddlepaddle OCR任务为例,简单的给个模型转换的过程和代码,如下

3.1、onnx 2 rknn

import cv2
from rknn.api import RKNN
import numpy as np
import onnxruntime as ort

"""
/home/hkl/project/ISSUES/RKNN_SDK/RK_NPU_SDK_1.3.0/rknpu2_1.3.0/examples/rknn_paddlle_ocr
"""
onnx_model = 'onnx_github/repvgg_s.onnx' #onnx路径
save_rknn_dir = 'onnx_github/repvgg_s.rknn'#rknn保存路径

def norm(img):
    mean = 0.5
    std = 0.5
    img_data = (img.astype(np.float32)/255 - mean) / std
    return img_data

if __name__ == '__main__':

    # Create RKNN object
    rknn = RKNN(verbose=True)

    # image = np.random.randn(1,3,32,448).astype(np.float32)             # 创建一个np数组,分别用onnx和rknn推理看转换后的输出差异,检测模型输入是1,3,640,640 ,识别模型输入是1,3,32,448
    image = cv2.imread('./imgs_data_rec/14.jpg')
    image = cv2.resize(image, (448, 32))
    image = np.expand_dims(image, axis=0)  # 添加批次维度
    image = np.transpose(image, (0, 3, 1, 2))  # 重新排列通道维度

    onnx_net = ort.InferenceSession(onnx_model)                        # onnx推理

    onnx_infer = onnx_net.run(None, {'input': norm(image)})                    # 如果是paddle2onnx转出来的模型输入名字默认是 "x"

    # pre-process config
    print('--> Config model')
    rknn.config(mean_values=[[127.5, 127.5, 127.5]], std_values=[[127.5, 127.5, 127.5]],
                target_platform='rk3588')  # 需要输入为RGB#####需要转化一下均值和归一化的值
    print('done')

    # Load ONNX model
    print('--> Loading model %s' % onnx_model)
    ret = rknn.load_onnx(model=onnx_model)
    if ret != 0:
        print('Load %s failed!' % onnx_model)
        exit(ret)
    print('done')
    # Build model
    print('--> Building model')
    # rknn.build(do_quantization=False)
    ret = rknn.build(do_quantization=True, dataset='datasets_rec.txt')
    #do_quantization是否对模型进行量化,datase量化校正数据集,pre_compil模型预编译开关,预编译 RKNN 模型可以减少模型初始化时间,但是无法通过模拟器进行推理或性能评估
    if ret != 0:
        print('Build net failed!')
        exit(ret)
    print('done')

    # Export RKNN model
    print('--> Export RKNN model')
    ret = rknn.export_rknn(save_rknn_dir)
    if ret != 0:
        print('Export rknn failed!')
        exit(ret)

    ret = rknn.init_runtime(target='rk3588', device_id="1f2bb12b381c00c1")            # 两个参数分别是板子型号和device_id,device_id在双头usb线连接后通过 adb devices查看
    if ret != 0:
        print('init runtime failed.')
        exit(ret)
    print('done')

    # Inference
    print('--> Running model')
    outputs = rknn.inference(inputs=[image])

    # perf
    print('--> Begin evaluate model performance')
    perf_results = rknn.eval_perf(inputs=[image])              # 模型评估
    print('done')
    print()

    print("->>模型前向对比!")
    print("--------------rknn outputs--------------------")
    print(outputs[0])
    print()

    print("--------------onnx outputs--------------------")
    print(onnx_infer[0])
    print()

    std = np.std(outputs[0]-onnx_infer[0])
    print(std)                                # 如果这个值比较大的话,说明模型转换后不太理想

    rknn.release()

参考链接:【工程部署】手把手教你在RKNN上部署OCR服务(上)

3.2、 rknn predict

这里的预测部分,是以paddle OCR官方给出的案例。对于瑞芯微官方的文档里面,也有好几个案例可供参考,其中提供了python的版本和C++的版本,建议去那里看看。

import numpy as np
import cv2
from rknn.api import RKNN
import torch
# from label_convert import CTCLabelConverter

class CTCLabelConverter(object):
    """ Convert between text-label and text-index """

    def __init__(self, character):
        # character (str): set of the possible characters.
        dict_character = []
        with open(character, "rb") as fin:
            lines = fin.readlines()
            for line in lines:
                line = line.decode('utf-8').strip("\n").strip("\r\n")
                dict_character += list(line)
        # dict_character = list(character)

        self.dict = {}
        for i, char in enumerate(dict_character):
            # NOTE: 0 is reserved for 'blank' token required by CTCLoss
            self.dict[char] = i + 1
        #TODO replace ‘ ’ with special symbol
        self.character = ['[blank]'] + dict_character+[' ']  # dummy '[blank]' token for CTCLoss (index 0)

    def encode(self, text, batch_max_length=None):
        """convert text-label into text-index.
        input:
            text: text labels of each image. [batch_size]
        output:
            text: concatenated text index for CTCLoss.
                    [sum(text_lengths)] = [text_index_0 + text_index_1 + ... + text_index_(n - 1)]
            length: length of each text. [batch_size]
        """
        length = [len(s) for s in text]
        # text = ''.join(text)
        # text = [self.dict[char] for char in text]
        d = []
        batch_max_length = max(length)
        for s in text:
            t = [self.dict[char] for char in s]
            t.extend([0] * (batch_max_length - len(s)))
            d.append(t)
        return (torch.tensor(d, dtype=torch.long), torch.tensor(length, dtype=torch.long))

    def decode(self, preds, raw=False):
        """ convert text-index into text-label. """
        preds_idx = preds.argmax(axis=2)
        preds_prob = preds.max(axis=2)
        result_list = []
        for word, prob in zip(preds_idx, preds_prob):
            if raw:
                result_list.append((''.join([self.character[int(i)] for i in word]), prob))
            else:
                result = []
                conf = []
                for i, index in enumerate(word):
                    if word[i] != 0 and (not (i > 0 and word[i - 1] == word[i])):
                        result.append(self.character[int(index)])
                        conf.append(prob[i])
                result_list.append((''.join(result), conf))
        return result_list


def narrow_224_32(image, expected_size=(224,32)):
    ih, iw = image.shape[0:2]
    ew, eh = expected_size
    scale = eh / ih
    # scale = eh / max(iw,ih)
    nh = int(ih * scale)
    nw = int(iw * scale)
    image = cv2.resize(image, (nw, nh), interpolation=cv2.INTER_CUBIC)
    top = 0
    bottom = eh - nh - top
    left = 0
    right = ew - nw - left

    new_img = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(114, 114, 114))
    return new_img


if __name__ == '__main__':
    dict_path = r"./onnx_github/dict_text.txt"
    converter = CTCLabelConverter(dict_path)
    # Create RKNN object
    rknn = RKNN()

    ret = rknn.load_rknn('onnx_github/rec.rknn')

    # Set inputs
    img = cv2.imread('imgs_data_rec/21.jpg')
    # img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # origin_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    image = narrow_224_32(img, expected_size=(448, 32))

    # init runtime environment
    print('--> Init runtime environment')
    ret = rknn.init_runtime(target='rk3588',device_id="1f2bb12b381c00c1")
    if ret != 0:
        print('Init runtime environment failed')
        exit(ret)
    print('done')

    # Inference
    print('--> Running model')
    outputs = rknn.inference(inputs=[image])

    # perf
    print('--> Begin evaluate model performance')
    perf_results = rknn.eval_perf(inputs=[image])
    print('done')

    feat_2 = torch.from_numpy(outputs[0])
    print(feat_2.size())

    txt = converter.decode(feat_2.detach().cpu().numpy())
    print(txt)
    cv2.imshow("img",img)
    cv2.waitKey()
    rknn.release()

四、总结

关于瑞芯微RK3588芯片的第一篇文章,主要就是记录了一些基本的环境按照,Linux系统与板子的联调,以及转换模型,是现在虚拟环境,和板端的运行。

第一阶段肯定还是先实操瑞芯微官方给的一些案例,这样出现了问题,可以及时的找到解决方法。下一篇文章就是根据官方给的yolo v5的案例,完整的记录模型转换中的步骤,和遇到的问题。


最后,如果您觉得本篇文章对你有帮助,欢迎点赞 👍,让更多人看到,这是对我继续写下去的鼓励。本系列文章都不会设定 VIP 和付费,如果能再点击下方的红包打赏,给博主来一杯咖啡,那就太好了。💪

  • 12
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值