✅博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅成品或者定制,扫描文章底部微信二维码。
(1) 双棱镜单摄像机被动立体视觉系统的设计与熔池图像采集
熔化极气体保护焊接过程中,焊缝的熔透状态直接影响焊接接头的质量和可靠性,准确检测熔透状态对于实现高质量自动化焊接具有重要意义。传统的熔透检测方法主要依赖熔池正面或背面的单一视角图像,难以获取熔池表面的完整三维形貌信息。本研究设计了一套基于双棱镜的单摄像机被动立体视觉系统,通过在单个工业相机镜头前方安装特制的双棱镜光学组件,将来自熔池表面的入射光线分为两束,在同一图像传感器上同时形成熔池的左右两个虚拟视图,从而实现单相机的立体成像。
该立体视觉系统的核心光学元件是由两块具有特定楔角的光学棱镜组成的双棱镜组件,两块棱镜相对安装,其楔角方向相反,使得通过左侧棱镜和右侧棱镜的光线产生不同方向的偏折。当熔池表面发出的光线经过双棱镜后,在相机传感器上形成具有一定视差的左右两幅图像,这两幅图像包含了熔池表面的立体信息。系统的标定采用张正友棋盘格标定法,通过在不同位置和姿态拍摄标定板图像,利用角点检测算法提取棋盘格角点坐标,然后通过非线性优化求解相机的内参数矩阵和外参数矩阵。由于双棱镜系统相当于两个虚拟相机,还需要额外标定两个虚拟相机之间的相对位置关系,包括旋转矩阵和平移向量。
在实际焊接实验中,采用冷金属过渡焊接工艺进行平板堆焊和V型坡口对接焊实验,通过调节焊接电流、焊接速度、送丝速度等工艺参数,获得不同熔透状态下的熔池立体图像。焊接过程中熔池表面温度极高且存在强烈弧光干扰,因此在图像采集时采用窄带滤光片滤除弧光波长以外的杂散光,同时选用高动态范围的工业相机以适应熔池表面的高亮度环境。采集到的原始图像首先进行图像分割,将左右两个虚拟视图分离,然后进行图像校正消除棱镜引入的畸变,最终得到可用于立体匹配的校正图像对。通过上述过程,建立了包含多种熔透状态的熔池立体图像数据集,为后续的深度估计和状态识别提供了数据基础。
(2) 基于变分优化的熔池表面稠密视差估计方法
立体匹配是从立体图像对中恢复场景深度信息的关键步骤,其目的是为左图像中的每个像素在右图像中找到对应的匹配点,从而计算视差值。熔池表面的立体匹配面临独特的挑战,主要体现在熔池表面呈现液态金属的镜面反射特性,纹理信息极度匮乏,传统的基于局部窗口匹配的方法难以获得准确的匹配结果。针对这一问题,本研究引入了一种基于变分框架的全局优化立体匹配算法,通过建立包含数据项和平滑项的能量函数,利用变分原理求解全局最优的稠密视差场。
能量函数的数据项用于度量左右图像对应像素之间的相似程度,采用基于灰度差异和梯度差异的混合代价函数。灰度差异项计算左图像像素与右图像对应位置像素的灰度绝对差值,梯度差异项则比较两个位置的图像梯度信息,两者加权组合形成最终的匹配代价。混合代价函数能够综合利用灰度和边缘信息,在纹理缺乏区域仍能提供一定的匹配约束。能量函数的平滑项用于施加空间连续性约束,基于相邻像素的视差值应该平滑变化的假设,采用全变分正则化项来惩罚视差场的剧烈变化。全变分正则化能够在保持视差不连续边缘(如物体边界)的同时实现平滑区域内的视差平滑。
能量函数的求解采用基于偏微分方程的变分迭代方法。首先推导能量函数极小化所对应的欧拉-拉格朗日方程,然后将其转化为时间演化的偏微分方程形式,通过数值迭代逐步逼近稳态解。在数值实现中,采用有限差分格式离散化偏微分方程,并使用高斯-赛德尔迭代或共轭梯度法加速收敛。为了进一步提高算法效率和准确性,采用由粗到精的多尺度策略,首先在低分辨率图像上求解初始视差估计,然后逐层上采样并细化视差结果。通过在自制的非标准凹面参照物上进行精度验证实验,结果表明重建的三维形貌在宽度方向的相对误差小于3.16%,深度方向的误差小于4.82%,达到了熔透状态识别所需的精度要求。
(3) 基于深度学习的熔透状态分类识别与可解释性分析
获得熔池表面的视差信息后,下一步任务是根据视差图像识别焊缝的熔透状态。本研究将熔透状态分为四类:未熔透、全熔透、过熔透和焊漏,这四种状态在熔池表面视差图上呈现出不同的形态特征。未熔透状态下熔池深度较浅,视差图中心区域的视差值相对较小;全熔透状态下熔池具有适当的深度,视差分布呈现规则的抛物面形态;过熔透状态下熔池深度过大,视差值整体偏大且分布不均匀;焊漏状态则表现为熔池中心区域出现异常的视差突变。为了实现自动化的熔透状态识别,本研究采用端到端的深度学习方法构建分类模型。
在立体匹配网络的选择上,采用金字塔立体匹配网络作为视差估计的主干网络。该网络采用空间金字塔池化模块聚合多尺度上下文信息,构建四维代价体积并通过三维卷积进行代价聚合,最后通过软argmin操作回归连续的视差值。网络首先在公开的立体匹配数据集上进行预训练,学习通用的立体匹配知识,然后在自制的熔池立体图像数据集上进行微调,适应熔池场景的特殊图像特性。为了提高模型在实际工业环境中的部署效率,在分类网络的选择上综合考虑识别精度和计算复杂度两个因素。通过对比VGG、ResNet、MobileNet等多种经典网络架构在该任务上的表现,最终选定18层残差网络作为分类器,其在测试集上达到了99.6%的分类准确率,同时具有较低的推理延迟
import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torchvision import models, transforms import numpy as np import cv2 class CostVolume(nn.Module): def __init__(self, max_disparity): super(CostVolume, self).__init__() self.max_disparity = max_disparity def forward(self, left_feat, right_feat): B, C, H, W = left_feat.shape cost_volume = torch.zeros(B, 2*C, self.max_disparity, H, W, device=left_feat.device) for d in range(self.max_disparity): if d == 0: cost_volume[:, :C, d, :, :] = left_feat cost_volume[:, C:, d, :, :] = right_feat else: cost_volume[:, :C, d, :, d:] = left_feat[:, :, :, d:] cost_volume[:, C:, d, :, d:] = right_feat[:, :, :, :-d] return cost_volume class StereoMatchingNetwork(nn.Module): def __init__(self, max_disparity=192): super(StereoMatchingNetwork, self).__init__() self.max_disparity = max_disparity self.feature_extractor = nn.Sequential( nn.Conv2d(3, 32, 3, 1, 1), nn.BatchNorm2d(32), nn.ReLU(), nn.Conv2d(32, 64, 3, 1, 1), nn.BatchNorm2d(64), nn.ReLU(), nn.Conv2d(64, 128, 3, 2, 1), nn.BatchNorm2d(128), nn.ReLU(), nn.Conv2d(128, 128, 3, 1, 1), nn.BatchNorm2d(128), nn.ReLU() ) self.cost_volume = CostVolume(max_disparity // 4) self.cost_aggregation = nn.Sequential( nn.Conv3d(256, 128, 3, 1, 1), nn.BatchNorm3d(128), nn.ReLU(), nn.Conv3d(128, 64, 3, 1, 1), nn.BatchNorm3d(64), nn.ReLU(), nn.Conv3d(64, 32, 3, 1, 1), nn.BatchNorm3d(32), nn.ReLU(), nn.Conv3d(32, 1, 3, 1, 1) ) def forward(self, left_img, right_img): left_feat = self.feature_extractor(left_img) right_feat = self.feature_extractor(right_img) cost = self.cost_volume(left_feat, right_feat) cost = self.cost_aggregation(cost).squeeze(1) disparity = self.soft_argmin(cost) return F.interpolate(disparity.unsqueeze(1), scale_factor=4, mode='bilinear').squeeze(1) * 4 def soft_argmin(self, cost): prob = F.softmax(-cost, dim=1) disp_range = torch.arange(0, cost.shape[1], device=cost.device).float() disp_range = disp_range.view(1, -1, 1, 1) disparity = torch.sum(prob * disp_range, dim=1) return disparity class PenetrationClassifier(nn.Module): def __init__(self, num_classes=4): super(PenetrationClassifier, self).__init__() self.backbone = models.resnet18(pretrained=True) self.backbone.conv1 = nn.Conv2d(1, 64, 7, 2, 3, bias=False) self.backbone.fc = nn.Linear(512, num_classes) def forward(self, disparity_map): return self.backbone(disparity_map) class VariationalStereoMatcher: def __init__(self, lambda_smooth=0.1, iterations=100): self.lambda_smooth = lambda_smooth self.iterations = iterations def compute_data_cost(self, left, right, d): h, w = left.shape shifted = np.zeros_like(right) if d < w: shifted[:, d:] = right[:, :-d] if d > 0 else right cost = np.abs(left.astype(float) - shifted.astype(float)) return cost def solve(self, left_img, right_img, max_disparity): if len(left_img.shape) == 3: left_gray = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY) right_gray = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY) else: left_gray, right_gray = left_img, right_img h, w = left_gray.shape disparity = np.zeros((h, w), dtype=np.float32) for i in range(self.iterations): for d in range(max_disparity): cost = self.compute_data_cost(left_gray, right_gray, d) update_mask = cost < self.compute_data_cost(left_gray, right_gray, int(disparity.mean())) disparity[update_mask] = d return cv2.GaussianBlur(disparity, (5, 5), 0) class WeldingDataset(torch.utils.data.Dataset): def __init__(self, image_pairs, labels, transform=None): self.image_pairs = image_pairs self.labels = labels self.transform = transform def __len__(self): return len(self.image_pairs) def __getitem__(self, idx): left_path, right_path = self.image_pairs[idx] left_img = cv2.imread(left_path, cv2.IMREAD_COLOR) right_img = cv2.imread(right_path, cv2.IMREAD_COLOR) if self.transform: left_img = self.transform(left_img) right_img = self.transform(right_img) return left_img, right_img, self.labels[idx] def train_pipeline(stereo_net, classifier, dataloader, epochs=50): optimizer_stereo = optim.Adam(stereo_net.parameters(), lr=0.001) optimizer_cls = optim.Adam(classifier.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss() for epoch in range(epochs): for left, right, labels in dataloader: optimizer_stereo.zero_grad() disparity = stereo_net(left, right) optimizer_cls.zero_grad() predictions = classifier(disparity.unsqueeze(1)) loss = criterion(predictions, labels) loss.backward() optimizer_stereo.step() optimizer_cls.step() print(f'Epoch {epoch+1}/{epochs} completed') def visualize_attention(classifier, disparity_map, target_class): classifier.eval() disparity_map.requires_grad_(True) output = classifier(disparity_map) classifier.zero_grad() output[0, target_class].backward() gradients = disparity_map.grad.data attention_map = torch.mean(torch.abs(gradients), dim=1, keepdim=True) attention_map = F.interpolate(attention_map, size=disparity_map.shape[2:], mode='bilinear') return attention_map.squeeze().cpu().numpy() if __name__ == '__main__': stereo_net = StereoMatchingNetwork(max_disparity=192) classifier = PenetrationClassifier(num_classes=4) print("Welding penetration detection system initialized")如有问题,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