Skip to content

连接器Pin针正位度检测

项目简介

项目背景

汽车连接器外形复杂,部分PIN针会被外壳包围,且PIN针十分微小,针尖细,具有高反射性,检测难度高。经过质检的PIN针直接影响后续产品的装配,导致最终产品在使用时出现连接不稳定、接触不良或直接报废。

本地图片

相机选型

LMI激光线扫相机 Gocator2430

检测要求

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

解决方案

AI-Vision采用Pin针匹配集成算子,一键得出所有的Pin高度并判断是否符合标准,降低了用户的使用难度同时提升工程运行速度。

设计思路

本地图片

执行效果展示

  • 工程结果展示:

    • 左边部分:

      本地图片

    • 右边部分:

      本地图片

      本地图片

  • HMI结果展示:

    本地图片

项目流程

一、初始化

[00] 选择Lua脚本语言工具,全局变量初始化,创建待接收数据的csv文件。

lua

--创建全局变量,用于HMI绑定显示,脚本第一次运行时,没有就创建
--读取指定名称的全局变量
value_h1 = GetVariable("value_h1")
--如果数据为空,则设置该全局变量,并赋值为0
if value_h1 == nil then
    SetVariable("value_h1", 0)
end

value_h105 = GetVariable("value_h105")
if value_h105 == nil then
    SetVariable("value_h105", 0)
end

value_h106 = GetVariable("value_h106")
if value_h106 == nil then
    SetVariable("value_h106", 0)
end

value_h111 = GetVariable("value_h111")
if value_h111 == nil then
    SetVariable("value_h111", 0)
end

value_h112 = GetVariable("value_h112")
if value_h112 == nil then
    SetVariable("value_h112", 0)
end

value_h196 = GetVariable("value_h196")
if value_h196 == nil then
    SetVariable("value_h196", 0)
end


--生成用于拼接的字符串
write = ""

--定义csv的列头内容
csvHead = "Time,"
for i=1,196,1 do
    csvHead = csvHead .. "pin" .. i .. ","
end
csvHead = csvHead .. "\n"

--创建y文件,脚本第一次运行时,没有就创建
if FileSize("./196pin_X.csv") <= 0 then
    fprint("./196pin_X.csv", csvHead)
end

--创建y文件
if FileSize("./196pin_Y.csv") <= 0 then
    fprint("./196pin_Y.csv", csvHead)
end

--创建H文件
if FileSize("./196pin_H.csv") <= 0 then
    fprint("./196pin_H.csv", csvHead)
end

--创建SideX文件
if FileSize("./196pin_SideX.csv") <= 0 then
    fprint("./196pin_SideX.csv", csvHead)
end

--创建SideY文件
if FileSize("./196pin_SideY.csv") <= 0 then
    fprint("./196pin_SideY.csv", csvHead)
end

--创建SideH文件
if FileSize("./196pin_SideH.csv") <= 0 then
    fprint("./196pin_SideH.csv", csvHead)
end

--创建Result文件
if FileSize("./196pin_Result.csv") <= 0 then
    fprint("./196pin_Result.csv", csvHead)
end

[01] 选择加载点云工具,获取点云。

[02] 选择3D裁切预处理工具,将点云复制一份到IM1(IM0不动,后面可以再次使用)。

[03] 选择3D变换预处理工具,将点云进行90度的旋转。

本地图片

二、左半部分处理

  • 图像定位

    [04] 选择多点定位工具,选择4个边界的pin针,通过4个Pin针的位置计算出中心位置,根据中心位置进行点云位置调整。

  • 拟合平面

    [05] 选择3D区域工具,根据现场要求选择区域,合并成一个大的区域并输出到寄存器中。

    本地图片

    [06] 选择3D平面工具,根据3D区域工具输出的区域,拟合平面。

    本地图片

  • 裁切滤波

    [07] 选择3D裁切预处理工具,将用来进行测量的区域裁切出来,并输出到IM2中进行处理。

    本地图片

    [08] 选择3D离群点滤波工具,有效去除待测区域周围的噪点。

    本地图片

  • 调整位置确定方向

    [09] 选择3D方形探针工具,选择中间一行pin针,根据选取到的pin针的中心点坐标,拟合出一条直线。

    本地图片

    [10] 选择3D位置调整工具,根据3D方形探针工具输出的直线,调整点云X和Y的位置。

  • Pin针匹配(需配合配方)

    [11] 选择Pin针匹配工具,选择输入图像。

    本地图片

    手动或者从寄存器选择区域。

    本地图片

    设置pin针最小间隔、最小点数,选择排序方式;设置是否输出pin针高度并选择高度测量的参数;选择是否输出pin针间隔;选择是否进行配方比对并设置要比对的配方路径;选择是否输出测量结果。

    本地图片

    将各pin针结果输出到创建的变量中。

    本地图片

    本地图片

