基于CNN的煤矸石自动分选:从图像采集到模型部署
2026/9/19 12:30:18 网站建设 项目流程

简介:一篇发表于《江苏建筑职业技术学院学报》2019年第4期的学术论文PDF,围绕基于CNN卷积神经网络的煤矸石自动分选展开,面向深度学习、计算机视觉及矿山智能化方向的科研人员和从业者。论文提出利用卷积神经网络这一深度学习算法,对煤块与矸石图像纹理特征进行多层次提取并自动分类,测试准确率达92%,有效规避了传统灰度特征在原始样本下易受色差干扰的问题。资源包含完整论文正文、摘要、图表数据及参考文献,仅1个PDF文件,压缩包约1.98MB,内容紧凑且结构清晰。读者通过该论文可以系统了解CNN在图像分类与工业分选中的应用流程,掌握卷积层、池化层、全连接层等核心概念,同时学习机器学习、数据建模在真实工业场景中的落地方法。目前该PDF已有338人学习,适合作为相关课题调研、论文写作或课程学习的参考资料。

1. 从煤流里认出矸石:CNN为什么成了这个问题的默认答案

煤炭开采中,煤和矸石(灰分一般高于50%的岩石)分离是选煤厂的第一个环节。传统人工手选费人力、效率低,而基于X射线和浮选的设备成本高、维护复杂。近五年的现场经验是:只要能用可见光相机拍到煤流,就能用CNN卷积神经网络做实时分类,把分选精度做到95%左右,单帧处理控制在几十毫秒。这个思路的实质是把煤和矸石当成两类图像特征,让卷积核自己去学颜色、纹理、边缘上的差异,而不是写一堆人工规则。这篇博文就按工程落地的顺序,把图像采集、模型设计、训练和部署串一遍;如果你是刚接手分选项目的算法工程师,可以直接照着搭。

2. 煤矸石图像数据的采集、标注与预处理

2.1 现场相机安装与图像采集的3个注意事项

煤矸石分选和普通图像分类不一样,现场环境不是实验室,皮带在跑、煤粉在飞、光照在变。相机安装位置直接决定模型能看到什么。我一般把相机架在皮带落料点后方的固定横梁上,垂直或略微倾斜俯拍,让每块煤或矸石在图像里占超过30%的像素面积。这样分类时才不会把一堆碎块混在一起当整体。

注意事项有三个。第一是光源均匀性,煤矸石表面有镜面反射,强点光源会造成高光区域,让纹理特征失效;建议用两排LED条形灯从两侧45度打光。第二是防尘,选煤厂粉尘大,相机镜头要配吹扫气幕,否则半小时后图像就开始发白。第三是触发方式,用皮带秤或光电传感器触发相机拍照,避免拍空带,减少无效数据和存储压力。

2.1.1 采集参数和样本数量的经验值

相机分辨率建议不低于500万像素,曝光时间1ms以内,否则皮带高速运动时会有运动模糊。下表是我在多个选煤厂使用的采集参数参考值,注意不同现场煤质不同,参数不是死的。

参数推荐值备注
相机类型工业面阵相机可见光,彩色
分辨率2048×1536保证小块矸石有足够像素
曝光时间0.5~1ms皮带速度越高,曝光越短
帧率30~60fps配合触发信号可降低
拍摄距离40~60cm视场宽度约40cm
图像格式PNG或BMP避免JPEG压缩丢失纹理

样本数量上,每类至少5000张,总共一万张打底。煤和矸石类别不平衡时,不要直接用原始样本训练,否则模型会偏向多数类。现场采集时还要覆盖不同煤种、不同含水量、不同粒度,否则模型换一个矿就失效。

2.2 标注格式与数据清洗

标注用矩形框还是整图分类?自动分选通常只需要判断皮带上每一块物体是煤还是矸石,所以先做目标检测,把单个物体框出来,再对框内图像分类。我的做法是先用YOLOv8或Faster R-CNN检测煤块位置,然后按检测框裁剪成小块,存成二分类数据集。这一步能复用已有检测模型,不用手工裁剪上万张图。

清洗时重点处理三类坏图。第一类是模糊图,运动模糊和失焦都算,用Laplacian方差低于阈值就丢弃。第二类是过暗或过曝图,基于灰度直方图判断。第三类是异物图,比如锚杆、木片、铁丝,这些不属于煤也不属于矸石,但会在现场出现,如果不剔除,模型会把它们和矸石混在一起。

