【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环境为例,激活环境后,进行如下操作打开标定工具:

bash 复制代码
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文件,结构如下所示:

cpp 复制代码
<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

python 复制代码
import os
import xml.etree.ElementTree as ET
import math
import numpy as np

def 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 / 2
    half_h = h / 2
    corners = 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"):
        continue
        
    xml_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:
            # 遍历所有object
            for 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:

python 复制代码
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:

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 GFLOPs
  s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [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:

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文件夹进行编译

bash 复制代码
cd build
bash 复制代码
cmake ..
bash 复制代码
make

在build下生成可执行文件文件:rknn_yolov8obb_demo

在build路径下输入

bash 复制代码
./rknn_yolov8obb_demo

运行结果如下所示:

原inputimage文件夹下的图片:

在执行完./rknn_yolov8obb_demo后在outputimage下的输出结果图片:


根据输出结果,可以看到检测效果还是可以的。

上述即博主此次更新的YOLOv8-obb部署RK3588,包含PT转ONNX转RKNN的全流程步骤,欢迎交流!

相关推荐
豆是浪个2 小时前
Linux(Centos 7.6)命令详解:usermod
linux·运维·centos
极地星光2 小时前
网络检测:Linux下实时获取WiFi与热点状态
linux·网络
屁股割了还要学3 小时前
快速过一遍Python基础语法
开发语言·python·学习·青少年编程
kikikidult3 小时前
Ubuntu20.04运行openmvg和openmvs实现三维重建(未成功,仅供参考)
人工智能·笔记·ubuntu·计算机视觉
189228048614 小时前
NW728NW733美光固态闪存NW745NW746
大数据·服务器·网络·人工智能·性能优化
大模型最新论文速读4 小时前
模拟注意力:少量参数放大 Attention 表征能力
人工智能·深度学习·机器学习·语言模型·自然语言处理
武当豆豆4 小时前
C++编程学习阶段性总结
开发语言·c++
lishaoan775 小时前
用TensorFlow进行逻辑回归(二)
人工智能·tensorflow·逻辑回归
慌ZHANG5 小时前
智慧气象新范式:人工智能如何重构城市级气象服务生态?
人工智能
Eumenidus5 小时前
使用ESM3蛋白质语言模型进行快速大规模结构预测
人工智能·语言模型·自然语言处理