☰
YOLOv8煤矿传送带堆煤堵塞预警系统:从环境搭建到部署实战
2026/9/28 1:50:31 网站建设 项目流程

简介:目标检测是工业视觉巡检的核心技术,YOLOv8凭借轻量级网络结构和高效的推理性能,在皮带运输场景中展现出强大适用性。其核心原理是通过单阶段回归直接预测目标边界框与类别,尤其适合对传送带堆煤区域进行实时框选。在煤矿井下复杂光照和粉尘环境下,结合面积比例判定与连续帧时间窗口,可显著降低堵料停机风险,提升非停事故的预警效率。本文从最基础的CPU环境搭建切入,覆盖Ubuntu下的PyTorch与ultralytics配置、LabelImg数据集标注、YOLOv8模型训练及损失曲线分析,并详细说明了部署阶段的可视化界面、阈值调整、跳帧优化及夜间误报抑制等工程实践,帮助开发者从零构建一套可运行的煤矿堆煤预警系统。

1. 基于YOLOv8的煤矿传送带堆煤堵塞预警系统:这套源码包到底该怎么用

煤矿传送带堆煤堵塞是现场最常见的非停原因:煤流在落料口堆积,皮带继续转动就把煤块甩出来,轻则堵料停机,重则撕裂皮带。以前靠人盯监视墙,几路画面轮播根本看不过来;装激光料位计又容易积灰误报。用YOLOv8做视觉检测,起手就是“把堆煤区域框出来”这个朴素目标,再结合面积比例和连续帧判定,就能在堆煤刚冒头时报警。你拿到的这套源码里一般包含推理脚本、可视化界面、整理好的数据集和部署说明;它适合毕设演示和课程设计,但想让它真正在矿上跑住,还需要自己会调环境、改标签、设阈值。下面我按从零跑通到落地报警的顺序,把每一步拆开讲。

2. 环境搭建:用 Ubuntu 20.04 把 YOLOv8 CPU 版先跑起来

2.1 为什么先跑 CPU 版,而不是一上来就啃源码

很多人打开部署教程第一件事就是配 CUDA、cuDNN,结果卡了两天。煤矿项目大概率跑在工控机上,现场机器不一定会配独显,而且毕设也常在普通笔记本上跑。用 CPU 版先把环境打通,后面再考虑 GPU 加速,成本和踩坑量都小一大截。YOLOv8 的官方库 ultralytics 对 CPU 支持很彻底,PyTorch 的 CPU 轮子装好就能推理,只是速度慢——640×640 输入下一张图大概要 1 到 2 秒,够在界面里做异步预警,但扛不住实时视频流逐帧检测。所以这里的策略是:先 CPU 跑通流程,最后再用 GPU 或在推理脚本里做跳帧。

你如果搜过 ubuntu20.04 搭建 YOLOv8 环境 CPU 版本,大概率看到一堆博客让你先装 Anaconda,再建虚拟环境,再装 ultralytics。这个路线是对的,但有个细节很多人没说清楚:conda 建环境时不要带 cudatoolkit,因为 CPU 版 PyTorch 装上后 CUDA 相关组件全是空的;一旦你顺手执行了nvidia-smi,看到驱动版本还在,就以为能用 GPU,其实 GPU 版 torch 根本还没装。反过来看,后面的训练也不建议一开始就盲目上 GPU,先拿 CPU 跑 10 轮看数据格式对不对,再谈加速。

CPU 环境的另一个优点是便于复现。拿同一个压缩包里的部署教程,放到另一台机器上,只要 conda 版本一致,pip 安装路径基本不会差。GPU 环境则容易被驱动版本、CUDA 版本和 PyTorch 版本绑定住,换一台机器就跑了半天。对毕设答辩来说,现场能跑起来比“理论速度更快”重要得多。

2.2 最小命令:conda 创建环境并验证推理

先用 conda 建立一个干净环境,命令如下:

# 创建独立环境,避免把系统 Python 搞乱 conda create -n yolo-coal python=3.9 -y conda activate yolo-coal # CPU 版 PyTorch,只安装 CPU 依赖,体积小很多 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics,YOLOv8 的训练/推理库 pip install ultralytics

