拓冰建站拓冰建站
首页 / 资讯中心 / 正文

三维点云焊锡检测:从npy数据链路到体积计算与飞锡识别

简介面向制造业质量控制与机器视觉开发者的三维点云焊锡缺陷检测完整实现覆盖相机数据采集、焊锡外观检测、正/侧面体积计算、飞锡检测及三维可视化等核心环节适合需要搭建工业级检测原型或系统学习点云处理链路的技术人员参考。压缩包共104个文件、约4.65MB以38个py算法主程序为主体配合cpp/h底层采集接口与so/dll动态库实现相机通信附6个npy示例点云数据、json配置参数及png效果示意图结构清晰便于按模块查阅。已有311人学习下载。代码中完整展现了“MQ传递npy文件地址→转pcd格式→三维重建与缺陷分析”的工程流转过程LJXA_ACQ采集、ProfileDataConvert转换、global_cfg.json参数配置等模块职责分明可直接借鉴其模块解耦思路与文件传输协议设计。无论用于课题研究还是产线预研都能在此基础上快速改造成适配自有相机与点云算法的检测原型节省大量基础工作。1. 三维点云焊锡检测从产线需求到 npy 数据链路在回流焊产线上焊点缺陷往往不是平面图像能看出来的。虚焊、枕头效应、锡珠飞溅问题都出在高度轮廓上。2D 相机拍焊点反光严重时连边界都提取不稳改用基于三维点云的检测方案后激光轮廓传感器扫过 PCB焊点的高度、体积、浸润角全部变成可量化的数值缺陷不再是老师傅用肉眼挑而是被自动分割、测量、标注出来。这个项目的模块化做得相当干净相机数据采集、焊锡外观检测、体积计算、飞锡检测、可视化各自独立模块之间不直接传点云本体而是通过 MQ 传递 npy 文件地址。算法侧拿到的是即取即用的 NumPy 数组不需要关心相机 SDK 的细节。这种解耦方式让采集、算法、界面可以分开开发、单独重跑产线上出了问题也容易定位。下面按数据流方向从采集链路一直拆到可视化界面和部署验证。2. 采集模块与数据落盘从 LJX8_IF 到 npy 文件先把采集这边的文件关系理清楚再往下走。2.1 采集源码的角色划分项目中几个关键文件在采集链路里的分工很清晰LJX8_IF_Linux.cpp 与 LJX8_IF_Linux.h 是传感器在 Linux 侧的驱动封装负责设备初始化、参数下发和数据读取LJXA_ACQ.cpp 是采集主程序控制触发节奏、读取轮廓、拼接点云ProfileDataConvert.cpp 做轮廓原始数据到统一坐标系的换算global_cfg.json 则集中管理传感器参数、MQ 配置和输出路径。实际产线上激光轮廓传感器安装在垂直于 PCB 运动方向的位置编码器每给一个脉冲传感器抓取一条轮廓线整板扫完后再拼接成 2.5D 点云。这里有个容易忽略的点传感器拿到的单条轮廓不是完整点云想得到 3D 数据必须保证编码器单位距离内的触发频率与传输带宽匹配。项目里同时存在 .dll 动态库和 Linux 源码说明这套采集代码跨平台维护底层接口封装和上层业务逻辑是分离的。2.2 采集主循环与坐标转换LJXA_ACQ.cpp 的核心循环可以简化为下面的骨架具体 API 名称以实际 SDK 头文件为准// 伪代码编码器节拍驱动的轮廓采集主循环 while (running) { if (encoder_tick()) { unsigned char* buf nullptr; int ret LJX8_IF_GetProfile(buf); // 获取一帧轮廓原始数据 if (ret 0) { ProfileConvert(buf, points); // ProfileDataConvert 统一坐标 append_to_cloud(points, z_offset); } } }这段代码的核心逻辑是外部的编码器信号驱动采集节奏每次触发后从设备缓冲区取一帧轮廓数据ProfileConvert负责把原始高度值换算成毫米坐标append_to_cloud将当前轮廓追加到全局点云容器。这里的z_offset是运动轴的累计位移通常由编码器计数换算得到。参数层面需要注意一帧轮廓包含 X 方向固定数量的测量点点数由传感器型号决定Z 方向高度值必须乘以缩放系数才是毫米。如果发现点云沿运动方向拉伸或压缩优先检查编码器分辨率与实际传动机构的比值这是几何失真的常见来源。采集完成后的写盘环节项目选择了直接保存为 npy 文件而不是 pcdnpy 是 NumPy 二进制格式numpy.load一条语句就能还原数组维度和数据类型都自带算法模块零解析成本。pcd 虽然更适合通用点云工具链但读取时需要显式指定字段类型多一层出错空间。2.3 global_cfg.json 与消息队列数据流global_cfg.json建议至少覆盖传感器、MQ、输出路径三块配置{ sensor: { model: LJX8_IF, trigger_mode: encoder, encoder_resolution: 100, profile_points: 3200 }, mq: { broker: localhost:5672, queue: pointcloud_jobs, mode: file_path }, output: { npy_dir: /data/clouds/npy, pcd_dir: /data/clouds/pcd } }采集进程把 npy 保存到npy_dir后通过 MQ 发送消息消息体只包含文件路径。检测进程收到消息后再去读取对应的 npy 文件。为什么不直接传二进制内容点云数据量动辄数 MB把几 MB 内容塞进消息队列会阻塞其他消息传文件路径则不同文件落盘天然持久化MQ 里只有轻量级元数据消费者崩溃大不了重读一次。消息队列的另一个收益是削峰相机采集速度由产线节拍决定检测模型一帧几百毫秒MQ 缓存让两端解耦队列积压也能直观反映算力余量。3. 检测链路的核心模型外观、体积与飞锡采集只是第一段真正的重点是检测部分。检测模块分成三个子模块数据上相互独立可以并行跑也可以逐个调优。3.1 焊锡外观检测模块点云特征构造外观检测的本质是判断焊点表面形态是否符合该类焊点的统计分布。系统先对点云做分割将每个焊点单独切出再提取三维形态特征。常用特征维度包括表面粗糙度、高度均匀性、边缘塌陷程度以及与标准焊盘模型的偏差。工程上容易踩的坑是特征提取前没有统一密度。缺陷类型点云特征表现检测特征建议虚焊高度低于正常值表面扁平平均高度与体积比墓碑一端明显抬高高度呈双峰分布高度 map 偏度锡珠小体积孤立簇聚类后体积阈值拉尖局部点云尖刺凸起局部曲率或法线突变一点实现思路对每个焊点区域先用 RANSAC 拟合基板平面再计算 Z 方向高度 map统计高度分布的偏度和峰值。正常焊点高度分布接近单峰高斯虚焊或墓碑会出现双峰或长尾。若要区分气孔、裂纹、短路等细化类别可以引入分类器用多尺度几何特征输入随机森林或小型监督模型。外观模块只负责定性判断体积模块负责定量测量两者分开迭代比耦合在一起更容易调参。3.2 焊锡体积计算模块双视角体素积分体积计算分为正面和侧面两个方向。正面体积靠点云表面与基板平面的积分适合焊点在 PCB 正上方的场景侧面体积解决悬空侧引脚和润湿角的定量问题。两个方向的输入数据格式一致只是需要旋转到对应坐标系再积分。实现的核心函数可以这样写import numpy as np def solder_volume(points, pitch0.02): 计算焊点体积 points: npy 数组 [N, 3]x, y, z 单位 mm pitch: 体素边长 mm根据点云密度设定 # 1. 用最低 10% 的点粗拟合基板平面 z_low np.percentile(points[:, 2], 10) base_mask points[:, 2] (z_low pitch) A np.c_[points[base_mask][:, :2], np.ones(base_mask.sum())] coeff, *_ np.linalg.lstsq(A, points[base_mask][:, 2], rcondNone) # 2. 计算每个点相对基板的高度差 z_base np.c_[points[:, :2], np.ones(len(points))] coeff h points[:, 2] - z_base h np.clip(h, 0, None) # 3. 体素积分单位 mm^3 n_bins_x int((points[:, 0].max() - points[:, 0].min()) / pitch) 1 n_bins_y int((points[:, 1].max() - points[:, 1].min()) / pitch) 1 hist, _, _ np.histogram2d( points[:, 0], points[:, 1], bins(n_bins_x, n_bins_y), weightsh ) count, _, _ np.histogram2d( points[:, 0], points[:, 1], bins(n_bins_x, n_bins_y) ) avg_h np.divide(hist, count, outnp.zeros_like(hist), wherecount 0) volume_mm3 np.sum(avg_h) * pitch * pitch return volume_mm3上面这段代码做了三件事第一步用最低百分之十的点做最小二乘平面拟合避开焊点主体对基板面的干扰第二步计算每个点相对基板的高度clip 掉负值避免基板下方噪声点把体积拉低第三步把点云投影到 XY 平面做体素积分pitch是体素边长最终体积等于平均高度乘以体素面积后累加。pitch的选取很关键X 方向受传感器 X 轴间隔限制Y 方向受编码器触发间距限制取两者较大值作为体素边长即可设得比点云间距更小没有实际意义。lstsq是 numpy 的最小二乘求解函数返回系数向量第一个元素对应 X 方向斜率第二个对应 Y 方向斜率第三个是截距。侧面体积计算的核心区别在于先做坐标旋转把侧视角点云变换到与正面一致的参考系后面的平面拟合和积分过程完全复用。3.3 飞锡检测模块孤立小簇识别飞锡检测是三个模块里对实时性要求最高的一个。飞锡的物理特征是空间孤立、簇内点数少、与最近主焊点之间存在明显空隙。实现上走的是聚类加体积筛选的路线用 DBSCAN 找连通体再按体积阈值过滤from sklearn.cluster import DBSCAN def detect_solder_splash(points, eps0.1, min_samples3, min_vol0.02): # eps 需大于点间距且小于焊点间距 labels DBSCAN(epseps, min_samplesmin_samples).fit_predict(points[:, :3]) candidates [] for lab in np.unique(labels): if lab -1: continue # 噪声点直接跳过 cluster_pts points[labels lab] vol solder_volume(cluster_pts) # 复用体积函数 if vol min_vol: # 远小于正常焊点体积 candidates.append(cluster_pts) return candidateseps是这个模块的核心参数。它必须大于传感器点间距否则一个锡珠内部会裂成多个簇又要小于焊点与相邻飞锡的距离否则飞锡会同主焊点合并。经验做法先统计整体点云平均间距d_mean取eps 2.5 * d_mean作为起点min_vol设为正常焊点体积的 2% 到 5%可以滤掉表面粗糙引起的小凸起。一个容易漏掉的误报源是焊点边缘的拉尖——体积和飞锡接近聚类上又贴着主焊点处理方式是把候选簇与最近焊点簇的距离加入判断距离小于 0.5 mm 时降级为告警而不直接报飞锡缺陷。4. 可视化界面npy 转 pcd 与三维交互呈现检测模型输出的结果最终要呈现在界面上可视化是连接算法与产线操作人员的枢纽。这个项目的可视化模块能够在三维点云上直接标注缺陷位置核心数据链路是npy 转 pcd再交给 Open3D 渲染。4.1 npy 到 pcd 的格式转换转换代码不复杂关键点在于点云字段的处理import numpy as np import open3d as o3d def npy_to_pcd(npy_path, pcd_path): pts np.load(npy_path) # [N, 3] 或 [N, 6] xyz pts[:, :3].astype(np.float64) pcd o3d.geometry.PointCloud() pcd.points o3d.utility.Vector3dVector(xyz) if pts.shape[1] 6: # 归一化到 0~1 也是防御姿势颜色异常时可检查这里 colors np.clip(pts[:, 3:6] / 255.0, 0, 1) pcd.colors o3d.utility.Vector3dVector(colors) o3d.io.write_point_cloud(pcd_path, pcd)npy 转 pcd 的一个重要理由是 Open3D 自带体素滤波、法线估计、区域生长分割和包围盒可视化工具这些函数在纯 NumPy 里重新实现一遍成本不低。算法模块保留 npy 最方便因为 numpy 直接操作数组可视化模块使用 pcd 格式则能直接复用点云工具链的能力。pts[:, :3]是 XYZ 坐标pts[:, 3:6]是 RGB 颜色颜色一般以 0-255 存储Open3D 期望 0-1 浮点数除以 255 前先 clip 防止越界。4.2 可视化界面的三层设计可视化界面在结构上建议拆成数据层、视图层、交互层。数据层加载 pcd 并缓存视图层用 Open3D 非阻塞窗口渲染交互层提供缩放、旋转和缺陷标注开关。用 PyQt5 嵌 Open3D 时记得把渲染放到独立线程import threading import open3d as o3d def show_cloud(pcd, defect_boxes): def worker(): vis o3d.visualization.Visualizer() vis.create_window(Solder Inspection, width1280, height720) vis.add_geometry(pcd) for box in defect_boxes: vis.add_geometry(box) opt vis.get_render_option() opt.point_size 2.0 vis.poll_events() vis.update_renderer() vis.run() vis.destroy_window() threading.Thread(targetworker, daemonTrue).start()这里有两个容易翻车的地方。一是不要在 PyQt 主线程里跑点云渲染否则界面拖动会明显卡顿二是 Open3D 的几何对象不要跨线程直接操作创建和销毁都限制在同一个线程内公共数据传入前先深拷贝。point_size控制点云显示颗粒大小产线工控机显卡性能有限建议设为 1 或 2过大时旋转视角会有明显延迟。缺陷标注可以使用AxisAlignedBoundingBox生成包围盒用不同颜色区分缺陷类型再把包围盒中心坐标传递给接口实现点击列表跳转视角。4.3 pcd 目录管理与结果回放pcd 文件会越积越多不清理会占满磁盘。建议按批次目录存放同时只保留有缺陷的样本正常批次在统计表里留记录即可。数据类别目录结构保留策略原始点云/data/clouds/npy/yyyyMMdd/保留 7 天可视化 pcd/data/clouds/pcd/yyyyMMdd/仅保留缺陷样本检测报告/data/reports/PCB序列号.json长期归档缺陷样本是模型迭代最宝贵的资产回放历史缺陷点云对后续调整阈值和做难例挖掘都有直接帮助。5. 部署阶段的参数校验与工程坑位部署阶段和参数验证更考验工程判断力这里结合产线经验展开。5.1 采集质量问题的排查顺序采集环节的问题会传导到检测模型的每个输出上先给一张排查表。现象最可能原因排查手段处理建议点云沿运动方向拉长/压缩编码器分辨率与传动比配置错误扫描标准量块比对点云长度校准每毫米脉冲数焊点边缘空洞明显曝光过长导致运动模糊调小曝光观察轮廓点数变化按产线速度计算曝光上限表面噪点多反光或环境光干扰查看单帧轮廓 Z 值抖动幅度调整激光功率或加滤光片体积系统性偏大/偏小基板平面拟合偏移用空白基板核对平面高度限制拟合区域避开焊点曝光、编码器触发、激光功率三者联动调整时逐项排查比整体猜测有效。一个准确率较高的判据把传感器固定手动移动 PCB 扫一条直边边缘点云呈锯齿说明编码器抖动呈拖影说明曝光过量两者处理方向完全相反不要先动标定。5.2 体积模块的标定验证口径体积计算的精度直接影响良率判定。部署前务必用标准件验证取已知体积的焊点样品各扫描 20 次统计体积均值和标准差。均值偏差超过 5% 时优先检查基板拟合是否把焊点边缘点纳入了拟合集这会让基板抬高造成体积整体偏小。标准差偏大则说明扫描密度不足需要降低编码器触发间隔或在 Y 方向加密采样。侧面体积的绝对精度受引线遮挡限制工程上重点看批次间的相对波动趋势不追求绝对精确。5.3 MQ 积压与可视化线程安全MQ 积压时不要急着加消费线程先分析单条 npy 的读取和体积累加耗时。如果单点耗时已接近产线节拍增加线程只会加剧磁盘 IO 竞争。可视化线程注意别在 Qt 主线程里跑渲染也别跨线程操作 Open3D 的几何对象公共数据传入前先深拷贝两个线程各自维护自己的可视化对象。至此整个系统从相机采集、npy 消息链路、检测模型、可视化到产线验证的完整路径已经拆解完毕。进一步可以优化的方向包括飞锡检测的阈值自适应、基于历史缺陷点云的数据增强以及把可视化从离线展示改造成实时在线标注。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门