当前位置: 首页 > ops >正文

【YOLOv8-obb部署至RK3588】模型训练→转换RKNN→开发板部署

在这里插入图片描述
已在GitHub开源与本博客同步的YOLOv8_RK3588_object_obb
项目,地址:https://github.com/A7bert777/YOLOv8_RK3588_object_obb
详细使用教程,可参考README.md或参考本博客第七章 模型部署

一、项目回顾

博主之前有写过YOLO11、YOLOv8目标检测&图像分割、关键点检测、YOLOv10目标检测、MoblieNetv2图像分类的模型训练、转换、部署文章,感兴趣的小伙伴可以了解下:
【YOLO11部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv8-pose部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv8部署至RK3588】模型训练→转换rknn→部署全流程
【YOLOv8seg部署RK3588】模型训练→转换rknn→部署全流程
【YOLOv10部署RK3588】模型训练→转换rknn→部署流程
【MobileNetv2图像分类部署至RK3588】模型训练→转换rknn→部署流程
YOLOv8n部署RK3588开发板全流程(pt→onnx→rknn模型转换、板端后处理检测)

最近做了一个YOLOv8-obb的关键点检测项目,涉及数据集标注,模型训练、转ONNX、转RKNN量化以及RK3588开发板调试部署,查了下CSDN上暂未有关于YOLOv8-obb在RK系列开发板的免费详细教程,遂开此文,相互学习。

二、文件梳理

YOLOv8-obb的训练、转换、部署所需四个项目文件:
第一个:YOLOv8-obb模型训练项目文件(链接在此),
第二个:瑞芯微仓库中YOLOv8的pt转onnx项目文件(链接在此);
第三个:用于在虚拟机中进行onnx转rknn的虚拟环境配置项目文件(链接在此);
第四个:在开发板上做模型部署的项目文件(链接在此)。

注:
1.第四个项目文件中的内容很多,里面涉及到rknn模型转换以及模型部署的所有内容,所以该文件在模型转换中也要与第三个文件配合使用。
2.我上面的四个链接都是已经链接到项目的对应版本了,打开链接后直接git clone或者download zip即可。
这四个文件的版本如下:
第一个模型训练文件是v8.3之前的版本,因为v8.3之后就是YOLO11了,此处选择的是v8.2.82
第二个ONNX转换文件为默认main分支
第三个文件rknn-toolkit2为v2.1.0
第四个文件rknn_model_zoo也用v2.1.0(rknn-toolkit2尽量和rknn_model_zoo版本一致)
如图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、YOLOv8-obb数据集标注

大家比较熟悉的是常规的固定角度矩形框数据集标定,即YOLOv8常规检测的格式,而obb是在此基础上的微调,在标签文件中加入更多参数让矩形框能够旋转一定的角度。YOLOv8等数据集的标定工具一般是labelme或者是在线标定网站Make Sense,而obb格式的数据集的标定工具常规选用roLabelImg工具,具体使用方法如下:

1. 在PC端安装roLabelImg

先在PC端安装好anaconda和Anaconda prompt,然后创建若任一conda环境,以我之前使用的labelme环境为例,激活环境后,进行如下操作打开标定工具:

git clone https://github.com/cgvict/roLabelImg
cd roLabelImg
pip install pyqt5 lxml
pyrcc5 -o resources.py resources.qrc  # 编译资源文件
python roLabelImg.py  # 启动

标定工具如下所示:点击左侧的“Create RotatedRBox”,创建矩形框,如下图所示:
在这里插入图片描述
然后通过zxcv四个键调整旋转框角度:
在这里插入图片描述

2. roLabelImg生成文件转换txt

标定生成的是xml文件,结构如下所示:

<object><type>robndbox</type><name>car</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><robndbox><cx>1970.4288</cx><cy>1428.5181</cy><w>154.8035</w><h>331.4003</h><angle>0.29</angle></robndbox></object>

