资讯动态

基于YOLOv10的樱桃成熟度检测系统:从数据标注到实时推理的完整实践

发布时间:2026/10/19 9:07:55 来源:尧图企业网站定制
做樱桃成熟度检测之前我先把丑话说在前面目标检测模型能跑通不难难的是让它拿真实果园环境下来不乱认。这套基于YOLOv10的樱桃成熟度检测系统训练端用PyTorch推理端用Python封装支持图片、视频和摄像头三种输入方式属于典型的“算法成熟、工程欠打磨”项目。YOLOv10本身的目标检测能力已经很强但真正决定系统好不好用的是数据够不够干净、训练参数踩没踩对、推理链路有没有把延迟控制住。这套系统适合谁适合正在做农业视觉落地的工程师适合想拿目标检测当毕业设计的同学也适合对YOLO系列有基础、但没跑过完整项目的人。我会把从环境搭建、数据标注、模型训练到推理封装整条链路拆开讲结合我自己跑通这套流程时踩过的坑尽量让后面的同学少走点弯路。1. 项目概述与整体方案拆解1.1 这个系统到底在解决什么问题樱桃成熟度检测本质上是一个“目标分类目标定位”的组合问题。人工判断樱桃熟不熟靠的是颜色、光泽、手感这些经验机器要复现这件事就得先把图像里的每一颗樱桃找出来再告诉使用者这个果子属于“成熟”还是“未熟”。注意这里不能只做分类因为大多数场景下图像里不止一颗樱桃如果不给出位置坐标下游的计数、分拣、采摘动作全都无从谈起。YOLOv10属于单阶段目标检测算法一次前向传播就能同时输出类别和边界框正好对应这个需求。系统支持图像、视频和摄像头三种输入是因为实际使用场景很不一样。果园里拍一张照片想快速看结果就用图像模式回放一段采摘机器人录制的视频想复盘误检情况就用视频模式架在分拣线旁边做连续检测就必须走摄像头实时模式。三种模式在代码层面都只需要解决一个共同问题把输入变成一帧一帧的图像再喂给YOLOv10模型最后把检测结果画出来。YOLOv10的输出结构很容易理解。每一组检测结果包含三个关键信息目标类别即成熟或未熟目标位置即边界框的左上角和右下角坐标置信度即模型对这个框有多大的把握。只要拿到了这三样东西上面的业务逻辑就可以随意扩展。比如统计一张图里成熟樱桃的数量或者把不成熟樱桃的坐标输出给分拣机械臂这类功能都是在检测结果之上做文章。1.2 为什么选YOLOv10而不是其他版本我当时选型的时候YOLOv5、YOLOv8、YOLOv10都在候选范围里。YOLOv5最稳定网上资料也多但迭代了这么多个版本架构上并没有质的突破YOLOv8的生态好、可玩性强但对部署场景来说并没有在推理效率上给出明显优势。YOLOv10最吸引我的点是它取消了NMS后处理。NMS非极大值抑制是目标检测里一个经典步骤。早期模型预测出来会有大量重叠的候选框每个框都觉得自己是“最佳答案”NMS做的就是从一堆重复框里挑出置信度最高的那个把其他框压掉。这个步骤在离线处理图片时不算什么大开销但在摄像头实时场景里每一帧都要多跑一次NMS延迟一下就上来了。YOLOv10通过双标签分配和一致匹配度量让模型在训练阶段就知道要保留哪个框推理时不需要NMS就能拿到干净结果端到端推理速度因此明显更优。YOLOv10还提供了n、s、m、b、l、x六个不同规模的模型。实测下来做摄像头实时检测yolov10n和yolov10s最划算离线跑视频分析或者对精度要求更高yolov10m和yolov10l能换来更稳的检测效果。我用yolov10s作为主力模型在保证帧率的前提下能拿到足够好的精度这也符合大部分农业视觉项目的实际算力条件——很多时候部署机器就是一台普通工控机别一上来就选最大模型。1.3 整体技术方案与代码结构这套系统的技术链路并不复杂。输入层由OpenCV负责不管是cv2.imread读图片、cv2.VideoCapture读视频还是继续用VideoCapture读摄像头最终都统一成BGR图像帧。中间层是YOLOv10模型推理将图像缩放成640x640送入网络得到类别、坐标、置信度。输出层则是把检测框画到帧上再通过imshow显示或imwrite保存。代码目录我建议这样组织datasets目录存放数据集和标注文件weights目录存放预训练权重和训练后的best.ptinference目录放图像、视频和摄像头的推理脚本utils目录放公共函数比如画框、计算FPS、日志输出。这个结构看起来很常规但真实项目里非常实用因为不会写着写着就把代码和数据混成一团后期换数据集、换模型权重都很方便。PyTorch在整个链路里的角色是训练框架同时也承担推理后端。选PyTorch不是因为它比别的框架跑得更快而是因为它的生态成熟、调试方便训练过程中可以随时打印张量、检查梯度对做工程调优的人来说省心。很多同学纠结什么框架“高级”实际做项目能让你最快定位问题、拿到结果的框架才是好框架。2. 环境搭建与数据集准备2.1 环境依赖怎么装最省心环境建议直接用conda建一个独立环境别把依赖装进系统Python里。Python版本选3.10或3.11都行。PyTorch版本根据你的显卡CUDA版本来定去PyTorch官网生成对应的安装命令即可没有独立显卡也可以用CPU版但训练速度会慢到让你怀疑人生。conda create -n cherry python3.10 -y conda activate cherry pip install torch torchvision --index-url 根据你的CUDA版本选择 pip install opencv-python pillow numpy matplotlib装完基础依赖之后把YOLOv10官方源码克隆到本地在项目目录下执行安装命令。源码仓库里自带推理和训练所需的大部分模块安装以后才能在命令行里直接调用训练脚本。依赖明细可以参照项目里的requirements.txt比手动一个个装靠谱。第一次装环境最容易翻车的点是PyTorch和CUDA版本不匹配。很多人装完一import torch就报错或者能用CPU但GPU加速起不来。判断方法很简单在命令行里执行python然后输入import torch; print(torch.cuda.is_available())返回True说明GPU可用。如果返回False多半是torch版本和本机CUDA驱动版本对不上重新安装对应版本的PyTorch即可。2.2 数据采集与标注的实操要点数据集是整个检测系统的地基。理想情况下成熟樱桃和未熟樱桃每个类别至少准备800到2000张图像。拍摄时要注意覆盖不同角度、不同光照、不同遮挡程度。不要只在晴天中午拍那样模型学到的其实是“阳光下颜色鲜艳的东西”一旦换到阴天或者人工光源下就失灵。我自己后来补数据时专门在早中晚各拍了一批还故意拍了不少逆光和阴影下樱桃的照片。标注工具用LabelImg就够用。打开图像画框选类别保存。YOLO格式的标注文件是txt文本每一行对应一个目标框类别序号、中心点x坐标、中心点y坐标、框宽度、框高度。这里特别注意边界框的xywh都是相对于图像宽高的归一化值范围在0到1之间。标注时候最忌讳的是把边界框标得太大把旁边的叶子和果柄都框了进来。我踩过这个坑模型训练完以后看到绿色背景就发警报以为那是樱桃。正确做法是让边界框紧贴果实边缘宁可稍微留一点缝隙也不要包进大量背景。还有一个细节两个樱桃挨在一起的时候不要图省事把两个果子框在一起要分别框两个小框。模型训练时对这类紧密目标需要明确边界否则推理时就会把重叠区域误判成一个目标输出置信度还特别高。2.3 数据增强和数据集划分数据增强不是越多越好但针对农业场景有几项是必须开的。HSV颜色变换能模拟不同光照条件随机左右翻转能增加样本多样性随机缩放和裁切能提升模型对不同距离下目标的适应能力。YOLOv10训练时自带的mosaic增强把四张图拼成一张训练对提升密集目标场景下的检测效果很有帮助默认开启就行。数据集划分按7:2:1分成训练集、验证集、测试集并且保证划分后的图像来自不同拍摄批次避免同一个场景的图像同时出现在训练集和验证集里。如果犯了这个忌讳验证集效果会好得离谱等真正部署到新场景时才露馅。划分逻辑上建议按文件名随机切分再用脚本统计每个类别在各子集里的占比确认没有类别严重失衡。# 划分后在数据集目录下创建images和labels两个子目录 # 每个子目录再划分train/val/test保持图片和标注一一对应数据准备阶段我个人的一个建议是训练前先用脚本抽查一下标注文件。把图片路径和txt标注内容对应起来画个框看看有没有坐标越界、类别序号错误。这类低级问题越早发现越省事等发现模型训练出来预测全乱才回来检查标注就太浪费时间了。3. 模型训练与调优细节3.1 配置文件和预训练权重训练前需要写一个数据集配置文件告诉模型图片在哪里、有几类、每个类别叫什么名字。这个文件是yaml格式内容很简单。类别顺序要跟标注时保持一致切记训练阶段和推理阶段不要改类别顺序否则预测结果会全部错位。path: ./datasets/cherry train: images/train val: images/val test: images/test nc: 2 names: 0: ripe 1: unripe预训练权重建议直接用yolov10n.pt或者yolov10s.pt。这里说的“预训练”意味着模型已经在大型公开数据集上学习过通用特征下载下来之后在这个基础上继续用小规模樱桃数据微调比从零开始训练收敛快得多最终精度也普遍更高。训练入口可以直接用命令行调用YOLOv10的脚本。把数据集配置和模型权重指进去设置好训练轮数和图像尺寸。以yolov10s为例训练100轮是比较稳妥的基线之后需要根据损失曲线再决定是否加训。yolo detect train datacherry.yaml modelyolov10s.pt epochs100 imgsz640 batch16 lr00.013.2 训练参数设置的思路训练参数的设置没有标准答案但有几个基准可以遵循。输入图像尺寸imgsz设640是基于精度和速度的折中如果目标看起来特别小可以尝试768甚至896代价是显存占用和推理耗时明显上升。batch大小主要取决于显存常见8G显存的情况下单卡batch取16比较稳如果要开混合精度训练AMP显存压力还能再小一些。图像尺寸和batch大小合在一起决定了每轮能“看”多少张图理解这一点比死记参数值更重要。学习率方面YOLO系列一般用动量SGD作为优化器初始学习率lr0从0.01开始跑问题不大。如果发现前几个epoch损失不降可以降低到0.001再试。训练轮数epochs设100到150都合理但别一味拉长。模型训练后期很容易过拟合典型表现是训练损失持续下降、验证损失反而上升。这种情况不需要硬扛直接回调最佳模型权重就行。训练过程中的监控不能只看一张loss曲线。我习惯同时关注三个指标训练集上的loss是否在稳步下降验证集上的精确率和召回率是否同步提升验证集上的mAP50是否达到预期。一般来说这种只有两个类别的检测任务mAP50如果低于0.9先不要怀疑模型结构回去查数据标注和类别数量是否均衡。3.3 训练中我最在意的几个指标很多人训练完只看mAP50但这不够。项目里真正要关心的是精确率和召回率分别怎么样。精确率低说明模型把很多非目标物体误判成了樱桃召回率低说明模型漏掉了一部分真实的樱桃。对樱桃分拣这个场景漏检的代价比误检更大——熟果混进生果筐问题比生果混进熟果筐严重所以必要时可以牺牲一点精确率去换召回率。推理阶段置信度阈值也会影响这两个指标。阈值设太低边界框一堆误检多阈值设太高漏检多很多置信度不高的熟樱桃会被丢掉。我实际跑下来成熟和未熟两类区分度比较明显的情况下conf阈值设在0.3到0.4之间比较合适。如果你的场景里遮挡多、光线差可以把阈值再降一点让模型“宁可多框不要漏框”。还有一个容易忽视的指标是各类别分别的召回率。如果成熟樱桃的召回率高未熟樱桃的召回率低那说明未熟样本太少或者标注质量差。处理办法是补充未熟样本而不是盲目增加训练轮数。我用过一个土办法把验证集里被漏检的图像专门收集起来手动调整标注后再混入训练集这个操作对提升召回率效果非常直接。4. 图像、视频和摄像头实时检测实现4.1 图像检测最基础的推理链路图像检测是整套系统的地基逻辑。训练完成后项目目录下会生成一个best.pt权重文件这就是后续推理用的模型。加载模型和推理的代码非常简洁。from yolov10 import YOLOv10 from PIL import Image model YOLOv10(weights/best.pt) img Image.open(test.jpg) results model.predict(sourceimg, conf0.3)predict返回的结果里包含了检测框的所有信息。一个常见的新手问题是不知道结果结构长什么样实际上可以直接遍历results的边界框属性来拿到坐标、类别和置信度。拿到这些数值以后由咱们自己用OpenCV画框和写文字。成熟樱桃用绿色框未熟樱桃用红色框置信度文字标在框上方信息一目了然。import cv2 img_cv cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) for box in results.boxes: x1, y1, x2, y2 map(int, box.xyxy[0]) cls int(box.cls[0]) conf float(box.conf[0]) label f{model.names[cls]} {conf:.2f} color (0, 255, 0) if cls 0 else (0, 0, 255) cv2.rectangle(img_cv, (x1, y1), (x2, y2), color, 2) cv2.putText(img_cv, label, (x1, y1-8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow(result, img_cv)这套画框逻辑在所有模式下是公用的所以我建议把它提取成一个函数。后续视频和摄像头模式直接复用不要每段代码里都复制一遍。图像检测模式下还有一个实用功能就是把每张图的成熟、未熟数量统计出来配合Excel导出成表格方便做批量统计报表。4.2 视频检测逐帧处理与结果保存视频检测本质上就是连续不断地重复图像检测。OpenCV读取视频流每一帧都是一幅图像YOLOv10对每一帧做推理再把结果画出来。帧率不需要花哨处理逐帧跑就能满足大多数离线分析场景。cap cv2.VideoCapture(test.mp4) while True: ret, frame cap.read() if not ret: break results model.predict(frame, conf0.3) # 在这里复用画框函数 if cv2.waitKey(1) 0xFF ord(q): break cap.release()视频模式下要注意有些视频分辨率很高1080p的图像直接送进模型缩放耗时和显存占用都会增加。建议在送入模型前先把帧缩放到合适尺寸比如resize到1280或960再用YOLO内部逻辑缩放至640。这样既保证检测效果也明显提升处理速度。缩放的另一个好处是画框坐标对应关系更直观算坐标时不容易乱。如果想把检测结果保存成新视频就用cv2.VideoWriter。编码格式建议用mp4v文件扩展名用.mp4。每次写完一帧记得write一次最后release。注意输出视频的帧率设置要和原视频一致不然画面会变成倍速播放。我保存视频的习惯是A、B两个窗口并排显示左边原视频右边检测结果方便逐帧对比误检漏检情况。4.3 摄像头实时检测延迟优化是重头戏摄像头实时检测和视频检测的代码骨架几乎一样只是VideoCapture参数从文件路径换成摄像头设备号。笔记本自带摄像头通常是0外接USB摄像头通常是1。如果读取摄像头时报错“cant open camera”先检查设备号再检查摄像头是否被其他程序占用。cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)实时检测最怕延迟。慢的原因通常不在模型而在输入输出的“周边开销”。第一是OpenCV自带的视频缓冲。摄像头驱动默认会缓存几帧推理速度慢的时候读到的反而是老帧画面看起来就卡顿。解决办法是设置缓冲区大小为1读取时拿到最新帧。第二是图像缩放次数太多每帧大图缩一次、送模型又缩一次重复消耗性能。第三是显示窗口里的等待时间waitKey参数不要乱加。如果偶尔达不到实时帧率有两个实用方案。一个是推理跳帧比如每两帧只处理一帧另一帧直接复用上一帧检测结果画框视觉效果几乎无差别但帧率直接翻倍。另一个是用线程单独读摄像头主线程专心做模型推理和绘制。摄像头读取通常有等待时间放在子线程里能显著减少主线程阻塞。这两个方案我都在自己项目里验证过推荐先从跳帧开始试改动最小、效果最可控。5. 常见问题与排查经验5.1 训练和安装阶段最容易翻车的点现象可能原因解决方案import torch时报错找不到模块环境变量混乱或PyTorch未正确安装重新创建conda环境按CUDA版本对应安装CUDA out of memorybatch或imgsz过大降低batch到8或4开启AMP混合精度训练时没有任何GPU占用torch和CUDA版本不匹配检查torch.cuda.is_available是否为True安装依赖时下载特别慢网络源的问题换合适的镜像源不要反复中断重试训练损失不下降学习率偏高或数据标注错乱调低学习率抽查标注文件和类别序号安装阶段的问题最好排查因为你只需要把环境搞对。训练阶段的问题则需要“定位原因优先于动手改参数”。我最常遇到的是有人看到loss下降慢就把学习率拉高结果直接损失爆炸。YOLO系列的训练相对稳定但盲目调参会浪费大量时间。5.2 检测效果不理想的常见原因如果训练跑完验证集指标不错但实拍视频里容易漏检原因多半出在数据覆盖不足上。比如训练集里樱桃都是大颗、完整、无遮挡的现场却是叶子遮挡、果实堆叠模型没见过这种样子自然认不出来。这类问题优先扩充数据不要急着换模型结构。叶子被误判为成熟樱桃这是农业检测项目里的经典问题。原因通常是标注时边界框包含太多背景或者数据增强里缺了绿色背景样本。我处理过几次之后发现最直接的缓解办法是在训练集里加入大量“只有叶子、没有樱桃”的负样本图像这类图像不需要标注模型会学着抑制背景响应。负样本不用特别多占训练集10%左右就有明显效果。检测框把两个挨着的樱桃框在一起也是常见问题。这不一定是模型问题有时候是标注阶段就把两个目标合并成了一个框。如果确认标注没问题但推理还是粘连可以尝试把推理图像尺寸调到更大让模型看到更多细节。5.3 摄像头实时检测的踩坑记录我自己在摄像头模式里踩过最狠的一次坑是摄像头画面非常流畅但检测框一直画在上一帧的位置。排查了半天发现是VideoCapture缓冲区堆积模型处理速度跟不上摄像头的帧率读出来的frame永远是几帧之前的旧帧。后来把缓冲区大小设置为1同时做了一层线程缓存最新帧这个问题才彻底解决。另一个印象深刻的问题是实际部署到果园时发现模型的误检率比实验室高了很多。主要原因是我训练用的图像都是在人工光源下面拍的果园里的自然光和阴影变化完全没覆盖到。后来我在训练数据里混入多种光照条件下拍摄的樱桃图像验证集也有意选择完全不同拍摄批次的数据这个状况才明显缓解。这也让我形成了个习惯任何检测系统在正式交出去之前一定要拿到真实环境里跑至少半小时实验室指标只能作为参考。关于置信度阈值我建议做成可配置参数。不同场景下最优阈值差异很大光线充足时0.4合适傍晚可能就要降到0.25。把阈值放到配置文件里而不是写死在代码里后面调整起来会舒服很多。再配合一个简单的按键事件实时调试的时候直接按键盘加减阈值这个功能虽然不起眼但在现场调试时很顶用。关于值班坚持始终最后说一点个人体会YOLOv10这套框架本身很成熟真正决定项目成败的是数据和细节。数据覆盖不够再好的算法也白搭推理链路没做延迟优化摄像头模式下体验就上不去。把数据标注、训练指标、推理效率这几个方面都扎实做一遍樱桃成熟度检测系统完全可以在真实场景里稳定跑起来。再往前走一步检测结果可以接入计数统计、产量分布图表甚至通过串口把筛选信号传给分拣设备整套系统就从一个视觉Demo变成真正的产线模块了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