news 2026/10/19 12:30:15

基于YOLOv9的水稻害虫小目标检测系统实践与边缘部署全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLOv9的水稻害虫小目标检测系统实践与边缘部署全解析

褐飞虱(brown planthopper)这类害虫,农业上习惯叫它“稻虱”,体长不过几毫米,一旦暴发就是成片枯死的“虱烧”景象。以前我们巡田主要靠人工,拿白瓷盘在稻丛中一拍,数虫口数量;水稻面积一大,人根本数不过来,等肉眼能明显看到枯黄,基本已经晚了。所以去年我们尝试用YOLOv9搭建了一套水稻害虫定点识别系统,类别列表里第一个就是brown-planthopper,之后陆续加上白背飞虱、稻纵卷叶螟、二化螟等。这篇内容就是把这套系统的完整落地过程记录下来,包括数据集怎么建、为什么选YOLOv9、训练调参时踩到的坑、模型怎么评估、最后怎么部署到田间的边缘设备。想用YOLOv9做小目标检测的朋友,或者正在做农业信息化项目的开发者,可以直接参考。

1. 认清目标:水稻害虫检测到底难在哪

1.1 检测对象本身就反人类

褐飞虱不是那种体型很大的虫子,成虫体长也就三四毫米,在图片上经常只有二十几个像素。它喜欢聚集在稻株基部,被叶片挡住,颜色又和水稻茎秆接近。更麻烦的是它的密度:暴发期一丛稻上可以有几十上百头,个体之间互相重叠,人眼都很难数清。

我们最初列出的检测类别就四个:

类别常见体型危害特点检测难度
brown-planthopper(褐飞虱)3~5mm刺吸汁液,成片枯死小目标、高密度、拟色
white-backed planthopper(白背飞虱)2~4mm苗期危害明显小目标、高密度
rice leaf roller(稻纵卷叶螟)蛾类翅展约15mm幼虫卷叶取食成虫翅面纹理易与背景混淆
striped stem borer(二化螟)成虫约12mm蛀茎造成白穗样本少、活动隐蔽

表格里最让人头疼的其实就是褐飞虱。它既小又多,检测系统稍有漏检,统计出来的虫量变化趋势就会失真。

1.2 我们要的是“早期预警”,不是简单分类

这套系统不是做一个学术Demo,而是要回答几个实际问题:某个田块现在有没有害虫?数量大概是多少?比昨天是增还是减?所以模型不能只输出“这张图里有飞虱”,得给出位置框、类别和置信度,让后端可以按时间维度去累计和预警。

这也是为什么传统图像处理方法在这里走不通。我们用颜色阈值、边缘检测、HOG加SVM都试过,一到自然光下全部失效:害虫颜色和水稻茎叶太接近,阴影、水珠、泥土都会变成误检来源。深度学习目标检测,尤其是YOLO这类实时模型,成为实际可用的突破口。

2. 为什么是YOLOv9:PGI和GELAN不是噱头

2.1 从YOLOv8s换到YOLOv9c的动机

最早我们用YOLOv8s做了一版,跑起来很快,褐飞虱的漏检率却不理想。后来对比了YOLOv9的开源实现,在同样输入分辨率下,YOLOv9c对小目标的召回有明显改善。注意,这不是说YOLOv9全面碾压YOLOv8,而是对我们这类“目标小、背景杂、密度高”的场景,它的两个核心设计刚好打在痛点上。

2.2 PGI可编程梯度信息,通俗理解就是“备用输油管”

YOLOv9论文里提出的PGI(Programmable Gradient Information),核心解决的是深层网络训练时的信息丢失问题。神经网络反向传播时,梯度要一层一层往回传,网络越深,浅层特征能拿到的有效梯度就越少。小目标的信息本来就弱,传到浅层时可能已经没剩多少,模型自然学不好。

PGI的做法是引入一个辅助的可逆分支,把完整的梯度信息保留下来,再补给主分支。辅助分支只在训练时用,推理时不参与,所以不增加线上计算量。用大白话讲,主分支是个上课记笔记经常记不全的学生,PGI是那个用录音笔把老师所有话都录下来、课后再把重点讲给主分支听的同学。

这个机制对水稻害虫特别重要。因为褐飞虱出现在画面里的特征大多集中在浅层细节上,比如虫体轮廓、触角、翅膀纹理,这些细节能不能在训练时被强化,直接决定最终检测效果。

2.3 GELAN结构:把参数花在刀刃上

GELAN是YOLOv9里采用的网络结构,本质上是把跨层连接和高效计算模块组合起来,在增加网络表达能力的同时控制计算量。相比之前常见的CSP结构,GELAN在相同参数量下能拿到更好的精度,对部署也友好。

我们当时的选型对比如下:

模型相对体量特点我们的选择
YOLOv9t轻量适合低算力设备备用方案
YOLOv9c中等精度和速度折中最好主力模型
YOLOv9e大模型精度上限高但部署成本大仅作对比

最后训练主力放在YOLOv9c上,原因很简单:在边缘设备上要跑到实时帧率,不能只盯着精度。YOLOv9e在测试集上AP高一些,但换到低功耗板卡后速度掉得厉害,现场用不起来。

3. 数据集的命脉:采集、清洗和标注决定模型上限

3.1 田间图像怎么采集,直接决定系统能不能用

很多项目输在第一步:拿公开数据集或者网上图片凑数,训练出来一到田里就废。我们的做法是在试验田里定点采集,覆盖三个关键维度:角度、距离、光照。

角度上要有俯拍、斜拍和贴近稻丛的特写。距离上至少包含两个尺度:一种是监控摄像头视角的远景(害虫只占几十像素),一种是用长焦镜头贴近后的近景(害虫占上百像素)。光照上则分五个时间段:清晨露水期、上午、正午、傍晚、夜间补光。实测下来,正午高温时害虫会往稻丛底部躲,图片里露头的很少;傍晚和夜间才是活动高峰,所以夜间数据反而要重点采集。

如果你只是拿手机去田里随手拍,大概率会得到一堆模糊、过曝、角度单一的照片。我们的建议是:固定好采集设备,比如把相机架在三脚架上,对准稻丛基部,每天定时拍一段视频,再从视频中抽帧。这样获得的图片背景多样性足够,害虫姿态也更真实。

3.2 数据清洗:别让脏数据浪费训练时间

采集完原始素材后,不能直接送去标注。我们先用脚本做了两轮筛选:

  • 拉普拉斯方差低于阈值的模糊帧直接丢弃;
  • 画面中明显没有稻株或田块的空镜头丢掉;
  • 连续视频里几乎一样的相邻帧,按时间间隔抽帧,避免重复样本过多。

这些处理虽然简单,但能避免模型在重复的背景上过拟合。最后我们保留了大约三万多张有效图像,其中大约六成含有目标。

3.3 标注规则:对密集小目标要提前定好规矩

标注这一步是整个项目里最花时间、也最决定上限的部分。我们用的是开源标注工具,类别名严格按配置文件来:

names: 0: brown-planthopper 1: white-backed-planthopper 2: rice-leaf-roller 3: striped-stem-borer

对普通目标,标注原则是框紧贴虫体边缘,不为了迁就图像分辨率而故意放大。对褐飞虱这类密集目标,我们定了几条硬性规定:

  • 遮挡超过一半、人眼无法判断边界的个体不标;
  • 高密度重叠时,能拆分的尽量拆分,实在分不清的整簇只标一个框,并在后续检查中去掉这类样本;
  • 宁可漏标,也不要标一个包含“半只虫加半片叶子”的框。

有人会问:漏标不会影响训练吗?实际上,模棱两可的标注对模型的干扰远大于漏标。让模型学到一个边界混乱的正样本,它会在推理时给出大量置信度虚高的错误框。

3.4 数据增强:针对小目标做“定向喂养”

YOLO训练管线里本来就有马赛克增强、HSV扰动、随机翻转,这些都要开。但对褐飞虱这种小目标,常规增强作用有限,最有效的是小目标复制粘贴:从近景图里把虫体切出来,经过随机缩放和旋转,贴到其他稻田背景图上。这样等于变相增加了小目标在训练中的出现次数。

一个小提醒:复制粘贴时要注意尺度。把一只三毫米的虫放大成三十毫米再贴上去,模型学到的还是错误的尺度概念。我们严格控制粘贴后的虫体尺寸和原图匹配,宁可贴小一点。

4. 训练复盘:参数、观测和试错记录

4.1 环境与训练命令

训练用的机器就是一块24G显存的GPU,PyTorch 2.x,CUDA 11.8,YOLOv9开源代码 clone 下来后按说明装依赖。数据集的配置长这样:

# rice_pest.yaml path: ./rice_pest_dataset train: images/train val: images/val names: 0: brown-planthopper 1: white-backed-planthopper 2: rice-leaf-roller 3: striped-stem-borer

训练命令大致是:

python train.py \ --data rice_pest.yaml \ --cfg models/detect/yolov9-c.yaml \ --weights yolov9-c.pt \ --batch-size 16 \ --img-size 640 \ --epochs 300 \ --device 0

注意,尽可能用在COCO上预训练过的yolov9-c.pt作为起点。随机初始化在小目标数据集上训练,前期收敛会慢很多。

4.2 输入分辨率的取舍

