简介:本资源面向煤矿智能化巡检与工业视觉检测方向的开发者、研究生及工程技术人员,提供一套基于YOLOv8的煤矿传送带矸石与锚杆异物检测完整方案,可直接用于推理部署,也可基于数据集重新训练。包内包含3000多张标注图像,标签为txt格式,已按train、val、test划分完毕并附data.yaml配置文件,yolov5、yolov7、yolov8、yolov9等主流算法均可直接接入训练。资源共2000个文件,以1991个xml标注文件为主,另含md说明、pdf环境配置教程与py脚本,压缩包约323.4MB,目录结构清晰,便于快速上手。配套PyQt可视化界面,可直观展示检测结果,适合课程设计、科研实验与工程验证。已有233人学习下载,能为煤矿异物检测任务提供从数据到模型再到交互界面的完整参考。
1. 煤矿传送带异物检测:YOLOv8 在矸石与锚杆场景里到底能解决什么
煤矿主运输巷道的传送带一旦混入矸石或锚杆,轻则划伤皮带、卡死托辊,重则撕裂输送带造成停产,甚至锚杆端头扎穿皮带引发火灾隐患。传统做法靠人工盯监控或金属探测器,前者疲劳漏检,后者对非金属矸石无能为力。YOLOv8 算法做煤矿传送带矸石与锚杆异物检测,本质是把目标检测模型部署到皮带上方摄像头,实时框出矸石和锚杆两类目标并联动停机或报警。这套方案适合煤矿智能化改造团队、做安全监测的集成商,以及想用真实工业数据集练手 YOLOv8 的算法工程师。3000 多张数据集加 PyQt 可视化界面,意味着从训练到演示的链路是完整的,不是只跑个 mAP 就结束。
2. 数据集怎么处理才能喂给 YOLOv8:从 Labelme 标注到训练格式
2.1 矸石与锚杆两类目标的标注规范
拿到 3000 多张煤矿传送带图像后,第一件事不是直接训练,而是统一标注口径。矸石和锚杆在图像里形态差异大:矸石多为不规则块状,颜色偏灰黑,与煤块容易混淆;锚杆是细长杆状,有时只露出一截端头,有时横跨整个画面。标注时我一般要求:矸石框住可见轮廓外沿,锚杆框住整根可见杆体,被遮挡超过 50% 的目标标为 difficult 或直接不标,避免模型学到半截特征。
常见做法是用 Labelme 做多边形或矩形标注,导出 JSON 后再转 YOLO 格式。但 Labelme 默认输出的是多边形点集,YOLOv8 需要的是归一化中心点加宽高的 txt。转换脚本必须处理两个边界:一是图像尺寸不一致时按各自宽高归一化,二是类别名到类别 ID 的映射要固定,矸石和锚杆的 ID 顺序一旦定下就不能在后续训练中改动。
import json import os from pathlib import Path # 类别映射:顺序固定,后续训练和推理必须一致 CLASS_MAP = {"gangue": 0, "anchor": 1} def labelme_to_yolo(json_path, output_dir): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) img_w = data["imageWidth"] img_h = data["imageHeight"] lines = [] for shape in data["shapes"]: label = shape["label"] if label not in CLASS_MAP: continue points = shape["points"] 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) # 归一化中心点与宽高 cx = (x_min + x_max) / 2.0 / img_w cy = (y_min + y_max) / 2.0 / img_h w = (x_max - x_min) / img_w h = (y_max - y_min) / img_h lines.append(f"{CLASS_MAP[label]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") out_name = Path(json_path).stem + ".txt" with open(os.path.join(output_dir, out_name), "w") as f: f.write("\n".join(lines)) # 批量转换 for jf in Path("labels_json").glob("*.json"): labelme_to_yolo(str(jf), "labels_yolo")这段脚本的核心逻辑是取多边形外接矩形再归一化。参数上,CLASS_MAP的 ID 必须与后续data.yaml里的names列表索引一致,否则训练时类别会错位。cx、cy、w、h都除以图像宽高,保证不同分辨率图像统一到 0-1 区间。转换后要抽查几张可视化,确认框位置没偏移。
2.2 数据集划分与 data.yaml 配置
3000 多张图按 8:1:1 划分训练、验证、测试集比较稳妥。煤矿场景有个坑:同一段皮带连续帧高度相似,如果随机划分,训练集和验证集会出现近乎重复的图,导致验证 mAP 虚高。我一般按时间或按皮带段做分组划分,同一组只进一个集合。
# 目录结构 dataset/ images/ train/ val/ test/ labels/ train/ val/ test/# data.yaml path: /home/user/dataset train: images/train val: images/val test: images/test nc: 2 names: ["gangue", "anchor"]path写绝对路径最稳,相对路径在 ultralytics 不同版本里解析基准可能不一致。nc为 2,names顺序与转换脚本的CLASS_MAP严格对应。如果后续要加第三类异物,改nc和names后必须重新生成所有标签,不能只改 yaml。
2.3 数据增强参数怎么设才不破坏矸石与锚杆特征
YOLOv8 默认增强里,mosaic和mixup对煤矿异物检测要谨慎。mosaic 把四张图拼一起,锚杆这种细长目标被裁切后可能只剩一小段,模型学到的锚杆特征不完整。我的经验是:mosaic保留但把close_mosaic设到 10 左右,让最后 10 个 epoch 关掉 mosaic 用原图微调;mixup直接设 0,因为煤矿背景单一,mixup 产生的半透明叠加反而引入噪声。
from ultralytics import YOLO model = YOLO("yolov8s.pt") model.train( data="data.yaml", epochs=150, imgsz=640, batch=16, mosaic=1.0, close_mosaic=10, mixup=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=5.0, translate=0.1, scale=0.5, fliplr=0.5, flipud=0.0, )hsv_h调小是因为矸石和煤块颜色接近,色相扰动太大会让矸石变成不存在的颜色。flipud设 0,传送带图像上下翻转不符合物理场景。degrees只给 5 度,锚杆方向在图像里基本固定,大角度旋转会引入不真实样本。scale给 0.5 是让模型适应不同距离的异物大小。
3. YOLOv8 训练煤矿异物检测模型的参数与显存调优
3.1 模型选型:n/s/m 在煤矿场景的取舍
YOLOv8n 最快但小目标召回低,锚杆端头在 640 分辨率下可能只有十几个像素,n 版容易漏。YOLOv8m 精度好但显存吃紧,GTX 1660 Ti 6G 跑 batch 16 会 OOM。我的建议:如果部署端是 RK3588 或边缘盒子,选 YOLOv8s 做蒸馏或直接量化;如果服务器端 GPU 推理,YOLOv8m 加 imgsz 960 对锚杆小目标提升明显。3000 张数据量下,s 版通常能到 mAP50 0.85 以上,m 版能到 0.9 左右,但推理速度差一倍。
# 查看显存占用与训练日志 nvidia-smi -l 2 # 训练时观察 loss 曲线 tensorboard --logdir runs/detect/train3.2 学习率与优化器:煤矿数据集的收敛技巧
YOLOv8 默认用 SGD,lr0=0.01。煤矿数据集背景单一、目标类别少,我一般把lr0降到 0.005,lrf设 0.01,让后期学习率足够小,避免在最优解附近震荡。warmup_epochs给 3,因为预训练权重已经很好,不需要太长预热。
model.train( data="data.yaml", epochs=150, lr0=0.005, lrf=0.01, warmup_epochs=3, optimizer="SGD", momentum=0.937, weight_decay=0.0005, cos_lr=True, )cos_lr=True用余弦退火,比阶梯下降更平滑。weight_decay保持默认,煤矿数据量不大,正则太强会欠拟合。如果发现训练 loss 下降但验证 mAP 不涨,先查验证集划分是否泄漏,再考虑加 dropout 或减小模型。
3.3 训练过程监控与早停策略
YOLOv8 自带patience参数,默认 50 个 epoch 没提升就停。煤矿数据 150 epoch 通常够,但如果你加了新类别或换了相机角度,建议patience=30,早点停省时间。监控时重点看metrics/mAP50-95和val/box_loss,如果 box_loss 持续降但 mAP 平,说明模型在过拟合背景。
model.train( data="data.yaml", epochs=150, patience=30, save_period=10, plots=True, )save_period=10每 10 epoch 存一次权重,方便回滚。plots=True自动画混淆矩阵和 PR 曲线,省得自己写脚本。训练完在runs/detect/train/weights/下拿best.pt做后续推理。
4. PyQt 可视化界面:把 YOLOv8 推理结果搬到操作台
4.1 PyQt5 界面布局与视频流接入
PyQt 界面要解决三件事:显示实时视频、叠加检测框、给出报警状态。布局上我用 QLabel 显示视频帧,QPushButton 控制启停,QTableWidget 记录报警历史。视频流用 OpenCV 的VideoCapture读 RTSP 或本地文件,每帧送 YOLOv8 推理后把boxes.xyxy画到帧上再转 QImage。
import cv2 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer, Qt from ultralytics import YOLO class DetectWindow(QMainWindow): def __init__(self): super().__init__() self.model = YOLO("best.pt") self.cap = cv2.VideoCapture("rtsp://camera_ip:554/stream") self.label = QLabel("等待视频流...") self.label.setAlignment(Qt.AlignCenter) self.btn = QPushButton("开始检测") self.btn.clicked.connect(self.toggle) layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.btn) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.timer = QTimer() self.timer.timeout.connect(self.update_frame) self.running = False def toggle(self): if self.running: self.timer.stop() self.btn.setText("开始检测") else: self.timer.start(30) # 约 33 FPS self.btn.setText("停止检测") self.running = not self.running def update_frame(self): ret, frame = self.cap.read() if not ret: return results = self.model(frame, conf=0.5, iou=0.45, verbose=False) annotated = results[0].plot() rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch = rgb.shape qimg = QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qimg).scaled( self.label.width(), self.label.height(), Qt.KeepAspectRatio)) if __name__ == "__main__": app = QApplication(sys.argv) win = DetectWindow() win.show() sys.exit(app.exec_())conf=0.5是置信度阈值,煤矿场景宁可误报不可漏报,可以降到 0.4。iou=0.45控制 NMS 重叠阈值,锚杆细长框重叠少,保持默认即可。timer.start(30)约 33 FPS,如果推理跟不上就调大间隔,避免界面卡死。results[0].plot()直接返回带框图像,省去手动画框。
4.2 报警联动与结果导出
检测到矸石或锚杆后,界面要给出明显提示并记录时间戳。我一般用 QTableWidget 追加一行,同时把帧存到本地alarm/目录,方便事后追溯。如果要对接触发器停机,通过串口或 GPIO 发信号,这部分在 PyQt 里用QSerialPort或单独线程处理,别阻塞 UI 线程。
from datetime import datetime def log_alarm(self, frame, class_names): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"alarm/{timestamp}.jpg" cv2.imwrite(filename, frame) row = self.table.rowCount() self.table.insertRow(row) self.table.setItem(row, 0, QTableWidgetItem(timestamp)) self.table.setItem(row, 1, QTableWidgetItem(",".join(class_names)))class_names从results[0].names取,只记录检出的类别。存图用原帧不用标注帧,保留原始证据。报警表加一列置信度更利于后续筛选。
4.3 界面卡顿与推理线程分离
PyQt 主线程跑推理,帧率高时界面会卡。常见做法是把推理放到 QThread 里,通过信号槽把结果传回主线程更新 UI。另一个坑是 OpenCV 的cap.read()在 RTSP 断流时会阻塞,加超时或重连逻辑。
from PyQt5.QtCore import QThread, pyqtSignal class InferThread(QThread): frame_ready = pyqtSignal(object) def __init__(self, model, cap): super().__init__() self.model = model self.cap = cap self.running = True def run(self): while self.running: ret, frame = self.cap.read() if not ret: continue results = self.model(frame, conf=0.5, verbose=False) self.frame_ready.emit(results[0].plot()) def stop(self): self.running = Falseframe_ready发 numpy 数组,主线程收到后转 QImage 显示。stop()在窗口关闭时调用,避免线程泄漏。RTSP 断流时ret为 False,这里用continue会空转,实际项目里加time.sleep(0.1)或重连计数。
5. 避坑与排查:煤矿异物检测落地时最容易翻车的 5 个点
5.1 验证集 mAP 很高但现场漏检严重
现象:训练日志里 mAP50 到 0.92,现场部署后矸石漏检率却超过 30%。原因:验证集和训练集来自同一段皮带、同一光照条件,模型没学到泛化特征。解决:按皮带段或时间段重新划分数据集,验证集必须包含不同光照、不同煤流厚度的图像;现场采集新数据做测试集,别用训练时的验证集当最终指标。
5.2 锚杆细长目标框回归不准
现象:锚杆检测框要么只框住一半,要么把旁边煤块也框进去。原因:锚杆长宽比大,YOLOv8 默认 anchor 对细长目标不友好,且标注时锚杆端头被遮挡部分没标。解决:标注时锚杆可见部分全标,不要截断;训练时把imgsz提到 960,小目标特征更清晰;如果还不行,改 CIOU 为 EIOU 或加角度回归,但改动大,先试前两步。
5.3 PyQt 界面显示颜色偏蓝或偏红
现象:OpenCV 读的 BGR 帧直接转 QImage 后颜色不对。原因:QImage 的Format_RGB888需要 RGB 顺序,OpenCV 默认 BGR。解决:cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)后再构造 QImage,注意bytesPerLine参数用ch * w,否则图像会错位。
5.4 训练时显存溢出但 batch 已经很小
现象:GTX 1660 Ti 6G 跑 YOLOv8s batch 8 还 OOM。原因:imgsz设了 1280,或者workers太多导致内存碎片。解决:imgsz降到 640,workers设 4 或 2,batch用-1让 ultralytics 自动找最大 batch;如果还不行,用amp=True混合精度,显存能省 30% 左右。
5.5 模型部署到 RK3588 后推理速度不达标
现象:PC 上 30 FPS,RK3588 上只有 5 FPS。原因:PyTorch 模型没转 RKNN,CPU 推理慢。解决:用 RKNN-Toolkit2 把best.pt先转 ONNX 再转 RKNN,量化用i8或fp16;输入分辨率降到 640,NPU 核心数设 3。转换时注意 YOLOv8 的输出层名字,不同版本 ultralytics 导出的 ONNX 节点名有差异,转 RKNN 前用 Netron 看一眼。
6. 从 3000 张到产线:让矸石与锚杆检测模型持续可用的几个习惯
模型训完不是终点。煤矿现场光照变化、皮带磨损、煤质变化都会让模型慢慢失准。我自己的习惯是:每两周从现场抽 200 张图跑一遍best.pt,把漏检和误检的图挑出来,用 Labelme 补标后加入训练集,用model.train(resume=True)增量微调 20 个 epoch。这样模型不会突然崩,而是跟着现场数据缓慢进化。
另一个习惯是给 PyQt 界面加一个「保存当前帧」按钮,操作员发现异常时一键存图,这些图比事后翻录像更有价值。报警记录表里加一列「是否误报」,定期导出统计,如果某类误报集中出现,比如把某段皮带接头误判为锚杆,就针对性地补负样本。
验证模型是否真的可用,别只看 mAP。我一般做三件事:一是拿一段 10 分钟现场视频跑离线推理,统计漏检次数;二是把conf从 0.5 调到 0.3 看召回提升多少,如果提升很大说明阈值卡太死;三是让操作员盲测,他们能一眼看出的异物模型必须也能检出,否则就是特征没学好。
这套方案从 3000 张数据集到 PyQt 界面,链路是通的,但真正落地时最花时间的不是调模型,而是数据闭环和现场适配。我踩过最大的坑是拿训练集当验证集,mAP 好看但现场翻车,后来老老实实按皮带段划分数据,指标降了 5 个点,但现场漏检少了一半。希望帮到你。
本文还有配套的精品资源,点击获取