简介:一份面向智慧农业、计算机视觉及农业信息化开发者的实践型PDF文档,围绕YOLOv11目标检测算法在作物生长阶段识别中的应用展开,并延伸至精准施肥决策设计。文档共37页,压缩包内为1个PDF文件,大小约2.3MB,支持阅读器左侧大纲与目录章节快速定位,内容完整、图表清晰。目前已有54人学习下载。全文按十个章节组织,既梳理了YOLO系列发展与YOLOv11核心架构,又详细讲解了作物数据集构建、数据标注与预处理、模型训练与优化、精准施肥决策算法设计、系统集成与部署,并通过实践案例展现真实落地流程。整体兼顾算法原理与工程实现,可帮助读者建立“生长阶段识别—施肥决策”的完整技术框架,适合用于课程设计、毕业设计、企业农业智能化改造等项目参考,也便于快速了解智慧农业中目标检测技术的应用路径。
1. 作物生长阶段识别为什么用 YOLOv11 打底
大田管理的施肥决策里,最容易被忽略的一环,是把作物当前所处的生长阶段量化成可输入决策模型的数据。传统做法靠农技员下田剥叶观察,一天只能覆盖几百亩,主观差异也大;换成机器视觉之后,问题就变成“用什么模型和流程,在可控成本内准确判断阶段”。YOLOv11 把训练、验证、导出、部署串成一条完整链路,从 n 到 x 五档尺寸覆盖不同算力设备,智慧农业团队拿到手可以先跑基线,再根据漏检情况定向优化。下面记录的是用 YOLOv11 完成作物生长阶段识别,并把检测结果换算成精准施肥建议的常见做法,大致走结构选型、数据准备、模型训练、决策映射和实地优化五段,适合已有基础目标检测经验、想把视觉结果真正接到农机或水肥一体机上的工程师。
2. YOLOv11 网络结构拆解:哪些设计对作物检测是增量
摄像头拍下的作物图像,不会因为检测框架宣传的“精度高”就直接得到正确结果,关键在于相同推理开销下,结构有没有把小目标和遮挡目标保留住。小麦、水稻这类俯视大田画面里,苗期叶片细长,刚抽出的稻穗往往只有几十像素,它们更多依赖浅层特征和较小的下采样步长。YOLOv11 的改进点大多围绕这个诉求展开。
2.1 骨干与颈部:C3k2 和 C2PSA 各自在什么位置生效
在骨干网络中,YOLOv11 用 C3k2 替代了上一代 YOLOv8 里的 C2f。C3k2 的瓶颈模块内部采用 3×3 卷积,并通过跨阶段连接把两条分支的特征拼回通道维度。这么调整的直接收益是计算量比 C2f 略低,但跨层特征复用能力没有被削弱。对作物识别来说,这意味着苗期小叶片、分蘖后拥挤的茎秆堆叠信息,在经过多次下采样之后仍然有机会出现在后续特征层中,而不是被深层网络直接忽略。
在较大尺寸的 YOLOv11 模型里还加入了 C2PSA 模块,这是跨阶段结构结合自注意力的组合。自注意力覆盖的是整张特征图的空间依赖,能把“叶片朝某个方向倒伏”“穗头集中出现在画面中央区域”这类场景上下文更早地编码进去。需要明确的是,C2PSA 只在 m 及以上尺寸启用,温室边缘盒子这类低算力环境没必要硬追。
颈部的 PAN-FPN 结构继续把三路特征层做自上而下和自下而上的双向融合。最终头部拿到的三路输出分别是 8 倍、16 倍、32 倍下采样特征图。8 倍下采样对应感受野较细,对 20 到 60 像素的小苗叶片和稻穗更友好;32 倍输出则负责捕捉整株、整垄的大目标。在作物阶段识别上,不要只盯着最终 mAP,而应分开统计不同尺寸目标的 AP,才能知道模型到底是靠大目标拉高指标,还是真的解决了小目标识别。
2.2 解耦检测头与作物阶段任务的对应关系
YOLOv11 的检测头延续 anchor-free 解耦设计,分类分支和回归分支分开输出。这样做的原因是,分类要区分苗期、分蘖期、拔节期这些视觉差异并不大的相邻阶段,回归则要精确框出叶片、茎秆、穗头的边缘;两个目标的梯度尺度不同,分开更新更容易收敛。
从实用角度看,解耦头带来一个操作要求:分类分支置信度和回归分支 IoU 要分开看。训练时若发现分类损失降不下去,多半是相邻阶段样本之间的视觉差异太弱,而不是模型结构有问题。对作物生长阶段这种“边界连续”的标注任务,更应该把同一植株按主茎发育程度归到唯一类别,而不是要求模型去识别过渡时刻的细分状态。
2.3 按算力选尺寸:n/s/m 在田间设备上的取舍
| 型号 | 参数量(约) | 推理算力(约) | 适用部署位置 |
|---|---|---|---|
| yolo11n | 2.6M | 6.5G | 树莓派、温室巡检小车、低端工控机 |
| yolo11s | 9.4M | 21.5G | 边缘计算盒子、中端工控机 |
| yolo11m | 20.1M | 68.0G | 服务器批量推理、云端定时任务 |
初到智慧农业项目时,我一般直接从 yolo11n 开始跑通全部流程,而不是一上来就上 x 型号。n 尺寸能在一张消费级显卡上完成训练,数据流动、标注格式、决策函数都能先打通;等拿到第一版验证集指标后,再根据中小目标 AP 的差距决定是否换成 s 或 m。从单位面积部署成本看,大田无人机巡检允许较慢的单张推理,但连续值班的边缘摄像头就需要更紧凑的模型。
3. 环境配置、数据准备与训练自己的作物阶段模型
把 YOLOv11 用到自己的地块数据上,不是拿官方预训练权重直接推理就完事。田野里的作物背景、光照、拍摄角度与公开数据集差异很大,必须在自有数据上微调。这一节写可复现的环境配置、数据目录组织方式和训练命令,暂不展开算法实验。
3.1 YOLOv11 环境配置与依赖安装顺序
先创建干净的 conda 环境,再按“先 PyTorch、后 ultralytics”的顺序安装,能避开最常见的依赖冲突。
# 创建独立环境并激活 conda create -n yolo11 python=3.10 -y conda activate yolo11 # 先固定 PyTorch 与 CUDA 版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124 # 再装 ultralytics pip install "ultralytics>=8.3"安装完成后检查 CUDA 是否真的可用:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"第一段命令固定了 Python 3.10 版本,避免后续依赖解析出偏差。先装 torch 再装 ultralytics 的原因在于,PyTorch 版本决定 CUDA 运行时,顺序反过来时 ultralytics 有可能拿到 CPU 版 torch,导致训练时显卡利用率一直为 0。第二行检查若返回True,说明可以直接在 GPU 上训练;返回False时先看显卡驱动,再看是否正确指定了--index-url里的 cu 版本。
提示:装完不要急着加一堆数据增强库。Ultralytics 自带的依赖已覆盖标注读取和基础增强,先跑通默认训练,再把额外库引进来。
3.2 作物生长阶段数据集的组织与标注
以小麦为例,把阶段拆成六类:苗期 seeding、分蘖期 tillering、拔节期 jointing、孕穗期 booting、抽穗期 heading、成熟期 maturity,覆盖一季作物的生长全周期。相邻阶段在外观上有重叠,拔节期和孕穗期的茎秆高度差异并不明显,标注时要约定以主茎形态为准,不要用叶片颜色这类主观性强的特征去分类。
数据集目录固定为 YOLO 格式,images 和 labels 分开,训练集与验证集按 8:2 划分:
wheat_data/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── wheat_stage.yamlwheat_stage.yaml的内容:
path: wheat_data train: images/train val: images/val names: 0: seeding 1: tillering 2: jointing 3: booting 4: heading 5: maturity标签文件是 txt 格式,每行一个目标,内容为class_id center_x center_y width height,坐标值归一化到 0 到 1 之间。标注时框住整株主体,而不是单独标每个分蘖;labelimg 或 label-studio 都支持 YOLO 格式导出,导出前要确认坐标系是归一化坐标而不是像素坐标。
标注完成后过一遍类别数量。常见问题有三类:叶片被遮挡时强行标注、跨期样本混进早期阶段、逆光照片未归类。前两者在稍作修剪后对模型伤害不大,但逆光低对比度样本大量进入训练集,会导致验证时小目标 AP 明显偏低,需要提前用直方图均衡化或增加曝光增强来补偿。
3.3 训练命令与参数怎么调
训练命令:
yolo detect train model=yolo11n.pt \ data=wheat_stage.yaml epochs=150 imgsz=640 batch=16 \ device=0 workers=4 project=runs/detect name=wheat_v1参数说明:model=yolo11n.pt表示加载官方 n 尺寸预训练权重,而不是从头训练,预训练特征对叶片纹理已有基础响应,可显著缩短收敛时间;imgsz=640是训练输入尺寸,边长会被缩放后作为网络输入;batch=16在 12GB 显存显卡上较稳妥,显存偏低时降到 8;workers=4控制数据加载线程数,机械硬盘压力大时可降到 2;name=wheat_v1是实验名,后续版本用 v2、v3 继续排列。
训练过程中除了看 loss 曲线,还要盯住验证集混淆矩阵。Ultralytics 的训练日志里results.png每轮刷新,矩阵对角线上数值最高的类别就是模型最有把握的阶段;分蘖期和拔节期互相混淆属正常现象,真正要处理的是苗期误判到成熟期这种跨大阶段错误,出现这类错误时先检查数据标签归属,而不是急着堆训练轮数。
如果验证集 mAP 不涨,常见原因不是模型结构,而是训练集中相邻阶段样本比例失衡,建议对样本量不足的阶段做上下翻转、小角度旋转增强,而不是直接加 epochs。mosaic 增强在作物叶片场景下会把叶子拼碎,影响小目标学习,可以适度关闭 mosaic 并对比验证集波动。
3.4 预测后保存推理结果:命令与 Python 接口两种写法
训练完成后,在验证集上做推理时不能只图看一眼效果,必须把结果保存下来用于后续误差分析。先用命令行快速跑一遍:
yolo detect predict model=runs/detect/train/weights/best.pt \ source=samples/ save=True save_txt=True save_conf=Truesave=True会输出标注了边界框的图片;save_txt=True留一份 YOLO 格式标签文件;save_conf=True在标签文件每行末尾追加置信度。农业复核场景里置信度是评估决策风险的重要来源,建议保留而不是省掉。
当需要对保存结果做二次处理时,推荐直接用 Python 接口,这样除了图片还能把每个检测框单独裁剪出来,方便回溯错误样本:
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict(source="samples/20240512", conf=0.25, iou=0.45) for i, r in enumerate(results): img = r.orig_img for j, box in enumerate(r.boxes): # 取当前框的整型坐标 x1, y1, x2, y2 = [int(v) for v in box.xyxy[0].tolist()] stage = r.names[int(box.cls[0])] score = float(box.conf[0]) crop = img[y1:y2, x1:x2] # 裁剪块写入独立文件夹,文件名带阶段和置信度 out_path = f"crops/{i}_{j}_{stage}_{score:.2f}.jpg" cv2.imwrite(out_path, crop) print(out_path, stage, score)这段代码先遍历每张图,再遍历图上每个检测框,取出类别 id 换成人可读的阶段名称,最后把裁剪块写出。裁剪图是后续改进数据集的直接素材:许多模型难以解释的误判,仅从标签坐标看不出来,但拿裁剪块对着原图一比较,常能发现标注遗漏或类别边界问题,这些新样本在一季之后可用于下一轮训练。
4. 从识别结果到精准施肥决策
目标检测输出的只是一堆边界框和类别,农业系统需要的却是“当前这个田块,按哪套施肥方案执行”。把视觉输出转成决策要经过三步:先把同一画面里多个植株的检测结果归结为一个阶段,再把这个阶段映射到施肥建议,最后让建议在时间维度上稳定。下面逐层展开。
4.1 单帧内多个检测怎么汇合成阶段结论
田间一张照片里经常出现几十株作物,每株检测结果可能不同。不能直接取单框最高置信度,因为个别发育异常的植株会带偏结论。我一般按类别做置信度和投票:同一画面中,每个目标产生一个置信度累加到对应类别上,票数最高的类别作为该帧的阶段。
import numpy as np def frame_stage(r): cls = r.boxes.cls.cpu().numpy() conf = r.boxes.conf.cpu().numpy() names = r.names # 按类别累计置信度 votes = {} for c, s in zip(cls, conf): votes[int(c)] = votes.get(int(c), 0) + float(s) best_id = max(votes, key=votes.get) total_conf = votes[best_id] / max(len(cls), 1) return names[best_id], total_conf用置信度和而不是简单计数,原因是低置信度检测在数量多时可能主导票池,而置信度和能让每个检测框按可靠度加权。返回的total_conf是归一化到目标数之后的平均置信度,用于后面做帧间稳定性判断。如果画面中出现大面积杂草导致误检,函数可能返回杂草类,此时要先过滤类别白名单再调用frame_stage;白名单应在部署配置里显式写出,不要散落在代码里。
4.2 将阶段编码成施肥建议函数
得到阶段后,接下来把农学配方案转换成可执行映射,这一层是确定性逻辑,不需要训练数据。以某个区域的氮磷钾配比方向为例:
| 阶段 | 氮 N | 磷 P | 钾 K | 备注 |
|---|---|---|---|---|
| 苗期 | 低 | 高 | 中 | 促根,防旺长 |
| 分蘖期 | 中 | 中 | 中 | 结合分蘖数调整 |
| 拔节期 | 高 | 中 | 中 | 需水肥关键期 |
| 孕穗期 | 中 | 中 | 高 | 穗分化需求 |
| 抽穗期 | 低 | 中 | 高 | 叶面补钾为主 |
| 成熟期 | 停 | 停 | 停 | 防贪青晚熟 |
上表只给趋势方向,落地时要用当地土壤检测数据和品种方案校准用量。决策代码保持简单,直接映射:
def recommend_fertilizer(stage): policy = { "seeding": {"n": 3, "p": 6, "k": 4, "score": 30}, "tillering":{"n": 8, "p": 4, "k": 5, "score": 50}, "jointing": {"n": 12, "p": 5, "k": 7, "score": 100}, "booting": {"n": 6, "p": 6, "k": 10, "score": 80}, "heading": {"n": 2, "p": 4, "k": 9, "score": 60}, "maturity": {"n": 0, "p": 0, "k": 0, "score": 0}, } return policy.get(stage, policy["seeding"])score字段是阶段可供与该阶段的建议施肥阈值评分,用于后续与变量施肥机联动,当前可以让农机按 N/P/K 值和地块面积换算实际施肥量。整体思路是把农学规则写成版本化策略字典,农艺师改任何阶段的配比时,直接改动字段即可,不需要重训视觉模型。
4.3 让决策稳定:白名单过滤、时间平滑与抽样节奏
单帧识别结果不能直接驱动设备,因为同一植株在不同光照下会出现置信度波动,相邻两帧可能给出不同阶段。做法上加两层保护。第一层是时间平滑:固定摄像头每 10 分钟拍 3 张,窗口内用一个队列记录最近阶段的投票结果,取出现次数最多的阶段作为输出。第二层是置信度过滤:阴雨天叶片颜色偏暗,正反馈误差会被放大,可在平滑前先丢弃置信度低于阈值的整帧。
from collections import deque, Counter stage_buffer = deque(maxlen=5) def smooth_stage(stage_name, min_conf=0.3): # 低于置信度的帧不进入缓冲区 if min_conf <= 0.5: stage_buffer.append(stage_name) if len(stage_buffer) < 3: return None return Counter(stage_buffer).most_common(1)[0][0]deque(maxlen=5)维护最近 5 帧,少于 3 帧时不输出,避免样本量不足产生抖动。平滑后的阶段再交给recommend_fertilizer,设备收到的阶段变化不会来回跳变。实际部署时,抽样节奏要匹配生长速度:苗期三到五天一次,拔节后每天一次即可,过密采样只会放大噪声。
需要强调的是,精准施肥的“精准”主要靠总量控制,而不是单轮识别精度。一季作物各阶段追肥量加起来不能超过目标产量对应的总养分预算,所以识别到成熟期后必须走零施肥逻辑,防止贪青晚熟。若要做整季追踪,可在固定机位场景下用 YOLOv11 目标跟踪配合 ByteTrack 为每株作物分配 ID,用 ID 对应历史阶段序列分析生长速率,再按速率修正用量;这是可选项,但能把“整块田一个阶段”的粗粒度提升到“每批作物一个阶段”。
5. 小目标优化、PIOUv2 与推理结果留痕
最后落到农业场景里反复出现的三个工程问题:小目标怎么提升、损失函数改造实验怎么做、结果怎么留存审计。
5.1 小目标优化:切片推理与分辨率调整
当验证集里 20 到 40 像素的小苗漏检率偏高,第一优先是提升imgsz到 960 或 1280,训练和推理同尺寸时,特征图对小目标有映射增益;缺点是显存随之上升,batch 必须缩小。若模型尺寸不允许进一步提升,常见做法是用切片推理处理航拍大图,在 ultralytics 上叠加 SAHI 实现:
pip install sahifrom sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="ultralytics", model_path="runs/detect/train/weights/best.pt", confidence_threshold=0.25, ) res = get_sliced_prediction( "samples/aerial_20240512.jpg", detection_model, slice_height=512, slice_width=512, overlap_height_ratio=0.2, overlap_width_ratio=0.2, )切片尺寸 512 表示把整幅航拍图切成多个 512×512 小块做检测,再把边界框拼回原图坐标。overlap_*_ratio设为 0.2,让相邻切片重叠一小条区域,减轻目标被切片边界截断造成的漏检。代价是单张推理次数增加;在 2000 像素宽的航拍图上,我用这个配置比单张整图推理耗时约多一倍,但小目标漏检通常明显下降。
5.2 PIOUv2:回归损失改进的实验路径
如果切片后仍卡在细微漏检上,再考虑从损失函数入手。近年社区优化目标检测任务时,会把回归分支的 IoU 损失换成对边界对齐更敏感的变体,PIOUv2 是其中一种有代表性的方向,它对细长叶片、弯穗这类非矩形目标可能比原版 CIoU 更灵敏。
农业项目里,损失函数替换适合作为第二优先实验,排在切片推理之后。原因在于农业数据量通常不大,损失函数改动带来的差异很难与随机种子区分。如果要做,在 ultralytics 的 loss 模块中找到回归损失对应的 IoU 类型,把参数换成 PIOUv2,用 30% 训练集跑一次基线,再对比验证集 mAP50 与 mAP50-95;差距不足 0.5 就回退默认损失。若当前版本的损失实现没有暴露可配置的 IoU 类型,不值得为它维护自定义代码分支。
5.3 留痕与验证:把预测保存成决策审计日志
生产环境使用阶段识别时,我一般把单帧决策连同证据保存成一条 JSON 记录,而不是只存原图或标签:
import json log_entry = { "frame_id": "20240710_143205", "field_id": "wheat_plot_05", "model": "yolo11s-20240620", "stage": "heading", "confidence": 0.71, "fertilizer": {"n": 2, "p": 4, "k": 9, "score": 60}, "crop": "logs/crops/20240710_143205_heading_0.71.jpg", "raw_txt": "logs/txt/20240710_143205.txt" } with open("logs/20240710_143205.json", "w", encoding="utf-8") as fp: json.dump(log_entry, fp, ensure_ascii=False, indent=2)日志包含阶段名、置信度、施肥建议和证据图片路径,农事审计按这条数据链就能回溯当时决策依据;后续迭代训练时,也可以拿联合日志自动采集难以区分的负样本,复核效率远高于翻整个原图库。
验证模型是否真正适合上线,最后建议单测三件事:先看验证集里按目标尺寸拆分的小目标 AP 是否同步上涨,避免大目标拉高整体指标;再看连续 100 个生产帧里阶段跳变次数占比,跳变过高说明平滑窗口或置信度阈值还要调整;最后抽一批裁剪图做人工复核,肉眼判断分类理由与农艺常识是否一致。这三项都通过,再放开决策函数给施肥机执行。
本文还有配套的精品资源,点击获取