我们一开始图省事,训练时用--img-size 512,速度快了,但褐飞虱的AP直接崩了。原因很清楚:原本只有二三十像素的虫体,再缩放到512分辨率,连人眼都难辨认。后来老老实实改回640训练,验证时用1280再测一遍,小目标召回明显上涨。

如果你显存不够,优先减batch,不要减分辨率。褐飞虱检测这种任务,输入分辨率是比batch size更敏感的因素。

4.3 训练过程中的观察点

整个训练大约跑了300轮,有几个值得记录的观察:

  • 前30轮mAP涨得很慢,尤其褐飞虱类别一直趴着不动,不要慌,预热阶段还没结束;
  • 到150轮以后,其他类别的AP基本稳定,褐飞虱还在缓慢上升,说明小目标类别需要更长的训练周期;
  • 我们一度在200轮后出现验证AP震荡,训练loss却一直降的典型过拟合信号。

针对过拟合,做法是增强里加上了随机擦除,并把余弦退火调度打开,收敛稳定了很多。

4.4 类别不均衡的补救

数据集里褐飞虱实例最多,占七成以上;二化螟最少,可能只有几百个实例。最初的模型在二化螟上AP不到0.5。

我们做了两件事:一是给少样本类别的损失加权,让模型“更在意”这些稀有类;二是对二化螟的样本做适度重复采样。这里要克制,不要无限制复制少样本图片,否则模型会把那几张图背下来,验证集上看不出来,到实际场景就露馅。我们最终每个少样本类别只做三倍以内的重复。

5. 评估指标拆开看:mAP漂亮不等于现场好用

5.1 别迷信单一mAP,要盯住召回

mAP50算是行业惯例,但我们自己评估时,更关注两个指标:褐飞虱类别的AP50,以及置信度调低后的召回率。原因前面说过,这套系统用于早期预警,漏一头迁入的褐飞虱,可能比误报十次更严重。宁可让模型多报,再由后端按“连续多帧报警”来过滤。

我们用验证集跑了F1曲线,按F1最大值选的置信度阈值,实际部署时又调低了一点。这个细节很多人忽略:训练代码里默认的置信度阈值通常是为了刷漂亮指标,不是为你现场业务设计的。

5.2 我们测试集上的实测数据

以下数据仅代表我们的测试集,不同场地、不同数据集会有明显差异,但可以大致感受一下:

类别AP50主要错检/漏检原因
brown-planthopper0.84若虫颜色浅,贴在稻茎上难以辨认
white-backed planthopper0.81体型小,远景下特征不足
rice leaf roller0.76翅面花纹被压缩后与叶片纹理混淆
striped stem borer0.71样本少,夜间活动数据不够

总体mAP50在0.85上下。说实话,单看这个数字还行,但在真实视频里依然会见到漏检,这正是为什么不能只看汇总指标。

5.3 坏例分析带来的调整

我们把错检和漏检图像分批拉出来看,发现两个规律:

  • 褐飞虱漏检集中在中午强光下,稻叶反光导致虫体轮廓消失;
  • 误检集中在背景里有水珠、稻粒和蜘蛛网上。

针对反光,增强时加重了亮度扰动和曝光扰动;针对误检,部署时在后端加了ROI区域排除,稻田画面里只有稻丛区域参与检测,田埂和天空直接屏蔽。这一改动让每小时误报数下降了一个数量级。

6. 部署到田间的最后一步:模型压缩与边缘端推理

6.1 边缘设备选型

模型最终要放在田间,不可能拖一台服务器过去。我们的硬件方案是一块低功耗的Orin架构边缘板卡,整体功耗控制在25W以内,配普通监控摄像头和防水箱。摄像头装在稻丛上方约半米处,斜向下45度对准稻株基部,这样既能拍到褐飞虱出没的位置,又避免直视天空导致过曝。

如果你只是做验证,树莓派加摄像头也能跑起来,但帧率会很难看。褐飞虱检测需要至少15FPS才能实时预警,建议还是用带CUDA支持的边缘设备。

6.2 从PyTorch到ONNX再到TensorRT

导出步骤不复杂,命令大致是:

python export.py --weights best.pt --include onnx --img-size 640

导出ONNX后,再用TensorRT工具链构建推理引擎。我们对比了几种运行方式的实测参考值:

运行方式帧率参考相对mAP50损失
服务器GPU,PyTorch FP32约100+基准
边缘板卡,PyTorch FP32约10几乎为0
边缘板卡,ONNX FP32约14几乎为0
边缘板卡,TensorRT FP16约30几乎为0
边缘板卡,TensorRT INT8约45约1%以内

虽然TensorRT INT8帧率最高,我们还是选了FP16。原因很简单:INT8对褐飞虱这种弱纹理小目标的精度损失不稳定,省出来的算力换来的收益,抵不过误检漏检率上升带来的现场投诉。如果你在部署时需要追求极致性能,建议先跑一遍自己的数据集对比,不要照搬别人的经验。

