
简介目标检测是计算机视觉的核心任务之一小目标检测更是衡量模型实用性的关键场景。高尔夫球在画面中往往仅占几十像素加之逆光、动态模糊与复杂草地背景容易漏检误检。YOLOv5凭借多尺度特征融合与成熟部署生态成为平衡精度和速度的务实选择。实际工程中数据集质量直接决定上限需覆盖多光照、多距离场景并利用负样本抑制误检针对工业相机常见的单通道灰度图可通过通道复制兼容模型输入。训练完成后借助ONNX与RKNN工具链可将权重迁移到RK3568等边缘设备实现实时推理。从高尔夫球检测出发这套数据准备、训练调参与嵌入式部署思路同样适用于其他小目标识别任务。 做高尔夫球检测这个项目最开始我以为是件小事——球就那么大个圆疙瘩颜色也统一训练个模型能有多难等真跑起来才发现这恰恰是目标检测里最容易被低估的场景之一白色或浅黄色的球在绿色果岭上滚动目标小、速度快还经常处于逆光和阴影里属于典型的工业级小目标检测问题。用YOLOv5来做这件事选型上是合理的它在精度和推理速度之间的平衡以及生态成熟度让这个项目既能跑在训练服务器上也能想办法挪到RK3568这类边缘设备上。我会把整个项目的完整链路写一遍从设计思路、环境搭建、数据集处理到训练调参、部署推理最后是常见问题排查。这套东西不仅适用于高尔夫球换成乒乓球、无人机、螺丝钉思路完全通用。你手里那份zip包里大概就是权重、数据配置和训练脚本这类东西我按我自己的实现方式来拆解你可以对照着看。1. 项目整体设计与关键设计决策1.1 为什么是YOLOv5而不是其他框架做目标检测能选的东西不少YOLOv8、Faster R-CNN、SSD都各有拥趸。但我做高尔夫球检测时还是选了YOLOv5理由很实在第一项目base在COCO上v5.s的权重很小训练和部署的门槛都低第二社区太大了遇到问题基本上搜一下就有答案第三部署生态成熟导出ONNX再转RKNN的过程v5有大量现成案例可以抄作业。高尔夫球检测本身是个小目标问题。一个标准高尔夫球直径4.27厘米在1080P画面里如果摄像机离得远球也就二三十个像素。YOLOv5里的P3特征层下采样8倍就是专门负责小目标的锚框设计上也对这种小尺寸目标比较友好。相比之下Faster R-CNN精度虽然高但速度慢做实时挥杆分析不现实YOLOv8那时候还没现在这么稳而且部署资料相对少。说白了选YOLOv5就是选了一个下限高、上限也有保证的方案。1.2 高尔夫球检测难在哪很多人觉得球就是个小圆点检测起来能有多难。实际上难点比想象中多目标尺寸小在特征图上可能只占几个像素极易和背景纹理混淆球和周围环境对比度不高白色的球在沙坑、阳光直射区域经常会过曝球在运动时有动态模糊帧与帧之间位置变化大容易漏检高尔夫球场的环境复杂天空、水面、树木、草地的颜色和纹理干扰很大可能要同时满足训练服务器的离线分析和嵌入式设备的实时推理这些特点决定了你不能简单套一个COCO预训练模型就完事必须自己采集数据、微调超参数才能达到可用状态。项目zip里的数据集和训练配置核心就是为了解决上述问题。2. 环境搭建YOLOv5安装与适配的坑2.1 搞环境之前先想清楚的事安装YOLOv5这步网上教程一抓一大把但很多人第一步就翻车原因基本是Python版本和PyTorch版本不匹配。我的建议是先确定你的计算平台再定版本。如果主力机器是NVIDIA显卡且有CUDA环境直接上PyTorch GPU版就好。如果和我一样还要照顾RK3568这类嵌入式平台那么训练端用x86机器跑就行部署端再单独处理模型转换和运行时。不要试图在ARM板子上直接训练YOLOv5哪怕rv1106这种带NPU的平台训练速度也慢到让人崩溃——训练在PC上完成板子只负责推理。2.2 标准安装步骤YOLOv5的官方仓库clone下来之后核心依赖都在requirements.txt里。我自己惯用的步骤是创建干净的虚拟环境Python版本选3.8或3.9太新或太旧都容易出幺蛾子先装PyTorch这里建议用官方命令装不要用pip默认源随机装再安装requirements.txt里的依赖# 创建环境 conda create -n golf python3.9 -y conda activate golf # 安装PyTorch以CUDA 11.8为例具体以官网为准 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 拉取YOLOv5仓库 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt显卡驱动和CUDA toolkit若还没配好先用nvidia-smi确认驱动版本再去找匹配的PyTorch。这里最容易踩的坑是PyTorch版本和CUDA版本对不上装完一跑就报CUDA error: no kernel image is available不是你的代码有问题纯粹是环境匹配问题。2.3 版本对应关系速查YOLOv5迭代版本较多不同commit对依赖的版本要求不完全一样。我整理了一个常用对照方便你排查软件推荐版本说明Python3.8~3.103.11以上部分依赖还没完全跟上PyTorch1.12~2.x跟CUDA版本走不建议用最新的torchvision与PyTorch配套版本必须严格对应超参数默认文件data/hyps/hyp.scratch-low.yaml默认保守适合起步推理框架OpenCV PyTorch导出ONNX后可用ONNX Runtime安装完之后先跑一个最小测试确认环境没问题python detect.py --weights yolov5s.pt --source data/images/bus.jpg能正常出结果说明环境OK再往下一步走。3. 数据集准备高尔夫球检测的胜负手3.1 数据采集要多场景、多光照YOLOv5虽然自带Mosaic等数据增强但数据集的底子不好增强也救不回来。高尔夫球检测尤其依赖场景多样性。我采集和整理数据时定了几条铁律同一个球要出现在不同背景里果岭草、沙坑、球道、长草、近景特写光照必须覆盖顺光、逆光、阴天、晴天、清晨和傍晚镜头距离要有层次近到球占画面四分之一远到球只有十几个像素既要静止帧也要运动模糊帧模拟球滚动和飞行过程按这个标准凑500~1000张图每张图上标注1~5个球基本够用。如果你想快速验证可以先拿网上的公开图片和自己的手机拍摄混着来但注意图片版权问题尽量用自己拍的。采集的时候用稳定分辨率很重要。我建议统一用1920x1080或者1280x720避免训练和推理输入尺寸差异过大导致精度下降。如果你用手机拍摄最好把画面裁到16:9再导出。3.2 标注细节决定模型上限标注工具我用的是LabelImg简单直接。关键点是标注框的贴合度高尔夫球是圆形标注框不需要完全内切但也不要留太多边。我一般让框边距球边缘留2%左右的余量这样模型学习的框回归目标更干净。小目标标注有个经验如果一个球在图上小于8x8像素我建议直接忽略不标。不是说这种目标不能检而是标注代价高、模型学习收益低还容易因为标注不准引入噪声。先保证标注质量再谈数量。标注格式和YOLO要求的txt格式一致每行是class_id x_center y_center width height所有坐标都是归一化到0~1之间的。LabelImg会自动生成这些不用手写。3.3 数据划分与YAML配置数据目录结构我习惯这么组织datasets/ └── golf/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 └── labels/ ├── train/ # 训练集标注txt └── val/ # 验证集标注txt然后写一个golf.yamltrain: datasets/golf/images/train val: datasets/golf/images/val nc: 1 names: [golf_ball]注意train和val路径既可以写绝对路径也可以写相对路径。如果你的目录和YOLOv5仓库不在同一级写好绝对路径能省去很多排查时间。划分比例我一般按9:1训练集尽量多但验证集至少要保证有30~50张覆盖不同场景的图否则评估结果波动会很大。3.4 数据增强参数的取舍YOLOv5自带了很多增强策略在hyp.scratch-low.yaml里可以调。高尔夫球这个场景我推荐这样设置hsv_h、hsv_s、hsv_v适度开启模拟不同光照degrees设个5~10度模拟拍摄角度微偏translate、scale打开丰富目标尺寸分布Mosaic保持默认开启对小目标训练帮助很大但要注意增强太猛也会坏事。如果训练集本身很干净你把degrees设成180度一个圆球转了180度还是圆球看起来没区别但实际会把锚框分布搞乱出现一些奇怪的框。我的经验是球是旋转对称的旋转增强对这个项目意义不大不如把增强预算花在亮度、对比度和模糊上。4. 训练配置与关键超参数调优4.1 核心超参数逐个拆解训练命令里最关键的几个参数是img、batch、epochs。这三个决定了训练速度的上限。img输入图片尺寸。我推荐640这是YOLOv5默认尺寸COCO预训练权重也在这个尺寸上表现最好。如果球在画面上偏小但整体尺寸不算特别小640够用想进一步提升小目标召回可以试1280但显存消耗会明显增加推理速度也下降。batch能设多大设多大但显存有限。8GB显存跑yolov5s、img640batch大概能到16如果你显存只有4GB建议batch降到8同时把workers调高一点让数据加载不拖后腿。epochs先跑100轮看趋势梯度下降正常的话100轮足够了。再往后提升有限还容易过拟合。超参数文件建议用hyp.scratch-low.yaml起步等第一轮训练结束看mAP曲线再决定要不要调lr0。我一般习惯把lr0设0.01warmup_epochs设3前几轮让模型先稳定下来。4.2 训练命令与监控训练命令大概长这样python train.py \ --data datasets/golf/golf.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --name golf_exp训练过程中重点看两个指标一个是val/box_loss和val/cls_loss的下降曲线一个是mAP0.5和mAP0.5:0.95的上升趋势。我见过太多人训练时盯着屏幕看loss值其实每个batch的loss波动很大单次数值没有参考意义要看平滑后的曲线趋势。mAP0.5对高尔夫球检测来说是比较关键的指标因为它允许一定的框偏移。如果mAP0.5能到95%以上你的模型在固定场景下基本可用了。mAP0.5:0.95稍微低一些没关系小目标本身的IoU对像素偏移非常敏感。4.3 单通道灰度图的特殊处理热词里出现了yolov5训练单通道这个问题确实有人问。YOLOv5默认输入是三通道RGB但有些工业相机输出的是灰度图只有单通道。直接把单通道图喂给YOLOv5会报错因为第一个卷积层的in_channels是3。解决办法有两种第一种最简单也最推荐读取灰度图后复制成三通道。虽然数据冗余但模型结构不用改也能正常训练和推理。代码上就一行事img cv2.imread(path, cv2.IMREAD_GRAYSCALE) img_3ch cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)第二种改模型第一层。把model/common.py里Conv的输入通道数改成1同时把所有预训练权重的第一层权重做平均合并。这种方法省显存但会破坏预训练权重的语义信息而且需要动代码新手不建议碰。如果目标平台对内存极敏感再考虑这个方案。4.4 训练结果评估与导出的常见误区训练结束会在runs/train/golf_exp目录下生成weights/best.pt和weights/last.pt。best.pt是验证集上表现最好的last.pt是最后一轮的前者用于部署后者用于继续训练。很多人拿last.pt去推理结果效果没best好就以为是模型没训好其实是选错了权重文件。另一个常见误区是本地训练效果很好一到部署板子上就掉点。原因通常是输入尺寸和图像预处理不一致。训练时你做了letterbox、归一化到0~1部署推理时也必须做完全一样的预处理否则模型吃到的数据分布不一致精度自然下降。5. 推理部署从Python Demo到边缘设备5.1 推理脚本与bbox输出训练完成后检测一张图的命令很简单python detect.py --weights runs/train/golf_exp/weights/best.pt --source test.jpg --conf-thres 0.25如果你想在视频或摄像头流上实时检测detect.py也支持--source 0表示默认摄像头或者传视频文件路径。conf-thres默认0.25对高尔夫球这种单类目标我建议调到0.3~0.4能有效过滤掉树叶、沙坑边缘的一些误检。如果要在自己的Python程序里调用模型别用detect.py直接加载模型更灵活import torch model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/golf_exp/weights/best.pt, force_reloadTrue) model.conf 0.35 model.iou 0.45 results model(frame) boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls]5.2 导出ONNX与RKNN转换训练好的PyTorch权重不能直接在RK3568或rv1106上跑需要先导出成ONNX再转成RKNN格式。ONNX导出的命令是python export.py --weights runs/train/golf_exp/weights/best.pt --include onnx --opset 12导出后建议用onnxruntime做个精度对比确保ONNX模型输出和PyTorch原模型基本一致。这一步经常出问题的是自定义算子不兼容YOLOv5里一般不会遇到但如果你改过模型代码就要小心。转RKNN时rknn-toolkit2会根据平台选择对应的配置。RK3568用rk3568rv1106用rv1106。模型输入尺寸要和训练时保持一致我用640就填640。量化类型我建议先用hybrid或i8如果精度下降明显可以试试混合量化只量化部分层。5.3 嵌入式平台部署的实测心得在RK3568上跑yolov5s的体验说实话速度不太乐观。如果直接转int8量化大概能跑到20~30 FPS属于勉强实时rv1106这种更低功耗的平台跑起来会更吃力可能需要把模型缩到yolov5n或者把输入尺寸降到320。为了在嵌入式平台上追求速度我通常会做这些事把模型从yolov5s换成yolov5n精度掉3~5个百分点但速度可能翻倍输入尺寸降到416甚至320如果球在画面中占比不太小效果依然能接受后处理输出只用前100个候选框减少非极大值抑制的耗时用RKNN的零拷贝接口减少图像在CPU和NPU之间的搬运开销如果你要部署在rv1106这类资源更紧张的平台务必先把应用层逻辑想清楚是固定场景的抓拍检测还是视频流实时分析固定场景可以每帧降采样检测视频流可以通过跳帧来降低负载。这套思路在任何低算力平台上都适用。6. 常见问题与排查技巧实录6.1 典型问题速查表现象可能原因解决办法训练loss一直是nan学习率太大或数据里有空标注降低lr0到0.001检查标注文件是否为空检测不到远处的小球训练时小目标样本太少增加小目标样本用Mosaic增强或提高输入分辨率推理时误检叶子/石头训练集里背景干扰样本少增加负样本图片不含球的背景图RKNN转换失败ONNX版本不兼容或算子不支持尝试ONNX opset 11/12更新rknn-toolkit2板子推理速度很慢没有走NPUCPU在跑确认是否成功加载RKNN模型而不是PyTorch模型训练时显存不足batch或img设置有冲突先降batch再降img避免workers过高占内存6.2 负样本的重要性很多人做目标检测只标正样本忽略了负样本这是大坑。高尔夫球场里背景千奇百怪沙坑中的小石子、雨后草地上的水珠、甚至远处观众的白点都可能在推理时被误判成球。我训练到第二轮时加了接近100张完全没有球的负样本图标注文件是空的。YOLOv5支持空标注训练你只需要在labels目录里放一个空的txt文件即可。加了负样本之后误检率肉眼可见地降低这个技巧强烈建议试一下。6.3 排查思路比答案更重要遇到过最多的情况是训练正常、验证集mAP也很高但放到现场视频里就漏检。这种问题通常不是模型的问题而是视频帧的质量问题——运动模糊、压缩伪影、亮度异常。我排查时的固定流程是截取现场视频中漏检的那一帧用训练时的预处理流程处理后人工看看图像是否清晰如果图像本身就很模糊先考虑摄像头参数和放置位置如果图像清晰但模型没检出来再考虑模型在类似光照下的置信度阈值这套流程看起来简单但能避免90%的无效调参。千万不要一上来就调超参数先确认数据从现场到模型输入这条链路每一步都没有问题。6.4 置信度阈值的两难置信度阈值设低了误检变多设高了漏检变多。高尔夫球这种小目标置信度天然比大目标低。我用过很多阈值组合之后比较推荐的做法是对固定场景比如挥杆训练、模拟器conf设为0.4稳定性优先对多场景泛化户外球场实时监测conf设为0.25并且在后处理里加一个目标尺寸过滤只保留面积在合理范围内的检测框7. 一点个人经验踩过几次坑之后我的体会是高尔夫球检测这个项目真正的难点从来不是训练一个YOLOv5模型而是数据质量的把控和部署环境的适配。模型本身的学习能力强得可怕你给它什么样的数据它就学会对付什么样的场景。如果你只想做个demo用COCO预训练权重加几十张图跑一下就有模有样但如果你想让它在实际球场上稳定工作那就要把精力花在数据采集的多样性、标注的精确度和部署的工程化上。最后再分享一个小技巧训练完成后别急着删掉last.pt。很多时候你想继续采集新数据来迭代last.pt就是最好的起点权重它比COCO预训练权重更贴近你的数据集继续训练时收敛更快最终精度也更高。这个项目做完后同样的流程我已经复用到乒乓球和无人机检测上只需要换数据和标签名骨架完全不用动。本文还有配套的精品资源点击获取