Appearance
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)