三、右半部分处理

[12-24] 此部分处理逻辑和左半部分的一模一样,请参考上一节的流程。

四、结果保存csv及数据库中

[25] 选择lua语言脚本工具,将Pin针匹配工具输出的结果解析并输出至csv文件和数据库中,并设置用于HMI展示的全局变量。

lua
--获取当前时间
time = os.date("%Y_%m_%d %H:%M:%S") .. ","
write_x = time
write_y = time
write_h = time
write_sidex = time
write_sidey = time
write_sideh = time
write_result = time

--定义方法:判断数组中是否有指定数据。
function tableContains(tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return true
        end
    end
    return false
end

--存数据库的变量
local sqlData = {}
local dataResult = {}
local currentTable = {}

--获取1-105pin的数据,需要转成json后再处理
pinStr1 = GetVariable("pin_1_105")



if pinStr1 ~= "{}" then
    pinJson1 = ParseJson(pinStr1)
    -- println(pinJson1)
    
    for i = 1, 105, 1 do
        -- 保存到csv
        --string.format中已经有了"%f,"格式,不需要再单独添加","
        write_x = write_x .. string.format("%.3f,", pinJson1.Result[i].PinPositionX)
        write_y = write_y .. string.format("%.3f,", pinJson1.Result[i].PinPositionY)
        write_h = write_h .. string.format("%.3f,", pinJson1.Result[i].PinHeight)
        write_sidex = write_sidex .. string.format("%.3f,", pinJson1.Result[i].PinSideX)
        write_sidey = write_sidey .. string.format("%.3f,", pinJson1.Result[i].PinSideY)
        write_sideh = write_sideh .. string.format("%.3f,", pinJson1.Result[i].PinSideHeight)
        write_result = write_result .. pinJson1.Result[i].PinReslult .. ","
    
        --保存测量变量,用于HMI显示,取哪几个pin的数据,需要根据实际情况来写if的条件
        if i % 21 == 0 or i % 21 == 1 then
            -- SetVariable("value_h" .. tostring(i), pinJson1.Result[i].PinHeight)
            SetVariable("value_h" .. tostring(i), pinJson1.Result[i].PinHeight)
            
            
            pinRecipe = GetRecipeSet(i - 1)
            local table1 = {}
            table1["PinIndex"] = "H" .. tostring(i)
            table1["PinHightStd"] = string.format("%.3f", pinRecipe.PinHight)
            table1["PinHightMax"] = pinRecipe.PinHightMax
            table1["PinHightMin"] = pinRecipe.PinHightMin * -1
            table1["PinHight"] = string.format("%.3f", pinJson1.Result[i].PinHeight)
            table1["result"] = pinJson1.Result[i].PinResult
    
            --数组中插入数据 OK 高度 目前所有变量
            table.insert(dataResult, pinJson1.Result[i].PinResult)
            table.insert(sqlData, pinJson1.Result[i].PinHight)
            table.insert(currentTable, table1)
        end
    end
else
    for i = 1, 105, 1 do
        if i % 21 == 0 or i % 21 == 1 then
            -- SetVariable("value_h" .. tostring(i), 0.0)
            pinRecipe = GetRecipeSet(i - 1)
            local table1 = {}
            table1["PinIndex"] = "H" .. tostring(i)
            table1["PinHightStd"] = string.format("%.3f", pinRecipe.PinHight)
            table1["PinHightMax"] = pinRecipe.PinHightMax
            table1["PinHightMin"] = pinRecipe.PinHightMin * -1
            table1["PinHight"] = 0.0
            table1["result"] = "NG"
    
            table.insert(dataResult, "NG")
            table.insert(sqlData,0.0)
            table.insert(currentTable, table1)
        end
    end
end

--获取106-111pin的数据,需要转成json后再处理
pinStr2 = GetVariable("pin_106_111")
pinJson2 = ParseJson(pinStr2)
for i = 1, 6, 1 do
    -- 保存到csv
    write_x = write_x .. string.format("%.3f,", pinJson2.Result[i].PinPositionX)
    write_y = write_y .. string.format("%.3f,", pinJson2.Result[i].PinPositionY)
    write_h = write_h .. string.format("%.3f,", pinJson2.Result[i].PinHeight)
    write_sidex = write_sidex .. string.format("%.3f,", pinJson2.Result[i].PinSideX)
    write_sidey = write_sidey .. string.format("%.3f,", pinJson2.Result[i].PinSideY)
    write_sideh = write_sideh .. string.format("%.3f,", pinJson2.Result[i].PinSideHeight)
    write_result = write_result .. pinJson2.Result[i].PinReslult .. ","

    SetVariable("value_h" .. tostring(i + 105), pinJson2.Result[i].PinHeight)
    pinRecipe = GetRecipeSet(i + 105 - 1)
    local table1 = {}
    table1["PinIndex"] = "H" .. tostring(i + 105)
    table1["PinHightStd"] = string.format("%.3f", pinRecipe.PinHight)
    table1["PinHightMax"] = pinRecipe.PinHightMax
    table1["PinHightMin"] = pinRecipe.PinHightMin * -1
    table1["PinHight"] = string.format("%.3f", pinJson2.Result[i].PinHeight)
    table1["result"] = pinJson2.Result[i].PinResult

    table.insert(dataResult, pinJson2.Result[i].PinResult)
    table.insert(sqlData, pinJson2.Result[i].PinHight)
    table.insert(currentTable, table1)
end

--获取112-196pin的数据,需要转成json后再处理
pinStr3 = GetVariable("pin_112_196")
pinJson3 = ParseJson(pinStr3)
for i = 1, 85, 1 do
    -- 保存到csv
    write_x = write_x .. string.format("%.3f,", pinJson3.Result[i].PinPositionX)
    write_y = write_y .. string.format("%.3f,", pinJson3.Result[i].PinPositionY)
    write_h = write_h .. string.format("%.3f,", pinJson3.Result[i].PinHeight)
    write_sidex = write_sidex .. string.format("%.3f,", pinJson3.Result[i].PinSideX)
    write_sidey = write_sidey .. string.format("%.3f,", pinJson3.Result[i].PinSideY)
    write_sideh = write_sideh .. string.format("%.3f,", pinJson3.Result[i].PinSideHeight)
    write_result = write_result .. pinJson3.Result[i].PinReslult .. ","

    --保存测量变量,用于HMI显示
    if i % 17 == 0 or i % 17 == 1 then
        SetVariable("value_h" .. tostring(i + 111), pinJson3.Result[i].PinHeight)
        pinRecipe = GetRecipeSet(i + 111 - 1)
        local table1 = {}
        table1["PinIndex"] = "H" .. tostring(i + 111)
        table1["PinHightStd"] = string.format("%.3f", pinRecipe.PinHight)
        table1["PinHightMax"] = pinRecipe.PinHightMax
        table1["PinHightMin"] = pinRecipe.PinHightMin * -1
        table1["PinHight"] = string.format("%.3f", pinJson3.Result[i].PinHeight)
        table1["result"] = pinJson3.Result[i].PinResult

        table.insert(dataResult, pinJson3.Result[i].PinResult)
        table.insert(sqlData, pinJson3.Result[i].PinHight)
        table.insert(currentTable, table1)
    end
end

--保存到csv,每个检测结束后都添加一个换行符
write_x = write_x .. "\n"
write_y = write_y .. "\n"
write_h = write_h .. "\n"
write_sidex = write_sidex .. "\n"
write_sidey = write_sidey .. "\n"
write_sideh = write_sideh .. "\n"
write_result = write_result .. "\n"

--HMI来控制是否保存csv文件
if GetVariable("isSaveCSV") == 1 then
    println("save csv")
    fprint("./196pin_X.csv", write_x)
    fprint("./196pin_Y.csv", write_y)
    fprint("./196pin_H.csv", write_h)
    fprint("./196pin_SideX.csv", write_sidex)
    fprint("./196pin_SideY.csv", write_sidey)
    fprint("./196pin_SideH.csv", write_sideh)
    fprint("./196pin_Result.csv", write_result)
end

--保存实时显示到HMI的数据
-- println(ConvertTableToJson(currentTable))
SetVariable("alltable", ConvertTableToJson(currentTable))

--保存到数据库的数据
fileds = { "h1", "h21", "h22", "h42", "h43", "h63", "h64", "h84", "h85", "h105", "h106", "h107", "h108", "h109",
    "h110", "h111", "h112", "h128", "h129", "h145", "h146", "h162", "h163", "h179", "h180", "h196", "OKNG" }

--之前设置的方法使用:用来判断数组中是否有该数据
if tableContains(dataResult, "NG") then
    table.insert(sqlData, 0)
else
    table.insert(sqlData, 1)
end

--数据写入数据库中(高度结果)
a = WriteHmiData("196pin_H", fileds, sqlData)

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