ARTICLE DETAIL

资讯详情

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

YOLOv11多作物叶片病害识别与Jetson部署全流程解析

YOLOv11多作物叶片病害识别与Jetson部署全流程解析 简介一份约26页的PDF技术文档聚焦YOLOv11在多作物叶片分析及病害识别中的完整落地流程适合从事精准农业、计算机视觉目标检测的研究者与工程人员。包体为单个PDF文件大小1.94MB支持目录章节跳转、左侧大纲显示与快速定位文字图表显示正常。内容按七章递进组织涵盖精准农业与病害识别背景、YOLOv11网络结构与训练原理、多作物叶片数据集的收集标注与预处理、模型训练优化、病害识别实现流程以及小麦、温室蔬菜、果园等实际落地案例并讨论了数据、模型、应用三方面的挑战与未来方向。文档强调YOLO单阶段检测在速度和精度上的优势可帮助读者建立从数据准备到模型部署的完整认知。目前已有62人学习。1. 精准农业里的 YOLOv11田间病害识别真正难在哪大田巡检的检测项目里模型精度常常不是最拖后腿的环节数据怎么组织、病斑太小查不出来、训练完在设备上推不动才是让一堆人卡在原地的主要原因。YOLOv11 多作物叶片分析技术与病害识别全流程就是一套覆盖“多作物叶片数据准备 → 病害识别模型训练 → 小目标优化 → Jetson 边缘部署”的完整做法一套 YOLOv11 模型同时识别水稻、玉米、番茄等不同作物的叶片区域和病害类别并输出每一张图的病害框、类别和置信度最后落地到田间设备上跑推理。这篇笔记写给刚接手农业检测项目、正在评估技术路线的工程师团队。下面的内容全部按我实际操作过的顺序来写包括脚本、参数和我翻过车的细节。2. 多作物叶片数据准备从田间照片到 YOLO 训练集2.1 多作物叶片分析的数据采集与目录规划农业检测和公开数据集最大的区别是一张照片里叶片重叠严重、背景是泥土和杂草、同一个病斑在不同作物上表现差异很大。所以第一步不是急着标数据而是先规划目录。我一般按作物建子目录文件名带作物前缀和拍摄日期例如rice_20240712_001.jpg、corn_20240720_015.jpg。这个命名习惯后面切训练集、验证集时非常关键能直接按前缀做分层划分避免某个作物的照片全进了训练集而验证集里全是另一个作物。目录结构我建议这样建datasets/leaf_disease/ ├── json/ # labelme 原始标注 ├── images/ # 原始图片 ├── labels/ # 转换后的 YOLO txt ├── train/images/ ├── train/labels/ ├── val/images/ ├── val/labels/ └── test/images/注意test目录不是必须的但强烈建议预留。因为农业模型的评估不能只在验证集上看数字最后要用一批拍新田块的照片来测那部分数据在训练期间一次都不能碰。采集照片时有个硬性要求病斑在画面里至少占 40 像素以上否则标注出来也没法训练。田间不是实验室手机、无人机、固定摄像头拍出来的叶片尺度差异极大同一个模型要同时应对这两种尺度后续要做第 4 章的切图优化。病害类别划分也要提前统一是只做“有病/无病”还是按作物细分具体病害。我建议按“健康 具体病害”的方式建类别因为农户最关心的就是“这是什么病、该打什么药”只告诉人家“有病”解决不了问题。2.2 用 labelme 标注转 YOLO 格式一个脚本解决问题标注工具我用 labelme因为它能画多边形叶片边缘不规则矩形框会带入大量背景干扰。labelme 输出的是 JSON 文件YOLOv11 训练需要的是 txt 文件两者格式差别很大。多作物场景下叶片分析技术的第一步就是做格式转换下面是我常用来做转换的脚本按自己的标注目录改一下就能跑。# labelme_to_yolo.py import json, os, glob from pathlib import Path # 注意类别顺序必须和后续训练 yaml 里的 names 一一对应 classes [healthy, rice_blast, corn_leaf_spot, tomato_late_blight, cucumber_downy_mildew, wheat_rust, grape_black_rot, apple_scab] def convert_labelme(json_path, out_dir): with open(json_path, encodingutf-8) as f: data json.load(f) img_w, img_h data[imageWidth], data[imageHeight] txt_path os.path.join(out_dir, Path(json_path).stem .txt) with open(txt_path, w, encodingutf-8) as out: for shape in data[shapes]: label shape[label] if label not in classes: continue cls_id classes.index(label) points shape[points] # 多边形外接矩形转 YOLO 归一化坐标 xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) if __name__ __main__: os.makedirs(labels, exist_okTrue) for f in glob.glob(json/*.json): convert_labelme(f, labels) print(converted:, len(glob.glob(json/*.json)))脚本里最关键的是classes的顺序定义。YOLO 格式的第一列是类别索引如果标注的标签名和classes顺序对不上后面训练出来的模型会出现类别错乱而且这种错乱没法从 loss 曲线上看出来。转完格式后随机抽几张图用yolo detect predict或者可视化脚本把标注框画回去检查一遍再进入训练这个步骤别省。另一个容易忽略的问题是坐标归一化。YOLO 格式里中心点、宽高都是 0 到 1 的小数脚本里除以img_w和img_h做了归一化。如果你的原始图片有 EXIF 旋转信息得先统一转成无旋转的 JPG 再做标注否则坐标和实际像素对不上这是很多标注工具里被隐藏起来的坑。2.3 样本均衡与划分按作物分层别让模型偏心多作物的样本不均衡远比公开数据集严重。同样是 1000 张图水稻稻瘟病的框可能有 4000 个而番茄晚疫病只有 300 个。直接训练会出现一个很典型的现象mAP 看着不错但按类别看番茄的 AP 只有 20%大模型在样本多的类别上表现好掩盖了这个问题。我一般用按作物分层的划分脚本保证每个作物在 train、val、test 里的比例基本一致。# split_by_crop.py import os, random, shutil from collections import defaultdict random.seed(42) images_dir images labels_dir labels out_dir split_dataset def split_for_crop(crop, imgs, train_ratio0.8, val_ratio0.15): n len(imgs) n_train int(n * train_ratio) n_val int(n * val_ratio) for i, img in enumerate(imgs): label img.replace(.jpg, .txt) if i n_train: dest train elif i n_train n_val: dest val else: dest test shutil.copy(os.path.join(images_dir, img), os.path.join(out_dir, dest, images, img)) shutil.copy(os.path.join(labels_dir, label), os.path.join(out_dir, dest, labels, label)) files [f for f in os.listdir(images_dir) if f.endswith(.jpg)] by_crop defaultdict(list) for f in files: crop f.split(_)[0] by_crop[crop].append(f) for crop, imgs in by_crop.items(): random.shuffle(imgs) split_for_crop(crop, imgs)这个脚本做的事很朴素但特别重要同一作物的图片随机打散后按 81.50.5 划分文件名里不体现作物的项目要先写个映射表。跑完查看每个子目录里各类别的框数量如果番茄晚疫病在验证集里只有几个框那宁可把这几个框挪进训练集也要保证验证集的每个类别有 30 个以上样本否则评估数字没有参考意义。3. YOLOv11 环境配置与训练从 yaml 到 best.pt3.1 YOLOv11 环境配置虚拟环境隔离是必须的“yolov11环境配置”翻车率很高。80% 的问题出在一个环境里同时装了 YOLOv8、YOLOv5 和 labelme依赖互相覆盖import 直接报错。我的做法是每个项目单独建一个 Python 虚拟环境。这里有个经验不要用 conda 的 base 环境也不要直接pip install ultralytics装到全局。python -m venv yolo11_env source yolo11_env/bin/activate pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 python -c from ultralytics import YOLO; print(YOLO.__version__)--index-url指定的是 PyTorch 官网的 CUDA 轮子地址cu118对应 CUDA 11.8。你的机器 CUDA 版本如果是 12.x把这里改成cu121或更高。Ultralytics 包会自动带一个匹配版本的 torch但那种装法经常装成 CPU 版本训练时 CUDA 不可用所以先装 torch 再装 ultralytics 的顺序更可靠。装完以后直接yolo predict跑一张图验证环境比训练到一半才发现显存没跑满要省事得多。3.2 数据 yaml 与最小训练命令YOLOv11 训练入口就是一个 yaml 文件加一条命令。这个 yaml 决定了模型认识哪几种树叶、从哪个目录读图配错了后面全部白干。多作物叶片分析里面叶片类别和病害类别混在同一份 names 列表里再常见不过。# datasets/leaf_disease.yaml path: /home/user/datasets/leaf_disease train: train/images val: val/images test: test/images nc: 8 names: 0: healthy 1: rice_blast 2: corn_leaf_spot 3: tomato_late_blight 4: cucumber_downy_mildew 5: wheat_rust 6: grape_black_rot 7: apple_scabpath是绝对路径建议写绝对路径而不是相对路径因为很多训练服务器上工作目录和代码目录不一致train/images是在 path 之下的相对路径。nc必须和 names 数量一致。训练命令用 yolov11s 起步内存和显存都吃紧的团队先用 s 而不是 x。yolo detect train \ datadatasets/leaf_disease.yaml \ modelyolo11s.pt \ epochs200 \ imgsz1280 \ batch16 \ patience30 \ device0 \ projectruns/leaf_disease \ nameexp01这条命令里我唯一强调的参数是imgsz1280。很多人拿着默认 640 直接训练病斑在缩小的图上只有十几个像素模型根本学不到纹理特征后面怎么调注意力都没用。GPU 显存不够的话先把 batch 从 16 降到 8不要动 imgsz1280 对农业病害识别来说是底线而不是上限。训练完成后产物在runs/leaf_disease/exp01/weights/下best.pt和last.pt。best.pt是按验证集指标保存的最优权重后续推理全部用它。训练日志里出现nanloss 的话优先查学习率是不是默认值在数据量太小时炸了把lr0从默认值降到 0.0005 重跑。3.3 推理结果保存save_txt 与 save_crop 的正确用法热词里常搜“yolov11保存推理结果”和“yolov11预测后保存”这里一次写清楚。训练完跑批量预测时saveTrue只能输出画了框的图片而真正落到病害清点系统里的是 txt 文件和按类别裁剪出来的病斑图。from ultralytics import YOLO model YOLO(runs/leaf_disease/exp01/weights/best.pt) results model.predict( field_photos/2024_07_rice/, imgsz1280, conf0.25, saveTrue, save_txtTrue, save_confTrue, save_cropTrue, ) for res in results: print(res.path, res.boxes.cls.cpu().numpy(), res.boxes.conf.cpu().numpy())sava_txtTrue会在每个图片目录下生成同名 txt里面是类别id 中心x 中心y 宽 高 置信度save_cropTrue会把框内区域按类别存成裁剪图这个功能在农业场景里价值很大病害识别结果最后要拿给农艺师看裁剪图比整图更容易放大查看病斑形态而且可以用来做二次复核数据集的素材。注意conf阈值的设定。田间场景类别不平衡时0.25 会漏掉大量低置信度的真实病斑农艺师更反感漏报而不是误报。实际项目里我会分两档给农户的告警用 0.4 以上给数据分析团队的用 0.15 的结果全量导出后面人工复核。4. 叶片病斑小目标优化精度从 60% 拉到 85% 的三种做法4.1 病斑为什么是小目标分辨率决定的水稻稻瘟病的典型病斑只有米粒大小在一张 3000×4000 的田间照片里可能只占 80×60 像素缩到 640×640 之后只剩 17×13 像素。YOLOv11 默认的检测头对这种尺寸非常吃力。小目标优化不是玄学核心只有三条路提高输入分辨率、切图推理、加针对小目标的检测头或注意力机制。三条路的效果是叠加的不是三选一。这里提一下网络结构层面的做法。“yolov11网络结构”在农业场景里比较大的改动是增加 P2 小目标检测头——把浅层的高分辨率特征图也接入检测分支让模型能看到更小的病斑细节。Ultralytics 官方没有直接提供 P2 配置社区里常见的做法是在 yaml 里手动扩展 neck 和 head 部分把 stride 为 4 的特征图接进来。这条路需要对网络结构比较熟新手可以先不做把输入分辨率和切图做起来效果提升比改结构来得快得多。注意力机制在叶片病害识别上也被广泛使用例如 HCANet 这类以通道注意力为核心的结构思路是让网络更关注病害区域在 RGB 通道上的细微色差。实际项目中叶片病斑和健康组织的颜色差异往往只集中在某个通道上通道注意力对这类任务有正向作用但注意加注意力模块会让推理速度下降边缘部署时可能得不偿失。4.2 切图预测SAHI 思路的最小实现把大图切成瓦片再预测是小目标优化里性价比最高的一步。本质就是训练时用 1280推理时用 640 的滑动窗口把大图切开放大后再进模型相当于变相把病斑放大。这也解释了热词里经常搜的“yolov11小目标优化”——大多数场景不需要改模型结构切图就能解决一大半问题。# tile_predict.py from ultralytics import YOLO import cv2 import numpy as np model YOLO(best.pt) tile_size 640 overlap 0.2 def non_max_suppress(boxes, scores, iou_thr0.5): # 简易 NMS实际可直接用 torchvision.ops.nms from torchvision.ops import nms keep nms(torch.tensor(boxes), torch.tensor(scores), iou_thr) return keep img cv2.imread(large_field.jpg) h, w img.shape[:2] step int(tile_size * (1 - overlap)) boxes_all, scores_all [], [] for y in range(0, h, step): for x in range(0, w, step): tile img[y:ytile_size, x:xtile_size] res model.predict(tile, imgsz640, conf0.2, verboseFalse)[0] for box, score in zip(res.boxes.xyxy.cpu().numpy(), res.boxes.conf.cpu().numpy()): # 把瓦片坐标还原成原图坐标 x1, y1, x2, y2 box boxes_all.append([x x1, y y1, x x2, y y2]) scores_all.append(score) # NMS 合并重叠区域的重复框 boxes_np np.array(boxes_all) scores_np np.array(scores_all)切图预测有两个关键参数。tile_size640对应训练时的输入尺寸如果你的模型训练时用的就是 1280这里可以用 640 或 832既放大原图细节又不至于让推理时间爆炸。overlap0.2是相邻瓦片的重叠率病斑正好被切到边缘时另一个瓦片能兜住重叠太小会漏检太大推理时间翻倍。合并阶段 NMS 的 IoU 阈值取 0.5 比较合适重叠产生的重复框一般 IoU 都在 0.7 以上0.5 能稳定合并掉。4.3 训练增强与后处理三个容易被忽视的细节第一关闭 Mosaic 增强。YOLOv11 默认训练开了 Mosaic它在通用目标检测里能提升鲁棒性但农业叶片场景反而会掉点多作物叶片分析里叶片往往占满整张图Mosaic 把四张图拼在一起后单个病斑被缩到极小模型学到的是拼接痕迹而不是病害特征。在训练 yaml 里显式设置mosaic: 0.0并用close_mosaic: 10让最后 10 个 epoch 彻底关闭效果会比较明显。第二训练用的类别名不要用中文。这不是 YOLO 的问题而是后续部署到 Jetson 或导出 TensorRT 时中文类别名在保存推理结果的 txt 和画框的字体渲染上会出乱码。用拼音或者英文维护一份 id 到中文病名映射表部署层再做展示转换这是农业项目最省心的做法。第三验证时把原始大图直接喂给模型而不是切图。很多人做完切图推理后拿切图 FPS 和大图指标对比发现 mAP 反而下降了误以为是切图的锅。实际上切图验证要用同样的切图流程重新跑一遍验证集再算指标否则“三种做法叠加提升了多少”根本没法衡量。每次改动都单独记一组指标别凭感觉判断。5. 部署与推理环境避坑在 Jetson Nano 上跑 YOLOv11 的排雷记录5.1 环境配套与导出流程“jetson nano部署yolov11”是搜索热度很高的方向但部署这一步真正动手时会发现坑全在环境配套上。Jetson 的 JetPack 版本决定 CUDA、cuDNN 和 PyTorch 的可用版本官方 PyTorch 轮子和 Jetson 的 CUDA 不完全兼容直接pip install torch装出来的版本百分之百报 CUDA 错误。常见做法是先查 JetPack 版本然后从对应渠道下载对应版本的 torch 和 torchvision。# 查看 JetPack 版本 cat /etc/nv_tegra_release # 在设备上用对应版本 torch 跑通一条预测 python -c import torch; print(torch.cuda.is_available())在电脑上训练完导出成 TensorRT 引擎再部署到 Jetson 上推理速度能快 3 到 5 倍。导出命令本身不复杂但有个坑导出的结果是针对你的 GPU 架构优化的在电脑上导出的 .engine 文件不能直接复制到 Jetson 上用必须在 Jetson 上重新导出一次。这个过程会跑几分钟别以为是指纹不对或者文件损坏。yolo export modelbest.pt formatengine device0在 Jetson 上推理时imgsz必须与导出时一致。导出时指定了 640推理时用 1280 会直接报 shape mismatch 或者自动重新编译速度慢且结果异常。5.2 三个常见问题现象、原因、解决问题一训练好的模型在 Jetson 上预测结果全是乱框。现象检测框大量重叠类别完全对不上。原因数据集 yaml 的 names 顺序和导出时不一致或者推理脚本里把类别 id 当成了类别名直接输出。解决把数据集 yaml 备份一份放在 weights 目录旁边部署时强制读取这份 yaml不要靠记忆手写 names 列表。踩过一次这个坑之后我的目录结构固定是weights/里同时放best.pt、leaf_disease.yaml和一份labels.txt部署脚本强制校验三者类别数一致。问题二保存推理结果的 txt 里坐标和图片对不上。现象画框在原图上看位置正确但 txt 里的坐标拿去清点系统里画就偏了。原因save_txtTrue输出的坐标是归一化的而且基于原始输入尺寸如果你的推理脚本对图片做了 resize 预处理需要先还原到原图坐标再保存。解决用results[0].boxes.xyxy拿到的绝对坐标自己做一次映射不要直接用 save_txt 的产物除非你确认推理时没有额外 resize。这个坑在部署到 Jetson 上做视频流推理时特别容易出现因为视频帧的尺寸和训练集不一致。问题三汉显乱码。现象画框的标签显示为方块。原因OpenCV 的 putText 不支持中文Jetson 上也未必装了中文字体。解决画框时只画类别 id 或拼音再在 UI 层做中文映射。别在检测环节里硬画中文既影响推理速度又给自己制造字体依赖。5.3 Jetson 部署中“yolov11预测后保存”的整体流程Jetson 上的推理结果保存和桌面端不完全一样。桌面端可以直接saveTrue让 Ultralytics 帮你存图Jetson 上内存吃紧一般建议只保留检测框数据异步写盘。做法是推理循环里只存boxes.xyxy和cls、conf的 numpy 数组攒满一批再统一回调保存避免每帧都做一次图像编码。这样跑视频流时内存占用能下降 30% 到 40%帧率也稳得多。前面提到的save_cropTrue在 Jetson 上慎用裁剪和编码非常费 CPU实测会让推理耗时翻倍。6. 模型验证与现场可用性不看单点 mAP看三类关键结果6.1 三个比 mAP 更能说明问题的指标训练结束的第一时间不要只盯着训练日志里的 mAP50。多作物叶片分析里我更关注验证集混淆矩阵、每个类别的单独 AP、以及低置信度样本的检测结果。混淆矩阵能直接看出番茄晚疫病和健康叶片被分错的比例这一点比整体 mAP 更关键——整体 mAP 高很可能只是因为健康叶片的样本多。按类别拆 AP 的做法是用model.val()跑完一次验证后结果里ap_class_index会把每个类别的 AP 输出到 CSV 里按类别名单独看。另一个容易被忽略的是 [email protected]:0.95 这个指标。田间病害识别最终要按病斑面积估损框的位置精度直接决定面积计算的准确性mAP50 只要求框和标注框有 50% 的重叠对面积估算来说太宽松。如果这个指标上不去大概率是标注框本身就不准回查标注数据比继续调模型收益更大。6.2 野外盲测保存推理结果做回看库验证的最后一步是盲测。我带 20 张完全没参与训练的新田块照片跑完推理后把结果全部保存下来和农艺师的人工判读做对比。注意盲测照片的拍摄设备要和部署现场的设备一致手机拍的模型不一定能适应无人机视角这是“多作物叶片分析技术”落地时最常被忽视的问题。现在我的习惯是每次盲测的推理结果都单独建一个error_analysis/目录把漏检和误检的图按原因分类归档比如“遮挡严重”“光照过暗”“病斑过小”“背景干扰”。一个项目迭代到第三轮时翻看误检归档比看指标曲线更能找到下一步优化方向——这个代码库的说明书给不了你只有你自己保存的推理结果能告诉你模型真正在什么地方失效。希望这些在田间和设备上磨出来的做法能帮到你。本文还有配套的精品资源点击获取
返回列表