import cv2 import numpy as np def is_blurry(image, threshold=80): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) var = cv2.Laplacian(gray, cv2.CV_64F).var() return var < threshold def is_bad_exposure(image, low=30, high=220): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) mean = gray.mean() return mean < low or mean > high

这段代码对每张裁剪图计算Laplacian方差,方差低说明边缘少,大概率是糊的。曝光判断用全局灰度均值,现场如果是暗煤,阈值要调整,不能全局套用。处理完成后,把所有有效图统一缩放到固定尺寸,比如128×128或224×224。

2.3 数据增强:把一万张变成足够泛化的训练集

煤矸石分类中,增强不是随便翻转,要贴合物理规律。水平翻转可以用,垂直翻转基本不用,因为皮带上的物体不会倒悬。随机旋转不超过15度,超过后碎煤块的纹理方向会失真。颜色增强适度,亮度扰动、对比度扰动是重点,因为现场光照确实会波动。还有一种常用的增强是Cutout或RandomErasing,模拟煤粉覆盖部分表面的情况。

import albumentations as A train_transform = A.Compose([ A.HorizontalFlip(p=0.5), A.Rotate(limit=15, p=0.8), A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.8), A.CoarseDropout(max_holes=8, max_height=16, max_width=16, p=0.3), A.Resize(128, 128) ])

参数说明:A.Rotatelimit=15表示随机旋转角度在-15到15度之间;CoarseDropout会在图上随机挖掉若干个小矩形区域,模拟煤矸石表面被粉尘遮挡。增强后模型对光照变化和局部遮挡的鲁棒性会显著提升。增强只能在训练时用,验证集和测试集只用Resize和归一化,否则评估指标会虚高。

3. CNN模型设计:从结构原理到煤矸石分类的落地配置

3.1 为什么选CNN而不是前馈神经网络

图像处理为什么用CNN不用前馈神经网络,这个问题在煤矸石分选场景下很好回答。前馈神经网络(也就是全连接网络)把图像展成一维向量,会破坏像素之间的空间相邻关系。煤和矸石的区别主要在局部纹理:煤的纹理呈条带和内生裂隙,矸石一般有颗粒状和块状结构。如果用全连接网络,它看到的只是散列的像素值,学不到“相邻像素组成边缘、边缘组成纹理”这个层次关系。

CNN通过卷积核在图像上滑动,每个卷积核只关注一个小邻域(比如3×3或5×5),再通过多层堆叠增大感受野。第一层学习边缘、纹元,第二层组合成局部形状,后面才学会区分整体类型。这和人类识别煤矸石的方式接近,先用纹理和光泽判断,而不是逐个像素比对。而且卷积核是共享权重的,参数量远小于全连接层,不容易过拟合,这对现场少量样本场景很友好。

3.2 一个可用的CNN结构:参考LeNet-5思路

LeNet-5卷积神经网络是1998年提出的经典结构,虽然年代久远,但它的“卷积-池化-全连接”骨架今天依然适用。煤矸石图像分辨率不高,128×128输入足够。我常用的一个基础模型如下:

import torch.nn as nn class CoalGangueCNN(nn.Module): def __init__(self, num_classes=2): super().__init__() self.features = nn.Sequential( nn.Conv2d(3, 16, kernel_size=3, padding=1), nn.BatchNorm2d(16), nn.ReLU(inplace=True), nn.MaxPool2d(2), # 128->64 nn.Conv2d(16, 32, kernel_size=3, padding=1), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.MaxPool2d(2), # 64->32 nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(2) # 32->16 ) self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(64 * 16 * 16, 128), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))

结构说明:三个卷积层+池化层组成特征提取器,卷积输出通道从16增加到64,空间尺寸从128降为16。经过Flatten后,16×16×64=16384个特征输入全连接层。这里没有用大卷积核,因为煤矸石纹理差异属于中频细节,3×3卷积堆叠比5×5参数更少、非线性更强。

3.3 卷积、池化、激活函数在煤矸石分类里的作用

卷积操作是核心。Conv2d(3, 16, kernel_size=3, padding=1)表示输入是3通道RGB图像,使用16个3×3卷积核,输出16个特征图。每个卷积核在图像上滑动,计算局部像素和权重乘积,提取一种特征。有的核学出来是横向边缘,有的是纵向边缘,有的是斑点纹理。关键参数padding=1让特征图尺寸不变,避免边缘像素被丢弃。

池化层MaxPool2d(2)把每个2×2区域取最大值,特征图尺寸减半,同时保留最强烈的特征响应。这样模型的空间敏感度降低,对煤块位置小幅偏移更鲁棒。如果去掉池化,感受野增加慢且计算量成倍增长。

BatchNorm和Dropout都是正则化手段。BatchNorm让每层输入分布稳定,可以用更大的学习率;Dropout在全连接层随机丢弃一半神经元,有效防止参数量大时过拟合。煤矸石数据集通常在一万到五万之间,全连接层极易过拟合,Dropout必须加。

3.4 从LeNet到现代结构的选型边界

LeNet适合在数据量和算力都受限的条件下用。如果你有更多数据和GPU,ResNet18或MobileNetV3是常见升级。ResNet加了残差连接,解决深层网络梯度消失问题;MobileNet用深度可分离卷积,参数量少,适合部署到边缘设备。

我的选型经验是:现场控制电脑有GPU,用ResNet18;如果是嵌入式设备比如Jetson Nano,先试MobileNetV3-small。但不要一上来就堆大模型,煤矸石分类任务相对简单,大概率基础CNN就能到98%以上的准确率,大模型只在纹理极度相似时才需要。

# 使用ResNet18时,只需替换最后一层全连接 import torchvision.models as models model = models.resnet18(pretrained=False) model.fc = nn.Linear(model.fc.in_features, 2)

注意预训练权重问题。ImageNet上没有煤和矸石,但底层特征(边缘、色彩)是通用的。如果数据量少,用pretrained=True可以加快收敛;如果数据量超过两万,从零训练和预训练效果差距很小。实际工程中,为了减少依赖,我更喜欢从零训练一个窄而浅的CNN,因为煤矸石图像的颜色分布与自然图像差异较大,预训练权重未必带来好处。

4. 训练流程与参数调优:从过拟合到95%+准确率

4.1 数据集划分与评估指标

在训练之前,先把数据按6:2:2划分为训练集、验证集、测试集。注意要按煤块ID分组,不能用同一块煤的多个裁剪图同时出现在训练集和验证集,否则验证集指标会虚高。我在现场踩过这个坑,模型在验证集95%准确率,上线后只有88%,原因就是数据泄漏。

评估指标不能只看准确率。煤矸石分选中,矸石被误捡成煤(漏检)会导致产品质量下降,煤被误捡成矸石(误检)会导致资源浪费。所以要同时看精确率(Precision)和召回率(Recall)。如果分选目的是“提高精煤质量”,应优先保证矸石召回率;如果目的是“减少煤损失”,则要降低误检率。业务指标要在模型训练前就定下来,不能训练完再谈。

指标公式含义在分选场景中的优先级
准确率 Accuracy(TP+TN)/(TP+TN+FP+FN)总的判断正确率参考
精确率 PrecisionTP/(TP+FP)检出的矸石中真矸石比例
召回率 RecallTP/(TP+FN)真实矸石中被检出的比例很高
F1 Score2×P×R/(P+R)均衡指标选型时用

4.2 损失函数、优化器与学习率设定

煤矸石二分类最常用交叉熵损失(CrossEntropyLoss),它直接衡量预测概率分布和真实标签的差距。在PyTorch里,nn.CrossEntropyLoss()会自动对模型输出做Softmax转换,所以模型最后一层不需要额外加Softmax。

优化器方面,Adam和SGD都常用。我的习惯是:小数据集用Adam,收敛快,对学习率不敏感;大数据集用SGD+Momentum,最终泛化效果好。下面是完整训练循环的骨架:

import torch import torch.nn as nn from torch.utils.data import DataLoader device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = CoalGangueCNN(num_classes=2).to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) for epoch in range(30): model.train() running_loss = 0.0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * images.size(0) epoch_loss = running_loss / len(train_loader.dataset) print(f'Epoch {epoch+1}, Loss: {epoch_loss:.4f}') scheduler.step()

关键参数说明:初始学习率1e-3对Adam比较稳定;scheduler每10个epoch把学习率缩小为原来的0.1,帮助收敛到平坦最小值。如果你的训练损失不下降,考虑把学习率降到3e-4;如果损失波动大,降低batch size或学习率。

4.3 过拟合判断与4个解决手段

训练过程中,如果训练loss持续下降而验证loss在第5个epoch开始上升,这是过拟合的典型信号。煤矸石数据类别简单,却很容易过拟合,因为矸石表面有大量细节,模型会记住噪声而不是本质特征。

