温室番茄采摘有个很反直觉的点果农真正关心的不是“这一串里有几个果”而是“哪一串能摘了”。站在温室过道里抬头看番茄是成串长的采摘作业的单位是果串不是单个果实。所以当时定“基于YOLOv5的温室番茄果串检测系统”这个题目时核心目标就两个一是从复杂温室背景里把果串完整框出来二是保证后续能实时跑、能低门槛复现。这篇文章就把整个系统的设计与实现过程完整拆开讲从需求分析、数据集的“脏活累活”到模型训练、推理后处理再到部署过程中遇到的坑和绕法全部说清楚。这套系统我能给的最直接定位是面向温室环境、以YOLOv5为核心的果串目标检测方案。它解决的问题不是“把番茄识别出来”而是“在茎秆、叶片、阴影、反光、疏果残留等强干扰背景下稳定检测出不同成熟度的番茄果串”并且做到实时推理方便后续接机械臂或者做产量统计。无论你是做农业AI毕设还是真的想给大棚装一套视觉采收辅助设备这篇都能当一份折腾过的参考笔记来看。1. 需求拆解与整体设计思路1.1 为什么检测对象要选“果串”而不是单果我一开始也想过直接检测单个番茄觉得边界更简单、数据更好标。但实际去温室里看了几次现场这个想法很快被否掉了。采摘角度温室种植的串收番茄采收时是整串剪下来的检测单果对采收作业没有直接决策价值反而会产生“这一串已经成熟但网络只检到一个果”的割裂判断。遮挡角度番茄叶片浓密果实互相挤靠单果边界大量重叠标注出来的框会非常碎模型学到的是碎片化特征泛化很差。果串虽然也有遮挡但整体轮廓比单果稳定得多。产量统计角度按果串计数做产量估算误差比按单果计数小一个量级。因为一串通常4到8个果错检一串只记1次错检一个果会把整串数乱。所以这个项目的核心检测单元就定为“果串truss”并且约定只要一个果串可见面积超过约三分之一就要标出来哪怕后面果实被茎叶挡住了也按完整检测目标处理。1.2 为什么选YOLOv5当时做选型对比时摆在我面前的是两条路线两阶段检测器传统上精度更好和单阶段检测器速度更快。温室里做检测环境相对固定但光照波动、蒸汽、喷灌水滴都会造成视觉干扰对实时性要求又很高——机械臂等待一个检测框超过200毫秒整个采摘节拍就崩了。最终选了YOLOv5主要有几个现实原因速度与精度平衡好。YOLOv5s在GTX显卡上可以达到100 FPS以上输入640分辨率精度指标mAP 0.5在自建果串数据集上也能到0.9左右。两阶段方法靠ROI二次判别精度上接近但速度差几倍工厂落地压力大。部署生态成熟。YOLOv5训练完导出ONNX、再转TensorRT的链路是通的社区案例极多遇到算子不支持的坑能找到现成解决办法。农业现场往往没有纯GPU环境能跑在带TensorRT的Jetson边缘设备上是硬需求。模型体量与迭代效率合适。YOLOv5系列有n/s/m/l/x五个量级可以先从最小模型开始做流程验证再逐步放大不像某些框架改网络结构就得改一堆配置。一句话总结这个选择YOLOv5并不是“最先进”的但它是在温室检测这个限定场景下工程综合成本最低的选项。1.3 系统整体架构整个项目分成四条线数据线、训练线、推理线、部署线。数据线负责从温室实地采集视频和图像通过抽帧、清洗、标注、增强得到标准YOLO格式数据集。训练线基于YOLOv5官方仓库做迁移学习加载预训练权重对果串类别做微调期间用验证集监控过拟合。推理线封装模型加载、图像预处理、非极大值抑制、结果可视化和坐标导出。部署线把训练好的权重转成ONNX在边缘设备上实测推理速度。这里特别提醒系统设计一定不要一上来就闷头训练模型。先跑通“采集几组图—标注100张—训练一轮—推一段视频”的极简闭环确认整个链路没有断裂再去扩大数据规模。否则会遇到标了3000张图最后发现标注格式错了一个字段全部白干的情况。我身边确实有人这么翻过车。2. 数据集构建温室场景下的采集、标注与增强2.1 温室数据采集的实用方法模型决定上限数据决定下限。在温室番茄果串检测这个任务里数据采集最大的难点不是“拍不到”而是拍摄环境变化太少模型容易过拟合在你的固定机位、固定光线上。我当时把摄像头支架往温室轨道车上一架分别选取晴天上午、傍晚、阴天、喷灌后、补光灯开启等多种时段各录了半小时视频再按两秒一帧抽帧。抽出的图像做一遍人工快速筛选原则是只保留画面清晰、未过度抖动的帧丢弃大面积白屏过曝的帧同一果串在一个视频片段里保留3到5帧不同角度即可避免连续帧造成训练集和验证集数据泄漏。筛选完毕图像数量大约5500张。如果全部人工标注工作量在40小时以上。对于想压缩时间的人可以先按训练集验证集测试集约8比1比1划分后再标注避免之后重复操作。2.2 标注规范与工具选择标注工具我推荐用LabelImg或者X-AnyLabeling。前者轻量好上手后者支持YOLO格式导出且可以调用预训练模型做半自动标注温室果串这种特征明显的目标半自动后人工检查能快一半以上。真正要花心思的是标注规范必须所有人统一标准否则模型学到的边界是乱的。我们内部明确了几条规矩果串定义一个完整的果穗连同其所有可见果实作为一个目标果实被叶片挡住一部分只要可见面积还在1/3以上就标注。边界框要求紧贴果串最外侧可见像素宁大勿小。因为前面说过采摘要框出整串。不标排除项完全被叶片遮住、仅露出1至2个小果尖且无法判断是不是果串的不标远距离图像中果串小于约20像素的不标。这类目标强行标注只会引入大量噪声。成熟度不作为类别区分刚开始做过成熟/未成熟二分类发现绿色果实和叶片颜色相近分类噪声反而干扰定位。这个系统只检测“果串”不细分成熟度。成熟度判断留到后续做分类网络或者直接靠图像颜色特征处理。所有标注完成之后导出成YOLO格式一张图像对应一个同名的txt文件每行是“类别id 中心x 中心y 框宽 框高”坐标值是相对于图像宽高的0到1之间的小数。输出之前务必抽查100张图把框画回到原图上看一遍这一步能筛掉坐标归一化错误、类别id混淆、标注框明显偏移等大量低级问题。2.3 数据增强不要一股脑全用YOLOv5训练时内置了Mosaic、随机HSV扰动、翻转等增强策略。我的经验是**温室果串检测场景下Mosaic是收益最高的增强方式因为它把四张图拼到一起模拟了拥挤、遮挡、多目标交叠的情况。**果串在真实温室里往往是几串挤在一起、叶片穿插Mosaic能显著提高密集场景的检测稳定性。但要注意三个坑训练后期最好关闭部分增强。YOLOv5的hyp配置里Mosaic开启概率是1.0我的做法是训练到最后50个epoch把Mosaic概率降到0.3让模型在接近真实分布的数据上最后精调mAP明显回升。验证集千万不要做随机增强。验证集做水平翻转会改变真实图像的分布让指标虚高。YOLOv5默认对验证集不做随机增强只做letterbox缩放保持这个默认即可。HSV扰动里色相幅度别调太大。番茄果实是红色偏暖色调如果色相范围太大训练时会把果实的红变成蓝色或绿色模型学到的颜色特征是“假特征”导致在真实图像上一遇反光就偏。一般HSV色相扰动控制在0.015以内。3. 模型训练与调优从骨架到收敛3.1 环境搭建与依赖配置这一部分对小白最劝退但其实照着做一次就顺了。我用的环境大概是Python 3.9、PyTorch 2.0、CUDA 11.8、OpenCV 4.8。YOLOv5官方仓库稳定迭代到某个版本后基本不跟PyTorch最新版走所以不要看到“最新”就冲。先创建虚拟环境python -m venv venv source venv/bin/activate # Windows下执行 venv\Scripts\activate pip install -r requirements.txtrequirements.txt里会锁核心库版本。装完先跑一下官方自带的检测demo比如对一张图执行python detect.py --weights yolov5s.pt --source data/images/bus.jpg如果自带的图和视频能正常出框说明环境没问题。很多人一上来就把自己的数据拿进去训练最后发现报错结果是环境缺库所以强烈建议先用官方demo验证环境。3.2 数据集与配置文件编写YOLOv5训练前需要准备两部分配置数据集描述文件和模型配置文件。数据集描述文件是一个YAML内容大致是path: /data/tomato_truss train: images/train val: images/val test: images/test nc: 1 names: [tomato_truss]path指定项目根目录train/val/test可以写相对路径。这里有个细节path一定写绝对路径或者统一前缀否则会出现在不同目录打开终端时路径找不到的诡异问题。我吃过这个亏后来直接把path写死。模型配置文件在models目录下一般选yolov5s.yaml作为基础。关键改动只有nc从COCO的80改成1我们只检测果串。yaml里depth_multiple和width_multiple不用动它们是控制模型规模的系数。3.3 训练策略与超参选择我的训练命令大概是这样python train.py \ --data tomato_truss.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 300 \ --imgsz 640 \ --patience 50几个参数背后的取舍值得展开使用预训练权重yolov5s.pt做迁移学习相当于“继承”了模型在超大规模自然图像上学到的纹理、边缘、形状等基础特征。果串虽然有特殊性但低层特征都是通用的。从头训练不是不行只是需要更多数据和更长训练时间对实验室场景完全不划算。batch-size要根据显存动态调整。我用的显卡显存是12GBbatch-size 16刚好能塞下训练速度也稳定。显存不足可以降到8甚至4但batch太小会导致梯度噪声大模型收敛不稳定。imgsz选了640而不是1280。果串目标相对较大640分辨率下平均目标尺寸仍约占图像面积的5%以上这个分辨率对于检测任务足够。分辨率提到1280会明显增加显存占用和推理耗时在温室场景性价比不高。如果后续发现小目标果串漏检会优先考虑图像切块而不是直接提分辨率。patience 50表示验证指标连续50轮没有上升就提前终止节省大量无效训练时间。3.4 训练过程的监控与调优训练过程中我重点盯这几个指标box_loss、obj_loss、cls_loss、mAP_0.5、mAP_0.5:0.95。训练阶段box_loss和obj_loss稳定下降说明模型在收敛不下降说明学习率过大或数据集有问题mAP_0.5在温室果串这个单一类别显任务上通常可以稳定到0.9左右。如果到0.85附近就上不去优先怀疑数据问题而不是模型问题比如某些果串没标、边界框风格不统一。mAP_0.5:0.95比mAP_0.5严格得多它要求框和真实框交并比从0.5到0.95一连串阈值都算准温室果串边界不规整这个指标0.6以上就算很好了不必死磕。训练结束后我一般先输出PR曲线、F1曲线和混淆矩阵。在果串这类单目标检测任务中PR曲线里置信度阈值附近出现一个明显“膝盖”转折说明置信度阈值有一个合理范围可以同时兼顾召回和精确率。我后面推理时设置的置信度阈值就参考了这个膝盖的位置——一般在0.25到0.45之间。另外best.pt和last.pt的选择也有坑。有的教程无脑选best.pt但best是基于验证集mAP挑出来的如果验证集和真实温室场景分布有偏移best不一定就是推理效果最好的。我的习惯是教练完用best.pt和last.pt各在手动挑选的现场难例上跑一遍人工目检检准率哪个好用哪个。4. 推理系统实现从模型到可用的检测结果4.1 推理脚本的核心步骤模型训练完只完成了工作的一半。真正要在现场用起来还要自己写推理脚本。核心流程是读图→letterbox预处理→模型推理→NMS后处理→坐标还原→可视化/输出。YOLOv5官方detect.py把一套流程封装好了直接用是很省事。但项目里我往往需要做定制化输出比如把检测框坐标保存成Json给机械臂控制端或者统计每帧果串数量。这时候就要自己写脚本。关键四步**第一步letterbox预处理。**要对原图做等比缩放并填充灰边到模型输入尺寸如640×640。这里有个容易忽略的点模型输出框坐标是相对于640×640输入图的做坐标还原时必须反向去掉比例和灰边否则最终画在原图上的框会整体偏移。官方utils里有一个scale_coords函数专门做这个可以直接复用。**第二步模型推理。**用PyTorch做推理时模型要设成eval模式并且用torch.no_grad()包裹避免计算梯度白白浪费显存和算力。**第三步NMS后处理。**YOLOv5推理输出是一个[1, 25200, 85]的Tensor1是batch25200是三个尺度特征图上的预测框总数85是框坐标4维目标置信度1维类别概率80维但我们类别为1所以后面变成5维。NMS会把大量重叠框合并成少而精的预测框。关键参数有两个置信度阈值conf_thres低于这个分数的框直接丢掉。我最终设为0.35。设0.25会多一点漏检召回但也会混进来一些虚检设0.5更干净但远处的小果串容易漏。IoU阈值iou_thres用于NMS判断“两个框算不算同一个目标”。设太低相邻的两个独立果串可能会被误并成一个框设太高一个果串上的重叠框无法被清理。测试了0.4到0.6的范围0.45在“密集果串不要并框”和“重复框要消除”之间比较均衡。**第四步坐标输出。**果串检测结果我最后保存成类似这样的JSON[ { bbox: [132, 58, 208, 103], score: 0.91, class: tomato_truss } ]bbox是还原到原图像素坐标的左上角和右下角。后面无论是画框、裁剪果串图像还是算果串中心点做机械臂引导这个数据格式都够用。4.2 推理实测表现在一台普通办公显卡峰值算力不算顶级上我们这版YOLOv5s模型处理单张640×640图像的推理时间大约是13到18毫秒。加上letterbox预处理和NMS时间单帧全流程大约20毫秒出头折算一下能达到每秒接近50帧的处理能力满足温室移动采摘平台的实时性要求。换用YOLOv5n能在边框精度略降的情况下把推理时间压到10毫秒以内适合更轻量的边缘设备换用YOLOv5m精度能高出一点但推理时间会涨到30毫秒以上。在温室场景我最终选了s作为速度与精度的平衡点。这东西没有“最佳”只有“你的设备性能下最合适”。4.3 系统集成脱离命令行跑起来命令行跑通只是第一步。项目要成为“系统”必须有一个能接收图像、返回结果的接口。我做了一个极简的Flask HTTP服务。核心逻辑是启动时加载模型一次常驻内存接收POST上传的图像做一次推理返回JSON结果。这样前端可以是一个摄像头采集端每隔几秒上传一帧后端维护一个果串计数状态前端再把框画到图像上或者直接存库。一个容易踩的坑是每次HTTP请求都重新加载模型内存会反复波动在设备上可能会出现卡顿。**模型加载必须放在服务启动时只做一次推理函数只做前向计算。**这个设计在并发量不高的场景下完全够用而且比用消息队列和独立推理服务简单很多项目演示和初版系统验证都非常推荐。5. 常见问题与排查技巧实录5.1 果串反复误检把叶片也当成果串这个问题出现过一次排查时我先看了标注。发现部分标注框标得非常大把旁边大片叶片包进去了模型学到的是“叶片果实”的联合特征。一旦现场图像里只有叶片没有果实模型就非常自信地给出误检。解决方法分两步一是到原图上把误标注的框重新修正给果串边缘留出1到2像素的贴合余量即可二是增加难负样本把那些包含大量叶片、没有果串的图像作为空标签样本放进训练集让模型明确知道“这种图输出为空是正常行为”。5.2 远距离小果串漏检当果串在图像里投影面积小于大约30×30像素时YOLOv5s漏检率明显上升。本质原因是果串的特征在低分辨率下被池化层冲掉了小目标在高层特征图上的响应太弱。我试过两种解法第一种是输入分辨率从640提高到960漏检率略有下降但推理时间也涨了接近一半第二种是图像切块推理把图像切成四块分别做检测再合并结果漏检率下降明显但可能把跨边界的果串目标切成两半需要额外的重叠区域合并逻辑工程复杂一些。实际项目里最后采用的是滑窗切块加重叠区拼接方案只对画面中果串密度高的区域启用。这是一个典型的“算法解决不了就工程解决”的思路虽然在学术纯度上不够好看但现场稳定才是第一位的。5.3 温室反光导致的帧间闪烁温室棚膜和滴灌水珠在强光下会产生强烈反光同一串番茄在相邻两帧里一会检到一会检不到。这类问题靠模型本身很难完全消除我用了一个非常朴素的时序平滑规则**如果一个目标在连续5帧里至少出现3帧才输出“有效检测”如果连续10帧消失才判定目标真正离开。**这个策略增加了约一帧的时延但换来了极其稳定的检测结果对于产量统计场景来说稳定性远比那一帧时延重要。5.4 训练的loss下降但mAP始终在0.85徘徊如果你的box_loss、obj_loss都在下降训练看起来一切正常mAP就是上不去大概率是数据集的标注风格不一致导致的。比如有的人把果串框得很紧有的人习惯把整体枝叶也框进去模型学到的目标边界混乱。此时不要盲目调网络结构和超参先把标注规则统一好把分布异常的样本框挑出来重新标注。我处理过后mAP直接从0.85升到0.92效果立竿见影。6. 部署落地与扩展方向6.1 导出ONNX与边缘设备推理验证完模型效果后我导出了ONNX格式作为分发和部署的中间格式。命令是python export.py --weights best.pt --include onnx --imgsz 640ONNX的好处是脱离PyTorch环境接口标准能被各种推理引擎加载。如果目标设备是Jetson这种带Tegra GPU的边缘设备还可以进一步用TensorRT把ONNX转成TensorRT引擎文件推理速度能比PyTorch再快一倍多。注意TensorRT版本不同转换要点略有差异现在网上不同版本教程常常混着讲实操时务必先在本机上查清楚对应版本的算子支持和接口调用方式。6.2 扩展方向一果串分割与成熟度判断检测只能告诉系统“哪里有一串番茄”但采摘要知道“这一串熟了没”。最直接的扩展是在检测框基础上联动成熟度分类对每个检测框裁剪出子图喂给一个轻量分类器输出“成熟/半熟/未熟”。这个分类器可以在果串检测框裁出来的子图上训练数据量不需要很大能大大提升采摘决策的准确度。6.3 扩展方向二产量统计与株间关联把检测结果按时间戳和空间位置做多目标跟踪就能实现“这一行种了多少株、每株挂了几串果”的高精度统计。配合一个简单的按检测框中心点坐标进行株间分桶的算法产量预测的准确率显著高于传统抽样估产方法而且这个功能不需要额外的硬件投入软件层完全可以实现。6.4 扩展方向三移动端与远程监控把推理服务封装成接口以后现场采集端大棚内部的工业相机或网络摄像头将图像传到部署推理服务的机器上手机上通过网页访问结果页面就能随时远程查看一定区域果串分布情况。温室管理人员不需要走进棚里可远程掌握成熟度分布从而规划次日采摘人手。这套方案前期成本极低但对小规模设施农业已经够用。7. 个人实操心得与避坑经验最后直接分享几条折腾下来的体会。第一这种项目最花时间的永远不是调模型而是整理数据。标注之前一定把标准写清楚宁可前三天慢一点也别到了第三天发现300张图的白标了。找人帮忙标更是要盯住边界框的贴合度不同标准的数据混在一起训练模型精度上不去的。第二置信度阈值不是一个固定数值。训练完模型的置信度分布不同使用场景也不同。一定要在验证集和现场真实视频上各看一遍然后做一次精确率和召回率的权衡再定阈值。我见过不少人就照搬默认0.25结果在要求低误检的生产场景里天天误报。结合温室采摘的实际需求我将阈值长期保持在0.35左右兼顾了漏检和误检。第三不要迷信“更高精度的模型”。我试过更大规格的模型在验证集mAP上确实涨了一个多点但推理延迟翻了将近一倍现场反而因为跟不上节拍没法用。工程项目的评价指标不能只看单一mAP速度、显存占用、模型文件体积、易部署性都算进去才能选出真正适合场景的模型方案。YOLOv5s在此平衡点上的表现恰好是我在这个温室项目中需要的。第四文档记录永远是值得做的。模型的配置文件、训练超参数、数据处理脚本全部用git管理起来每次改动留一句commit说明。这个习惯帮我节省了大量回溯排查的时间——训练好模型之后一个月再回来看如果不记录当时的增强策略和阈值选择真的会完全想不起来当初为什么这么设置。靠记忆做项目在农业AI这种参数链路长的项目里一定会翻车。