Skip to content

BGA焊球高度和直径测量

工程背景

测量背景

BGA 封装工艺为制造晶圆集成电路 IC 和 PCB 电路板之间的连接提供更多可能。BGA 工艺有一定的技术要求,必须严格且精确控制其生产制作,包括确保 BGA 球数量、尺寸和位置的精确性,从而保证封装质量。

本地图片

相机选型

Hypersen-LCF3000

测量项

BGA小球的高度与直径

检测要求

测量精度 ≤ 0.01mm 测量重复性 ≤ 0.004mm 测量周期 ≤ 1s

解决方案

使用AI-Vision软件,首先获取BGA球的位置,然后通过循环测量的方式,测量BGA球的高度与直径信息。运行速度快,精度高,同时流程简单,模块化,方便搭建。

设计思路

本地图片

执行效果展示

  • 工程结果展示

    • 高度结果:

      本地图片

    • 直径结果:

      本地图片

项目流程

一、初始化

[00] 使用Lua脚本工具,生成用于保存数据的数组型全局变量“PositionX”、“PositionY”、“PositionZ”,字符型全局变量“ResultHeight”、“ResultR”;生成用于计数的全局变量“Count”;生成保存数据的csv文件“Height”、“Circle”。

lua

println('Hello AI-Vision!')

-- 生成用于保存数据与计数的全局变量
SetVariable("PositionX",{})
SetVariable("PositionY",{})
SetVariable("PositionZ",{})
SetVariable("Count",1)
SetVariable("ResultHeight","")
SetVariable("ResultR","")


-- 生成保存数据的csv文件
write = ""
-- 判断文件是否存在,如果不存在,则生成文件
if FileSize("./Height.csv") <= 0 then   
    write = write .. "Time,"  
    for i = 1,196,1 do     
        write = write .. "H" .. i  .. ","
    end
    --"\n"表示换行
    write = write .. "\n"    
end
write = write .. os.date("%Y_%m_%d %H:%M;%S") .. ","  
-- 将字符串写入表中
fprint("./Height.csv",write)   

write = ""
if FileSize("./Circle.csv") <= 0 then   
    write = write .. "Time,"  
    for i = 1,196,1 do     
        write = write .. "R" .. i  .. ","
    end
    write = write .. "\n"    
end
write = write .. os.date("%Y_%m_%d %H:%M;%S") .. ","     
fprint("./Circle.csv",write)

[01] 使用加载点云工具,加载点云。

本地图片

二、初定位

[02-05] 使用3D方形探针工具3D几何交点工具输出BGA板左上侧顶点坐标并推送至寄存器中;同时使用3D位置调整工具,以该点作为新原点,调整X轴Y轴的位置。

本地图片

[06-07] 使用3D区域工具3D平面工具选择区域并拟合零平面,同时调整Z轴位置。

本地图片

[08] 使用3D裁切工具,将BGA球截取至IM2中,便于后续处理。

三、斑点定位

[09-16] 连续四次使用3D斑点工具Lua脚本工具进行BGA小球的定位和位置保存。(由于寄存器的个数有限,最大可以保存70个数据,如果直接输出196个点的位置,前126个点的位置会被覆盖,所以分步获取位置并保存到全局变量中。)

本地图片

本地图片

本地图片

本地图片

lua

-- 读取全局变量中的值.
PositionX = GetVariable("PositionX")
PositionY = GetVariable("PositionY")
PositionZ = GetVariable("PositionZ")

-- 循环将3D Blob工具查找到的点的位置信息保存到PositionX,PositionY,PositionZ中。 

for i =55 ,0 , -1 do
   x,y,z = GetPoint(i)
   table.insert(PositionX,x)
   table.insert(PositionY,y)
   table.insert(PositionZ,z)
end

-- 将PositionX,PositionY,PositionZ的值写入全局变量中。
SetVariable("PositionX",PositionX)
SetVariable("PositionY",PositionY)
SetVariable("PositionZ",PositionZ)

四、高度测量

[17] 使用3D裁切工具,将IM2中的点云复制到IM3中,便于后续处理。

[18] 使用标记工具,设置Restart用于循环跳转。

[19] 使用Lua脚本工具,读取当前循环数,通过循环数读取球的中心位置,同时计算用于测量的盒子并将其推送至寄存器中。

lua

