
✅ 博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅ 具体问题可以私信或扫描文章底部二维码。
(1)活塞环作为发动机的核心密封与传热部件,其表面质量的优劣直接关系到整机的运行稳定性、燃油效率及使用寿命。在高速、高压、高温的极端工况下,即便是微米级别的表面瑕疵,如细微划痕、麻点或材质疏松,都可能成为应力集中点,加速磨损过程,甚至引发断裂或漏气等严重故障。因此,在生产制造环节实现对其外观缺陷的精准、高效检测,是保障产品质量、提升企业竞争力的关键所在。传统的人工目检方法不仅存在主观性强、标准不一、效率低下等固有弊端,而且长时间工作易导致检测员视觉疲劳,造成漏检和误检率居高不下,难以满足现代化大规模生产线对质量控制的一致性和实时性要求。为了克服这些瓶颈,本研究致力于构建一套基于机器视觉与深度学习技术的全自动在线检测系统。该系统的首要任务是建立一个高质量、高代表性的活塞环表面缺陷图像数据集。为此,我们首先对生产线上常见的缺陷类型进行了系统性的归纳与形貌特征分析。这些缺陷主要包括:划痕,通常呈现为线性或不规则曲线状,其宽度和深度变化不一,在特定光照下会形成明显的反光或暗影;凹坑与麻点,表现为表面局部区域的微小凹陷,形状多为圆形或椭圆形,边缘清晰或不清晰;气孔,是材料内部气体逸出形成的孔洞,在图像上呈现为暗色斑点;崩边或磕碰伤,发生在活塞环的棱角或端面,形态不规则,破坏了原有的几何轮廓;以及烧伤,因加工过程中局部过热导致材料组织变化,在图像上呈现出与周围基体不同的色泽和纹理。基于这些缺陷的物理特性,我们设计并搭建了一套专用的图像采集系统。该系统硬件核心选用高分辨率工业面阵相机,配合远心镜头以消除透视误差,确保图像几何尺寸的精确性。照明方案则采用了复合光源设计,包括提供均匀无影照明的穹顶光源,用于凸显表面凹凸不平的低角度环形光源,以及能够强化金属表面划痕和烧伤色差的同轴光源。通过程序控制多光源的协同工作与快速切换,可以在单次检测流程中获取包含不同缺陷特征信息的多幅图像,极大地丰富了缺陷的表现力。机械部分则由高精度伺服电机驱动的旋转台和传送带构成,能够实现活塞环的自动上料、360度全表面扫描以及下料分拣。在该系统平台上,我们采集了数万张包含各类缺陷及完好样本的原始图像。为了增强模型的泛化能力,数据集在构建时充分考虑了样本的多样性,涵盖了不同生产批次、不同光照强度微小波动、不同安装位置以及不同污染程度的图像。随后,进入了繁琐但至关重要的数据标注阶段。我们采用了专业的图像标注软件,对每一张图像中的缺陷进行精确的像素级语义分割标注,即沿着缺陷的轮廓绘制多边形掩码,而非简单的矩形框。这种标注方式能够为模型提供更精确的缺陷位置和形状信息,尤其对于边缘不规则的小目标缺陷至关重要。整个标注过程由多位经验丰富的工程师交叉进行,并制定了详细的标注规范,以确保标注结果的一致性和准确性,最终形成了一个规模庞大、标注精细、类别均衡的活塞环缺陷专用数据集,为后续深度学习模型的训练与优化奠定了坚实的数据基础。
(2)在工业现场应用中,检测设备往往面临成本和算力的双重限制,无法部署搭载高性能GPU的服务器。因此,在保证检测精度的前提下,对检测网络进行轻量化设计,使其能够在资源受限的嵌入式设备或普通工业PC上高效运行,是本研究需要解决的核心技术难题。为此,我们摒弃了传统的、参数量巨大的卷积神经网络(如VGG、ResNet等)作为主干网络,转而从零开始设计了一种全新的轻量级特征提取网络。该网络的设计思想核心在于最大化地利用每一个计算单元,提升特征提取的效率。其基本构建模块采用了深度可分离卷积技术,这是一种将标准卷积操作分解为深度卷积和逐点卷积两步的策略。深度卷积负责在单个通道内进行空间滤波,而逐点卷积则负责通道间的信息融合。通过这种解耦,计算量和参数数量得到了数量级的下降,同时又能有效地提取空间特征和通道特征。在此基础上,我们引入了倒置残差结构,与传统残差块先降维再升维的“沙漏型”结构相反,倒置残差块先使用1x1卷积进行通道扩展,再应用深度可分离卷积进行特征提取,最后通过1x1卷积进行投影降维。这种结构使得大部分计算量集中在低维度的深度卷积层,进一步降低了计算负载,同时扩展的中间层维度有助于保留更多由小目标缺陷携带的细节信息,缓解了因网络加深导致的信息丢失问题。整个主干网络由多个这样的轻量级模块堆叠而成,通过精心设计每一阶段的通道数量和下采样时机,实现了在速度、精度和模型大小之间的最佳平衡。与原型网络相比,我们设计的轻量级主干网络在计算复杂度(以浮点运算次数FLOPs衡量)上降低了近一个数量级,模型参数量也大幅减少,但其特征提取的效率和有效性却得到了显著提升。然而,仅仅依靠轻量化的主干网络,对于检测活塞环上尺寸微小、对比度低的缺陷仍然存在挑战。在深层网络中,微小缺陷的特征经过多次下采样和卷积操作后,其响应信号会变得极其微弱,很容易被背景噪声或更显著的特征所淹没,导致漏检。为了解决这一小目标检测难题,我们提出了一种新颖的多尺度特征融合策略,并在融合路径中嵌入了一个关键模块——特征细化模块。传统的特征金字塔网络(FPN)通过自顶向下的路径和横向连接来融合高层语义信息和低层细节信息,但其简单的相加或拼接操作无法有效处理不同层级特征图之间的语义冲突。FRM的作用正是在融合前对特征进行“净化”和“增强”。该模块包含两个并行的子结构:通道注意力分支和空间注意力分支。通道注意力分支通过全局平均池化操作捕获每个通道的全局信息,然后经过一个微型的全连接网络学习各个通道的重要性权重,从而自适应地增强包含缺陷特征的通道,抑制无关背景通道。空间注意力分支则通过对特征图在通道维度上进行聚合,生成一个二维的空间权重图,该图能够突出显示缺陷可能出现的空间位置,同时抑制背景区域的响应。将这两个注意力分支的输出与原始特征图相乘,便得到了一个在通道维度和空间维度上都经过精细调整的特征图。通过在FPN的每一个融合节点前加入FRM,可以有效抑制来自高层特征的强语义信息对低层微小目标特征的干扰,防止微小目标的特征信息在融合过程中被“冲淡”或“覆盖”,从而显著提升了模型对小缺陷的感知能力。此外,为了进一步优化模型的训练过程,我们针对缺陷检测中普遍存在的前景-背景极度不平衡问题,对损失函数进行了改进。在一张活塞环图像中,属于缺陷的像素点仅占总像素的极小部分,而背景像素占据了绝大多数。如果使用标准的交叉熵损失函数,模型会因为大量简单背景样本的主导而倾向于将所有像素都预测为背景,导致对缺陷的学习不充分。为此,我们引入了Focal Loss作为分类损失函数。Focal Loss通过一个调制因子,降低了那些被模型轻易分类正确的样本(即大量简单背景样本)在损失计算中的权重,而将学习焦点更多地放在那些难以分类的样本上(即包含缺陷的样本)。这种机制迫使模型在训练过程中更加关注困难样本,从而有效缓解了类别不平衡问题,提高了模型对缺陷,尤其是小缺陷和模糊缺陷的召回率。
(3)理论研究与模型算法的最终价值体现在实际应用中。为了将上述轻量化检测模型落地,我们设计并开发了一套完整的活塞环表面缺陷智能检测系统。该系统是一个集成了光学、机械、电子、计算机视觉与人工智能技术的综合性解决方案,旨在实现活塞环外观缺陷的全自动、高精度、高速度在线检测与分类。系统的硬件平台构成了物理基础,它围绕检测工位精心布局。前端是一套自动上料机构,通过振动盘和传送带将待检活塞环有序地输送到检测区域。检测区域的核心是前文所述的图像采集系统,包括工业相机、镜头、复合光源以及由PLC控制的旋转定位台。当活塞环到达指定位置后,光电传感器触发信号,PLC指令相机执行拍照动作,同时控制旋转台带动活塞环匀速转动,确保相机能够捕捉到完整的内外圆周面及端面图像。图像采集完成后,系统通过后端的气动分拣机构,根据检测结果将合格品与不合格品自动分流到不同的收集盒中。整个硬件系统的设计充分考虑了工业现场的严苛环境,具备良好的稳定性、抗干扰能力和易维护性。系统的“大脑”是智能检测软件系统,它运行在工业控制计算机上,是整个检测流程的指挥中心。该软件系统采用模块化的设计思想,主要包含以下几个核心模块:图像采集与预处理模块、模型推理引擎、后处理与决策模块、人机交互界面(HMI)以及通信模块。图像采集与预处理模块负责与相机SDK进行底层通信,完成图像的实时获取、格式转换、裁剪、去噪等初步处理,为模型推理准备标准化的输入数据。模型推理引擎是软件的核心,它加载了我们训练好的轻量化检测模型。为了最大化推理速度,我们利用了模型量化与加速技术,例如将训练好的PyTorch模型转换为ONNX格式,并进一步使用TensorRT等推理优化库进行部署,使得模型在配备NVIDIA Jetson或普通RTX系列显卡的工控机上也能达到每秒数十帧的处理速度,完全满足生产线的节拍要求。后处理与决策模块负责解析模型输出的原始预测结果,包括大量的候选框、类别置信度和掩码。该模块首先应用非极大值抑制算法,剔除针对同一缺陷的冗余检测框,然后根据预设的置信度阈值和缺陷类型规则,对检测结果进行最终判定,生成“合格”或“不合格”以及具体缺陷类别的决策指令。人机交互界面是操作员与系统沟通的桥梁,界面设计直观友好,实时显示带有检测框和缺陷标签的图像、当前产品的检测状态、生产线运行统计数据(如总产量、合格率、各类缺陷的分布比例等)。操作员可以通过界面启停检测流程、校准相机、调整检测参数、查询历史记录和报警信息。通信模块则负责软件系统与外部硬件的协同工作,它通过TCP/IP或Modbus等工业协议与PLC进行数据交换,将“合格/不合格”的决策信号实时发送给PLC,由PLC控制分拣执行机构完成物理上的分拣动作。最终,这套集成的智能检测系统在实际生产线上经过了严格的测试与验证。
import torch
import torch.nn as nn
class LightweightBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride, expansion_ratio):
super(LightweightBlock, self).__init__()
hidden_dim = in_channels * expansion_ratio
self.use_res_connect = stride == 1 and in_channels == out_channels
layers = []
if expansion_ratio != 1:
layers.append(nn.Conv2d(in_channels, hidden_dim, 1, 1, 0, bias=False))
layers.append(nn.BatchNorm2d(hidden_dim))
layers.append(nn.ReLU6(inplace=True))
layers.extend([
nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
nn.BatchNorm2d(hidden_dim),
nn.ReLU6(inplace=True),
nn.Conv2d(hidden_dim, out_channels, 1, 1, 0, bias=False),
nn.BatchNorm2d(out_channels),
])
self.conv = nn.Sequential(*layers)
def forward(self, x):
if self.use_res_connect:
return x + self.conv(x)
else:
return self.conv(x)
class ChannelAttention(nn.Module):
def __init__(self, in_planes, ratio=16):
super(ChannelAttention, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.fc1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
self.relu1 = nn.ReLU()
self.fc2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))
max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))
out = avg_out + max_out
return self.sigmoid(out)
class SpatialAttention(nn.Module):
def __init__(self, kernel_size=7):
super(SpatialAttention, self).__init__()
assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
padding = 3 if kernel_size == 7 else 1
self.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
x = torch.cat([avg_out, max_out], dim=1)
x = self.conv1(x)
return self.sigmoid(x)
class FeatureRefinementModule(nn.Module):
def __init__(self, in_channels):
super(FeatureRefinementModule, self).__init__()
self.channel_att = ChannelAttention(in_channels)
self.spatial_att = SpatialAttention()
def forward(self, x):
out = x * self.channel_att(x)
out = out * self.spatial_att(out)
return out
class LightweightDetector(nn.Module):
def __init__(self, num_classes=6):
super(LightweightDetector, self).__init__()
self.first_conv = nn.Sequential(
nn.Conv2d(3, 32, 3, 2, 1, bias=False),
nn.BatchNorm2d(32),
nn.ReLU6(inplace=True)
)
self.layer1 = self._make_layer(32, 24, 2, 1, expansion_ratio=2)
self.layer2 = self._make_layer(24, 32, 3, 2, expansion_ratio=2)
self.layer3 = self._make_layer(32, 64, 4, 2, expansion_ratio=4)
self.layer4 = self._make_layer(64, 96, 3, 1, expansion_ratio=4)
self.layer5 = self._make_layer(96, 160, 3, 2, expansion_ratio=4)
self.frm3 = FeatureRefinementModule(64)
self.frm4 = FeatureRefinementModule(96)
self.frm5 = FeatureRefinementModule(160)
self.lat_layer3 = nn.Conv2d(64, 96, 1, 1, 0)
self.lat_layer4 = nn.Conv2d(96, 96, 1, 1, 0)
self.lat_layer5 = nn.Conv2d(160, 96, 1, 1, 0)
self.pred_head = nn.Sequential(
nn.Conv2d(96, 96, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(96, num_classes, 1)
)
def _make_layer(self, in_channels, out_channels, n_blocks, stride, expansion_ratio):
layers = []
layers.append(LightweightBlock(in_channels, out_channels, stride, expansion_ratio))
for _ in range(1, n_blocks):
layers.append(LightweightBlock(out_channels, out_channels, 1, expansion_ratio))
return nn.Sequential(*layers)
def forward(self, x):
x = self.first_conv(x)
c1 = self.layer1(x)
c2 = self.layer2(c1)
c3 = self.layer3(c2)
c4 = self.layer4(c3)
c5 = self.layer5(c4)
p3 = self.frm3(c3)
p4 = self.frm4(c4)
p5 = self.frm5(c5)
p3_lat = self.lat_layer3(p3)
p4_lat = self.lat_layer4(p4)
p5_lat = self.lat_layer5(p5)
up_p5 = nn.functional.interpolate(p5_lat, scale_factor=2, mode='nearest')
merge4 = up_p5 + p4_lat
up_merge4 = nn.functional.interpolate(merge4, scale_factor=2, mode='nearest')
merge3 = up_merge4 + p3_lat
out = self.pred_head(merge3)
return out
if __name__ == '__main__':
model = LightweightDetector(num_classes=6)
dummy_input = torch.randn(1, 3, 512, 512)
output = model(dummy_input)
print(output.shape)


如有问题,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
1220

被折叠的 条评论
为什么被折叠?