逻辑说明:python 3.9 和 ultralytics 的兼容问题相对少,3.10/3.11 也可以,但 3.9 遇到 numpy、opencv 这些依赖的坑最少。--index-url指定 PyTorch 官方 CPU 源,pip 只会下载 CPU 版 torch,不碰 CUDA 库。ultralytics 安装时会把 opencv-python、numpy 等依赖自动带上,不需要逐个手动装。如果之后想切 GPU,只需要把 torch 换成对应 CUDA 版本再装一次,ultralytics 本身不用重装。

环境装好后,用一条命令验证推理:

yolo predict model=yolov8n.pt source=test.jpg

第一次运行会联网下载yolov8n.pt权重文件,然后打印检测结果,输出目录在runs/detect/predict。如果部署机器不能联网,提前把权重文件放到项目目录,再把命令里的模型路径改成相对路径:

yolo predict model=./yolov8n.pt source=./test.jpg

看到Results saved to runs/detect/predict就说明环境正常。这里要提醒一句:预训练权重是在 COCO 数据集上训练的,只会识别 person、truck 这些日常类别,不可能直接识别“堆煤”。很多新手在这里以为模型坏了,其实下一步就该换自己的数据集重新训练。

2.3 环境配好后还容易炸的三个位置

  • opencv 版本冲突:如果系统里预先装了老版本 opencv,conda 环境内再装 ultralytics 可能把 opencv 升到 4.x,导入时报libGL.so.1: cannot open shared object file。解决办法是用conda install libgl补系统库,或者在 Docker 里装。这个坑在无桌面 Linux 服务器上很常见。
  • yolo命令找不到:通常是环境没激活,或者 pip 指向了系统 Python。用which yolo看路径,如果发现指向系统目录,重新激活环境再装一次。
  • CPU 推理特别慢:不要立刻怪环境,先用nvidia-smi确认有没有 GPU。若是纯 CPU,就把输入尺寸降到 416,或者用yolov8n而不是yolov8s。现场如果是 Intel CPU,做部署时还能用 OpenVINO 加速,这部分后面再展开。

3. 处理数据集用于 YOLOv8 训练:从图片采集到标签文件

3.1 堆煤检测数据要拍什么、拍多少

先明确标注对象。传送带场景里有两种常见拍法:一种是只标“煤块堆”这一个类别,把堆煤区域框出来;另一种是把“正常皮带”和“堆煤皮带”作为两个类别一起标。我更建议只用coal_pile一个类,因为预警系统的输出归根结底是一块“需要处理的区域”,不是对整幅图做分类。类别少,模型更容易学,误报也少。把“正常皮带”作为背景即可,不影响检测。

采集数据时不能光在实验室里拍。矿下现场有煤尘、水雾、逆光、夜间灯光,这些“脏图”才是模型真正要面对的。所以至少要覆盖这几类场景:皮带正常空载、皮带正常有煤流、堆煤刚起来(煤块高出皮带护栏)、堆煤已经堵到落料口、堆煤盖住托辊。数量上新手先按 300~500 张来,宁缺毋滥;类别不平衡时,把严重堆煤的图片轻微旋转也能凑样本,但别靠复制硬凑,模型会过拟合。

3.2 用 LabelImg 或 Labelme 标注并导出 YOLO 格式

YOLOv8 默认的标签是矩形框格式,行业里用得最多的是 LabelImg。Labelme 也可以做多边形标注,但需要多一步转成矩形框,仅在目标形状特别不规则时才值得。我一般用 LabelImg,快捷键w画框,d切换下一张,标完直接导出 YOLO 格式的 txt。如果你拿到的是 json 或其他格式的数据集,要统一转成 YOLO:每张图片对应一个同名 txt,一行一个目标,格式为class x_center y_center width height,四个坐标都是相对图片宽高的浮点数。

先建立标准目录:

mkdir -p coal_dataset/images/train coal_dataset/images/val mkdir -p coal_dataset/labels/train coal_dataset/labels/val

目录说明:images和labels分开存放,训练时 ultralytics 根据 yaml 里的路径,自动把同名图片和标签匹配起来。图片名不要重名,不要带中文和空格,否则 Windows 和 Linux 行为不一致,部署到矿上工控机容易找不到文件。图片统一为 jpg;png 也可以,但有些矿上摄像头抓图是 bmp,体积很大,建议先批量转成 jpg 再标注,训练会明显加快。