6.3 部署后的运行机制

模型只是系统的一部分。我们把推理结果送入一个简单的计数模块:按帧累计每个类别框的数量,计算单位时间内出现的目标次数,超过阈值就推送预警。由于单帧会有抖动误检,还加了帧间投票,连续三帧同一区域出现目标才确认报警。

这个机制非常土,但非常有效,把模型的独立性误检大幅度滤掉了。后来甚至发现,不用改模型,光靠这个后端逻辑,现场误报率就能从每小时多次降到每天一两次。

7. 回看整个项目:真正起作用的几个经验

7.1 数据比模型更值钱,尤其阴雨天数据

项目跑通以后,我们回头复盘,发现最大的一次精度提升不是来自换模型,而是来自补充了一批阴天和傍晚光照的数据。当时只加了大约三千张图,褐飞虱类别AP直接涨了约4个点。原因是傍晚正是褐飞虱取食活动高峰期,而原始数据这部分严重不足。

7.2 输出虫情指数,别让模型直接下防治结论

检测系统的职责是“看到什么”,不是“该怎么办”。我们把模型输出汇总成中长期的虫情指数,再交给植保人员结合气象、作物生育期做判断。千万不要在界面上写“建议立即用药”这种结论,模型没有能力理解田间综合因素,贸然给结论只会让系统失去信任。

7.3 建立误检漏检回流机制,模型要持续迭代

部署不是终点。我们每周抽一段现场视频,把模型预测结果和人工复核结果做比对,挑出错例补充到训练集里重训。三个版本迭代下来,褐飞虱和稻纵卷叶螟的稳定性明显提升。这个“数据回流”过程比调任何超参数都重要。

7.4 警惕物理极限:画面里只有几个像素时,什么模型都救不了

最后说一个很直白的体会:有很多用户以为把模型换大、把分辨率调高,就能把所有虫都检测出来。但褐飞虱远景下只有六到八个像素时,它已经不再是机器学习能解决的问题,而是物理成像问题。现场要保证检测效果,要么摄像头离稻丛更近,要么用特写镜头,要么配置灯光诱集装置把虫子引到固定区域再拍。设备机位设计到位,后面所有环节都会轻松很多。

如果让我重做一遍,我会先把采集设备的机位想清楚,而不是先把模型跑到50FPS。画面里的虫子只有八个像素时,任何算法都救不回来;把这个问题想透了,YOLOv9也好,其他模型也好,才能真正在稻田里稳定工作。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 12:28:40

链表倒数第k个节点:双指针解法与面试边界全解析

最近在帮一些同学做模拟面试时,"返回链表倒数第k个节点"几乎是我必问的一道基础题。这道题在各类题库里出现频率很高,题目本身不长:给定一个单链表的头节点,要求返回倒数第k个节点的值。看起来简单,但实际面…

作者头像 李华
网站建设 2026/10/19 12:25:21

技能管理实操指南:从个人能力拆解到团队技能矩阵搭建

"skills"这个标题看起来极简,背后却拉出了一整条关于个人能力建设、团队效能评估、职业路径规划的主线。我这两年做团队搭建和新人培养,几乎每轮复盘都会回到这个词上。最初我以为它只是简历上的关键词堆砌,后来才意识到&#xff0…

作者头像 李华
网站建设 2026/10/19 12:23:41

DeepSeek-OCR 2:从传统OCR到视觉语言模型的文档结构化实战指南

上个月我拿到一个挺折磨人的活儿:把几十份上世纪的老合同扫描成可检索的电子档案。传统OCR流水线跑完,单字识别率看着接近满分,可到了人工核对阶段,几个人对着电脑来回翻,表格跨页全错位,手写批注被吞掉&am…

作者头像 李华
网站建设 2026/10/19 12:22:46

基于DS1302的51单片机万年历:掉电不丢时间的实现与避坑

简介:这份资源是一套基于DS1302时钟芯片的万年历课程设计完整方案,面向正在做单片机课程设计或电子类毕业设计的学生,尤其适合需要51单片机实战案例的初学者。方案以1602液晶屏作为显示界面,DS1302提供时间基准,通过三…

作者头像 李华
网站建设 2026/10/19 12:20:37

纯Python手写CNN:从im2col到梯度流的完整实现

简介:本资源是一份面向深度学习初学者与原理探究者的纯Python卷积神经网络(CNN)实现方案,不依赖TensorFlow、PyTorch等框架,聚焦底层计算逻辑与反向传播机制的理解。适合高校学生、转行学习者及希望夯实CNN数学基础的开…

作者头像 李华