CNN煤矸石分选全流程:从机器视觉到模型部署实战
发布时间:2026/9/30 8:29:25来源:尧图网络
简介这份PDF资料是一篇基于CNN卷积神经网络的煤矸石自动分选研究论文发表于《江苏建筑职业技术学院学报》2019年第4期。内容针对传统灰度信息分选煤矸石的局限提出利用卷积神经网络对煤块与矸石图像进行多层次纹理特征提取实现自动识别测试准确率达到92%并详细阐述CNN卷积层、池化层、全连接层工作原理及实验对比适用于深度学习、计算机视觉、工业智能分选等领域研究者与相关专业学生阅读参考。资源为单一PDF文件大小1.98MB包含论文全文、图表、数据与参考文献。目前已有338人学习对于开展神经网络图像分类课题或毕业设计具有参考价值可以有效帮助读者理解CNN建模思路、特征提取方法及工业场景落地流程。1. 煤矸石自动分选为什么非要用CNN从手选工位到机器视觉的转变去过选煤厂的人都知道手选矸石是整个流程里最苦也最不可控的环节。工人在皮带旁一站八个小时靠眼睛从煤流里把灰白色的矸石捡出来这种岗位招人越来越难而且人眼疲劳后漏捡率会明显上升。煤矸石自动分选的研究方向这些年其实一直有但真正让它从实验室走向车间的是CNN卷积神经网络在图像识别上的稳定表现。传统机器视觉靠颜色阈值、形状规则去判断遇到不同煤种、不同井下环境就失效而CNN能自己从像素里学出煤和矸石在纹理、光泽、边缘上的深层差异。这篇文章我把从采集图像、做标注、训练模型到现场部署的完整路径拆开讲尤其要说说那些论文里不会写的坑。读者如果是选煤厂的技术员、做机器视觉的工程师或者正在调研这个项目的决策者可以照着这个思路去评估和落地。2. 从成像到识别煤矸石分选里的CNN原理与选型2.1 煤和矸石在图像上到底差在哪CNN能学到什么煤和矸石在肉眼看来最直观的差异是颜色煤是黑色或深灰色矸石往往是灰白、浅黄甚至带点红。但如果只是在RGB空间里设定一个灰度阈值现场会立刻翻车。因为不同矿区的煤质差异很大无烟煤表面是亮黑的褐煤偏棕矸石表面的煤粉又会让它发黑。传统视觉算法把这些差异归结为“光照不均”“煤粉污染”本质上是因为它只能提取表层特征。CNN卷积神经网络的不同之处在于它通过卷积核在图像局部滑动逐层抽象出边缘、纹理、角点、局部形状这样的特征。煤块在外力破碎后断面通常是参差的、有光泽的而矸石断面更致密、缺乏玻璃光泽。这些特征不是靠程序员定义而是由网络在训练中自动归纳。对我来说CNN真正解决的是“特征说不清”的问题。过去做选型要跟机械厂家解释为什么不能用颜色传感器现在直接用图片训练一个网络让数据说话。卷积神经网络原理里有个核心概念叫“感受野”。浅层卷积核只能看到很小区域比如3x3的像素提取的是边缘深层卷积核能覆盖更大范围提取的是“这一块是煤还是矸石”这样的语义信息。煤矸石分选任务并不复杂不需要识别上千类物体但需要网络对局部纹理足够敏感同时不受大面积反光影响。所以网络不用太深ResNet18或者自建一个四层的CNN就够用盲目上ResNet152反而容易过拟合而且推理速度跟不上皮带速度。2.2 为什么不用SVM或传统机器视觉CNN和RNN的边界在CNN之前煤矸石识别的主流做法是提取手工特征再加SVM分类器。常用特征包括灰度直方图、局部二值模式LBP、灰度共生矩阵GLCM。这些特征在某些矿区能跑到95%以上的准确率但换一个矿煤质一变准确率可能掉到80%以下。问题出在手工特征的可迁移性太差。你针对“亮黑煤”设计的特征遇到“粉煤”就失效。SVM和CNN原理的对比本质是“人工定义特征”和“数据驱动特征”的差别。SVM需要先做特征工程而且对图像的空间结构不敏感CNN直接吃原始像素通过卷积操作天然保留了空间邻域关系。在煤矸石分选这种缺陷样本差异微妙、环境多变的场景下CNN的鲁棒性更好。那CNN和RNN的边界在哪RNN擅长处理序列数据比如一段文本、一段语音或者输送带上的连续帧——如果你想把一个煤块的视频序列送进去用RNN也许合理。但煤矸石分选的工业相机通常在皮带上方垂直拍摄每个物料块只需要单帧静态图就能判断。单个图像是二维网格结构RNN会破坏空间关系而CNN的卷积结构恰恰匹配图像。所以常见的做法是如果现场有高速相机的连续帧仍用CNN对每一帧独立识别再用后处理逻辑做跟踪而不是直接上RNN。2.3 CNN的基本结构卷积层、池化层、全连接层怎么搭一个用于煤矸石二分类煤/矸石的CNN基本结构我一般这样设计输入层固定图像尺寸常见是224x224x3也可以压缩到128x128减少计算量。煤矸石分选本质是二分类图像细节要求不高128x128对现场部署更友好。卷积层前两层用32和64个3x3卷积核步长1padding为1。每层后面接ReLU和2x2最大池化。这个小结构足够提取煤块的纹理和边缘。第三层用128个3x3卷积核再接全局平均池化而不是直接拉平。全局平均池化能显著减少参数防止过拟合。输出层一个全连接层输出2个节点接Softmax。注意不要把网络搭太深。煤矸石图像结构简单类别少深网络不但训练慢还容易出现梯度消失。我见过有人直接搬VGG16训练集准确率99%测试集准确率92%但推理速度只有20fps根本跟不上1.5m/s的皮带速度。用上面这个浅层CNN用GPU推理可以到200fps以上。选型阶段还应该考虑部署硬件。如果打算用PLC控制喷吹阀那视觉系统推理延迟必须小于物料通过相机视场的时间。比如皮带速度1.5m/s相机视场宽度40cm一个煤块从被拍摄到被吹掉的可用时间约260ms扣除相机曝光、传输和PLC动作时间留给模型推理的时间往往只有50ms以内。因此CNN模型在部署阶段要做的量化、剪枝从选型号那一天就要考虑进去。不要先训一个大模型再想着压缩应该一开始就定目标推理时间。3. 做数据集是分选项目的头号工程采集、标注与增强3.1 现场图像采集的硬性要求没有干净的数据集CNN再厉害也白搭。煤矸石分选的数据集必须从现场实际工况里采不能用实验室模拟图。我在推进项目时第一个月基本全在搞采集。相机安装位置要选在皮带平稳段避开溜槽口和转载点。因为转载点物料飞溅图像模糊而且粉尘大。光源要用白色LED线性光从斜上方45度照射避免煤块表面的镜面反射直接打入镜头形成高光斑。高光区域会让卷积神经网络误判为“亮色”非常致命。帧率与皮带速度要匹配。比如皮带速度1.5m/s相机视场宽度40cm一对COB LED频闪能冻结运动模糊但快门时间要小于1/2000s。我建议用千兆网工业相机分辨率不需要太高500万像素足够但帧率至少要30fps。实际采集时不是连续录像而是用光电传感器触发物料进入视场时拍一张这样每帧都是清晰的单个料堆。采集样本量方面煤和矸石各至少5000张原始图。这是底线。如果矿区煤质多变最好按不同煤层、不同湿度分批采样。我见过一个项目只采集了晴天白天的图结果阴雨天现场光照一变识别率直接掉到及格线以下。所以采集时要有意识地把光照强度、物料湿度、煤粉浓度这三个变量都覆盖进来。3.2 标注格式与工具选择标注就是给每张图里的每个物料块画框或者画轮廓。煤矸石分选常用的标注格式是YOLO风格的txt文件每行一个目标依次是类别、中心点x、中心点y、宽、高坐标都用相对于图宽高的比例表示。我一般用LabelImg做画框标注虽然它老但稳定、学习成本低。如果要做像素级分割用Labelme。煤矸石分选大多数场景只需要检测“矸石在哪”所以画矩形框就够了。但注意一个细节煤块之间往往挤在一起一个框里可能同时框到小煤块和矸石。标注时框要紧贴目标边界不要把相邻的无关物料包进去。标准是“宁缺毋滥”——框大一点可以但绝不能框进另一块截然不同的物料。标注完了别急着训练先做一轮交叉检查。让两个人分别标一百张图计算IoU一致性低于0.8就说明标注规范没定清楚需要重新开会对齐。# 把LabelImg导出的VOC XML转成YOLO txt格式 import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, out_dir, class_list): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size).find(width).text) img_h int(root.find(size).find(height).text) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in class_list: continue cls_id class_list.index(cls) box obj.find(bndbox) x1, y1 int(box.find(xmin).text), int(box.find(ymin).text) x2, y2 int(box.find(xmax).text), int(box.find(ymax).text) x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)这段脚本做的事情是把标注工具导出的XML文件解析出来读取每张图的宽高和每个目标的类别与边界框然后换算成YOLO需要的归一化坐标。为什么要归一化因为网络输入尺寸固定训练时会做缩放如果坐标是绝对像素值缩放后就全乱套了。转换后一定要抽查几个txt文件打开原图对比一下框的位置。常见错误是有一个类别的名字拼写不一致导致那个类别的目标全部被跳过但程序不报错训练集里矸石样本悄悄变少了。3.3 数据增强的适度使用数据增强是CNN训练里绕不开的一环煤矸石图像尤其需要。但要注意增强操作不能破坏煤矸石的物理本质。我常用的增强组合是随机水平翻转概率0.5。煤块的形状虽然不规则但翻转不影响判断而且能增加样本多样性。随机亮度调整范围0.8~1.2。因为不同光照下煤面亮度差异大让模型适应亮度变化很有必要。但过大的亮度调整会把黑色煤块变成灰色破坏类别内的一致性。随机对比度调整范围0.9~1.1。增强边缘锐度帮助模型学习纹理。随机裁剪裁剪比例0.8~1.0。模拟物料被局部遮挡的情况。禁用垂直翻转。煤块从皮带上看顶部和底部的纹理分布有方向性垂直翻转会引入错误的学习信号。禁用旋转超过90度。矸石长条状时旋转90度就变成了立着的不符合实际落料姿态。增强的数量一般把每张原始图扩到2~4倍。扩太多会让模型过度适应“人造样本”在真实图像上反而变差。我常用torchvision里的transforms不用自己写import torch from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((128, 128)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.1), transforms.RandomResizedCrop(size128, scale(0.8, 1.0)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])这里用了ImageNet的标准化参数其实煤矸石图像是灰度偏黑的更合理的做法是根据自己数据集的统计值计算均值和标准差。你可以先加载所有训练图片用代码算出来然后替换掉上面两个数组。我在早期的项目里偷懒直接用了ImageNet的参数结果发现训练收敛变慢后来改成自算均值/方差损失下降明显快了。4. 训练一个能进车间的煤矸石识别模型PyTorch落地代码与参数4.1 最小可训练代码选定了PyTorch作为训练框架下面的代码足够跑通一个基础CNN训练流程。代码里我把重点放在结构和主循环完整的数据加载部分用伪代码表示因为每个项目的文件目录不一样。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader class CoalGangueCNN(nn.Module): def __init__(self): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(64, 128, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool2d(1) ) self.classifier nn.Linear(128, 2) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) return self.classifier(x) model CoalGangueCNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # train_loader 返回 (images, labels) for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() print(fEpoch {epoch1} loss: {running_loss/len(train_loader):.4f})这个网络的features部分就是上一章说的结构三层卷积不断把通道数从32加宽到128同时用池化把空间尺寸缩小最后用自适应平均池化把特征图压成1x1再交给全连接层分类。为什么用AdaptiveAvgPool2d(1)它不管输入特征图是多大都输出1x1这样就能把全连接层固定下来即使训练时改了输入分辨率也不用改网络结构。煤矸石分选模型部署时有些时候会为了提速把推理图像从128改到96这个设计能避免改代码。上面代码里没有验证集实际训练时必须留出10%~15%的数据做验证。我习惯在每个epoch结束后计算验证集准确率并保存验证准确率最高的一次模型参数而不是保存最后一个epoch的。4.2 三个必调参数第一个是学习率。用Adam时我一般默认0.001但煤矸石数据集小0.001经常让模型在头几个epoch快速收敛然后验证准确率就卡住了。我通常把学习率降到0.0003训练更稳定。另外加一个学习率衰减策略比如每10个epoch乘以0.1能有效提升收敛精度。第二个是batch size。这个参数受显存限制但煤矸石图像小128x128输入batch size可以放到64甚至128。注意batch size太小时BatchNorm层会抖动loss曲线上下跳太大则模型容易陷入尖锐极小值泛化差。我用32和64做过对比64在准确率和训练速度上比较均衡。第三个是输入图像尺寸。很多人在这个问题上欠考虑。煤矸石分选的物料大小从几厘米到二十厘米不等相机拍摄后如果图像缩到224小块矸石在图上只有几十个像素卷积核根本看不清纹理。我建议针对物料尺寸设置一个最小像素约束即矸石的最小边在图像上至少占32个像素。如果相机安装高度高导致矸石在图像上太小就要减小视场或者裁剪。4.3 训练结果怎么看准确率、召回率与误捡率分类准了不等于分选成功。在煤矸石分选场景里必须把“矸石被误判成煤”和“煤被误判成矸石”分开计算。我用的是检测任务的术语召回率(true positive rate)真实矸石里被检出的比例。这个指标关系到“矸石带走率”如果偏低说明还有矸石混入精煤。准确率(precision)在模型判为矸石的目标里真正是矸石的比例。这个指标关系到“精煤损失率”如果偏低说明很多煤被当矸石吹掉了。实际生产中矸石被吹掉无所谓但煤被误吹掉就是钱。所以模型往往需要在精度和召回率之间取一个偏向精度的阈值。默认Softmax输出时取0.5作为分类阈值但训练完我会在验证集上画ROC曲线找那个“误捡率低于5%时召回率最高”的阈值。比如模型输出矸石概率大于0.65才触发喷吹这样虽然漏掉一点点矸石但保住精煤产量。这个阈值是要配合现场喷吹系统联调的。训练曲线怎么看如果训练loss下降验证loss先降后升就是过拟合。煤矸石数据集通常不大过拟合很常见对策是增加增强强度或者把网络中的Dropout加上。我一般在全连接层前加一个Dropout(0.5)效果立竿见影。如果训练loss一直在高位降不下去检查标注数据——大概率是坐标没归一化或者标签错位了。5. 煤矸石分选落地避坑指南从标注脏数据到现场干扰的5个常见问题5.1 问题1标注时煤矸石边界模糊模型学不到边缘现象训练出的模型在测试集上准确率能到93%但把单块矸石的图片单独送进去经常分错。检查标注发现矸石表面有煤粉覆盖边界和背景煤流混在一起标注员画框时把边界画大了把旁边的煤也框了进来。原因煤矸石并非黑白分明特别是井下刚采出来的矸石表面会沾一层煤粉视觉上呈灰黑色人眼都容易混淆。标注框里混入背景煤块后CNN同时学习了煤和矸石的特征提取出的特征就不纯粹。解决标注前先把矸石用高压水冲洗后拍照作为标准参照图给标注员看。标注时要求沿矸石与煤流的明暗交界处画框宁小勿大。另外可以在标注完成后用“分割检测”结合的方式验证——先用颜色阈值把明显黑色的像素剔除掉把剩下区域的框收紧。我们后期直接用图像处理软件把标注框向内收缩2~3个像素效果改善很明显。5.2 问题2训练集准确率95%现场一上输送带就翻车现象模型在实验室的验证集上表现很好准确率95%。安装到车间后识别率降到70%左右。最后发现现场的皮带上面有震动相机拍摄到的图像有运动模糊而训练集里的图像都是静止状态下拍的。原因数据集与现场部署的分布不一致。工业现场的皮带速度、震动、曝光时间都会让图像模糊度发生变化。CNN对模糊非常敏感因为卷积核学到的是清晰边缘纹理一旦边缘在图像上拖影特征就匹配不上了。解决采集阶段把相机架到皮带上拍真实视频然后从视频里截帧作为训练图像不要单独用手持相机拍静态图。如果已经训完了可以加入随机高斯模糊和运动模糊的数据增强来弥补。另外现场相机曝光时间要尽量短很多项目把曝光时间调到200微秒配合频闪光源基本能消除运动模糊。5.3 问题3煤粉、水雾、灰尘导致图像退化现象现场试运行前几周效果很好后来准确率越来越差。检查发现相机镜头和窗口玻璃上落了一层煤粉图像对比度严重下降。原因工业环境粉尘大尤其是干选车间煤粉无孔不入。镜头污染呈现渐进性不容易被及时发现。CNN的输入是干净图像一旦镜头变脏图像整体灰度偏移模型自然就崩了。解决必须配置防尘罩和压缩空气吹扫。相机安装在一个带石英玻璃窗口的密封箱内窗口倾斜安装并定期用电磁阀控制压缩空气吹扫。同时加一个亮度和模糊监测模块如果图像平均灰度超过正常范围的10%或者高频分量下降明显就自动报警提示清洁镜头。这不是模型问题是运维问题但决定项目能否长期稳定。5.4 问题4正负样本不平衡模型只会说“都是煤”现象训练好的模型在验证集上准确率非常高但单独把矸石样本拿出来测准确率只有20%。查看训练集发现煤块样本8000张矸石样本只有800张。原因矿上煤多矸石少是常态但这类占比样本不平衡会让CNN偏向多数类。交叉熵损失在类别不平衡时模型倾向于把不确定性高的样本判为多数类也就是“都是煤”。准确率虚高掩盖了问题。解决不要只看总体准确率。训练时给矸石类更高的类别权重或者用Focal Loss。最简单的做法是计算每个batch时对不同类别加权PyTorch的CrossEntropyLoss直接传一个weight参数。另外可以在采集时多挑矸石样本把矸石和煤的样本比例控制在1:1到1:2之间。5.5 问题5标签错位和漏标loss曲线震荡现象训练loss呈周期性尖峰每训练几步就突然跳高然后又降下来。验证准确率也忽高忽低很难收敛。原因数据集里混有一些标签和图像不对应的样本。比如采集视频时某个矸石刚进入视场但上一帧的位置对应下来已经离开了标注框和实际目标错位这个样本会在训练时产生巨大的梯度拉乱权重。解决数据清洗阶段把每张图和对应的标注框在原图上画出来人工浏览一遍给明显错位的样本做标记并删除。另一个常见做法是在训练时开启异常样本过滤当某个样本的loss超过当前batch平均loss的3倍时把它剔除出该次迭代。虽然这在PyTorch里需要改一点点逻辑但能显著提升训练稳定性。注意不要用阈值过滤掉所有高loss样本那样模型就学不到困难样本了。6. 从离线模型到在线分选验证方法、模型导出与部署技巧6.1 用“留一矿”策略验证泛化能力如果项目涉及多个矿区的煤质我建议按矿区划分训练集和验证集拿一个矿的图做验证其他矿的图做训练。这样做出来的验证指标才有泛化意义。如果只有一个矿的数据则按采集时间分片用前两周的图训练、后一周的图验证模拟时间泛化。6.2 模型导出为ONNX并用TensorRT加速训练完成的模型要部署到工控机推荐先导出成ONNX格式再转成TensorRT引擎。下面是导出脚本import torch from torchvision import models # 假设 model 已经加载训练好的权重 model.eval() dummy_input torch.randn(1, 3, 128, 128) torch.onnx.export(model, dummy_input, coal_gangue.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, opset_version11)导出后可以用onnxruntime的GPU模式做一次推理测试确保输出和PyTorch一致。在NVIDIA工控机上转TensorRT需要用到trtexec工具转换时注意设FP16精度。煤矸石是二分类FP16对识别精度影响很小但推理速度能提升近一倍。转换完的引擎不要直接信任需要用现场采集的含矸石图跑一遍和ONNX输出对比相对误差大于1%的话就说明某些层被过度剪枝了。6.3 现场联调的三个检查习惯第一先在静态皮带停机时放几个标准矸石块测试确认相机取图触发正常。第二动态运行时用一把小铲子随机从皮带上取走被喷吹机构吹掉的物料分类统计吹掉的矸石和煤的比例。这个“取样验证”比看系统日志可靠。第三保留最近一周所有触发喷吹的图像每周抽一天回放看有没有误喷情况。检查时特别注意模型输出的阈值是不是需要调整因为白天和夜间的照明可能变化。我自己的教训是模型训练完成后不要急着调阈值先装上设备空跑一天统计一下输出概率的分布。如果输出概率大多集中在0.95以上或0.05以下说明模型很有信心如果一堆样本集中在0.5附近说明特征还没学够需要回炉加数据。煤矸石分选的难点从来不在网络结构而在数据质量和现场工程习惯。希望这些思路能帮你在自己的项目里少走几段弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网