3.3 数据划分脚本与标签校验

准备数据最怕的是划分不一致:图片在train,标签却留在all目录,训练时一半样本没有标签,模型会异常。写一个 Python 脚本做随机划分,顺手把标签一起复制:

import os, random, shutil from pathlib import Path img_dir = Path("coal_dataset/images/all") lab_dir = Path("coal_dataset/labels/all") train_img = Path("coal_dataset/images/train") val_img = Path("coal_dataset/images/val") train_lab = Path("coal_dataset/labels/train") val_lab = Path("coal_dataset/labels/val") imgs = list(img_dir.glob("*.jpg")) random.seed(42) random.shuffle(imgs) val_n = int(len(imgs) * 0.2) # 取20%做验证集 for i, img in enumerate(imgs): lab = lab_dir / (img.stem + ".txt") if i < val_n: shutil.copy(img, val_img / img.name) if lab.exists(): shutil.copy(lab, val_lab / lab.name) else: shutil.copy(img, train_img / img.name) if lab.exists(): shutil.copy(lab, train_lab / lab.name)

脚本做的事:读取所有 jpg,随机打乱后按 8:2 划分,并复制对应的 txt 标签。random.seed(42)保证每次划分结果一致,方便复现。复制而不是移动,是为了保留原始标注数据,划分错了还能重来。

划分后立刻做两件事。第一,检查标签文件是否为空:

find coal_dataset/labels -name "*.txt" -empty -print

如果发现空标签文件,说明有不带目标的图片混进了训练集;要么删除,要么给图片补一个难例样本。第二,检查类别编号:

cut -d' ' -f1 coal_dataset/labels/train/*.txt | sort | uniq -c

输出应该只有0,表示类别编号和 yaml 里的0: coal_pile一致。如果出现1或2,多半是标注工具里的标签顺序没和 yaml 对齐。这里翻车的典型症状是训练 loss 一直乱跳,最后模型把背景也框出来,根源就在标签编号。

3.4 数据不够时的离线增强

煤矿现场拍图不容易,几百张原图往往不够让模型在夜间、逆光下稳住。常见做法是离线增强,用 Python 生成亮度、对比度、翻转和拷贝粘贴后的新样本。

import cv2 import numpy as np img = cv2.imread("sample.jpg") hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hsv[:, :, 2] = cv2.add(hsv[:, :, 2], 40) # 提亮模拟白天逆光 bright = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) flip = cv2.flip(img, 1) # 水平翻转 cv2.imwrite("sample_bright.jpg", bright) cv2.imwrite("sample_flip.jpg", flip)

参数说明:add(hsv 的 V 通道, 40)是提亮 40 个灰度级,数值太大会让煤块过曝,太小效果不明显;水平翻转对传送带场景安全,因为皮带的左右特征对堆煤检测没有方向性。增强图片生成后,标签坐标不用改,因为几何变换没有改图片尺寸,但亮度增强后要手动抽查几张,防止目标被提亮到看不见。离线增强只能锦上添花,真正影响模型的还是原始数据里是否覆盖了现场光照。

4. 训练自己的堆煤检测模型:参数怎么设、损失曲线怎么看

4.1 训练命令与必须调的参数

数据准备好后,在项目根目录写一个coal.yaml:

path: coal_dataset train: images/train val: images/val nc: 1 names: 0: coal_pile

nc是类别数,这里只有堆煤一个目标。path可以填绝对路径,也可以填相对路径,但要保证运行yolo train时所在目录和 path 对应得上,否则会报数据集不存在。我最常踩的坑是把path写成datasets/coal/...,但项目目录下没有这个层级,一训练就报AssertionError: train dataset not found。调试办法是打印 yaml 解析后的完整路径,别凭感觉改。

执行训练:

yolo detect train data=coal.yaml model=yolov8n.pt epochs=50 imgsz=640 batch=8 device=cpu

参数说明:model=yolov8n.pt表示用预训练的 n 版做迁移学习,直接在随机权重上从头训,会需要更多数据和更长 epoch。epochs=50是起步值,毕设数据量小,50 轮已经能看到明显收敛;训练集有几千张时可以加到 100。imgsz=640是标准输入尺寸,堆煤在画面里通常占比较大,降到 416 能提速,但小堆煤的召回率会掉。batch=8取决于显存/内存,CPU 版会大量占用内存,8 比较保守;如果是 6GB 显存的 GTX 1660 Ti,yolov8s + batch=8也能跑,但接近显存上限,建议降到batch=4。device=cpu是纯 CPU 训练,换成 GPU 则写device=0。别写device=0,1,多卡并行还要调 DDP 参数,工程上容易翻车。

4.2 损失函数曲线图怎么看:过拟合与欠拟合

训练结束后,runs/detect/train目录里会自动生成results.png,包含 box_loss、cls_loss、dfl_loss 和 precision/recall 曲线。很多第一次训的人只盯着 mAP 看,等到最后一轮才发现模型废了。正确做法是从第 5 轮开始就看损失曲线趋势,不用等到训练结束。

正常的 loss 曲线是一条快速下降、后期带着小幅波动的线:前三轮 loss 下降最快,之后每轮只降一点。如果 val 的 box_loss 连续 10 轮还在明显上升,而 train loss 继续下降,就是过拟合;解决方法是增加数据增强、减小模型复杂度,或直接减少 epoch。反过来说,如果 train loss 都不降,往往是标签格式错了,或者类别名和标签编号不匹配,先回上一章做数据体检。

YOLOv8 的网络结构里有 C2f 模块和 anchor-free 检测头,这些在训练时不用手动改,但知道会带来一个现象:小目标多的时候,cls_loss比box_loss更敏感。堆煤是典型的大目标,边界框回归误差不太影响漏报,真正影响预警的是类别置信度。所以训练完不要只看 mAP,要再跑一次验证,把置信度阈值调低一点,看 recall 到底能到多少。

4.3 模型评估:mAP 与 PR 曲线

模型训练完成后,用验证集做评估:

yolo detect val model=runs/detect/train/weights/best.pt data=coal.yaml

重点看mAP50和mAP50-95。mAP50 到 0.8 以上,说明“堆煤”这个目标在正常场景下基本能框住;mAP50-95 比 mAP50 低很正常,因为它在 IoU 阈值 0.5 到 0.95 之间做了平均。如果 mAP50 小于 0.6,我会回头找三类问题:一是标注框漏掉了一半目标,模型学成了“只框堆煤最亮的一块”;二是训练集里正常皮带图片太多,堆煤图片太少,类别不平衡;三是夜间和白天图片混杂,但没有做曝光归一化。这时候别急着换更大的模型,yolov8n 到 x 的差异没有数据质量带来的差异大。

5. 部署与可视化界面预警:4个必踩的坑与排查顺序

5.1 可视化界面到底拆哪几块

部署时,拿到的源码包通常有两种界面形态:基于 PyQt5 的桌面程序和基于 Flask 的网页版。毕设演示一般用 PyQt5,多路视频画面放在 QLabel 里,右侧是报警列表;课程设计用 Flask 更方便在浏览器里查看。不管哪种形态,模块划分都应该是:视频采集线程 -> 模型推理线程 -> 报警判定 -> 界面刷新,不要在一个线程里又收帧又推理又绘图,否则帧率会掉到每秒一两帧。

一个最小的 PyQt5 推理循环骨架如下:

import threading import cv2 from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") class VideoThread(threading.Thread): def __init__(self, src=0): super().__init__() self.cap = cv2.VideoCapture(src) self.running = True self.latest = None def run(self): while self.running: ret, frame = self.cap.read() if not ret: continue results = model(frame, imgsz=640, conf=0.5) boxes = results[0].boxes.xyxy.cpu().numpy() # 在这里计算最大框面积占比,超过阈值就发报警信号 self.latest = frame, boxes

说明:conf=0.5是置信度阈值,实矿环境建议设 0.45~0.6。model在摄像头线程外只初始化一次,每次推理复用同一个实例,不然每次预测都重新加载权重,内存会一直涨。报警信号不要直接在这段代码里弹窗,应该通过一个队列把事件发给界面主线程。

5.2 坑1:摄像头画面卡顿,帧率上不去

现象:现场画面一到推理线程就掉到 2 帧,报警延迟 5 秒以上。

原因:视频采集和模型推理串行执行,cv2.VideoCapture.read()本身也有等待,一个循环就要 500ms 以上。

解决:用两个线程,一个只read(),另一个只做推理;在推理线程里做跳帧,比如每 3 帧检测一次。CPU 上检测一次要 1~2 秒,报警并不需要每一帧都推理,关键是结果要连续。跳帧后的间隔和报警窗口参数要联动,见第 6 章。

5.3 坑2:一个画面里同时有多个堆煤区域,报警怎么算

现象:模型一次输出好几个框,有的框是煤堆,有的框是皮带上的煤流。

原因:堆煤检测的目标是“异常堆积”,不是“有没有煤”。皮带正常载煤时也会被框出来。

解决:在框后增加判断规则。我一般计算每个框面积占画面总面积的比例,当最大的框占比超过 15% 且连续多帧都存在时,才触发预警。这个比例阈值要根据摄像头安装位置标定:摄像头装近了阈值调低,装远了调高。不要把报警逻辑写进检测模型里,放进预警模块单独维护,后面调阈值就不用重训模型。

5.4 坑3:模型在实验室好好的,部署到工控机上就报 CUDA / 内存错误

现象:一台配了 NVIDIA 显卡的工控机跑训练好的模型,启动界面秒退,终端报CUDA out of memory,或报Input type (torch.cuda.FloatTensor) ... CPU。

原因:部署机没有装和模型匹配的 PyTorch CUDA 版本,但代码里强制model.cuda();或者显卡显存只有 2G,界面、视频、模型抢显存。

解决:使用device="cuda" if torch.cuda.is_available() else "cpu",让程序自动选择设备。显存不够就改成 CPU 推理,或者把推理输入的imgsz降到 320。界面刷新不要用 OpenCV 的imshow,它会在无显示器环境下崩,改用cv2.imencode转成 jpg 字节流再交给前端。

5.5 坑4:夜间和逆光环境误报激增

现象:夜间开启补光灯后,煤堆反光导致模型把皮带边缘误检成堆煤;逆光时把煤流阴影检测成堆煤。

原因:训练数据没有覆盖这些光照条件,模型只学到了“黑色的高对比区域”这种弱特征。

解决:对输入帧先做一次 CLAHE 直方图均衡,再送进模型;同时按时间段切换置信度阈值,白天 0.5,夜间 0.65。这个方案比重新训练模型便宜得多,而且见效很快。

import cv2 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) enhanced = clahe.apply(gray) enhanced_bgr = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2BGR) results = model(enhanced_bgr, imgsz=640, conf=0.6)

参数说明:clipLimit=2.0控制对比度增幅,太小效果不明显,太大会让噪点一起被放大;tileGridSize=(8,8)是局部对比度的分块大小,对煤场这种暗部细节多的场景比较合适。处理后的灰度图再转回三通道,模型输入通道数才一致。

6. 进阶:把“单帧检测”升级成“时间窗口预警”

堆煤预警最容易被质疑的点是“偶发误报”。单帧检测到一个煤堆框就报警,会让值班员习惯性忽略警报。解决办法是先做时间窗口,而不是简单用单帧判定。

from collections import deque class FrameWindow: def __init__(self, window=5, hit_ratio=0.8): self.window = window self.hit_ratio = hit_ratio self.hits = deque(maxlen=window) def update(self, is_alarm): self.hits.append(1 if is_alarm else 0) return sum(self.hits) / len(self.hits) >= self.hit_ratio

逻辑说明:记录最近 5 帧的判定结果,超过 80% 的帧都报警,才真正触发。窗口大小由摄像头帧率和推理速度共同标定:25fps 视频建议用 10 帧窗口,CPU 推理只有 2fps 时,5 帧窗口就够了。窗口值太小起不到滤除偶发误报的作用,太大又会让真正的堵煤延迟报警。

另一个技巧是:在界面里保留报警前 10 秒的视频截图,方便复盘。一旦出现误报,你需要拿回放和算法参数对照,看阈值定得对不对。没有截图回溯,报警记录再全也只是黑匣子。

我给自己留的规矩是:模型训完后先在离线视频上跑一周,把每天报警次数和误报次数记下来,再调面积阈值和窗口参数;等误报率降到平均每班 2 次以内,才敢接到值班室。这套流程看着慢,但部署完返工最少。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询