解决手段按优先级排:第一加大数据增强,特别是随机旋转和色彩抖动;第二增加Dropout比例,从0.5提高到0.7;第三减少模型复杂度,把通道数减半;第四添加权值衰减(weight_decay=1e-4)。在训练代码中加入optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)即可实现L2正则化。

此外,还可以用早停法(Early Stopping)。当验证loss连续5个epoch没有改善时,保存当前最优模型并停止训练。PyTorch没有内置早停,需要自己写一个简单的判断逻辑。

4.4 类别不平衡与难例挖掘

如果现场采集到的煤比矸石多很多,类别比例可能达到5:1。直接训练会让模型偏向预测煤。常见做法是调整损失函数的权重:

criterion = nn.CrossEntropyLoss(weight=torch.tensor([1.0, 3.0]).to(device))

weight数组对应类别顺序,矸石类的权重设高一些,让误判矸石的惩罚变大。另一种方法是Focal Loss,它可以自动降低易分类样本的权重,让模型聚焦于难分样本。煤矸石中难分样本通常是粒度和颜色都很接近的小块。

训练完成后,用测试集计算混淆矩阵。不要只看一个指标。建议导出如下结果:TP、FP、FN、TN,然后和业务方讨论。如果漏掉的矸石都是同一种粒度,说明采集数据时粒度覆盖不全,需要补充样本而不是继续调参。

5. 部署到自动分选设备和模型验证

5.1 把PyTorch模型转成ONNX并推理

训练完成的模型要部署到分选控制单元,常见方案是TensorRT(NVIDIA GPU)或OpenVINO(Intel CPU)。PyTorch模型先转成ONNX是标准步骤:

model.eval() dummy_input = torch.randn(1, 3, 128, 128).to(device) torch.onnx.export( model, dummy_input, "coal_gangue.onnx", input_names=['input'], output_names=['output'], opset_version=11 )

转换时注意两点:一是model.eval()必须调用,否则BatchNorm和Dropout仍然在训练模式,推理结果不稳定;二是固定输入尺寸,ONNX不支持动态尺寸在部署时更麻烦。转换后可以用onnxruntime验证输出是否和PyTorch一致:

import onnxruntime as ort import numpy as np sess = ort.InferenceSession("coal_gangue.onnx") ort_out = sess.run(None, {"input": dummy_input.cpu().numpy()})

5.2 和现场PLC联动:从模型输出到喷阀动作

自动分选本质上是一个时序控制问题。相机拍到煤矸石后,模型输出类别,但喷阀不能立刻动作,因为物料从相机位置到执行机构有传输延迟。常见的做法是:在相机位置识别出矸石后,记录时间戳,通过PLC延迟设定值再触发喷阀。延迟时间根据皮带速度和相机与喷阀的距离计算:

延迟时间(ms) = 距离(mm) / 带速(mm/ms)

如果带速是1.5m/s,距离是600mm,延迟就是400ms。这个值在调试时要微调,因为物料可能在皮带上滑动。我一般先用LED标定球做链路测试,再把模型输出接入PLC。部署时给模型一个置信度阈值,比如confidence > 0.7才触发,低于阈值默认放行,因为把煤当矸石打掉的成本高于矸石混入煤里的成本。

5.3 量化与加速:让模型跑得更快

如果推理速度不满足要求,常见做法是INT8量化。TensorRT可以把FP32模型转成INT8,速度提升2~3倍。但量化需要校准数据,收集500张代表性的煤矸石图像作为校准集,放到量化工具中计算每个激活值的分布。量化后准确率一般下降0.5~1%,可接受。如果下降超过2%,就退回FP16,或者使用更大的校准集。

实际部署环境还要考虑粉尘和震动对工控机的影响。不建议在现场训练模型,只做推理。把训练好的模型参数固化到只读目录,启动时加载,防止意外断电导致文件损坏。

5.4 验证体系:上线前和上线后的闭环

上线前,把测试集里的难例图像单独列出来,逐一确认模型判断。上线后,记录每天的分选准确率和误检率,和人工抽检结果对比。如果发现准确率持续下降,先检查相机镜头是否污染、光源是否衰减,再检查煤种是否有变化。煤炭来源变化时,模型需要增量训练,使用原来的权重继续训练新数据,学习率调小到原值的十分之一。

自动分选模型不是一个静态交付物,而是一个需要持续维护的系统。把现场的图片定期回传,每周做一次人工复核,把错分的图片加入到训练集,这是保证模型长期有效的关键。CNN只是识别核心,真正决定项目成败的是数据反馈闭环。

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

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

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

立即咨询