资讯动态

基于YOLOv8的煤矿传送带矸石与锚杆检测实战

发布时间:2026/10/9 16:44:16 来源:尧图企业网站定制
简介面向煤矿智能化巡检场景这份资源提供基于YOLOv8的传送带矸石与锚杆异物检测方案包含3000多张已标注图像、训练好的模型权重和PyQt可视化界面适合算法工程师、矿山信息化人员直接部署或二次训练。数据集已划分train/val/test并配好data.yaml标签与YOLO系列训练要求一致v5、v7、v8、v9等算法均可直接调用附带的环境配置与运行步骤PDF能帮助新手快速跑通检测流程。资源包共2000个文件以1991个XML标注文件为主体另有4个Markdown说明、3个PDF教程和2个Python界面脚本整体323.4MB目录结构清晰便于按模块查阅。已有235人学习下载无论是做煤矿异物检测课题验证还是落地皮带运输场景的实时告警这套数据与代码都能有效缩短从模型训练到界面演示的周期。1. 煤矿传送带上的矸石和锚杆为什么值得专门训一个YOLOv8模型井下皮带运输系统里煤流中混进矸石是常态锚杆这类铁器一旦混入轻则卡坏破碎机重则划伤皮带导致停产。靠人工盯监控屏视线疲劳后误报漏报几乎必然。这个项目的做法是用YOLOv8在皮带监控画面上实时框出矸石和锚杆再套一个PyQt界面把检测结果变成工人能直接操作的工具。3000多张现场图像撑起来的检测模型核心价值不是能识别而是在现场光照、煤尘、遮挡下还能稳定识别。适合谁一类是煤矿智能化改造中的算法工程师需要一套能落地的检测方案另一类是刚接触目标检测、想用真实工业数据练手的开发者。前者能直接复用数据集组织和训练流程后者能看懂从标注到界面封装的完整链路。这篇笔记不绕弯子直接讲数据怎么整理、参数怎么设、界面怎么写、坑在哪。2. 从数据集到标注格式3000多张煤矿图像的整理与清洗2.1 数据采集场景与类别定义矸石、锚杆到底长什么样矸石和煤在外观上都是黑色块体但表面光泽、纹理和形状有明显差异。矸石通常棱角分明、表面粗糙、反光弱煤块则有油润感、断面较亮。锚杆是细长金属杆件在图像上常呈现为一条亮色长条有时伴随端部螺纹或托盘。类别定义不要贪多常见做法是两类gankuang矸石和 maogan锚杆煤块不单独标注作为背景让模型去区分。采集时要覆盖不同皮带速度、不同煤流量、井下白光和暖光两种光照。图片分辨率建议不低于1080P因为锚杆是小目标分辨率不够后期很难补。我一般会让现场人员分批次采集每天不同时段拍一段再从视频流里抽帧避免相似帧过多导致训练集和验证集信息重叠。另外要注意类别的数量分布如果锚杆样本只有矸石的三分之一训练出来锚杆recall必然偏低。遇到这种情况优先补采集而不是做简单复制增强复制出来的样本只是让模型记住同一个目标的不同位置对泛化没有帮助。2.2 标注工具选择与YOLO格式转换脚本标注工具首选LabelImg矩形框够用、操作简单导出Pascal VOC格式的XML。如果现场数据里有大量密集小目标可以用开源的半自动标注工具先跑一个预训练模型生成初稿再人工修正。不过初始版本我还是推荐全手动标注3000多张图一个人两天到三天能完成质量最可控。锚杆这类目标比较特殊标注时要顺着杆件方向拉框不要为了省事画一个正方形把周围背景都包进去。标注完成后需要把XML转成YOLO需要的txt格式每行一个目标class_id cx cy w h坐标全部归一化。转换脚本如下import xml.etree.ElementTree as ET from pathlib import Path # 修改为你的标注目录和图片目录 xml_dir Path(./annotations) out_dir Path(./labels) out_dir.mkdir(exist_okTrue) # 类别顺序要和后续data.yaml里的names保持一致 class_map {gankuang: 0, maogan: 1} for xml_path in xml_dir.glob(*.xml): tree ET.parse(xml_path) root tree.getroot() # 读取图片宽高用于坐标归一化 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 转成YOLO格式归一化的中心点坐标和宽高 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # 输出txt和图片同名放在labels目录 out_path out_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines))脚本逻辑很简单遍历XML读宽高把每个目标的绝对坐标归一化到0到1之间。参数上有两个容易翻车的点一是class_map的顺序必须和训练配置文件data.yaml里的names一致顺序错了模型训练出来类别全对不上二是归一化坐标保留6位小数足够但不要用科学计数法否则个别训练框架解析会出错。转换完成后要做一次抽检把txt坐标画回原图肉眼确认框的位置和大小没有偏差。这一步虽然繁琐但能拦住八成标注错位问题尤其是XML里xmax写反、width和height写颠倒这样的低级错误。提示抽检建议写个小脚本把原图和标签画成一张对比图每20张拼一屏快速过目比一张张打开快了不是一点半点。2.3 数据集划分与增强策略让3000多张图发挥出上万张的效果划分数据集时不能直接把所有图随机打散因为同一视频抽出的帧高度相似。常见做法是按视频片段分组一个片段的帧要么全进训练集、要么全进验证集否则验证集指标虚高模型上线后直接现原形。比例上train:val:test按8:1:1测试集最好是现场换一个班次、换一条皮带采集的新视频才真正算得上看不见的数据。增强策略上YOLOv8自带的mosaic增强在训练前期很有效能模拟多目标堆叠场景。井下场景我额外开启了轻度HSV扰动hue ±0.015、sat ±0.5、val ±0.4因为现场不同时段皮带灯光的色温漂移明显。但要注意井下煤尘大、图像对比度低对比度增强不要开太大否则模型会把暗部的煤块误学成矸石纹理。翻转增强对矸石这种不规则块体要慎用水平翻转还好垂直翻转会让模型学到矸石会飘在皮带上空这种离谱特征。3. YOLOv8训练参数与关键命令让3000张图训出稳定模型3.1 环境搭建与数据集配置文件训练环境用conda隔离Python 3.10配合PyTorch 2.x即可。安装步骤分两步先按显卡驱动装对应CUDA版本的PyTorch再装YOLOv8的ultralytics包。如果顺序反了pip很可能拉一个CPU版本的PyTorch进来训练速度差一个数量级而且不容易察觉——训练能跑就是慢得离谱。conda create -n yolo8 python3.10 -y conda activate yolo8 # 先装GPU版PyTorch再装YOLOv8检测框架 pip install torch torchvision pip install ultralytics python -c import torch; print(torch.cuda.is_available())最后一行print输出True说明GPU可用。如果输出False先别急着重装用nvidia-smi看驱动版本和CUDA版本的匹配情况最常见的问题是驱动太老不支持新CUDA。数据集配置文件data.yaml放在项目根目录path: ./dataset # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 test: images/test # 测试图片目录用于最终验证 nc: 2 # 类别数量 names: [gankuang, maogan] # 类别名称顺序必须与标注一致这里的path可以用绝对路径避免在Windows上因为盘符问题找不到数据集。train和val是相对path的目录图片目录和标签目录的对应关系是YOLO内部约定images/train里的每张图对应labels/train里同名txt。这个目录结构不要自己改改了训练时标签全部加载不到。data.yaml里我把test也显式写出来了因为第3.3节的验证命令要依赖这个字段。3.2 训练参数详解与首次实验设置训练命令本身不长但参数取舍很关键。井下皮带场景我第一次训练用的是yolov8m因为锚杆是小目标m型号的特征提取能力比n和s强而x型号在3000多张图的数据量下容易过拟合。预处理尺寸imgsz用了960而不是默认640这个选择直接影响后面所有调试的基准。yolo detect train \ datadata.yaml \ modelyolov8m.yaml \ epochs200 \ imgsz960 \ batch16 \ device0 \ optimizerSGD \ lr00.01 \ weight_decay0.0005 \ patience30 \ project./runs/train \ nameconveyor_maogan参数说明epochs200对于3000多张图偏多但配合patience30早停不会浪费太多时间optimizerSGD在数据集不大的时候比AdamW泛化更稳收敛也更容易控制imgsz960是为了照顾锚杆这类小目标但显存压力会明显上升如果显卡只有8G显存batch降到8、imgsz降到832是合理折中。weight_decay保持默认的0.0005就好不用刻意调大井下的图像噪声已经够多了。训练时日志里要盯三个东西train/box_loss是否平滑下降、metrics/precision和metrics/recall是否同步上涨。如果precision上去了recall掉下来说明模型开始把矸石框得保守需要检查标注是不是有大量漏标——漏标的样本会被当成背景模型学到的是这部分不算目标recall自然上不去。另一个常见的现象是loss在训练中段出现一个台阶式下跌这通常是mosaic增强在最后10个epoch自动关闭导致的不是bug。3.3 从结果目录里挑出真正能用的权重训练结束后runs/train/conveyor_maogan/weights下会有两个文件best.pt和last.pt。很多新手直接拿last.pt用这是错的。last.pt是最后一个epoch的权重如果早停触发前模型已经在过拟合阶段它反而比中间某个epoch更差best.pt是根据验证集综合指标选出来的最优权重默认评判标准是mAP50-95但对我来说recall更关键——漏检一个锚杆可能就意味着一次皮带事故。所以我一般会额外跑一次验证用best.pt在测试集上输出详细指标并且按类别分开看yolo detect val \ modelruns/train/conveyor_maogan/weights/best.pt \ datadata.yaml \ splittest \ imgsz960 \ save_jsonTrue注意splittest要求data.yaml里显式定义了test字段否则会退回验证集。save_jsonTrue会生成predictions.json里面每个检测框都有score和class_id后续调置信度阈值全靠它。验证结果里有一个混淆矩阵.png打开看一眼如果锚杆大量被预测成矸石说明两类特征在细长形状和暗色纹理上有重叠需要回到标注环节再抠细节而不是盲目调参。如果box_loss正常但分类损失下不去优先检查数据集的类别标签是否贴错这种错误用混淆矩阵一眼就能定位。4. PyQt可视化界面把检测模型封装成现场能用的工具4.1 界面布局与功能设计模型训好了交付物不能是命令行里的一串输出现场工人要的是一个打开的窗口、按一个按钮就能看的界面。PyQt做这件事很合适界面开发快OpenCV的图像帧可以直接转成QImage显示不需要额外的图像库。界面布局我一般分四块左侧是实时视频显示区右侧上方是检测结果统计当前帧矸石数量、锚杆数量右侧下方是操作按钮和置信度阈值滑块底部是报警状态栏。关键的一点是把推理放进QThread子线程否则视频卡顿和界面假死会同时出现这个问题第5章会展开。阈值滑块为什么要放在界面上因为现场不同时段煤流量差异很大煤多的时候矸石被遮挡、置信度普遍偏低煤少的时候误检增多操作员需要在不改代码的情况下实时调整。4.2 核心代码加载模型、推理与结果绘制界面核心代码我拆成两个文件main_window.py负责界面detector.py负责推理。这里给出detector.py的关键部分from ultralytics import YOLO import cv2 class BeltDetector: def __init__(self, model_pathbest.pt, conf0.45): # 加载训练好的模型只跑推理 self.model YOLO(model_path) self.conf conf self.class_names [gankuang, maogan] # 矸石用红色框锚杆用黄色框便于现场一眼区分 self.colors {gankuang: (0, 0, 255), maogan: (0, 255, 255)} def detect_frame(self, frame): # 模型推理agnostic_nmsTrue避免同类框互相压制 results self.model(frame, confself.conf, imgsz960, agnostic_nmsTrue, verboseFalse) dets results[0] boxes dets.boxes if boxes is None: return frame, 0, 0 gangue_count 0 bolt_count 0 for box in boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cls_id int(box.cls[0]) score float(box.conf[0]) name self.class_names[cls_id] color self.colors[name] # 画框和标签 cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) label f{name} {score:.2f} cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) if cls_id 0: gangue_count 1 else: bolt_count 1 return frame, gangue_count, bolt_count逻辑说明YOLO(model_path)加载权重后每次调用直接把BGR帧传给模型内部会做预处理和NMS。agnostic_nmsTrue的意思是所有类别一起做非极大值抑制防止同一个锚杆同时被两个类别框住。返回的boxes里xyxy是绝对像素坐标直接用于画框不需要还原归一化坐标。参数上要注意两点一是imgsz960必须和训练时一致模型虽然支持任意尺寸输入但输入尺寸跳变会让小目标检测结果明显变差二是conf阈值不要写死在代码里界面的滑块要能实时改现场调试时不同的皮带煤流量差异很大阈值需要随时微调。4.3 实时视频流接入与报警联动视频流接入用OpenCV的VideoCapture打开摄像头或RTSP流但在Qt里不能直接在QTimer回调里读帧加推理否则界面刷新会卡到没法看。正确做法是开一个QThread线程里循环读帧、推理、把结果帧和计数信号发回主线程from PyQt5.QtCore import QThread, pyqtSignal import cv2 class VideoThread(QThread): # 每一帧检测结果通过信号发给主线程更新界面 frame_ready pyqtSignal(object, int, int) def __init__(self, detector, video_source0): super().__init__() self.detector detector self.video_source video_source self.running True def run(self): cap cv2.VideoCapture(self.video_source) while self.running: ret, frame cap.read() if not ret: # 现场RTSP流断线常见断开后等一秒重连 self.msleep(1000) cap cv2.VideoCapture(self.video_source) continue result_frame, gangue, bolts self.detector.detect_frame(frame) # 信号里传图像帧和两个计数 self.frame_ready.emit(result_frame, gangue, bolts) cap.release() def stop(self): self.running False self.wait()主线程槽函数里把frame_ready收到的帧转成QImage显示当bolts 0时触发报警状态栏变红、播放提示音。这里有个性能细节detect_frame返回的帧直接用不要再做一次拷贝OpenCV画框是原地修改QImage构造时可以做浅拷贝显示深拷贝会让帧率掉一半以上。RTSP断线重连写进run循环而不是让线程退出现场摄像头网络抖动很常见线程一退界面就黑屏操作员只能重启程序体验很差。5. 煤矿传送带检测的5个典型踩坑与排查记录这些坑不是从手册上看来的是现场一个一个踩出来的。下面五条按出现频率排序每一条都按现象、原因、解决三层说清楚你在自己项目里大概率会遇到至少三条。5.1 矸石误检率居高不下框满天飞现象验证集mAP有0.92上线到现场视频里误检率超过30%煤块、皮带接头、托辊全被框成矸石。原因训练集里矸石标注框太松很多框把矸石周围的煤也包进去了模型学到的是大块暗色区域而不是矸石纹理。另外验证集和训练集来自同一批视频抽帧指标虚高掩盖了问题。解决重新规范标注标准标注框四边必须贴住矸石边缘宁可漏一点也不能多包。处理完后再看训练集里置信度Top的误检图凡是框住煤块的样本检查它附近是否存在漏标——漏标的矸石会让模型认为这个长相不算矸石这也是误检的隐性来源。5.2 锚杆目标太小中远距离频繁漏检现象矸石全检出来了锚杆在远处画面里只有十几个像素宽recall只有0.4。原因默认imgsz640对锚杆来说分辨率不够一个十几像素的目标被缩放后特征基本消失。解决训练和推理统一提升到imgsz960同时锚杆单独算一次recall不要只看总mAP。如果显存不够另一个做法是按皮带区域把画面裁剪成两段分别检测相当于用切图换分辨率。我在现场两种都试过960直接推理比切图省事且更稳切图引入的拼接边界问题反而增加调试成本。5.3 PyQt界面推理时窗口假死现象打开视频后窗口转圈拖拽窗口无响应几秒后系统提示程序未响应。原因把cap.read()和模型推理写在了主线程推理一帧要80到150毫秒主线程被阻塞Qt事件循环无法处理重绘和鼠标消息。解决严格按4.3的QThread方案主线程只负责接收信号刷新界面。注意线程里不要碰任何QWidget控件所有界面更新必须通过信号发回主线程这是Qt线程模型的铁律违反它会出现比假死更诡异的偶发崩溃。5.4 光照突变导致连续漏检现象皮带启动瞬间灯光闪一下或者矿车经过挡住光源之后连续十几帧检测不到任何目标。原因模型对训练集的光照分布过拟合训练时图像白平衡和亮度分布比较集中没有覆盖灯闪这种瞬态变化。解决给训练集增加亮度抖动增强同时在采集阶段专门录几段灯闪和遮挡的视频加入训练。推理端做一个简单的自适应检测前先用createCLAHE做一次光照均衡但参数要控制好增强过头会把暗色煤块提亮成矸石纹理反而增加误检。5.5 工控机上推理速度从30帧掉到8帧现象开发机上GPU跑得飞快部署到现场工控机后帧率惨不忍睹CPU占用拉满。原因工控机没有独立显卡模型在CPU上用FP32推理960分辨率下单帧要120毫秒以上。解决两条路。一是换小模型从yolov8m降到yolov8s甚至n3000多张数据量训练n型号精度损失控制在2到3个点速度翻三倍二是现场有NVIDIA显卡就导出TensorRT的engine格式FP16推理在960分辨率下能跑25帧以上。如果不能换硬件优先把imgsz降到640锚杆漏检用按ROI裁剪来补偿。6. 现场验证方法与参数调优让模型在皮带上长期扛住权重和界面都做完之后别急着交付。我习惯做三件事第一拿一段真实现场监控视频做离线回放验证时间跨度超过4小时覆盖白班、夜班和交接班时段统计每小时的误检数和漏检数——这两个数字比mAP更能让现场负责人放心。第二看按类别拆分的指标锚杆的recall必须单独记录因为它直接关系到设备安全。第三把测试集里所有漏检图导出来逐张判断是标注漏了还是模型确实没识别两个原因的修法完全不同。参数调优上现场调试阶段我会把置信度阈值做成界面上的滑块让操作员自己微调。常见做法是矸石阈值保持0.5以上减少误报锚杆阈值降到0.3保证召回。如果锚杆还是漏检查NMS的IoU阈值默认0.7在锚杆密集堆积时会互相压制降到0.5能让相邻锚杆各自保留。还有一个容易被忽略的地方视频流分辨率如果高于训练分辨率比如训练用了960但摄像头输出1080P推理时模型内部会先缩放检测框坐标是缩放后的画框前要按缩放比例映射回原图否则框会整体偏移。这套方案做完我在现场最深的体会是模型训练只占三分之一的精力数据规范和界面交互占另外三分之二。3000多张图不算多但把标注做规范、把增强做贴合、把界面做成工人愿意用的样子它就能稳定扛住现场运行。希望这些踩坑记录能帮你在做类似项目时少走几步弯路祝顺利。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