然而,YOLOv8-obb的数据集要求的是txt文档为如下格式:
在这里插入图片描述
即“目标类别ID, x1, y1, x2, y2, x3, y3, x4, y4”
因此需要将roLabelImg标定生成的xml文件转换成符合要求的txt文件,可通过以下rotlabellmgxml2yolov8obbtxt.py脚本批量化自动转换,只需要修改xml_dir 和output_dir 参数即可:

rotlabellmgxml2yolov8obbtxt.py:

import os
import xml.etree.ElementTree as ET
import math
import numpy as npdef convert_rotated_rect(cx, cy, w, h, angle):"""将旋转矩形(cx,cy,w,h,angle)转换为四个角点坐标"""# 计算旋转矩阵cos_a = math.cos(angle)sin_a = math.sin(angle)# 计算未旋转前的四个角点(相对于中心)half_w = w / 2half_h = h / 2corners = np.array([[-half_w, -half_h],[ half_w, -half_h],[ half_w,  half_h],[-half_w,  half_h]])# 应用旋转rot_matrix = np.array([[cos_a, -sin_a], [sin_a, cos_a]])rotated_corners = np.dot(corners, rot_matrix.T)# 加上中心坐标,得到绝对坐标abs_corners = rotated_corners + np.array([cx, cy])# 将四个角点展平为一维数组 [x1,y1,x2,y2,x3,y3,x4,y4]return abs_corners.flatten().tolist()# 设置路径
xml_dir = "/xxx/Others/xml"  # 替换为你的XML文件夹路径
output_dir = "/xxx/Others/txt"  # 替换为输出文件夹路径# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)# 遍历所有XML文件
for xml_file in os.listdir(xml_dir):if not xml_file.endswith(".xml"):continuexml_path = os.path.join(xml_dir, xml_file)try:tree = ET.parse(xml_path)root = tree.getroot()# 获取图像尺寸size = root.find("size")img_w = float(size.find("width").text)img_h = float(size.find("height").text)# 准备输出TXT文件txt_filename = os.path.splitext(xml_file)[0] + ".txt"txt_path = os.path.join(output_dir, txt_filename)with open(txt_path, "w") as f:# 遍历所有objectfor obj in root.findall("object"):robndbox = obj.find("robndbox")cx = float(robndbox.find("cx").text)cy = float(robndbox.find("cy").text)w = float(robndbox.find("w").text)h = float(robndbox.find("h").text)angle = float(robndbox.find("angle").text)# 转换旋转矩形为四个角点corners = convert_rotated_rect(cx, cy, w, h, angle)# 归一化坐标 (除以图像宽高)并确保在0-1范围内normalized_corners = []for i in range(0, len(corners), 2):x = max(0.0, min(1.0, corners[i] / img_w))  # 限制在0-1之间y = max(0.0, min(1.0, corners[i+1] / img_h))  # 限制在0-1之间normalized_corners.extend([x, y])# 格式化为字符串: class_index x1 y1 x2 y2 x3 y3 x4 y4# 类别索引为0 (只有pipeline一类)# 先写整数类别索引,然后写浮点数坐标line_str = "0 " + " ".join([f"{x:.6f}" for x in normalized_corners])f.write(line_str + "\n")print(f"已处理: {xml_file} -> {txt_filename}")except Exception as e:print(f"处理 {xml_file} 时出错: {str(e)}")print(f"转换完成! 共处理 {len(os.listdir(xml_dir))} 个XML文件。")

在转换完成后,即可开始PyTorch的YOLOv8-obb模型训练。

四、YOLOv8-obb模型训练

YOLOv8-obb的模型训练和此前的YOLOv8、YOLOv10基本一致。
先从训练环境搭建开始,YOLOv8-pose的环境搭建非常简单,不需要再pip install -r requirements.txt和pip install -e .了。

步骤如下:
1. conda create -n yolov8 python=3.9
2. conda activate yolov8
3. pip install ultralytics