-- 读取全局变量中位置信息。
PositionX = GetVariable("PositionX")
PositionY = GetVariable("PositionY")
PositionZ = GetVariable("PositionZ")

-- 读取当前BGA球的个数。
i = GetVariable("Count")

-- 通过位置信息计算用于测量的Box。
sx = PositionX[i] - 0.2
sy = PositionY[i] - 0.2
sz = PositionZ[i] - 0.1
ex = PositionX[i] + 0.2
ey = PositionY[i] + 0.2
ez = PositionZ[i] + 0.1

-- 将Box信息推送至寄存器中。
PushBox(sx,sy,sz,ex,ey,ez)

-- 将Box绘制在image上。
DrawBox(3,sx,sy,sz,ex,ey,ez)

[20] 使用3D高度工具,通过读取上一步写入寄存器中盒子的信息,进行BGA球高度测量。

本地图片

[21] 使用Lua脚本工具,保存当前测量的高度信息至全局变量中,同时对当前BGA球个数进行判断以确定是否继续循环。

lua

-- 读取当前BGA球的个数。
i = GetVariable("Count")

i = i+1

-- 读取全局变量中高度的信息。
Height = GetVariable("ResultHeight")

-- 将当前测量的高度拼接至Height字符串中。
Height = Height .. string.format("%.3f,",GetMeasure(0))


SetVariable("ResultHeight",Height)
 
-- 对当前BGA球数进行判断,当当前个数小于197时,继续循环,否则结束循环。
if i <197 then
    SetVariable("Count",i)
    Jump("ReStart")
else
    SetVariable("Count",1)
    Jump("End")
end

[22] 使用标记工具,设置End用于循环跳转。

[23] 使用Lua脚本工具,将测量的196个球的高度信息写入文件中。

lua

-- 读取全局变量中的高度结果信息。
Height = GetVariable("ResultHeight")

-- 写入csv文件后进行换行。
Height = Height .. "\n"

-- 将数据写入到Height.csv文件中。
fprint("./Height.csv",Height)

五、直径测量

[24] 使用3D裁切工具,将IM2中的点云复制到IM4中,便于后续处理。

[25] 使用标记工具,设置Restart1用于循环跳转。

[126] 使用Lua脚本工具,读取当前循环数,通过循环数读取球的中心位置,同时计算用于测量的盒子并将其推送至寄存器中。

lua

-- 读取全局变量中位置信息。

PositionX = GetVariable("PositionX")
PositionY = GetVariable("PositionY")
PositionZ = GetVariable("PositionZ")

-- 读取当前BGA球的个数。
i = GetVariable("Count")

-- 通过位置信息计算用于测量的Box。
sx = PositionX[i] - 0.2
sy = PositionY[i] - 0.2
sz = PositionZ[i] - 0.1
ex = PositionX[i] + 0.2
ey = PositionY[i] + 0.2
ez = PositionZ[i] - 0.01

-- 将Box信息推送至寄存器中。
PushBox(sx,sy,sz,ex,ey,ez)

-- 将Box绘制在image上。
DrawBox(4,sx,sy,sz,ex,ey,ez)

[20] 使用3D孔工具,通过读取上一步写入寄存器中盒子的信息,进行BGA球底部圆拟合,并输出半径信息。

本地图片

[21] 使用Lua脚本工具,保存当前测量的直径信息至全局变量中,同时对当前BGA球个数进行判断以确定是否继续循环。

lua


-- 读取当前BGA球的个数。
i = GetVariable("Count")
i = i+1

-- 获取圆的位置与半径信息 
x,y,z,r = GetCircle(0)

-- 读取全局变量中高度的信息。
R = GetVariable("ResultR")

-- 将当前计算的直径拼接至R字符串中。
R = R .. string.format("%.3f,",r*2)

SetVariable("ResultR",R)

-- 对当前BGA球数进行判断,当当前个数小于197时,继续循环,否则结束循环。
if i <197 then
    Jump("ReStart1")
else
    Jump("End1")
end

SetVariable("Count",i)

[22] 使用标记工具,设置End1用于循环跳转。

[23] 使用Lua脚本工具,将测量的196个球的直径信息写入文件中。

lua

-- 读取全局变量中的直径结果信息。
R = GetVariable("ResultR")

-- 写入csv文件后进行换行。
R = R .. "\n"

-- 将数据写入到Circle.csv文件中。
fprint("./Circle.csv",R)

AI-Vision,让3D测量更简单