资讯动态

YOLOv8实战:田间作物识别与智能除草坐标输出全流程

发布时间:2026/10/15 15:33:32 来源:尧图企业网站定制
很多人第一次看到杂草和农作物幼苗并排摆在一起的照片时都会觉得这东西根本没有难度——叶子形状不一样颜色深浅不一样为什么还要专门写代码去识别但等你真把几百张野外实拍的图像丢给程序问题就全冒出来了同一株作物在不同光照、不同生长阶段下长得完全不一样而不同地块里的杂草种类五花八门有些甚至比作物还像作物。我这次做的田间识别项目目标很朴素从杂草丛生的实拍图像里用 Python 把农作物一株一株框出来输出坐标给后续的机械除草动作做引导。这篇文章把数据准备、模型选型、训练调参到落地部署的完整过程记录下来重点讲清楚每个决定背后的原因适合正在搞农业图像处理、智能除草或无人机巡检的读者参考。1. 为什么“杂草里找作物”这么难先看清问题再选技术1.1 农业场景里那些反直觉的难点在动手写代码之前我花了不少时间梳理问题本身。农业场景和工业质检最大的区别在于工业场景光照均匀、背景可控、目标姿态固定而田间图像几乎把我能想到的计算机视觉难题全都装进了一张图里。首先是光照。同一片叶子在直射阳光下和阴天树荫下拍出来RGB 颜色值能差出好几个量级。如果直接用颜色阈值分割上午能跑通的参数下午换块地就不行了。其次是遮挡。作物叶子和杂草叶片交叉叠在一起在二维图像里根本没有干净的轮廓线更别说什么“先分割再识别”的经典流程。第三是形态相似性。很多禾本科杂草在苗期和作物幼苗长得极其接近比如玉米苗初期像一根细针稗草在这个阶段也差不多光看形状人眼都容易走眼。最后是尺度问题。无人机巡检图像里一株刚出苗的作物可能只有十几个像素脸都看不清更别说提取纹理特征。也就是说传统图像处理那套“颜色阈值 形态学开闭运算 轮廓提取”的流程在自然农田场景下几乎必挂。不是算法不够好而是环境噪声大到特征根本不稳定。1.2 分类、检测、分割三条路线怎么选同样是“识别农作物”背后其实对应三种不同的任务图像分类给定一张图判断里面有没有作物。适合大范围巡检撒网但不知道作物长在哪。目标检测输出每个目标的边框坐标。适合智能除草、定点喷洒拿到坐标就能指挥机械执行器。语义分割给每个像素打标签精确到叶子边缘。适合需要精细操作的场景但标注成本和计算开销都更高。我这次的核心需求是输出每株作物的位置坐标所以目标检测是最直接的选择。分类不给位置分割成本高且对机械控制来说是“过度精细”——除草机械臂需要的只是一个中心点而不是每一片叶子的轮廓。后文的所有代码都围绕目标检测来写但我也会提一句分割方案做对比时的结论。1.3 选型的底层逻辑输出坐标是唯一硬指标很多初学者有一个误区先选个看起来很厉害的模型再想它能干什么。我的习惯刚好相反先列出业务硬指标再倒推技术方案。这次项目的硬指标有三个第一必须输出像素坐标供下游机械控制使用第二需要能实时处理至少每秒 10 帧第三模型要能在单块消费级显卡上训练不能指望着云端算力。基于这三点我直接把方案锁定在 YOLO 系列上。不是因为它多先进而是因为它在“精度 - 速度 - 易用性”这个三角上的平衡最好而且生态成熟、社区资料多遇到问题能搜到答案。自己搭 CNN 分类网络听起来很酷但工程落地时你会发现光是数据加载、增强、训练监控、模型导出这一套流程就够写几千行代码而 YOLO 开箱即用。2. 数据集的采集与标注成败在这里就已经决定了2.1 拍摄规范先想清楚部署场景再动手数据集是整个项目里最花时间、也最决定上限的部分。模型再厉害喂进去的图片质量不行训练出来也是白搭。我当时定的拍摄规范是这样的用普通手机摄像头或者地面巡检小车自带的相机不要用专业单反因为部署现场就是普通摄像头覆盖多角度俯拍为主但也拍一些斜侧视角模拟机械在不同高度作业时看到的景象覆盖多时段晴天、阴天、早上逆光、中午强光都拍一遍这样模型见到的光照分布足够广每类目标至少 100 张原始图片总量到 500 张时第一版基本可以跑通。一个容易被忽略的细节拍摄高度要尽量和部署时的相机安装高度一致。如果部署时相机高度是 30 厘米训练图片却都是人站着俯拍的一米五视角那模型在部署时的表现会明显下降。目标大小、视角变形这些信息都藏在拍摄参数里前期不统一后面有的折腾。我还做了一件事每拍一批图片就在文件夹里记录拍摄条件。比如“晴天中午逆光黄土地”后面模型误检变多了回头查这批数据就能很快定位问题是不是出在某个特定的光照条件下。2.2 标注策略只标作物还是连杂草一起标标注工具我用的是 LabelImg操作逻辑简单框选目标、选择类别、保存为 YOLO 格式的 txt 文件就行一个多小时就能上手。但真正值得讨论的是标注策略——到底只标作物还是把杂草也标出来第一版我建议只标作物。理由有两个一是省时间因为农田图像里的杂草数量往往远多于作物标全杂草的工作量可能是标的作物的好几倍二是如果杂草类别标注不齐全模型反而会学会只关注作物本身的特征而不是“去和某个特定的杂草做对比”。我后来试过把几类常见杂草也标进去加了两个类别但精度并没有明显提升训练时间却变长了。不过只标作物也有代价模型不知道“杂草长什么样”在杂草密集的区域容易把部分杂草框成作物。这个问题我放在后面“样本不平衡”部分详细讲这里先埋个伏笔。2.3 数据增强用最少的图片把模型“喂饱”在没有外部数据集、纯靠自己采集的情况下数据增强是绕不开的一步。好在 YOLOv8 的训练流程里默认开启了马赛克增强、随机翻转、缩放和色彩抖动所以代码层面几乎不用写任何额外的增强逻辑。但这里有一个值得留意的点Ultralytics 默认的色彩抖动范围是经过工业场景调校的直接用在农业图像上有时会“增强过头”。比如它会把绿叶的颜色随机调成偏黄甚至偏枯这在真实场景里不常见反而让模型学到一些不存在的颜色变化。我踩过一次这个坑后来通过 fiddling 配置项把 HSV 的增强幅度调低了把增强重点放在几何变换上比如轻微透视扭曲和随机裁剪——因为田地里叶片姿态变化才是真实存在的变量。3. 基于 YOLOv8 的完整 Python 实现3.1 环境安装与数据集目录组织YOLOv8 的 Python 接口做得很干净安装就是一条命令pip install ultralytics如果你的机器没有现成的 PyTorch 环境可能还需要先装 torch 和 torchvision。这里有个容易踩的坑ultralytics 依赖的 PyTorch 版本不能太旧如果机器上原本装的是很久以前的版本建议先升级再装否则会出现看似安装成功、import 报错的怪问题。我有一次在环境里折腾了半天才发现是 torch 和 torchvision 版本不配套。数据集的目录结构建议按下面这样组织全部放在同一个根目录下crop_data/ ├── train/ │ ├── images/ │ └── labels/ └── val/ ├── images/ └── labels/LabelImg 保存的标注文件默认是每张图片对应一个同名的 txt 文件里面每一行代表一个目标框格式是“类别 中心x 中心y 宽度 高度”都归一化到 0-1。注意训练集和验证集的图片绝不能混用否则验证指标会虚高看起来模型很牛一上真实场景就现原形。3.2 训练脚本关键参数逐个说训练前要写一个数据配置文件告诉 YOLO 数据在哪、有几个类别、类别叫什么# crop_data.yaml train: ./train/images val: ./val/images nc: 1 names: [crop]然后就可以写训练脚本了from ultralytics import YOLO model YOLO(yolov8s.pt) # 使用预训练权重做迁移学习 model.train( datacrop_data.yaml, epochs80, imgsz640, batch16, device0, patience10, workers4, seed42, cos_lrTrue, )逐个说几个我认为最重要的参数预训练权重加载yolov8s.pt让模型从在 COCO 上学到的特征开始训练收敛速度和最终精度都远好于随机初始化。这里的“s”是模型尺寸s 是 small还有 n、m、l、x 几个档位。农业目标不算复杂s 档足够速度还快。imgsz640训练图像分辨率。这个值越大小目标越容易被检测到但显存占用成平方上升。我的显卡是 8GB 显存640 是稳妥选择。batch16受显存限制。如果你显存大可以考虑 32训练更快而且通常更稳。patience10连续 10 个 epoch 验证指标不提升就提前停止避免浪费时间。训练开始后终端会实时打印每个 epoch 的 loss、精度、召回率等指标。Ultralytics 还会自动保存best.pt验证集上表现最好的权重和last.pt最后一个 epoch 的权重不用自己手动管理。我习惯训练完直接把best.pt拿出来做后续推理验证。3.3 推理与可视化从模型输出到可用坐标训练完成后推理代码同样很简单from ultralytics import YOLO import cv2 model YOLO(runs/detect/train/weights/best.pt) results model.predict(field_test.jpg, conf0.25, imgsz640) for r in results: boxes r.boxes print(f检测到 {len(boxes)} 个目标) for b in boxes: x1, y1, x2, y2 b.xyxy[0].tolist() cls int(b.cls[0].item()) conf float(b.conf[0].item()) print(f作物 {cls}, 置信度 {conf:.2f}, 坐标 ({x1:.0f}, {y1:.0f}) - ({x2:.0f}, {y2:.0f}))conf0.25是置信度阈值只有置信度高于这个值的目标才会被输出。这个值需要在实际验证集上根据精度和召回率的平衡来调并不是越低越好。我刚开始用 0.25发现杂草误检比较多后来在验证集上画了 F1-置信度曲线确定 0.42 左右才是当前数据下的甜点值。要想直观看到检测效果直接把框画回原图img cv2.imread(field_test.jpg) for b in boxes: x1, y1, x2, y2 map(int, b.xyxy[0].tolist()) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{conf:.2f}, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite(result.jpg, img)跑出结果之后不要只看几张贴图就完事了。我建议写一个批处理脚本把整个验证集的每张图都跑一遍统计总体的精确率、召回率、mAP再按图片逐一检查失败案例。比如把检测置信度低于 0.1 但标注里确实有作物的图片单独收集起来这些往往是“难样本”分析它们的共性往往能指导下一轮数据采集。3.4 为什么选 YOLOv8 而不是自己搭 CNN我知道有些读者会觉得用现成的 YOLO 是不是太“拿来主义”了自己写个 ResNet 做分类再滑窗检测不是显得更有技术含量吗我从实际角度说自己搭 CNN 做作物分类在实验室里确实能跑出不错的结果因为测试集是从同一批数据里分出来的分布完全一致。但一上真实农田泛化能力立刻崩塌。YOLOv8 不仅是一个模型它自带了一套完整的数据增强、训练策略、anchor 匹配、NMS 后处理、指标评估和导出工具链这些工程细节加起来的价值远超过模型网络结构本身的差异。与其花三个星期造轮子不如把精力投在数据采集和实地测试上这才是农业识别项目里真正的胜负手。4. 训练路上踩过的坑小目标、光照和样本不平衡4.1 小目标检测幼苗只有十几个像素时怎么办第一版模型在验证集上的 mAP 到了 0.85看着挺不错但一到无人机拍的图上漏检率陡然升高。我排查后发现问题就出在目标尺度上近景拍摄的作物幼苗能占 100 多个像素而无人机视角下每株可能只有 15 到 20 个像素。YOLO 的网络结构经过多次下采样后小目标的特征信息在这个尺寸下已经所剩无几。这一轮的解决思路我先后试了三种把imgsz从 640 提高到 1280。网络输入变大小目标的像素占比自然变高。代价是训练时间和显存占用大约翻了三倍推理帧率也降到不能实时跑的边缘。引入 SAHI 切图推理。思路很简单把大图切成有重叠的小块每个小块分别送进模型检测再把所有结果合并回原图坐标系。这是目前解决农业遥感小目标问题最实用、成本最低的方案而且 YOLOv8 和 SAHI 配合得很顺。换更大尺寸的模型从 s 到 m 甚至 l。效果有提升但不如前两个方案显著。我最终的方案是前两招打组合训练时保持imgsz640推理时用 SAHI 切图。这样既保住了训练效率又解决了小目标检测问题。如果你的硬件条件允许直接imgsz1280训练是最省事的思路——前提是你受得了那个训练时长。4.2 光照与色彩数据增强不是万能的农业图像的第二个大头是光照。我遇到过一个很典型的情况模型在阴天拍摄的验证集上表现良好但一到晴天正午大量漏检。分析原因是训练集里晴天图片占比太少模型没见过强光下叶片反光、阴影遮挡的样子。一开始我想用数据增强来补把色彩抖动幅度调得很大结果非但没帮上忙反而让模型把一些杂草误认成了作物。原因是过度调色后的“假图片”干扰了模型对绿色植物纹理的真实认知。后来改用 CLAHE限制对比度自适应直方图均衡化做预处理在训练和推理时都用同样的 CLAHE 参数相当于把图像先统一到一个标准对比度空间模型不用再自己适应复杂光照。这个改动单独就贡献了大约四个百分点的 mAP 提升。那段预处理代码很短几行就能写完import cv2 def process(img): lab cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b cv2.split(lab) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) l clahe.apply(l) lab cv2.merge((l, a, b)) return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)最关键的一条经验是训练和推理必须走同一条预处理管道。如果训练时不做 CLAHE推理时做了模型看到的数据分布和训练时不一致精度反而下降。4.3 标注不一致导致的震荡与解决另一类问题不太容易被人提但它非常折磨人标注不一致。所谓不一致就是一百张训练图里有的标注把整株作物连根茎都框进去了有的只框了叶片还有的把被土半遮住的幼苗直接跳过了。这种不一致会让模型训练非常震荡loss 怎么都降不下去。我后来复盘发现根因是标注人员对“什么算一株作物”的理解不统一。这个在人类眼里很模糊的问题在模型眼里就是白纸黑字的错。解决思路是给标注规则定死几条硬标准一株作物只框最核心的绿色叶片区域不包含周围泥土叶片被遮挡超过一半但能明确判断的仍算一株完全分辨不清是作物还是杂草的宁可跳过也不乱标。我花了一整个下午把之前的数据重新过了一遍删掉了一些明显不规范的框重新训练后模型的收敛速度和最终指标都上了一个台阶。这段经历给我一个很大的教训标注质量的重要性远高于标注数量。与其匆忙标两千张质量参差的图不如好好标一千张严谨一致的图。模型学的不是图片本身而是你标注里的规律。4.4 一次完整的迭代记录从 77% 到 91% 的 mAP50 提升路径为了让上面的经验更直观我把中间几次关键迭代的改动和效果整理成了一个简表这比任何抽象的建议都更有说服力迭代版本主要改动mAP50 结果V1首批 500 张图片imgsz640YOLOv8s0.77V2增加 CLAHE 预处理训练推理统一0.81V3清洗标注统一框选规则剔除歧义框0.86V4增加晴天正午和背光场景图片 200 张SAHI 切图推理0.91V5替换 YOLOv8m提高置信度阈值到 0.420.92每一步改进其实都不是靠模型结构而是靠数据和预处理。这是农业视觉项目和普通目标检测比赛最大的不同比赛拼模型田里拼数据。我在做 V4 那轮时只是有针对性地补拍了模型漏检最严重的时段数据几乎没有调任何模型参数mAP 就涨了五个点。这说明模型本身已经在正常工作缺的是数据覆盖。5. 模型导出与部署从 Python 脚本到实际机械动作5.1 ONNX 导出与推理加速在开发环境跑通 Python 脚本只是第一步真正要部署到巡检小车或者除草机器人上通常不会直接在设备里装整个 PyTorch 环境。一来包体积太大二来 CPU 上 PyTorch 的推理效率不算高。我这次部署到一台低功耗边缘设备上用的是 ONNX 格式的模型。导出只需要一行model.export(formatonnx, dynamicTrue, halfFalse)dynamicTrue允许动态 batch 输入halfFalse是因为有些边缘设备的 NPU 对半精度支持不够稳定。导出后再用onnxruntime做推理速度比原始 PyTorch 在 CPU 上快接近一倍而且安装包小很多pip install onnxruntime这里有一个需要留意的点导出后的 ONNX 模型在边界框解码等预处理、后处理上和原始 PyTorch 推理略有差异最好用同一组测试图片跑一遍对比结果确认坐标误差在可接受的范围内再上真机。我在第一次导出时发现框的位置整体偏移了几个像素排查后发现是预处理阶段的图像缩放方式没对齐。5.2 检测框坐标如何换算成执行器动作模型输出的只是图像中的像素坐标要让机械执行器动作还需要一个坐标映射过程。我的做法比较简单粗暴适用于固定相机安装的巡检小车先在相机视野里放置几个已知间距的标定物算出图像坐标系到地面坐标系的像素与米换算比例检测到作物后取检测框底边中心点作为植株根部位置因为叶子中心会有透视偏差底部中心更接近真实位置用换算比例把图像坐标换算成相对小车中心的平面偏移量将这个偏移量通过串口发送给下位机驱动除草机械臂或喷洒装置。如果你的项目里相机装在机械臂上随臂运动那就需要完整的手眼标定流程复杂度会高很多。这里不展开但提一句先做固定安装的验证再做运动版本不要一上来就挑战最复杂的情况否则出了问题你根本分不清是识别错还是标定错。5.3 部署时最容易忽视的现场差异部署阶段我遇到的最大问题是“训练场景”和“部署现场”不一致导致的灾难性掉点。室内模拟田测试精度 90%一到真正的地里光线、土壤颜色、作物品种全变了精度掉到 70%。后来我学到一个很实用的思路正式部署前先到现场拍两百张图用训练好的模型在“实测模式”下跑一遍把置信度低的样本挑出来人工修正后做一次小规模的微调训练。微调训练不是重新从零开始而是在已有的best.pt基础上继续训练几十个 epoch数据量也不用大200 张就够了。这一步操作简单但能让模型迅速适应新的现场环境可以说是“性价比”最高的调优手段。我后来每一次换地块都会重复这个流程模型精度基本能稳定在 90% 以上。5.4 前后端联动一个最小可用的巡检脚本框架等你把模型、标定、通信这几件事都打通就可以顺手搭一个最小的联动框架。拿我这次来说就是让巡检小车每帧拍到图后先跑模型再判断画面里有没有作物框如果有就计算目标中心点的地面坐标然后把坐标包成一条简单的帧协议发送给下位机。这样的框架代码加起来不到一百行但因为每一块都已经在前期分别验证联调的时候几乎没出岔子。我在实际调试中体会到模块化地去组织这个流程比把识别、坐标换算、通信全塞在同一个主循环里要省心得多。每类功能的职责边界清晰线上出了问题也知道去哪一层排查漏检查模型权重和预处理框偏了查标定和坐标换算动作没执行查串口协议。最后再分享一点我的亲身体会这类农业视觉项目数据的时间占比七成以上模型部分反而是最顺利的环节。标注的一致性、拍摄条件与部署场景的统一性都是看不见但决定成败的细节。如果让我重来一次我会在第一周就把“标注规则文档”写出来而不是凭感觉边标边改。希望这篇文章能帮你少走一些弯路把精力真正花在刀刃上。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