田间地头的杂草问题,搞农业的人都知道有多头疼。尤其是水稻田里的稗草,跟秧苗长得那叫一个像,人工拔草费时费力,除草剂撒多了又怕伤苗、怕残留。我去年接到一个智慧农业相关的项目需求,就是要做一套能自动识别稻田杂草的视觉检测系统。当时对比了好几代目标检测模型,最终选定了YOLOv11来做核心检测器。这篇文章就把整个项目的来龙去脉、数据集处理、环境配置、训练调优和部署落地全盘托出,给同样在做农业视觉检测的朋友一条可以少走弯路的参考路径。
这个项目要解决的核心问题其实很朴素:在水稻生长的不同阶段,准确区分出哪些是水稻、哪些是杂草,进而为后续的精准除草、变量喷洒提供视觉依据。整套系统可以拆成三个关键部分:一是稻田杂草检测数据集的建设,二是YOLOv11检测模型的训练与调优,三是把训练好的模型落地到实际检测场景中。
1. 项目整体设计与技术选型思路
1.1 为什么选YOLOv11而不是YOLOv8或更早的版本
先聊一个很多人纠结的问题:检测模型那么多,为什么最后选了YOLOv11,而不是“久经考验”的YOLOv5或者YOLOv8?
从实际测试数据来看,YOLOv11在COCO数据集上的精度和速度表现都优于YOLOv8同尺寸版本,这主要得益于它在网络结构上做了一些调整。YOLOv11引入了C3k2模块来替代部分C2f结构,同时新增了C2PSA模块,这个模块相当于把注意力机制嵌进了特征提取的主干网络里。对于稻田杂草这种目标小、背景杂、遮挡多的场景,注意力机制能帮助模型更聚焦在杂草本身的纹理和形态特征上,而不是被水面反光、泥泞背景带偏。
还有一个很现实的因素是Ultralytics生态的成熟度。YOLOv11和YOLOv8共用同一套API,从数据标注格式到训练脚本再到部署导出,整个流程都是现成的。团队里如果已经有人用过YOLOv8,切换到YOLOv11基本零学习成本。我项目里就是因为团队以前跑过YOLOv8的车辆检测,所以迁移到YOLOv11非常顺滑,训练脚本改个模型权重名就能跑。
1.2 稻田杂草检测的难点拆解
做农业场景的目标检测,跟做通用物体检测完全是两码事。稻田里的情况之复杂,得先拆开揉碎看清楚了,才能理解后面每个技术决策的原因。
首要难点是类间相似度极高。水稻幼苗期的叶片形态和稗草的叶片形态非常接近,都是细长条带状的绿色叶片,颜色上也几乎没有差别,全靠叶鞘颜色、叶片毛糙程度、叶形夹角这些细微特征来区分。即便是人眼去看,没有经验的人也经常分不清,模型要学到的区分特征就更加微妙了。
其次是目标的多尺度问题。无人机拍的稻田俯视图里,一株杂草可能只占几十个像素,属于典型的小目标。而近地面视角拍的图像里,杂草可能占据画面很大一部分,叶片纹理清晰可见。同一个模型要同时处理这两种量级的目标,对特征金字塔的融合能力要求很高。这也是后来我调整anchor和输入分辨率的重要原因。
再就是环境干扰因素多。稻田里有水层反光、泥浆飞溅、稻叶互相遮挡、风导致的叶片晃动、光照角度变化等问题,这些都会给检测带来干扰。我刚开始用通用场景下训练出来的模型直接去测田间照片,误检率高得离谱,后来把数据多样性提上来之后才明显好转。
农业场景的独特之处还体现在目标本身会“长大”。水稻从苗期到分蘖期再到抽穗期,形态变化非常大,杂草也在不断生长和枯萎。一套系统要覆盖整个生长周期,数据集中就必须包含足够的生长阶段样本,否则模型的泛化能力根本不够用。
2. 稻田杂草数据集:从采集到增强的完整链路
2.1 数据采集方案与场景覆盖策略
数据集是整条链路的地基。我在项目里规划的采集方案分三个维度:设备维度、时间维度、地域维度。
设备维度上,我用三种方式并行采集。第一种是无人机低空航拍,飞行高度控制在5到10米,主要获取稻田的整体俯视画面,这类图像的特点是覆盖范围大、杂草目标小。第二种是手持拍摄,手机或微单贴近地面拍摄,获取杂草的细节形态特征。第三种是固定相机定时抓拍,在田间架设设备每隔一段时间自动拍一张,这样可以低成本积累大量不同光照条件下的样本。
时间维度是最容易被忽略的。水稻从移栽返青到成熟收割,每个阶段的杂草种类和形态都不一样。我在项目启动前先列了一张采集时间表,从秧苗期开始每10天采集一轮,一直持续到分蘖期结束,保证数据覆盖了杂草最容易跟水稻混淆的几个关键时期。
地域维度上,我跑了三个不同的水稻种植区,因为不同地区的土壤环境、水质条件会导致杂草形态有差异。比如同一品种的稗草,在不同肥力田块里长出来的颜色深浅都不同。所以我要求采集时每张图片都要记录田块位置、水稻品种、采集日期这些元信息,方便后续分析模型在不同子数据集上的表现差异。
2.2 数据标注与格式转换的坑
标注这块我直接用了LabelImg,因为团队熟、操作快,导出的XML格式转YOLO格式也就是几行代码的事。但有几个细节极其影响标注质量,这里必须展开讲。
第一个是标注边界的把握。稻田杂草经常是叶片互相交错,一株稗草的叶子会伸到另一株的框里。我的经验是宁可框得紧一点也不要框得松,框太松会把旁边的杂草叶子包进来,导致两个类别的特征混淆。标注规范里我明确写了:只框目标的主体部分,超出画面边缘的叶片不用管,严重遮挡的目标如果可辨认度低于六成就不框。
第二个是类别的设定。初期我把杂草分成了稗草、千金子、鸭舌草、异型莎草、节节菜五大类,但训练完发现模型在稗草和千金子上混淆严重。后来复盘发现是样本数量差距太大,稗草样本有近万张,千金子只有两千多张。后来我重新调整了任务边界,把数据量不足的类别暂时合并成“其他杂草”,等之后专门补数据再拆细。
第三个是数据清洗。田间采集的照片不是每张都能用的。凡是画面严重模糊的、阳光直射导致过曝的、大面积泥浆遮挡的,直接删除。我做了一轮初筛加一轮细筛,初筛用脚本检查图片的清晰度和亮度分布,细筛靠人眼过一遍。清洗后数据量大概损失了15%,但训练效果反而上来了。
转换格式的时候要注意YOLO格式的坐标归一化问题。LabelImg导出的XML里记录的是绝对像素坐标,转成YOLO格式时要除以图像的宽和高,这个除法必须用浮点数运算,不能四舍五入成整数,否则小目标的框会偏移得很离谱。我当时写转换脚本时就踩过这个坑,跑完训练才发现有一部分标注框整体偏移了几个像素,重新标注浪费了不少时间。
2.3 数据增强策略与样本平衡
数据增强不是越多越好,而是要贴合场景。
YOLOv11内置的增强管线里,Mosaic增强非常适合稻田杂草这种密集小目标场景。Mosaic把4张图拼在一起训练,等于增加了单张图像里的目标数量,模型能学到更丰富的上下文信息。我开启Mosaic后,模型在密集杂草地块的检测效果提升明显。
但Mosaic也有副作用。因为拼接后图像里的小目标实在太多,模型很容易产生“检测饱和”,就是一张图里明明只有几十个目标,它却输出了几百个框。后来我通过降低Mosaic的启用概率,同时加大HSV颜色增强的幅度来解决这个问题。稻田场景里绿色调的图像居多,适度增强色调和饱和度让模型不至于对特定颜色过度敏感。
样本平衡方面,我统计了各类别的标注框数量,按照数量从少到多排序之后,对尾部类别做了离线增强。具体操作是对少量类别的原始图片做了旋转、缩放、平移,再额外合成了一些含该类别的Mosaic图。这个方法比简单地重复采样效果好,因为单纯重复采样会让模型过拟合到那几张图上。
3. YOLOv11环境配置与模型训练实操
3.1 零基础环境配置:Python、CUDA与ultralytics
新手最容易在这个环节卡住,我先给一套经过验证的配置流程。
我用的环境是Ubuntu 20.04 + Python 3.9 + PyTorch 2.0.1 + CUDA 11.8。显卡是单张RTX 3090。这里要提醒,CUDA版本和PyTorch版本的匹配非常关键,版本不匹配会导致torch能装上但cuda不可用,训练时直接跑CPU,速度慢到你怀疑人生。安装PyTorch时直接用官方命令最稳妥:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118装完验证一下CUDA是否可用:
import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果是True且输出了3090,环境就没问题了。然后是装ultralytics:
pip install ultralytics这个包会带装opencv-python、matplotlib、pandas等一堆依赖,耐心等就行。版本我建议锁定在8.3.x以上,因为我测试过YOLOv11在8.3.0之后才稳定支持,太老的版本跑yolov11权重会报错。
Windows用户如果不想折腾CUDA,可以先用CPU版PyTorch跑通流程,哪怕慢一点,至少先把数据集和训练脚本验证到位。值得提醒的是,Windows下ultralytics的字体显示有时会出问题,训练时进度条和图像预览可能乱码,这只是显示问题,不影响训练本身。
3.2 训练超参数配置的实测经验
模型结构我选了yolov11s,这个尺寸在精度和速度之间最平衡。如果你的检测目标更小、图片更密,可以上yolov11m,但推理时间会增加约40%。对于无人机拍摄的俯视大图,我建议用yolov11m搭配切片推理,效果比直接用s模型提升明显。
训练命令我常用这样的格式:
yolo train data=/path/to/dataset.yaml model=yolov11s.pt epochs=200 imgsz=640 batch=16 optimizer=AdamW lr0=0.005这里解释几个核心参数的选择逻辑。
输入分辨率imgsz选640是因为这是COCO预训练权重的标准输入,直接用640可以最大限度利用预训练特征。如果你只做近景图像检测,可以尝试768甚至896,小目标AP会提升,但显存占用和训练时间都会涨。我实测过imgsz=640到768,mAP@0.5提升了大概2.3个点,但训练时间涨了接近一倍,最终还是根据实际部署设备的算力选了640。
batch大小16在3090的24G显存下刚好跑满。如果显存报错,优先把batch降到8,不要轻易降低图片尺寸,因为图片尺寸对检测精度的影响远大于batch大小。lr0初始学习率0.005是我多次试验后的经验值,这个值配合AdamW优化器在农业数据集上表现稳定。
数据集配置文件dataset.yaml要特别注意路径和类别顺序一定要跟标注时保持一致,否则训练出来的模型类别名全乱了。我见过有人把类别顺序调换了之后不停训练,模型一直不收敛,查了半天才发现是yaml里类名顺序跟标注文件里的类别ID对不上。
3.3 训练过程监控与收敛判断
训练时的loss曲线是判断模型状态的关键。我在项目里习惯同时盯三个指标:box_loss、cls_loss、dfl_loss。正常情况下三条曲线都应该是下降趋势且逐渐平缓。
如果cls_loss降到一定程度后开始震荡,说明学习率偏大了,可以在训练后期用余弦退火调度器把学习率降下来。如果box_loss一直不降,大概率是标注框有问题,回去检查标注质量比调参更有效。
训练完成后不要只看loss,一定要看验证集上的mAP指标。我在项目的第150轮时看到mAP@0.5达到0.876,但mAP@0.5:0.95只有0.512,这个差距说明模型对目标框的精准定位能力还不够,后来我通过在数据增强里增加轻微旋转和小幅缩放,把mAP@0.5:0.95提升到了0.583。
这里放一组我实测的不同配置对比表格,供参考:
| 配置方案 | mAP@0.5 | mAP@0.5:0.95 | 推理耗时(ms) |
|---|---|---|---|
| yolov8s, 640, batch16 | 0.812 | 0.447 | 4.2 |
| yolov11s, 640, batch16 | 0.876 | 0.512 | 3.6 |
| yolov11s, 640, batch16, 增强后 | 0.893 | 0.583 | 3.7 |
| yolov11m, 768, batch8 | 0.917 | 0.621 | 7.1 |
4. 模型评估、推理测试与部署落地
4.1 评估指标的正确打开方式
模型训练完,得先过评估这一关。除了篇幅表格里的mAP值,precision和recall也要单看。杂草检测里,recall比precision更要紧,因为漏掉一株杂草顶多后面再补一次除草,但如果误喷了除草剂把水稻伤了,损失就大了。所以我在实际部署时的置信度阈值调得比较低,大概是0.25,宁愿多框一些候选区域,再由后续的决策层来做最终判断。
我还会专门看每个类别的单独AP。因为我目标是稗草、千金子、鸭舌草、异型莎草这几类,如果某一类的AP明显低于整体平均值,就说明某些特征没有学好。比如我第一次训练完,鸭舌草的AP只有0.41,远远低于其他类别。后来排查发现鸭舌草的样本大多生长在水边阴影区域,画面亮度低,后续我补了一批明亮光照下的鸭舌草样本,AP才提上去。
4.2 推理结果保存与批量测试
YOLOv11的推理接口用起来很简单,但有几个细节需要注意。跑单张图片检测:
yolo predict model=best.pt source=/path/to/image.jpg save=True conf=0.25关键参数conf控制置信度阈值,save=True会把标注结果保存成新图片。要保存检测出的物体裁剪图,可以用save_crop=True,这个对后续做样本积累很有用。
批量测试时,我推荐直接在Python里写脚本,方便同时统计性能指标:
from ultralytics import YOLO model = YOLO("best.pt") results = model.predict(source="/path/to/images/", imgsz=640, conf=0.25, save=True, project="/path/to/output/") for r in results: boxes = r.boxes print(f"检测到 {len(boxes)} 个目标, 类别分布: {r.boxes.cls.unique()}")这里有个经验之谈:如果用视频作为输入源来做测试,建议按帧切图或者降低检测频率,否则推理速度会跟不上视频帧率,结果看起来就会卡顿。
4.3 部署方案选择:边缘设备与TensorRT加速
稻田场景大多在野外,信号不稳定、供电条件差,所以部署设备选择了NVIDIA Jetson系列边缘盒子。Jetson的GPU架构跟桌面显卡不同,直接用.pt权重推理很慢,需要在目标设备上把模型转成TensorRT格式。
转换命令很简单:
yolo export model=best.pt format=engine device=0但要注意,TensorRT engine文件跟CUDA版本强绑定,必须在部署机上执行转换,不能在开发机上转完再拷过去,不然会报错。另外,Jetson上跑YOLOv11建议开启FP16精度,精度损失在0.5%以内,推理速度接近翻倍。
部署后的推理代码可以直接用ultralytics的API,关键是把device参数指到GPU上。我做了一个简单的异常重试机制,如果某帧检测结果为空,不要直接丢弃,可以降低置信度阈值重新推理一次,因为田间场景偶尔会有水面反光导致漏检的情况。
5. 常见问题与避坑实录
5.1 训练不收敛的排查顺序
遇到训练loss不降或指标上不去,我建议按这个顺序排查:先看数据标注有没有错,再看数据增强是不是太激进,最后才调学习率和优化器。太多人一上来就调参,结果问题出在标注文件里某个类别ID错乱,调再多参也没用。
最经典的一个坑是类别不平衡导致模型只学大类别。我做过一个测试,当稗草和其他杂草的比例超过15:1时,模型几乎把所有杂草都预测成稗草。后来用Focal Loss或者直接做类别重加权来解决。ultralytics里可以通过修改损失权重来实现简单版本的重加权。
5.2 小目标漏检问题
这是农业检测里绝对躲不开的问题。无人机拍的俯视图里杂草目标太小,常规特征图在小目标位置的分辨率不够。我的解决方案是组合拳:第一,提高输入分辨率到768或更高;第二,开启推理时的自动切片功能;第三,就是后面数据层面增加更多小目标样本,让模型在训练时就能充分看到小目标特征。
切片推理这个功能ultralytics支持得不错,把大图切成重叠的小块分别推理再合并结果,对无人机俯视图这种场景效果非常显著。代价是推理时间会成倍增加,但对离线分析场景来说完全可接受。
5.3 标注错误对模型的隐形影响
很多初学者低估了标注错误的影响。我认为农业场景的标注错误比通用场景的影响还要大,因为杂草和水稻长得太像了,哪怕框偏了几个像素,框进去的特征可能就从“杂草”变成了“水稻”。
我的经验是标注完成后必须抽检,按照每个类别随机抽10%的标注结果让第二个人复核。我跟标注团队开了三次标注规范对齐会,专门讨论边界情况和疑难样本的处理规则,这才保证了后面训练的稳定性。
5.4 环境配置常见问题速查
| 问题 | 原因 | 解决办法 |
|---|---|---|
| torch.cuda.is_available()返回False | CUDA版本和PyTorch不匹配 | 用官方安装命令重装对应cu版本的PyTorch |
| 训练时显存溢出 | batch太大或图片尺寸太大 | 降低batch值,优先保证图片尺寸 |
| 导出engine文件报错 | 设备间CUDA版本不一致 | 在部署设备上重新导出engine |
| 推理结果全是背景误检 | 置信度阈值设置偏低 | 提高conf阈值,或检查类别名称是否错乱 |
| 标注框偏移 | XML转YOLO格式时整数运算 | 用浮点数做归一化坐标 |
写在最后
这个项目做下来,我最深的感触是智慧农业视觉检测真正的难点不在模型结构,而在于对农业场景的理解和数据层面的精细化处理。稻田杂草检测尤其如此,因为目标之间的差异实在是太细微了,任何环节偷懒都会在最终精度上还回来。我自己在数据清洗和标注规范上花的时间,最终都转化成了模型性能的提升。如果你正在做类似的项目,我的建议是先别急着上复杂的模型结构,踏踏实实把数据集做好,把环境配稳,把每一步的评估指标吃透,YOLOv11只是最后那个顺理成章的收尾而已。