銅卷位置檢測及尺寸測量
專案簡介
專案背景
利用雲臺感測器拍攝影像,對需要進行包裝入箱的銅箔卷,檢測其在鐵托盤上的前後左右及高度位置,為機械臂的抓取提供引導,同時讀取產品上的二維碼資訊以匹配裝銅箔的木箱規格; 其次檢測木箱規格以及在小車上的左右位置,以核對木箱規格,同時為機械臂抓取銅箔入箱提供位置引導資訊。

相機選型
SICK雲臺線掃
檢測要求
X向精度 ≤ 0.025mm
Y向精度 ≤ 0.03mm
測量週期 ≤ 2s
解決方案
Vision-Studio軟體針對3D點雲進行處理,首先通過Blob工具進行銅卷定位,輸出銅卷的位置等資訊,再通過圓柱工具擬合出圓柱,從而進行體積的計算 。
設計思路

執行效果展示
工程結果展示:

HMI結果展示:

專案流程
一、初始化
- 選擇
Lua脚本语言工具,全域性變數初始化,建立待接收資料的csv檔案。
- 拼接表頭字串,儲存初始化csv檔案
經驗
.. 用於string型別字串拼接
part1 = "Hello, "
part2 = "world!"
result = part1 .. part2
print(result) # 輸出: Hello, world!
,作為分隔符,分割每個資料項
--初始化全局变量
SetIntVariable('CurrentTime',0)
write = ''
--对文件进行判断,如果当前文件不存在,则生成文件
if FileExists("./H.csv") == false then
write = write .. 'Time,'
for i = 1,15,1 do
write = write .. 'h' .. i .. ","
end
write = write .. '\n'
--"\n"表示换行
end
-- 每行第一列写入当前时间
write = write .. os.date("%Y_%m_%d %H:%M;%S") .. ","
-- 将刚才的内容写入csv文件中
WriteToFile("./H.csv",write)
--生成用于拼接的字符串
write = ''
if FileExists("./Position.csv") == false then
write = write .. 'Time,'
for i = 1,15,1 do
write = write .. 'x' .. i .. ","
write = write .. 'y' .. i .. ","
write = write .. 'z' .. i .. ","
write = write .. ','
end
write = write .. '\n'
end
-- 每行第一列写入当前时间
write = write .. os.date("%Y_%m_%d %H:%M;%S") .. ","
-- 将刚才的内容写入csv文件中
WriteToFile("./Position.csv",write)- 選擇
加载点云工具,獲取點雲。
二、預處理

選擇
3D变换工具,將影像繞x軸反方向旋轉90度。選擇
3D裁切工具,將圓柱切割開並輸出到IM1中。

選擇
3D下采样工具,減少點雲中點的個數,方便後續操作。選擇
3D离群点滤波工具,過濾掉雜點。

三、圓柱高度與位置測量

- 選擇
3D斑点工具,分割出15個圓柱。

選擇
lua语言脚本,繫結上一步運算元變數中bolb位置變數,編寫lua指令碼儲存儲存圓柱位置至檔案中。選擇
3D圆柱工具,將銅卷擬合成圓柱

四、資料儲存
選擇lua语言脚本,繫結上一步輸出的運算元變數(圓柱的兩頂點座標),計算得到尺寸資訊並儲存至檔案中。
拼接擬合的圓柱尺寸資訊到字串並將字串儲存到全域性變數
儲存單個圓柱尺寸資訊到全域性變數
經驗
string.format 用於格式化字串 %.nf表示將浮點數格式化為保留 n 位小數。
pi = 3.14159 str = string.format("Pi is approximately %.3f", pi) print(str) -- 輸出: Pi is approximately 3.141
write = ""
write = write .. string.format("%.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f,"
,math.abs(Circle1.Bottom.X - Circle1.Top.X),math.abs(Circle2.Bottom.X - Circle2.Top.X),
math.abs(Circle3.Bottom.X - Circle3.Top.X),math.abs(Circle4.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle5.Top.X),math.abs(Circle6.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle7.Top.X),math.abs(Circle8.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle9.Top.X),math.abs(Circle10.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle11.Top.X),math.abs(Circle12.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle13.Top.X),math.abs(Circle14.Bottom.X - Circle3.Top.X),
math.abs(Circle3.Bottom.X - Circle15.Top.X))
SetFloatVariable("h" .. "1" ,math.floor(1000*math.abs(Circle1.Bottom.X - Circle1.Top.X))/1000)
SetFloatVariable("h" .. "2" ,math.floor(1000*math.abs(Circle2.Bottom.X - Circle2.Top.X))/1000)
SetFloatVariable("h" .. "3" ,math.floor(1000*math.abs(Circle3.Bottom.X - Circle3.Top.X))/1000)
SetFloatVariable("h" .. "4" ,math.floor(1000*math.abs(Circle4.Bottom.X - Circle4.Top.X))/1000)
SetFloatVariable("h" .. "5" ,math.floor(1000*math.abs(Circle5.Bottom.X - Circle5.Top.X))/1000)
SetFloatVariable("h" .. "6" ,math.floor(1000*math.abs(Circle6.Bottom.X - Circle6.Top.X))/1000)
SetFloatVariable("h" .. "7" ,math.floor(1000*math.abs(Circle7.Bottom.X - Circle7.Top.X))/1000)
SetFloatVariable("h" .. "8" ,math.floor(1000*math.abs(Circle8.Bottom.X - Circle8.Top.X))/1000)
SetFloatVariable("h" .. "9" ,math.floor(1000*math.abs(Circle9.Bottom.X - Circle9.Top.X))/1000)
SetFloatVariable("h" .. "10" ,math.floor(1000*math.abs(Circle10.Bottom.X - Circle10.Top.X))/1000)
SetFloatVariable("h" .. "11" ,math.floor(1000*math.abs(Circle11.Bottom.X - Circle11.Top.X))/1000)
SetFloatVariable("h" .. "12" ,math.floor(1000*math.abs(Circle12.Bottom.X - Circle12.Top.X))/1000)
SetFloatVariable("h" .. "13" ,math.floor(1000*math.abs(Circle13.Bottom.X - Circle13.Top.X))/1000)
SetFloatVariable("h" .. "14" ,math.floor(1000*math.abs(Circle14.Bottom.X - Circle14.Top.X))/1000)
SetFloatVariable("h" .. "15" ,math.floor(1000*math.abs(Circle15.Bottom.X - Circle15.Top.X))/1000)
write = write .. "\n"
-- 将数据写入表中
WriteToFile("./H.csv",write)