配置好环境后,把另外一些必要的文件准备好:
在这里插入图片描述
自己创建一个train_obb.py脚本,放在和ultralytics文件夹同级位置,然后把ultralytics文件夹中的yolov8-obb.yaml文件复制出来,在把yolov8n.pt放进来,因为训练开始前会用预训练权重进行Automatic Mixed Precision(AMP自动混合精度)check,如果你没放预训练权重,终端会自己下载,但是速度较慢,所以先提前放置过来。各配置文件内容如下所示:

train_obb.py:

from ultralytics import YOLO# 加载模型
model = YOLO("yolov8-obb.yaml")  # 从头开始构建新模型
#model = YOLO("yolov8n.pt")  # 加载预训练模型(推荐用于训练)# Use the model
results = model.train(data="linshi.yaml", epochs=300, batch=8)  # 训练模型

yolov8-obb.yaml:

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 Oriented Bounding Boxes (OBB) model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parameters
nc: 1 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'# [depth, width, max_channels]n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPss: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPsm: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPsl: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPsx: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs# YOLOv8.0n backbone
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4- [-1, 3, C2f, [128, True]]- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8- [-1, 6, C2f, [256, True]]- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16- [-1, 6, C2f, [512, True]]- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32- [-1, 3, C2f, [1024, True]]- [-1, 1, SPPF, [1024, 5]] # 9# YOLOv8.0n head
head:- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 6], 1, Concat, [1]] # cat backbone P4- [-1, 3, C2f, [512]] # 12- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 4], 1, Concat, [1]] # cat backbone P3- [-1, 3, C2f, [256]] # 15 (P3/8-small)- [-1, 1, Conv, [256, 3, 2]]- [[-1, 12], 1, Concat, [1]] # cat head P4- [-1, 3, C2f, [512]] # 18 (P4/16-medium)- [-1, 1, Conv, [512, 3, 2]]- [[-1, 9], 1, Concat, [1]] # cat head P5- [-1, 3, C2f, [1024]] # 21 (P5/32-large)- [[15, 18, 21], 1, OBB, [nc, 1]] # OBB(P3, P4, P5)

linshi.yaml:

train: /xxx/Dataset/previous_dataset/car_100_obb/images/train
val: /xxx/Dataset/previous_dataset/car_100_obb/images/val
# number of classes
nc: 1
# class names
names: ['car']

执行train_obb.py文件:python train_obb.py,训练完成后,得到所要的besr模型,我将其重命名为car_100_best_300epoch_relu.pt,并复制到当前目录,如下所示:

在这里插入图片描述

这里说一下为什么有人是用脚本训练,有人是用yolo task=detect mode=train…训练,二者区别如下:

两种训练命令的区别机制

  1. ​​python train.py(问题方式)​​
    ​​执行路径​​:显式调用当前目录的脚本
    ​​模块加载​​:触发Python的本地模块导入机制,优先加载同目录下的ultralytics
    ​​典型表现​​:修改conv.py等文件立即生效,验证了本地代码被调用
  2. ​​yolo task=detect mode=train …(正确方式)​​
    ​​执行路径​​:通过环境中的​​入口点脚本​​调用
    安装ultralytics包时自动生成yolo命令行工具
    存储路径示例:YOLO11/bin/yolo
    ​​模块加载​​:直接调用环境安装的包,完全隔离本地目录干扰
    ​​验证方法​​:执行 which yolo 将返回环境路径(如/root/anaconda3/envs/YOLO11/bin/yolo

整理如下:
在这里插入图片描述
因为博主需要频繁修改ultralytics的源码,因此选择使用脚本训练,如果读者喜欢标准训练/部署,可用yolo命令行训练,但还是建议脚本训练更加有可操作性。

五、PT转ONNX

把前面训练得到的car_100_best_300epoch_relu.pt模型放置到第二个项目文件中
在这里插入图片描述
注:放yolov8n.pt是为了避免自动下载模型,因为要做AMP自动混合精度检测,提前放模型进去,避免龟速下载。

调整 ./ultralytics/cfg/default.yaml 中 model 文件路径,默认为 yolov8n.pt,若自己训练模型,请调接至对应的路径。支持检测、分割、姿态、旋转框检测模型。我修改的结果如下:
在这里插入图片描述

修改完default.yaml后,在终端输入:
export PYTHONPATH=./
python ./ultralytics/engine/exporter.py
执行完毕后,会生成 模型,如下所示:
在这里插入图片描述
会在当前路径下生成一个同名的onnx模型:car_100_best_300epoch_relu.onnx
注意:我转换onnx时使用的环境仍为yolov8,和训练模型时是同一个环境

★★★
这里要着重说一下,如果你之前在配置yolov8的conda环境时,命令如下:pip install ultralytics,那么你的环境列表中应该有当前最新版本的ultralytics,输入conda list -n xxx,查询你现在环境中的ultralytics版本,博主此时的最新版本为8.3.112
在这里插入图片描述
在这里插入图片描述
这是合理的,因为在瑞芯微提供的ultralytics_yolov8中,他们使用的ultralytics即ultralytics文件夹是8.2.82的,而当前工作目录下的 ultralytics 8.2.82文件夹覆盖了已安装的包版本,所以如果你在转ONNX模型时,终端显示若不是8.2.82版本的ultralytics,则转换失败
在这里插入图片描述

所以说,瑞芯微统一提供了一个使用8.2.82版本的ultralytics(并在此基础上做了微调)供模型转换。

得到onnx模型后,用netron工具打开,看输入输出是否正常:
在这里插入图片描述
附一张图,这是瑞芯微在github的官方rknn_model_zoo仓库里的yolov8n-obb.onnx模型的示例:
在这里插入图片描述
如果转出的onnx模型和官方示例的onnx模型输出不同,则模型有问题,不用再去转rknn了,一定会有问题。

六、ONNX转RKNN

在进行这一步的时候,如果你是在云服务器上运行,请先确保你租的卡能支持RKNN的转换运行。博主是在自己的虚拟机中进行转换。
先安装转换环境
这里我们先conda create -n rknn210 python=3.8创建环境,创建完成如下所示:
在这里插入图片描述

现在需要用到rknn-toolkit2-2.1.0文件。
进入rknn-toolkit2-2.1.0\rknn-toolkit2-2.1.0\rknn-toolkit2\packages文件夹下,看到如下内容:
在这里插入图片描述
在终端激活环境,在终端输入pip install -r requirements_cp38-2.1.0.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
然后再输入pip install rknn_toolkit2-2.1.0+708089d1-cp38-cp38-linux_x86_64.whl
然后,我们的转rknn环境就配置完成了。
现在要进行模型转换,其实大家可以参考rknn_model_zoo-2.1.0\examples\yolov8_pose下的README指导进行转换:
在这里插入图片描述
这里我也详细再说一遍转换流程:先进入rknn_model_zoo-2.1.0\examples\yolov8_obb\python文件夹,先打开convert.py,进行适配参数修改:
在这里插入图片描述
修改完成后,在yolov8_obb/model下放自己的ONNX模型,然后在yolov8_obb/python下启动终端并启动环境,执行命令:python convert.py ../model/car_100_best_300epoch_relu.onnx rk3588
在这里插入图片描述
然后在yolov8_obb/model下可以看到在刚才量化转换得到的rknn模型:
在这里插入图片描述
然后把rknn模型复制到win下,用netron模型打开,看模型结构是否符合要求:
在这里插入图片描述

七、模型部署

如果前面流程都已实现,模型的结构也没问题的话,则可以进行最后一步:模型端侧部署。
我已经帮大家做好了所有的环境适配工作,科学上网后访问博主GitHub仓库:YOLOv8_RK3588_object_obb

重点:请大家举手之劳,帮我的仓库点个小星星
点了小星星的同学可以免费帮你解决转模型与部署过程中遇到的问题。

在这里插入图片描述

git clone后把项目复制到开发板上,按如下流程操作:
①:cd build,删除所有build文件夹下的内容
②:cd src 修改main.cc,修改main函数中的如下三个内容,将这三个参数改成自己的绝对路径:
在这里插入图片描述
③:cd src 修改postprocess.cc下的txt标签的相对路径:
在这里插入图片描述

解释一下,这个标签路径中的内容如下所示:
在这里插入图片描述
其实就是你在训练yolov8时在yaml配置文件中的类别名

④修改include/postprocess.h 中的宏 OBJ_CLASS_NUM:
在这里插入图片描述
⑤:把你之前训练好并已转成RKNN格式的模型放到YOLOv8_RK3588_object_obb/model文件夹下,然后把你要检测的所有图片都放到YOLOv8_RK3588_object_obb/inputimage下。
在运行程序后,生成的结果图片在YOLOv8_RK3588_object_obb/outputimage下

⑥:进入build文件夹进行编译

cd build
cmake ..
make

在build下生成可执行文件文件:rknn_yolov8obb_demo
在build路径下输入

./rknn_yolov8obb_demo

运行结果如下所示:
在这里插入图片描述
原inputimage文件夹下的图片:
在这里插入图片描述请添加图片描述
请添加图片描述

在执行完./rknn_yolov8obb_demo后在outputimage下的输出结果图片:
在这里插入图片描述
请添加图片描述
请添加图片描述
根据输出结果,可以看到检测效果还是可以的。
上述即博主此次更新的YOLOv8-obb部署RK3588,包含PT转ONNX转RKNN的全流程步骤,欢迎交流!

http://www.xdnf.cn/news/15157.html

相关文章:

  • Jenkins+Gitee+Docker容器化部署
  • super task 事件驱动框架
  • 用AI做带货视频评论分析【Datawhale AI 夏令营】
  • 冒泡排序和快速排序
  • 「Linux命令基础」文本模式系统关闭与重启
  • 【C/C++】动态内存分配:从 C++98 裸指针到现代策略
  • Linux操作系统之进程间通信:命名管道
  • 飞算JavaAI:给Java开发装上“智能引擎”的超级助手
  • vue入门学习教程
  • 车载诊断进阶篇 --- 关于网关转发性能引起的思考
  • 匿名函数作递归函数引用
  • uniapp制作一个视频播放页面
  • C++11中的std::minmax与std::minmax_element:原理解析与实战
  • WIFI协议全解析06:Beacon帧、Probe帧你必须懂,搞WiFi通信绕不开它们
  • 【理念●体系】Windows AI 开发环境搭建实录:六层架构的逐步实现与路径治理指南
  • SEQUENCE在RAC多实例开启CACHE的NEXTVAL数值乱序问题
  • 从代码学习深度强化学习 - PPO PyTorch版
  • Go语言WebSocket编程:从零打造实时通信利器
  • Linux操作系统从入门到实战:怎么查看,删除,更新本地的软件镜像源
  • 蔚来测开一面:HashMap从1.7开始到1.8的过程,既然都解决不了并发安全问题,为什么还要进一步解决环形链表的问题?
  • Spring的事务控制——学习历程
  • HarmonyOS NEXT端云一体化开发初体验
  • [Dify] -基础入门4-快速创建你的第一个 Chat 应用
  • 牛客:HJ17 坐标移动[华为机考][字符串]
  • Leaflet面试题及答案(1-20)
  • [实战]调频三角波和锯齿波信号生成(完整C代码)
  • 深入浅出:什么是MCP(模型上下文协议)?
  • 力扣网编程134题:加油站(双指针)
  • C++中柔性数组的现代化替代方案:从内存布局优化到标准演进
  • Debian:从GNOME切换到Xfce