十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YOLOv8-OBB的生姜种芽朝向检测与部署实践

基于YOLOv8-OBB的生姜种芽朝向检测与部署实践 简介这是发表在《农业工程学报》2021年第37卷第1期的学术论文PDF面向农业机械化、深度学习目标检测及智能农机装备领域的研究者、研究生与工程技术人员聚焦解决生姜机械化播种中种芽快速识别与朝向判定难题。论文以YOLO v3为基线融合Mosaic数据增强、DIoU边框回归损失函数以及基于IoU的K-means聚类生成9个先验框并完成壮芽选取与朝向判断实验显示平均精度达98.2%F1为94.9%GPU加速下检测速度可达112帧/s较原YOLO v3分别提升1.5%和4.4%。全包为1个PDF文件共4.64MB除完整摘要、引言、材料与方法、结果与分析、结论外还附有中图分类号、文献标志码、基金项目及引文格式信息可作为论文写作参考文献和深度学习农业应用方法设计的重要参考。目前已有147人学习下载适合需要搭建图像识别实验方案、优化目标检测损失函数或撰写农业工程类论文的研究者。1. 为什么水平目标检测不足以完成种芽朝向判定生姜种植领域存在一个细分但极其刚性的需求播种时要求种块的芽尖朝上角度偏差过大会直接影响出苗率和后续产量。传统人工摆放效率低且难以保证一致性自动化的第一步就是先用机器视觉把“芽在哪里、朝向哪里”读出来。视觉系统面对的是一批刚从窖藏里取出、带着泥土、形态长短不一的姜种反差大、遮挡多靠传统的阈值分割加轮廓分析很难稳定工作。深度学习方案把这个问题拆成了两个子任务定位种芽并识别类别同时估计芽所在直线与图像水平方向的夹角。种芽呈细长条状并且可能以任意角度出现在画面中普通目标检测输出的水平矩形框axis-aligned bounding box在这里会碰到一个本质性问题水平框的面积会随着种芽角度的变化而剧烈变动即使种芽中心位置不变框内也会混入大量非目标区域的背景像素导致特征混淆和回归困难。举一个直观例子一根斜 45° 的种芽用水平的四边形包住它四个顶角基本都是背景某些角度下包络面积甚至是物体本身的三四倍。这会让网络在训练时很难学到“不同角度的芽本质上是同一物体”的概念最终表现为漏检多、框不准。更严重的是朝向不能用水平框的宽高比来间接推导——因为宽高比在角度变化时并不单调且对 0° 与 180° 完全不可区分。因此必须采用旋转目标检测Oriented Bounding BoxOBB思路让输出直接携带角度信息。此时的输出一个五元组——中心坐标 (x, y)、宽度 w、高度 h以及旋转角 θ。宽度指芽体沿长轴的尺寸高度指垂直于长轴的尺寸θ 表示长轴与图像横轴之间的夹角。视觉系统拿到这个五元组就可以换算成播种机械手需要的下压方向和角度。这里的 θ 是循环量0° 和 180° 在物理意义上等价芽尖朝向无所谓头尾但在数值上差了一百八十度。如何设计损失函数使这个循环特性不至于导致训练震荡是整个朝向判定落地过程中最需要处理的一个细节。后面章节会逐步展开工程实现。2. 数据采集与旋转框标注搭建可用的生姜种芽数据集训练一个 OBB 检测器前提是拥有一批带旋转标注的样本。种芽数据集不像 COCO 那样可以公开获取一般需要自采。采集环境的搭建直接影响后续打标和训练的代价建议优先在固定工位完成而不是在田间随机拍摄。2.1 图像采集环境参数推荐使用工业面阵相机搭配环形无影光源分辨率不低于 1200 万像素保证单个种芽在图像中至少占 100×40 像素。相机垂直俯拍工作距离约 500 毫米。种块平铺在深色传送带上背景尽量抹平纹理以降低目标检测的干扰。光源用白色或淡蓝色低角度环形光避免形成高光反射。为了避免种芽相互堆叠遮挡单幅画面中放置 4~6 块姜种相互间隔 50 像素以上。提示采集时以需要覆盖的典型角度为首要约束。尽量均匀覆盖 0°~180° 的朝向分布而不是自然随机摆放。手动摆盘时把部分种芽横卧、部分斜插、部分直立这样角度直方图才接近均匀否则训练出来的模型对某一个角度区间的识别能力会明显偏弱。2.2 标注工具与格式转换通用检测标注工具 LabelImg 只能标水平框不能直接用于本任务。推荐使用 roLabelImg该工具支持在一个矩形内继续拉旋转框保存为 TXT 格式每行表示一个目标的八点坐标和类别名类名按左边置顶的顶点顺序排列。标注界面里要注意先画外接矩形再拖动右侧边的旋转锚点调整角度保存后得到形如x1 y1 x2 y2 x3 y3 x4 y4 类别索引的文本。Ultralytics YOLO 生态的 OBB 训练并不直接使用八点坐标而是要求转换为cx, cy, w, h, angle五参数形式。下面这段 Python 脚本把 roLabelImg 输出批量转换成 Ultralytics OBB 格式。import os, math def polygon_to_obb(points): 将 4 个角点 (x1,y1, x2,y2, x3,y3, x4,y4) 转换为中心点、长宽和弧度角。 x1, y1, x2, y2, x3, y3, x4, y4 points cx (x1 x2 x3 x4) / 4.0 cy (y1 y2 y3 y4) / 4.0 # 计算两条相邻边向量 dx1, dy1 x2 - x1, y2 - y1 dx2, dy2 x3 - x2, y3 - y2 len1 math.hypot(dx1, dy1) len2 math.hypot(dx2, dy2) if len1 len2: w, h len1, len2 angle math.atan2(dy1, dx1) else: w, h len2, len1 angle math.atan2(dy2, dx2) # 归一化到 [-pi/2, pi/2)避免角度跳变 if angle math.pi / 2: angle - math.pi elif angle -math.pi / 2: angle math.pi return cx, cy, w, h, angle def convert_roLabelImg_to_yolo(label_dir, out_dir, img_w, img_h): os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: lines f.readlines() out_lines [] for line in lines: parts list(map(float, line.strip().split())) # roLabelImg 保存的是 8 个坐标 1 个类别索引 coords parts[:8] cls_id int(parts[8]) cx, cy, w, h, ang polygon_to_obb(coords) # 归一化到 0~1与 YOLO 系列保持一致 out_lines.append(f{cls_id} {cx/img_w:.6f} {cy/img_h:.6f} f{w/img_w:.6f} {h/img_h:.6f} {ang:.6f}\n) with open(os.path.join(out_dir, fname), w) as f: f.writelines(out_lines)注意角度归一化到[-pi/2, pi/2)是非常重要的。种芽长轴方向是一条无向直线用区间长度 pi 的弧度表示即可这样能天然避免 180° 跳变。若使用[0, pi)区间训练过程中预测值靠近 0 和靠近 pi 的样本极容易被误判为差异巨大损失函数难以收敛。2.3 数据集组织与 YAML 配置转换后的标签文件与图像文件按以下目录结构组织dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml至少包含路径、类别名和类别数量。其中 path 建议使用绝对路径避免在不同机器上运行时因相对路径问题找不到文件。# data.yaml path: /home/user/ginger_dataset train: images/train val: images/val nc: 1 names: 0: ginger_sprout2.4 数据增强对角度标签的影响训练阶段通常开启 Mosaic、随机翻转、随机旋转、HSV 扰动等增强策略。但旋转增强对 OBB 任务存在一个隐蔽陷阱如果对图像实施了任意角度的旋转标注框的五参数必须同步重新计算而不仅仅是图像矩阵旋转后直接取原角度。Ultralytics 的 OBB 训练内部已自动处理数据增强时的坐标与角度变换这是它便于工程落地的原因之一也是不建议在没有充分校验的情况下自行编写增强模块的原因。翻转方面水平翻转会让角度符号反转垂直翻转会让角度变为pi - angle再进行归一化。这些变换规则看起来简单但任何一步遗漏都会造成标注与图像不匹配训练 loss 下降缓慢、val mAP 几乎为零。如果在自建 pipeline 中实现先做仿真验证再进训练。最终标注数据量建议每类不低于 300 张图像每张含 4~6 个实例总量在 1200~1800 个标注框之间。种芽形态变化有限这个规模足以让深度学习模型收敛到不错的精度过多采集反而浪费人力。3. 模型主体基于 YOLOv8-OBB 的种芽检测与角度回归3.1 选择 YOLOv8-OBB 的理由在 OBB 检测方向可选的框架包括旋转 Faster R-CNN、RoI Transformer、Oriented R-CNN、RTMDet-R 等。生产环境往往更看重训练效率和部署便捷性YOLOv8-OBB 在工程社区生态成熟度较高同一个仓库同时支持数据处理、训练、导出和 C/Python 部署能显著缩短从样本到实机的链路。YOLOv8-OBB 的结构由三部分构成CSPDarknet 骨架负责提取多尺度特征PAN-FPN 特征金字塔将高层语义与低层空间细节融合最后的检测头同时输出分类概率、框回归值和角度值。检测头输出的角度是一个循环变量因此在损失函数上做了特殊处理仅框回归分支使用 ProbIoU 损失角度分支也直接参与该损失的计算。ProbIoU 的思想是将旋转框替换为二维高斯分布通过巴氏距离度量两个框的相似度这样损失值对角度变化是连续可微的同时天然规避了角度周期性带来的不连续问题。3.2 训练命令与关键参数Ultralytics 封装了完整的训练流程训练指令十分简洁yolo obb train \ data/home/user/ginger_dataset/data.yaml \ modelyolov8n-obb.yaml \ epochs150 \ imgsz1024 \ batch16 \ lr00.001 \ lrf0.01 \ optimizerAdamW \ device0model参数可以选择预训练配置文件。这里使用yolov8n-obb.yaml它对应的是 nano 级别网络适合验证数据标签是否有效如果精度不足可以切换到yolov8s-obb.yaml或yolov8m-obb.yaml。imgsz1024是针对于生姜种芽细长形态的策略——原图分辨率较高时直接缩小到 1024 可以保留芽尖细微结构。若显存有限可降到 800但建议不要低于 640否则小芽尖的特征几乎丢失。训练轮次设置 150 是一个平衡值。由于数据量不大epochs 过多容易过拟合过少则 ProbIoU 角度分支还无法收敛到预期的角度误差。3.3 推理输出与角度解读训练完成后加载模型进行推理输出的results对象包含 OBB 数据from ultralytics import YOLO model YOLO(/home/user/runs/obb/train/weights/best.pt) results model.predict(/home/user/dataset/images/val/test_001.jpg) for r in results: obb r.obb if obb is None: continue # xywhr 中 r 为弧度取值范围在 [-pi/2, pi/2) for box in obb.xywhr.cpu().numpy(): cx, cy, w, h, angle_rad box angle_deg angle_rad * 180 / 3.14159 print(f中心({cx:.1f}, {cy:.1f}) 长轴{w:.1f}px 短轴{h:.1f}px 角度{angle_deg:.1f}°)这里的角度是从长轴到图像横轴按逆时针方向的夹角。若需要芽尖朝下的实际摆放角度要在控制逻辑中再叠加一个固定的传感器标定偏移。最终送给机械臂的是绝对角度即整个系统的统一坐标系角度。4. 训练策略与调优损失权重、学习率与评估指标4.1 损失函数权重配置YOLOv8-OBB 的超参数存储在hyp.yaml文件中。默认配置中box损失权重为 7.5cls损失权重为 0.5dfl为 1.5。对于旋转框场景额外存在一个angle_loss_gain参数默认值通常为 0.7。这个参数控制角度回归在整个损失中的占比。生姜种芽识别特殊性在于种芽的短轴方向几乎不影响播种因此角度误差权重可以适度降低更聚焦中心点与长边的定位精度。但角度直接决定摆放精度如果角度误差超过 10° 就会导致种植角度不合格。实践中更常见的问题是角度分支收敛不稳定主要表现为训练后期 loss 在小范围内震荡验证集角度误差不再下降。针对这种情况建议将angle_loss_gain下调至 0.5同时提高box权重至 8.0。mmdetection 在旋转框任务中也验证了类似策略强化位置和尺寸回归弱化角度贡献反而提升了最终角度准确率。4.2 学习率与优化器数据量只有一千多张时使用AdamW配合lr00.001效果明显优于 SGD因为 AdamW 对学习率的敏感性较低对于这种增量式微调更为友好。训练结束后段lrf设为 0.01即最后一步学习率衰减到初始值的 1%帮助收敛到更光滑的局部最优。可以在训练过程中添加 warmup 阶段。Ultralytics 默认启用 3 个 epoch 的 warmup这是一般建议保留的设置因为刚加载预训练权重时特征分布与目标域差别较大直接使用高学习率可能导致特征崩塌。有人会问既然是细长目标是否应该提高输入分辨率到 1280实际操作后1024 到 1280 的精度提升通常不到 1% mAP但训练时间和显存占用接近翻倍。选择 1024 是在数据总量较小的前提下的最稳妥折中。4.3 评估指标与自定义角度误差除了 YOLO 自带的 mAP50、mAP75 之外需要额外关注角度误差的分布。mAP 衡量的是框定位的好坏mAP 高并不代表角度回归已经达标。编写一个简单的评估脚本统计推理结果中长轴中点与标注中心的距离误差以及角度误差考虑循环特性即取两个角度的最小差值import numpy as np def angle_error(pred_deg, gt_deg): diff np.abs(pred_deg - gt_deg) % 180 return min(diff, 180 - diff) errors [] # 假设 preds 和 gts 分别为预测和标注的角度列表 for pred, gt in zip(preds, gts): errors.append(angle_error(pred, gt)) errors np.array(errors) print(fMAE: {np.mean(errors):.2f}°) print(f角度误差 5° 的占比: {(errors 5).mean() * 100:.1f}%)这个脚本是评估质量的核心工具。一般以角度误差 5° 的样本占比达到 85% 以上为生产部署的下限。若该指标低于 80%首先排查标注环节是否存在角度定义不一致的问题其次再考虑增加训练数据中该角度区间的样本。4.4 典型调优路径参考问题现象可能原因调整手段角度误差稳定在 15° 以上框定位已准确角度分支权重过大或过小将angle_loss_gain设为 0.4~0.6 重新训练小目标漏检芽尖短小输入分辨率不足将imgsz从 640 提高到 1024训练曲线震荡、验证 mAP 上不去数据增强旋转过于激进关闭degrees90尝试degrees30近距离遮挡场景下漏检多数据样本覆盖不足单独补充遮挡和泥渍样本做局部增广5. 部署与性能优化从 PyTorch 到 TensorRT 的边缘计算加速训练不是终点真正的挑战在部署环节。姜种播种机通常需要在 0.2 秒内完成每帧图像的分析否则传送带速度受限产线节拍跟不上。训练好的 PyTorch 模型直接跑推理很难达到这一速度必须经过模型转换和硬件加速。5.1 导出 ONNX 与 TensorRT 引擎Ultralytics 提供了导出接口先导出 ONNX再构建 TensorRT 引擎。这里以 Nvidia Jetson 边缘设备为例做性能评估但流程在任意带 N 卡的机器上均可复用。# 导出 ONNX固定输入尺寸为 1024×1024 yolo export model/home/user/runs/obb/train/weights/best.pt formatonnx opset12 imgsz1024 # 利用 trtexec 构建 FP16 引擎 /usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --saveEnginebest_fp16.trt \ --fp16 \ --workspace4096 \ --minShapesimages:1x3x1024x1024 \ --optShapesimages:1x3x1024x1024 \ --maxShapesimages:4x3x1024x1024FP16 精度下模型的推理速度通常能提升 60%~80%而角度误差下降在 0.2° 以内完全可接受。5.2 生产环境推理代码TensorRT 引擎需要通过 ONNX Runtime 或 TensorRT Python API 加载。为了确保部署环境的可移植性推荐使用 ONNX Runtime 的 TensorRT 执行提供程序代码如下import onnxruntime as ort import numpy as np import cv2 sess ort.InferenceSession( best_fp16.trt, providers[TensorrtExecutionProvider, CUDAExecutionProvider] ) # 预处理缩放并归一化到模型输入区间 def preprocess(image, input_size1024): h, w image.shape[:2] scale min(input_size / h, input_size / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(image, (new_w, new_h)) canvas np.zeros((input_size, input_size, 3), dtypenp.float32) canvas[:new_h, :new_w] resized / 255.0 # YOLOv8 输入为 (N, C, H, W) tensor canvas.transpose(2, 0, 1)[None, ...].astype(np.float32) return tensor input_name sess.get_inputs()[0].name image cv2.imread(test_001.jpg) tensor preprocess(image) outputs sess.run(None, {input_name: tensor}) # 输出 [batch, 4 num_classes, n] 形状需要按通道解析 # 后续接 NMS 过滤得到最终 OBB这段代码中 NMS 部分省略因为ultralytics的推理封装已经内置了 OBB 后处理部署抽取时可以直接调用其结果。实际项目里将YOLO对象封装为常驻进程通过共享内存接收图像帧、返回检测结果避免反复加载模型带来的延迟抖动。5.3 性能对比与选型建议以yolov8n-obb在 1024 分辨率下进行基准测试参考硬件条件下的推理耗时如下硬件环境推理耗时/帧角度误差增量NVIDIA RTX 3080 (FP16)12 ms0.1°Jetson Orin Nano (FP16)28 ms0.1°Jetson Orin Nano (INT8)18 ms0.4°仅 CPU (Intel i7)380 ms不适用从表可见精度角度受 FP16 影响几乎可以忽略。如果要在更低功耗设备上运行可以考虑 INT8 量化但需谨慎评估角度误差增量能否被产线接受。对于 1 秒处理 5 帧的产线节拍Jetson Orin Nano 用 FP16 就足够。若平台是主机式设备RTX 系列有相当大的余量。部署后的最后一环是在线验证。运行连续生产的首 2000 帧统计实际漏检率和角度误差分布与测试集的评估结果对比。环境光照变化、传送带振动导致的图像模糊、姜种上附着的干泥都会让测试集评估结果与线上表现拉出间隙。建议在出口增加一个下位机信号当连续 5 帧检测结果不可信时视觉系统主动停机报警而不是带病运行这能显著降低漏检造成的生产损失。本文还有配套的精品资源点击获取
返回列表