實(shí)戰(zhàn):OpenCV與Halcon視覺方案解析)
鋰電池極片裁切毛刺檢測(cè)這件事我在視覺檢測(cè)這一行摸爬滾打了十多年真正把它當(dāng)作一個(gè)嚴(yán)肅的課題來研究是從一次產(chǎn)線上的短路事故分析開始的。那批電芯在化成分容階段出現(xiàn)了不少低壓不良拆解后確認(rèn)是負(fù)極片邊緣的金屬毛刺刺穿了隔膜。從那以后我花了大量時(shí)間在Baumer相機(jī)、OpenCV和Halcon這套組合上專門對(duì)付極片毛刺。極片裁切毛刺之所以讓人頭疼是因?yàn)樗幌癖砻鎰潅菢尤庋劭杀?。裁切邊緣的毛刺可能只有幾微米到幾十微米形態(tài)上可能是豎立的尖刺、翻卷的金屬屑甚至是藕斷絲連的“拖尾”。這些毛刺在后續(xù)卷繞或疊片工藝中一旦刺穿隔膜正負(fù)極直接接觸內(nèi)部短路就發(fā)生了。輕則電芯自放電率超標(biāo)重則引發(fā)熱失控這不是鬧著玩的。所以產(chǎn)線上對(duì)極片裁切質(zhì)量的管控毛刺檢測(cè)是剛需中的剛需。這篇文章我不打算泛泛而談而是把我在實(shí)際項(xiàng)目中驗(yàn)證過的5個(gè)核心方法完整拆解出來配上Baumer相機(jī)的選型思路、OpenCV的快速驗(yàn)證代碼和Halcon的工程化實(shí)現(xiàn)。適合正在做鋰電視覺檢測(cè)的工程師、準(zhǔn)備入行機(jī)器視覺的開發(fā)者以及產(chǎn)線上被毛刺問題困擾的工藝人員參考。內(nèi)容會(huì)涉及具體的參數(shù)和算法邏輯有些地方會(huì)比較硬核但我盡量把每一步的為什么講清楚。1. 極片裁切毛刺的物理特性與光學(xué)成像難點(diǎn)為什么這活兒不好干搞視覺檢測(cè)的人第一反應(yīng)往往是“不就檢測(cè)個(gè)毛刺嗎拍清楚不就完了”。真上了產(chǎn)線你會(huì)發(fā)現(xiàn)極片毛刺檢測(cè)是視覺檢測(cè)里的硬骨頭難點(diǎn)不在于“看到”而在于“看得準(zhǔn)”和“看得快”。1.1 毛刺的尺寸量級(jí)與形態(tài)分類極片裁切后的邊緣毛刺按照行業(yè)通行標(biāo)準(zhǔn)一般分為幾種形態(tài)。豎立毛刺垂直于極片表面方向生長(zhǎng)尺寸通常在5到50微米之間最危險(xiǎn)因?yàn)樗姆较蛘脤?duì)著隔膜卷邊毛刺裁切時(shí)金屬延展形成翻卷形狀像鉤子容易在卷繞時(shí)刮傷隔膜還有金屬屑?xì)埩暨@是裁切刀磨損后掉落的碎屑可能粘附在極片邊緣或表面。這里要說一個(gè)關(guān)鍵點(diǎn)電芯廠對(duì)毛刺的管控尺寸不同工藝段要求不同。分切工序的毛刺標(biāo)準(zhǔn)一般控制在15微米以內(nèi)模切工序要求更嚴(yán)有些高端動(dòng)力電池廠已經(jīng)要求控制在8微米以內(nèi)。這意味著視覺系統(tǒng)的分辨率設(shè)計(jì)至少要能穩(wěn)定分辨5微米以下的缺陷。按照像素精度經(jīng)驗(yàn)公式要穩(wěn)定檢測(cè)一個(gè)目標(biāo)至少需要3到5個(gè)像素覆蓋其最小尺寸。也就是說檢測(cè)5微米毛刺單個(gè)像素對(duì)應(yīng)的物理尺寸要到1微米左右。以Baumer相機(jī)為例如果使用500萬像素相機(jī)分辨率2448×2048要覆蓋的視野如果是24mm×20mm那像素精度正好是10微米這只能檢測(cè)30微米以上的毛刺。要檢測(cè)5微米的毛刺要么縮小視野要么上更高分辨率的相機(jī)。我在項(xiàng)目中常用的是Baumer的LX系列或VLXT系列搭配高倍率遠(yuǎn)心鏡頭把視野控制在10mm×8mm左右像素精度能做到2微米級(jí)別。1.2 光學(xué)成像的三大干擾源毛刺檢測(cè)的光學(xué)方案最怕三件事極片表面紋理干擾、金屬反光過曝、環(huán)境光波動(dòng)。極片是涂布后經(jīng)過輥壓的表面有碳酸鈣或?qū)щ娞亢陬w粒形成的微觀起伏在背光照射下會(huì)呈現(xiàn)出類似“噪點(diǎn)”的紋理。這些紋理的灰度變化常常比毛刺本身的灰度變化還明顯。如果用簡(jiǎn)單的全局閾值分割紋理就會(huì)被當(dāng)成缺陷檢出來造成大量的過殺。金屬反光是另一個(gè)大問題。極片裁切邊緣是裸露的金屬斷面對(duì)光線的反射率極高尤其是鋁箔正極片和銅箔負(fù)極片。如果是明場(chǎng)照明邊緣區(qū)域容易出現(xiàn)高光飽和毛刺完全淹沒在白光里如果是暗場(chǎng)照明金屬反光又會(huì)形成耀眼的亮點(diǎn)同樣干擾判斷。環(huán)境光波動(dòng)在產(chǎn)線上幾乎無法完全避免。設(shè)備周圍的照明燈、車間的自然光、相機(jī)鏡頭上的灰塵都會(huì)導(dǎo)致圖像整體灰度漂移。這就要求算法本身具備一定的灰度自適應(yīng)能力不能依賴固定的閾值。1.3 相機(jī)選型與采集鏈路的關(guān)鍵參數(shù)Baumer相機(jī)在鋰電視覺檢測(cè)領(lǐng)域用得比較多原因無非是穩(wěn)定可靠、SDK完善、GigE Vision接口兼容性好。選型時(shí)我重點(diǎn)看幾個(gè)參數(shù)。分辨率決定了檢測(cè)精度的上限前面已經(jīng)說了不再重復(fù)。傳感器類型上全局快門是必須的極片在高速運(yùn)動(dòng)中即使有輕微震動(dòng)全局快門也能保證圖像不畸變卷簾快門在運(yùn)動(dòng)場(chǎng)景下會(huì)出拖影。幀率方面假設(shè)產(chǎn)線速度是30米/分鐘極片寬度200mm要檢測(cè)整幅極片相機(jī)幀率至少需要保證在觸發(fā)頻率的1.2倍以上否則會(huì)丟幀。我常用Baumer的VLXT-500C幀率能做到24fps2448×2048配合硬件觸發(fā)完全夠用。鏡頭選擇上遠(yuǎn)心鏡頭是毛刺檢測(cè)的首選。普通工業(yè)鏡頭存在視差問題極片邊緣在不同高度時(shí)會(huì)成像在不同的位置影響測(cè)量精度。遠(yuǎn)心鏡頭的光線是平行光成像不隨物距變化特別適合做高精度尺寸測(cè)量。我常用的是0.5倍或1倍遠(yuǎn)心鏡頭搭配環(huán)形光源或同軸光源。采集鏈路還有一個(gè)容易被忽視的環(huán)節(jié)網(wǎng)卡和驅(qū)動(dòng)。Baumer相機(jī)走GigE Vision協(xié)議網(wǎng)卡推薦使用Intel服務(wù)器級(jí)網(wǎng)卡開啟巨幀(Jumbo Frame)功能包大小設(shè)為9000字節(jié)可以減少網(wǎng)絡(luò)傳輸延遲和丟包率。同時(shí)要關(guān)閉網(wǎng)卡的節(jié)能模式Windows系統(tǒng)下還要把相機(jī)的中斷調(diào)節(jié)改為“關(guān)閉”否則會(huì)不定期出現(xiàn)幀率波動(dòng)。2. 五個(gè)核心方法拆解從圖像預(yù)處理到毛刺量化這一章是整個(gè)項(xiàng)目的核心。我根據(jù)實(shí)際項(xiàng)目經(jīng)驗(yàn)把毛刺檢測(cè)拆成了5個(gè)環(huán)環(huán)相扣的方法每一個(gè)解決一個(gè)具體環(huán)節(jié)的問題。需要說明的是這5個(gè)方法在實(shí)際工程中通常是組合使用的并不是獨(dú)立的選擇題。方法目標(biāo)解決的核心問題關(guān)鍵工具方法一高對(duì)比度光學(xué)成像讓毛刺從背景中“跳出來”背光源、遠(yuǎn)心鏡頭、Baumer相機(jī)方法二多尺度背景抑制消除極片紋理干擾OpenCV頂帽變換、DoG方法三亞像素邊緣定位精確測(cè)量毛刺尺寸Halcon亞像素提取、卡尺工具方法四動(dòng)態(tài)閾值分割適應(yīng)環(huán)境光波動(dòng)與灰度漂移OpenCV自適應(yīng)閾值、OTSU方法五形態(tài)學(xué)毛刺量化區(qū)分真實(shí)毛刺與噪聲開運(yùn)算、閉運(yùn)算、Blob特征篩2.1 方法一背光照明下的高對(duì)比度成像毛刺檢測(cè)的光學(xué)方案我首選背光照明原因很簡(jiǎn)單背光能創(chuàng)造出“剪影”效果。極片本身不透明在背光照射下極片區(qū)域是黑色的背景是白色的。如果邊緣有毛刺毛刺會(huì)遮擋光線在亮背景上形成暗色的凸起。這種對(duì)比度方案比任何明場(chǎng)或暗場(chǎng)方案都要穩(wěn)定得多。背光光源的選擇有講究。LED背光源分為平行光和漫射光兩種。平行光配合遠(yuǎn)心鏡頭可以得到邊緣銳利的圖像適合做精確的毛刺尺寸測(cè)量漫射光的均勻性好適合檢測(cè)表面缺陷但邊緣銳度不如平行光。毛刺檢測(cè)是邊緣檢測(cè)所以平行光是首選。光源顏色方面極片材料對(duì)紅光的透過率低對(duì)藍(lán)光的透過率也不高但紅光在金屬表面的衍射效應(yīng)更明顯藍(lán)光的波長(zhǎng)更短對(duì)細(xì)小毛刺的分辨能力更強(qiáng)。實(shí)際項(xiàng)目里我更喜歡用藍(lán)色背光450nm到470nm波段的配合相機(jī)前加裝對(duì)應(yīng)波段的帶通濾光片可以有效濾除環(huán)境光干擾讓圖像信噪比提升一個(gè)檔次。這是一個(gè)性價(jià)比極高的做法很多工程師想不到。還有一個(gè)細(xì)節(jié)是光源的亮度調(diào)節(jié)。背光亮度不是越亮越好而是要讓背景灰度穩(wěn)定在200左右極片區(qū)域灰度在20以下這樣動(dòng)態(tài)范圍最大后續(xù)閾值分割的余量也最大。Baumer相機(jī)的曝光時(shí)間設(shè)置我通常設(shè)為50到100微秒配合光源控制器調(diào)節(jié)亮度保證信噪比最優(yōu)。2.2 方法二頂帽變換與多尺度背景抑制背光圖像拍出來極片紋理依然會(huì)干擾毛刺檢測(cè)。極片表面不是絕對(duì)平整的涂層顆粒會(huì)在背光下產(chǎn)生明暗不均的斑點(diǎn)灰度波動(dòng)范圍可以達(dá)到30到50個(gè)灰度級(jí)。而毛刺的灰度差異在背光下其實(shí)是“黑色物體在亮背景上的小突起引起的灰度凹陷”差別往往只有20到30個(gè)灰度級(jí)。紋理干擾和毛刺信號(hào)混在一起直接閾值分割必然出錯(cuò)。頂帽變換是解決這個(gè)問題的利器。頂帽變換的定義是原圖減去形態(tài)學(xué)開運(yùn)算的結(jié)果。開運(yùn)算是先腐蝕后膨脹會(huì)消除圖像中小于結(jié)構(gòu)元素尺寸的亮細(xì)節(jié)保留下大尺寸的背景。用原圖減去開運(yùn)算結(jié)果剩下的就是小于結(jié)構(gòu)元素尺寸的亮細(xì)節(jié)。但毛刺在背光圖像里是暗細(xì)節(jié)不是亮細(xì)節(jié)所以要用黑帽變換也就是原圖減去閉運(yùn)算結(jié)果的絕對(duì)值。我實(shí)際用的處理流程是這樣的先對(duì)ROI區(qū)域做高斯模糊降噪然后用80×80像素的矩形結(jié)構(gòu)元素做黑帽變換提取暗細(xì)節(jié)。結(jié)構(gòu)元素的尺寸選擇要略大于毛刺的最大寬度這樣毛刺整體被保留而大面積的紋理起伏被當(dāng)作背景消除。實(shí)戰(zhàn)經(jīng)驗(yàn)是單一尺度的結(jié)構(gòu)元素往往不夠。極片紋理的尺度范圍很寬有些極片的大顆粒炭黑在圖像上是3到5個(gè)像素的亮點(diǎn)有些則是20到30個(gè)像素的暗斑。我通常做多尺度黑帽變換分別用15×15和40×40的結(jié)構(gòu)元素各做一次然后把兩次結(jié)果取最大值合并。這樣就兼顧了小毛刺和大毛刺的檢測(cè)。在OpenCV里實(shí)現(xiàn)很簡(jiǎn)單// C OpenCV多尺度黑帽變換 cv::Mat gray, kernel1, kernel2, blackhat1, blackhat2, combined; // gray為輸入灰度圖 cv::blur(gray, gray, cv::Size(3, 3)); cv::Mat kernel1_ cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)); cv::morphologyEx(gray, blackhat1, cv::MORPH_BLACKHAT, kernel1_); cv::Mat kernel2_ cv::getStructuringElement(cv::MORPH_RECT, cv::Size(40, 40)); cv::morphologyEx(gray, blackhat2, cv::MORPH_BLACKHAT, kernel2_); cv::max(blackhat1, blackhat2, combined);這段代碼跑完后毛刺和細(xì)小紋理的暗細(xì)節(jié)都被強(qiáng)化了大面積的不均勻背景被消除。這個(gè)步驟做完后面所有判斷都是在這個(gè)“干凈”的圖上進(jìn)行的。2.3 方法三亞像素邊緣坐標(biāo)的精確提取毛刺尺寸的量化靠像素級(jí)別的邊緣檢測(cè)是不夠的。一個(gè)5微米的毛刺在2微米像素精度的圖像上也才2.5個(gè)像素寬。像素級(jí)邊緣提取的誤差在正負(fù)0.5像素也就是正負(fù)1微米對(duì)于8微米標(biāo)準(zhǔn)的判定來說誤差已經(jīng)相當(dāng)可觀了。這時(shí)候要用亞像素邊緣提取。Halcon在這方面做得非常成熟它的亞像素邊緣提取基于邊緣灰度曲線的插值擬合精度可以達(dá)到0.1像素甚至更高。具體操作上我不直接用edges_sub_pix拿全部邊緣而是用卡尺工具(measure_pos)沿著極片邊緣的法線方向逐段測(cè)量。卡尺工具的原理是在ROI內(nèi)定義一條測(cè)量線沿著測(cè)量線的方向取左右一定寬度的灰度剖面然后用高斯濾波平滑再通過曲線梯度求極值點(diǎn)。這個(gè)極值點(diǎn)就是邊緣的亞像素位置。Halcon的measure_pos可以返回每個(gè)測(cè)量點(diǎn)的邊緣位置坐標(biāo)和邊緣幅度配合極片邊緣的直線擬合毛刺引起的邊緣偏移就一目了然。用Halcon實(shí)現(xiàn)的步驟是先從圖像中提取極片邊緣的大致直線區(qū)域擬合出邊緣的基準(zhǔn)直線然后計(jì)算邊緣上每個(gè)像素點(diǎn)到基準(zhǔn)線的距離。如果距離超過閾值就判定為毛刺候選點(diǎn)。距離的連續(xù)區(qū)域就是毛刺的范圍最大距離就是毛刺的高度。具體代碼邏輯* 假設(shè)輸入圖像是Image極片邊緣在圖像中是一個(gè)豎直邊 * 1. 提取邊緣亞像素區(qū)域 edges_sub_pix (Image, Edges, canny, 1.5, 20, 40) * 2. 選擇豎直方向的邊緣 select_shape_xld (Edges, SelectedEdges, phi, and, -0.2, 0.2) * 3. 擬合直線得到基準(zhǔn)線 fit_line_contour_xld (SelectedEdges, tukey, -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist) * 4. 測(cè)量點(diǎn)到基準(zhǔn)線的距離 distance_pl (ContourRow, ContourCol, RowBegin, ColBegin, RowEnd, ColEnd, Distance)這段代碼里擬合直線選用‘tukey’權(quán)重是為了剔除毛刺點(diǎn)對(duì)直線擬合的影響。如果用最小二乘法擬合毛刺點(diǎn)的距離會(huì)被平均掉一部分導(dǎo)致基準(zhǔn)線偏移影響毛刺高度的判定。2.4 方法四動(dòng)態(tài)閾值分割的自適應(yīng)策略很多項(xiàng)目在實(shí)驗(yàn)室里跑得好好的一上產(chǎn)線就頻繁誤報(bào)。最大的原因就是環(huán)境光波動(dòng)導(dǎo)致圖像灰度整體漂移。比如車間燈光閃爍、早晚陽光照射角度變化、光源衰減等都會(huì)讓背景灰度從200漂到180或者220。固定閾值的分割方案在這種情況下必然失效。動(dòng)態(tài)閾值的思路是不用固定值而是根據(jù)圖像局部的灰度統(tǒng)計(jì)來生成閾值。局部自適應(yīng)閾值的基本原理是對(duì)每個(gè)像素計(jì)算其鄰域內(nèi)的灰度均值或高斯加權(quán)均值然后將原始灰度與這個(gè)局部均值比較若差值超過一個(gè)相對(duì)值就判定為前景。在OpenCV里自適應(yīng)閾值函數(shù)adaptiveThreshold可以直接使用但它的參數(shù)調(diào)節(jié)比較敏感。blockSize鄰域尺寸比毛刺的尺寸大2到5倍效果最好C值偏移量通常取5到15之間需要根據(jù)實(shí)際圖像調(diào)試。我個(gè)人的方案是結(jié)合全局OTSU和局部自適應(yīng)先對(duì)整幅圖計(jì)算OTSU閾值判斷圖像整體灰度水平。如果整體灰度水平在合理范圍內(nèi)波動(dòng)就用局部自適應(yīng)閾值細(xì)分割。這種兩級(jí)策略既保證了穩(wěn)定性又避免了單一自適應(yīng)閾值在某些區(qū)域產(chǎn)生噪聲。還有一種做法是為每個(gè)ROI區(qū)域動(dòng)態(tài)計(jì)算閾值這在對(duì)多片極片同時(shí)檢測(cè)的整幅圖像中效果很好。具體而言將圖像分成若干個(gè)固定大小的子塊對(duì)每個(gè)子塊單獨(dú)做OTSU然后對(duì)閾值矩陣進(jìn)行插值平滑得到平滑的閾值面。這樣做的好處是即使極片表面不同位置的反光特性不同也能保證分割效果一致。2.5 方法五形態(tài)學(xué)操作與拓?fù)涮卣骱Y選的毛刺量化分割出來的二值圖里除了真實(shí)毛刺還有極片邊緣的微小翻邊、顆粒殘留、噪點(diǎn)等。要把真正的毛刺挑出來不能只看面積還要看形態(tài)和分布位置。我總結(jié)了三個(gè)關(guān)鍵特征毛刺凸出于極片輪廓之外的面積、毛刺的徑向長(zhǎng)度從輪廓線到毛刺尖端、以及毛刺的寬高比。形態(tài)學(xué)操作在這個(gè)階段主要用來整理分割結(jié)果。先用閉運(yùn)算把斷裂的毛刺連接成完整的連通域再用開運(yùn)算去除細(xì)小的孤立噪點(diǎn)。注意這里的開運(yùn)算結(jié)構(gòu)元素尺寸要小于最小毛刺尺寸否則會(huì)把真毛刺也去掉。拓?fù)涮卣骱Y選是整個(gè)方法鏈里最需要經(jīng)驗(yàn)和技巧的地方。我來看一個(gè)實(shí)際的例子極片裁切邊緣如果產(chǎn)生卷邊毛刺它可能是一個(gè)又寬又扁的形狀面積不小但高度不高這種毛刺危險(xiǎn)度中等而豎立毛刺面積可能很小但高度突出危險(xiǎn)度極高。如果只看面積豎立毛刺可能被當(dāng)成噪點(diǎn)漏掉卷邊毛刺可能被當(dāng)成大缺陷報(bào)警。所以必須結(jié)合邊緣位置信息來判斷。正確的做法是先找到極片的主體輪廓然后計(jì)算輪廓的凸缺陷(凸包與輪廓之間的差異區(qū)域)。毛刺本質(zhì)上就是邊緣上的凸缺陷。凸缺陷的幾個(gè)屬性——起始點(diǎn)、結(jié)束點(diǎn)、最遠(yuǎn)點(diǎn)以及到凸包的距離——正好對(duì)應(yīng)毛刺的位置、寬度和高度。OpenCV的convexityDefects函數(shù)可以直接返回這些數(shù)據(jù)。// OpenCV凸缺陷檢測(cè)毛刺 std::vectorcv::Vec4i defects; cv::convexityDefects(contour, hull, defects); for (const auto d : defects) { int startIdx d[0], endIdx d[1], farIdx d[2]; double depth d[3] / 256.0; // 到凸包的距離即毛刺高度 // 根據(jù)depth閾值篩選 if (depth minBurrHeight) { // 記錄毛刺位置與高度 } }這里minBurrHeight就是根據(jù)電芯廠工藝要求換算出來的像素閾值。比如要求8微米像素精度2微米/像素那么minBurrHeight就等于4個(gè)像素。但是要注意如果極片邊緣本身有一定的粗糙度凸缺陷距離會(huì)頻繁超過閾值這時(shí)候需要再增加一個(gè)限制條件——凸缺陷在邊緣上的橫向跨距。豎立毛刺的橫向跨距通常只有幾個(gè)像素而邊緣粗糙造成的凸缺陷橫向跨距往往更大且深度分布均勻。3. OpenCV實(shí)戰(zhàn)用C快速驗(yàn)證毛刺檢測(cè)算法OpenCV在這套檢測(cè)方案里的定位是快速驗(yàn)證和原型開發(fā)。前面已經(jīng)陸續(xù)給出了一些代碼片段這一章我把完整的檢測(cè)流程串起來給出一套可以直接跑的實(shí)驗(yàn)代碼。需要說明的是這套代碼解決的是“從單張極片圖像中檢測(cè)邊緣毛刺”的核心邏輯實(shí)際產(chǎn)線上還需要考慮多ROI、觸發(fā)、通信等工程問題。3.1 完整檢測(cè)流程的OpenCV實(shí)現(xiàn)整個(gè)流程分成5步圖像讀取與ROI裁剪、灰度化與去噪、多尺度黑帽變換、自適應(yīng)閾值分割、凸缺陷分析與毛刺標(biāo)記。代碼里我用cv::Mat全程操作并注釋了每個(gè)步驟的意圖。#include opencv2/opencv.hpp #include iostream #include vector using namespace cv; using namespace std; int main() { // 1. 讀取圖像灰度化 Mat src imread(pole_sheet.bmp, IMREAD_GRAYSCALE); if (src.empty()) { cerr Failed to load image! endl; return -1; } // 2. 裁剪ROI只保留包含極片邊緣的區(qū)域 // 視現(xiàn)場(chǎng)情況設(shè)置這里假設(shè)邊緣在圖像左側(cè)1/4區(qū)域 Rect roi(0, 0, src.cols / 2, src.rows); Mat roiImg src(roi).clone(); // 3. 高斯濾波去噪 Mat blurred; GaussianBlur(roiImg, blurred, Size(3, 3), 0); // 4. 多尺度黑帽變換 Mat blackhat1, blackhat2, combined; Mat kernel1 getStructuringElement(MORPH_RECT, Size(15, 15)); Mat kernel2 getStructuringElement(MORPH_RECT, Size(40, 40)); morphologyEx(blurred, blackhat1, MORPH_BLACKHAT, kernel1); morphologyEx(blurred, blackhat2, MORPH_BLACKHAT, kernel2); max(blackhat1, blackhat2, combined); // 5. 自適應(yīng)閾值分割 Mat binary; adaptiveThreshold(combined, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 31, 10); // 6. 形態(tài)學(xué)清理閉運(yùn)算連接斷裂開運(yùn)算去除孤立噪聲 Mat cleaned; Mat closeKernel getStructuringElement(MORPH_RECT, Size(5, 5)); Mat openKernel getStructuringElement(MORPH_RECT, Size(3, 3)); morphologyEx(binary, cleaned, MORPH_CLOSE, closeKernel); morphologyEx(cleaned, cleaned, MORPH_OPEN, openKernel); // 7. 找輪廓并計(jì)算凸缺陷 vectorvectorPoint contours; findContours(cleaned, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); // 8. 遍歷輪廓檢測(cè)毛刺 for (size_t i 0; i contours.size(); i) { vectorPoint hull; convexHull(contours[i], hull, false); vectorVec4i defects; convexityDefects(contours[i], hull, defects); for (const auto d : defects) { double depth d[3] / 256.0; if (depth 4.0) { // 4像素對(duì)應(yīng)8微米像素精度2um/px cout Burr detected, depth depth px endl; } } } return 0; }這段代碼跑通之后你就有了一個(gè)毛刺檢測(cè)的最小可行版本。我建議你在自己的樣本上跑一遍記錄不同毛刺形態(tài)下的檢測(cè)結(jié)果然后根據(jù)誤報(bào)和漏報(bào)情況調(diào)整三個(gè)參數(shù)黑帽變換的結(jié)構(gòu)元素尺寸、自適應(yīng)閾值的blockSize和C值、凸缺陷的depth閾值。3.2 算法參數(shù)調(diào)優(yōu)經(jīng)驗(yàn)過殺與漏檢的平衡這里的過殺指把合格的極片邊緣誤判為毛刺漏檢指真正有害的毛刺沒被檢測(cè)出來。兩者互為代價(jià)參數(shù)調(diào)優(yōu)就是把平衡點(diǎn)壓到客戶能接受的范圍。從項(xiàng)目實(shí)施角度我建議按照“先保證不漏檢再逐步降低過殺”的原則來調(diào)參。先把depth閾值調(diào)低到3像素此時(shí)可能會(huì)有大量邊緣粗糙點(diǎn)被標(biāo)記為毛刺形成過殺然后逐步提高閾值同時(shí)觀察被標(biāo)記為毛刺的區(qū)域的形態(tài)特征。如果某個(gè)候選毛刺的橫向跨距明顯大于其深度例如深度5像素、橫向跨距30像素那大概率不是毛刺而是邊緣翹曲。相反如果深度5像素、橫向跨距只有6像素那就是典型的豎立毛刺必須判級(jí)。自適應(yīng)閾值的blockSize對(duì)過殺的影響也很大。blockSize越小局部對(duì)比度變化越敏感細(xì)小紋理越容易被分割出來過殺越多。blockSize越大分割越粗糙細(xì)小毛刺可能被吞沒。我實(shí)測(cè)下來blockSize取31在大多數(shù)極片圖像上表現(xiàn)穩(wěn)定C值控制在5到15之間。還有一點(diǎn)形態(tài)學(xué)開運(yùn)算結(jié)構(gòu)元素的尺寸一定不能設(shè)大了。很多人習(xí)慣用5×5或7×7去“清理噪聲”但對(duì)于5微米、8微米級(jí)別的毛刺3×3的開運(yùn)算已經(jīng)會(huì)削弱毛刺信號(hào)了。如果噪聲實(shí)在太多優(yōu)先去調(diào)整照明和黑帽變換的參數(shù)而不是加大開運(yùn)算結(jié)構(gòu)元素。3.3 OpenCV檢測(cè)結(jié)果的可視化與Debug技巧調(diào)試毛刺檢測(cè)算法最忌諱直接看最終判定結(jié)果。正確的做法是可視化每一個(gè)中間步驟。我自己的調(diào)試流程是顯示原圖、顯示黑帽變換結(jié)果、顯示二值圖、顯示輪廓和凸缺陷標(biāo)記圖。在這個(gè)基礎(chǔ)上把被判為毛刺的每個(gè)候選點(diǎn)用彩色圓圈標(biāo)注出來同時(shí)打印出它的深度、橫向跨距等特征值。這樣參數(shù)調(diào)整就有了明確的依據(jù)。OpenCV里可視化凸缺陷的代碼非常簡(jiǎn)單用circle函數(shù)在最遠(yuǎn)點(diǎn)位置畫圓用line函數(shù)把起始點(diǎn)、結(jié)束點(diǎn)連接起來。我強(qiáng)烈建議你在開發(fā)階段把檢測(cè)到的毛刺區(qū)域放大顯示用resize函數(shù)放大10倍肉眼看清楚這個(gè)區(qū)域到底是不是真的毛刺。這個(gè)過程雖然原始但對(duì)提升算法的判斷力非常有效因?yàn)槟銜?huì)積累起對(duì)“各種毛刺在圖像上長(zhǎng)什么樣”的直觀認(rèn)知。另外如果需要在現(xiàn)場(chǎng)快速驗(yàn)證一批樣本的檢測(cè)效果建議寫一個(gè)批量處理腳本遍歷文件夾里的所有測(cè)試圖把判定結(jié)果保存成一個(gè)CSV文件同時(shí)生成標(biāo)注圖像方便和工藝工程師一起評(píng)審。4. Halcon工程化實(shí)現(xiàn)測(cè)量穩(wěn)定性優(yōu)先的場(chǎng)景OpenCV原型驗(yàn)證通過后真正的產(chǎn)線項(xiàng)目我一般會(huì)用Halcon來做工程化實(shí)現(xiàn)原因有兩個(gè)一是Halcon的亞像素測(cè)量算子穩(wěn)定性和精度確實(shí)做得更好二是在產(chǎn)線上要處理多相機(jī)、多ROI、與PLC通信等復(fù)雜邏輯Halcon的HDevelop環(huán)境和導(dǎo)出C#/C的能力讓部署變得高效。4.1 Halcon毛刺檢測(cè)的算子鏈設(shè)計(jì)Halcon處理毛刺檢測(cè)我的核心思路和OpenCV方案是一樣的先定位極片邊緣再沿邊緣法線方向做亞像素測(cè)量通過邊緣偏移量判斷毛刺。但Halcon的算子封裝度更高代碼更簡(jiǎn)潔而且測(cè)量穩(wěn)定性明顯更好。先看如何用Halcon動(dòng)態(tài)定位極片邊緣并測(cè)量毛刺。這里有一個(gè)很實(shí)用的算子組合先用threshold把極片區(qū)域分割出來再用reduce_domain把檢測(cè)限定在邊緣附近最后用卡尺測(cè)量邊緣坐標(biāo)。* 讀取圖像 read_image (Image, pole_sheet.bmp) * 1. 分割極片區(qū)域背光下極片為暗區(qū)域 threshold (Image, Region, 0, 80) * 2. 提取極片邊緣區(qū)域 boundary (Region, Border, inner) * 3. 膨脹邊緣區(qū)域創(chuàng)建測(cè)量ROI dilation_circle (Border, ROIBorder, 15.5) * 4. 在ROI內(nèi)提取邊緣亞像素輪廓 reduce_domain (Image, ROIBorder, ImageROI) edges_sub_pix (ImageROI, Edges, canny, 1.5, 20, 40) * 5. 按方向篩選豎直邊緣 select_shape_xld (Edges, SelectedEdges, phi, and, -0.3, 0.3) * 6. 擬合基準(zhǔn)直線剔除毛刺點(diǎn)影響 fit_line_contour_xld (SelectedEdges, tukey, -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)這個(gè)流程里fit_line_contour_xld的權(quán)重參數(shù)選了‘tukey’這個(gè)選擇很關(guān)鍵。Tukey權(quán)重對(duì)離群點(diǎn)也就是毛刺點(diǎn)的加權(quán)極小幾乎不影響擬合結(jié)果所以基準(zhǔn)線是“干凈”的。如果選‘least_squares’毛刺點(diǎn)會(huì)把基準(zhǔn)線往外拉導(dǎo)致毛刺高度被低估。得到基準(zhǔn)線后怎么測(cè)量毛刺我的做法是生成一條垂直于基準(zhǔn)線的測(cè)量線序列均勻分布在邊緣的整個(gè)長(zhǎng)度范圍內(nèi)然后用measure_pos逐條測(cè)量。測(cè)量線之間的距離一般是5到10個(gè)像素太密則重復(fù)計(jì)算太稀則可能漏掉短小毛刺。每條測(cè)量線得到的邊緣點(diǎn)與基準(zhǔn)線的偏移距離就是該位置的毛刺高度。4.2 Halcon與C/C#混合開發(fā)的部署要點(diǎn)Halcon的算法在HDevelop里開發(fā)調(diào)試最終要部署到產(chǎn)線設(shè)備上。常見的模式是導(dǎo)出為C#或C代碼嵌入到上位機(jī)軟件中與相機(jī)采集、PLC信號(hào)、數(shù)據(jù)庫等模塊聯(lián)動(dòng)。Baumer相機(jī)與Halcon的集成主要通過GenICam接口。Halcon從17.12版本開始通過GenICamTL支持Baumer相機(jī)在HDevelop里直接用open_framegrabber (‘GigEVision2’, ...) 或者更簡(jiǎn)單地用‘Baumer’接口名直接打開相機(jī)。實(shí)際上Halcon原生支持的相機(jī)接口列表里包含Baumer這意味著你不需要額外的SDK封裝就能在Halcon里直接采圖。采圖與檢測(cè)的典型流程是open_framegrabber打開相機(jī)set_framegrabber_param設(shè)置曝光和觸發(fā)模式grab_image循環(huán)采圖然后調(diào)用檢測(cè)過程。這個(gè)流程的好處是采圖和檢測(cè)在同一樣本空間里避免圖像數(shù)據(jù)在不同庫之間轉(zhuǎn)換的開銷。部署時(shí)我要提醒幾個(gè)容易踩坑的地方。第一個(gè)是許可證問題Halcon的加密狗如果沒插好或者license到期整個(gè)檢測(cè)程序會(huì)直接崩潰或無法啟動(dòng)。產(chǎn)線上最好配置雙狗冗余一個(gè)主狗一個(gè)備狗同時(shí)建立license到期預(yù)警機(jī)制。第二個(gè)是Halcon的runtime版本要和開發(fā)版本匹配否則導(dǎo)出的程序在新機(jī)器上跑不起來。第三個(gè)是內(nèi)存釋放長(zhǎng)時(shí)間連續(xù)運(yùn)行Halcon程序如果不注意clear_*算子的調(diào)用內(nèi)存會(huì)緩慢增長(zhǎng)運(yùn)行一周后可能出現(xiàn)內(nèi)存不足。解決方法是定期重啟檢測(cè)線程或者在每個(gè)檢測(cè)循環(huán)中顯式釋放不再使用的圖像和區(qū)域?qū)ο蟆?.3 Halcon的標(biāo)定與測(cè)量精度驗(yàn)證Halcon算子再強(qiáng)如果相機(jī)沒標(biāo)定測(cè)出來的數(shù)據(jù)也是不可信的。毛刺檢測(cè)對(duì)精度要求極高標(biāo)定必須做扎實(shí)。Halcon標(biāo)定分兩部分相機(jī)內(nèi)參標(biāo)定和像素當(dāng)量標(biāo)定。內(nèi)參標(biāo)定用Halcon的標(biāo)準(zhǔn)標(biāo)定板例如30mm×30mm7×7圓點(diǎn)陣列通過find_calib_object算子自動(dòng)提取標(biāo)定板圖像然后用calibrate_cameras計(jì)算相機(jī)內(nèi)參。這一步能校正鏡頭畸變讓圖像中的直線在物理空間中也是直線。像素當(dāng)量標(biāo)定則更簡(jiǎn)單直接用已知物理尺寸的標(biāo)準(zhǔn)件比如量塊放在視野里測(cè)出它在圖像中占據(jù)的像素?cái)?shù)用物理尺寸除以像素?cái)?shù)得到每個(gè)像素對(duì)應(yīng)的物理尺寸。但這個(gè)標(biāo)定有一個(gè)前提就是鏡頭必須是遠(yuǎn)心鏡頭或者物距固定不變。普通FA鏡頭在不同物距下像素當(dāng)量的變化很敏感這就回到前面強(qiáng)調(diào)的毛刺檢測(cè)必須用遠(yuǎn)心鏡頭。標(biāo)定做完后我用一組已知毛刺高度的標(biāo)準(zhǔn)樣件做驗(yàn)證。標(biāo)準(zhǔn)樣件可以通過線切割或精密加工制作在極片邊緣預(yù)制不同高度的凸起。實(shí)測(cè)值與標(biāo)準(zhǔn)值對(duì)比偏移在0.5微米以內(nèi)才算合格。我在項(xiàng)目中常用這樣的驗(yàn)證流程每隔4小時(shí)點(diǎn)檢一次標(biāo)準(zhǔn)樣件如果測(cè)量結(jié)果漂移超過1微米就觸發(fā)校準(zhǔn)提示。這個(gè)點(diǎn)檢流程能有效防止因相機(jī)松動(dòng)、光源衰減、鏡頭污染等引起的系統(tǒng)偏差。5. 從實(shí)驗(yàn)室到產(chǎn)線的工程化落地現(xiàn)場(chǎng)問題與性能優(yōu)化算法在實(shí)驗(yàn)室跑通只是第一步真正讓它在產(chǎn)線上穩(wěn)定運(yùn)行還會(huì)遇到一堆實(shí)驗(yàn)室里想象不到的問題。這一章我重點(diǎn)說說現(xiàn)場(chǎng)落地中我踩過的坑和對(duì)應(yīng)的解決方案。5.1 相機(jī)觸發(fā)與極片運(yùn)動(dòng)的同步控制產(chǎn)線上的極片是連續(xù)運(yùn)動(dòng)的相機(jī)必須在極片到達(dá)視野的特定位置時(shí)觸發(fā)拍攝。這個(gè)觸發(fā)信號(hào)通常來自編碼器或接近傳感器。如果觸發(fā)時(shí)機(jī)不準(zhǔn)確拍攝到的極片位置飄忽不定ROI設(shè)置就會(huì)失效。Baumer相機(jī)通過GigE Vision的硬件觸發(fā)接口Line0或Line1接收外部觸發(fā)信號(hào)。我建議在配置時(shí)將trigger source設(shè)置為L(zhǎng)ine0trigger mode設(shè)置為on然后用PLC或運(yùn)動(dòng)控制卡在極片到達(dá)指定位置時(shí)發(fā)一個(gè)5V的脈沖。關(guān)鍵點(diǎn)是脈沖寬度不能太短Baumer相機(jī)要求觸發(fā)信號(hào)至少保持一定時(shí)間寬度才能被可靠接收一般建議不小于10微秒。編碼器觸發(fā)則要處理脈沖頻率與相機(jī)幀率的匹配問題。如果極片速度突變編碼器脈沖頻率也會(huì)變化相機(jī)可能會(huì)因?yàn)閬聿患疤幚矶鴣G幀。解決方案有兩種一種是設(shè)置相機(jī)的幀率上限超過上限的觸發(fā)脈沖自動(dòng)忽略另一種是在軟件層面加入緩沖隊(duì)列但要注意隊(duì)列深度不能太大否則實(shí)時(shí)性變差。5.2 光照一致性維護(hù)與圖像質(zhì)量監(jiān)控產(chǎn)線是24小時(shí)運(yùn)行的LED光源會(huì)有衰減鏡頭上會(huì)落灰塵極片的批次變化也會(huì)影響圖像質(zhì)量。如果不做監(jiān)控可能運(yùn)行三個(gè)月之后檢測(cè)精度已經(jīng)下降了很多但沒人發(fā)現(xiàn)。我建議在軟件里加一個(gè)圖像質(zhì)量監(jiān)控模塊每采集一張圖像就計(jì)算幾個(gè)統(tǒng)計(jì)量背景區(qū)域的平均灰度、極片區(qū)域的灰度標(biāo)準(zhǔn)差、邊緣區(qū)域的對(duì)比度。這些統(tǒng)計(jì)量一旦超出預(yù)設(shè)范圍系統(tǒng)自動(dòng)報(bào)警提醒維護(hù)人員檢查光源、鏡頭或相機(jī)。這個(gè)模塊開發(fā)成本很低但對(duì)保障檢測(cè)穩(wěn)定性貢獻(xiàn)極大。另一個(gè)實(shí)用做法是在產(chǎn)線無產(chǎn)品運(yùn)行時(shí)讓相機(jī)拍一張空背景圖像與標(biāo)準(zhǔn)背景圖對(duì)比。如果灰度差異超過閾值說明鏡頭或光源可能污染了。這個(gè)“空跑自檢”可以定時(shí)執(zhí)行比如每小時(shí)一次完全不影響生產(chǎn)。5.3 檢測(cè)速度優(yōu)化并行處理與硬件加速高速產(chǎn)線對(duì)檢測(cè)速度的要求是無止境的。我遇到過產(chǎn)線速度提升后原來200毫秒的檢測(cè)時(shí)間壓縮到80毫秒的情況。Halcon在這方面有一些優(yōu)化空間。首先Halcon自帶并行處理機(jī)制默認(rèn)情況下會(huì)自動(dòng)使用多核CPU。但要注意如果算法中使用了大量的全局操作比如全圖閾值、全圖濾波并行效率反而不高。折中方案是把圖像分成若干子區(qū)域每個(gè)子區(qū)域獨(dú)立處理最后合并結(jié)果。我在多片極片同時(shí)檢測(cè)的場(chǎng)景里就是按極片數(shù)量切分子區(qū)域用parallel_for并行調(diào)用檢測(cè)流程速度幾乎線性提升。其次ROI之外不做無效計(jì)算。很多時(shí)候一幅2000萬像素的圖像里真正需要處理的邊緣區(qū)域只占不到10%。在檢測(cè)前先定位極片位置用reduce_domain把計(jì)算限定在邊緣附近運(yùn)算量能減少一個(gè)數(shù)量級(jí)。第三GPU加速是最后的武器。Halcon的Filter、Morphology等算子支持OpenCL加速配置好OpenCL環(huán)境后部分算子的執(zhí)行時(shí)間可以縮短到原來的三分之一。但GPU加速不是銀彈算子切換和數(shù)據(jù)傳輸?shù)拈_銷在部分場(chǎng)景下反而更慢。我建議先用profiler分析算子的耗時(shí)分布如果瓶頸在有OpenCL版本的算子上再考慮啟用GPU。6. 實(shí)戰(zhàn)代碼整合OpenCV與Halcon雙軌方案的完整示例前面的內(nèi)容都是分步講解這一章給出一套相對(duì)完整的代碼框架你可以把它當(dāng)成腳手架根據(jù)實(shí)際場(chǎng)景修改參數(shù)。6.1 OpenCV版本完整示例適用于算法驗(yàn)證與教學(xué)演示// burr_detection_opencv.cpp // 功能鋰電池極片裁切毛刺檢測(cè)OpenCV實(shí)現(xiàn) // 環(huán)境OpenCV 4.5 / C11 #include opencv2/opencv.hpp #include iostream #include fstream #include vector using namespace cv; using namespace std; struct BurrInfo { Point farPoint; double depthPx; // 毛刺深度像素 double widthPx; // 毛刺橫向跨距像素 double startAngle; // 起始點(diǎn)角度調(diào)試用 }; vectorBurrInfo detectBurrs(const Mat gray, double minDepthPx) { vectorBurrInfo result; Mat blurred, blackhat1, blackhat2, combined; // 預(yù)處理 GaussianBlur(gray, blurred, Size(3, 3), 0); Mat k1 getStructuringElement(MORPH_RECT, Size(15, 15)); Mat k2 getStructuringElement(MORPH_RECT, Size(40, 40)); morphologyEx(blurred, blackhat1, MORPH_BLACKHAT, k1); morphologyEx(blurred, blackhat2, MORPH_BLACKHAT, k2); max(blackhat1, blackhat2, combined); // 動(dòng)態(tài)閾值 Mat binary; adaptiveThreshold(combined, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 31, 10); // 形態(tài)學(xué)清理 Mat cleaned; morphologyEx(binary, cleaned, MORPH_CLOSE, getStructuringElement(MORPH_RECT, Size(5, 5))); morphologyEx(cleaned, cleaned, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(3, 3))); // 輪廓分析 vectorvectorPoint contours; findContours(cleaned, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); for (size_t i 0; i contours.size(); i) { if (contours[i].size() 50) continue; // 過小輪廓跳過 vectorPoint hull; convexHull(contours[i], hull, false); vectorVec4i defects; convexityDefects(contours[i], hull, defects); for (const auto d : defects) { double depth d[3] / 256.0; if (depth minDepthPx) continue; int startIdx d[0], endIdx d[1], farIdx d[2]; Point startPt contours[i][startIdx]; Point endPt contours[i][endIdx]; Point farPt contours[i][farIdx]; double width norm(startPt - endPt); // 過濾橫向跨距明顯大于深度的非毛刺凸起 if (width depth * 5) continue; result.push_back({farPt, depth, width, 0.0}); } } return result; } int main(int argc, char** argv) { string imgPath (argc 1) ? argv[1] : test.bmp; Mat src imread(imgPath, IMREAD_GRAYSCALE); if (src.empty()) { cerr Cannot open image! endl; return -1; } double pixelSizeUm 2.0; // 像素當(dāng)量單位um/px double burrSpecUm 8.0; // 工藝標(biāo)準(zhǔn)單位um double minDepthPx burrSpecUm / pixelSizeUm; // 最小檢測(cè)深度像素 Rect roi(0, 0, src.cols / 2, src.rows); // 假設(shè)邊緣在左半幅 Mat roiImg src(roi).clone(); auto burrs detectBurrs(roiImg, minDepthPx); Mat color; cvtColor(roiImg, color, COLOR_GRAY2BGR); for (auto b : burrs) { double depthUm b.depthPx * pixelSizeUm; circle(color, b.farPoint, 4, Scalar(0, 0, 255), -1); putText(color, format(%.1fum, depthUm), b.farPoint Point(5, -5), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 255), 1); } imwrite(result_opencv.png, color); cout Detected burrs: burrs.size() endl; return 0; }這段代碼在真正投入產(chǎn)線前還需要補(bǔ)充多ROI管理和統(tǒng)計(jì)報(bào)表功能。但作為算法驗(yàn)證和演示它已經(jīng)能完整跑通毛刺檢測(cè)的處理流程。6.2 Halcon版本完整示例產(chǎn)線級(jí)穩(wěn)定測(cè)量Halcon版本我給出一個(gè)完整的HDevelop工程操作步驟從相機(jī)采集到輸出檢測(cè)結(jié)果包含關(guān)鍵參數(shù)的實(shí)際取值。* burr_detection_halcon.hdev * 1. 打開Baumer相機(jī) open_framegrabber (Baumer, 1, 1, 0, 0, 0, 0, default, 8, rgb, -1, false, auto, GigE, 0, Camera_1, 0, -1, AcqHandle) set_framegrabber_param (AcqHandle, ExposureTime, 80.0) set_framegrabber_param (AcqHandle, TriggerMode, On) * 2. 主循環(huán) for Idx : 1 to 10000 by 1 grab_image (Image, AcqHandle) * 3. 檢測(cè)毛刺調(diào)用自定義過程 detect_burrs_halcon (Image, BurrRegions, BurrHeights, BurrCount) * 4. 視覺輸出與判級(jí) if (BurrCount 0) * 保存不合格圖片及毛刺信息 write_image (Image, bmp, 0, NG_ Idx .bmp) endif * 5. 與PLC通信邏輯在此處通過異步處理完成 endfor close_framegrabber (AcqHandle) * 自定義檢測(cè)過程 * detect_burrs_halcon (Image, BurrRegions, BurrHeights, BurrCount) * 輸入灰度圖Image * 輸出毛刺區(qū)域、毛刺高度數(shù)組、毛刺數(shù)量 * 實(shí)現(xiàn)要點(diǎn) threshold (Image, DarkRegion, 0, 80) boundary (DarkRegion, Border, inner) dilation_circle (Border, RoiRegion, 15.5) reduce_domain (Image, RoiRegion, ImageRoi) edges_sub_pix (ImageRoi, Edges, canny, 1.5, 20, 40) select_shape_xld (Edges, VEdges, phi, and, -0.3, 0.3) fit_line_contour_xld (VEdges, tukey, -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist) * 生成測(cè)量線 gen_measure_rectangle2 (MidRow, MidCol, Phi, Length1, Length2, Width, Height, nearest_neighbor, MeasureHandle) measure_pos (Image, MeasureHandle, 1, 30, all, all, RowEdge, ColEdge, Amplitude, Distance) * 計(jì)算邊緣偏移 distance_pl (RowEdge, ColEdge, RowBegin, ColBegin, RowEnd, ColEnd, DistArr) * 超過閾值的點(diǎn)即為毛刺候選 BurrIndices : abs(DistArr) 4.0這段Halcon代碼的特點(diǎn)是把測(cè)量過程完全建立在亞像素基礎(chǔ)上穩(wěn)定性比OpenCV版本高一個(gè)檔次但同時(shí)對(duì)標(biāo)定和相機(jī)參數(shù)的要求也更嚴(yán)格。在項(xiàng)目初期我通常先用OpenCV版本做快速驗(yàn)證確認(rèn)算法思路可行后再切換到Halcon版本做工程化部署。6.3 兩套方案的優(yōu)劣對(duì)比與選型建議篇幅有限我只說結(jié)論性建議。如果項(xiàng)目處于預(yù)研階段需要在幾天內(nèi)驗(yàn)證算法可行性用OpenCV開發(fā)速度快可視化調(diào)試方便社區(qū)資料多。如果項(xiàng)目進(jìn)入量產(chǎn)交付階段對(duì)測(cè)量精度、穩(wěn)定性、以及與PLC/MES系統(tǒng)交互有嚴(yán)格要求用Halcon它的亞像素算子和標(biāo)定工具更成熟部署形態(tài)更靈活。從成本角度OpenCV免費(fèi)Halcon需要購買license。但工業(yè)項(xiàng)目里的成本大頭從來不是軟件license而是現(xiàn)場(chǎng)調(diào)試時(shí)間和停機(jī)損失。一個(gè)穩(wěn)定可靠的Halcon方案省下的調(diào)試時(shí)間成本可能遠(yuǎn)超license費(fèi)用。所以我不建議在毛刺檢測(cè)這種高精度場(chǎng)景下一味追求免費(fèi)方案。還有一個(gè)折中路線視覺算法用OpenCV跑但測(cè)量部分的關(guān)鍵算子自己用C實(shí)現(xiàn)亞像素邊緣提取。這條路對(duì)團(tuán)隊(duì)的綜合能力要求較高適合有資深算法工程師的團(tuán)隊(duì)。我個(gè)人在幾個(gè)項(xiàng)目里用過這種方案效果也不錯(cuò)但開發(fā)周期明顯更長(zhǎng)。7. 我的實(shí)測(cè)心得那些軟件教程里不會(huì)告訴你的細(xì)節(jié)最后這一章我想說說代碼之外的東西。毛刺檢測(cè)這個(gè)項(xiàng)目最難的部分往往不在算法本身而在你對(duì)整個(gè)系統(tǒng)的判斷和經(jīng)驗(yàn)積累。7.1 毛刺樣本的積累比算法更值錢我見過不少團(tuán)隊(duì)一上來就寫代碼跑幾張圖覺得效果不錯(cuò)就敢上產(chǎn)線。結(jié)果現(xiàn)場(chǎng)一跑各種漏檢、誤報(bào)加班排查發(fā)現(xiàn)是樣本覆蓋不夠。毛刺的形態(tài)千變?nèi)f化豎立毛刺、卷邊毛刺、拖尾毛刺、根部凹陷、邊緣撕裂不同刀具狀態(tài)、不同材料批次、不同裁切速度產(chǎn)生的缺陷形態(tài)都不同。正確做法是在項(xiàng)目啟動(dòng)初期就建立缺陷樣本庫。找工藝同事要?dú)v史的不合格品用顯微鏡拍下每種毛刺的形態(tài)照片按危險(xiǎn)等級(jí)分類。然后針對(duì)每一類樣本做算法調(diào)優(yōu)保證算法對(duì)每一類都有足夠的檢測(cè)余量。這個(gè)工作看起來很笨但它是檢測(cè)穩(wěn)定性的根本。算法工程師的真正的功力不在代碼寫得多花哨而在對(duì)各種缺陷形態(tài)的理解深度。我自己的習(xí)慣是每次到現(xiàn)場(chǎng)出差都會(huì)把當(dāng)天的檢測(cè)NG圖導(dǎo)出按缺陷類型分類歸檔。半年下來樣本庫積累了數(shù)千張真實(shí)缺陷圖后續(xù)算法優(yōu)化都有了依據(jù)。7.2 誤殺率與漏檢率的權(quán)衡和工藝部門達(dá)成共識(shí)毛刺檢測(cè)的判定標(biāo)準(zhǔn)不是算法自己定的而是要和工藝部門反復(fù)對(duì)齊的。電芯廠對(duì)毛刺的要求“內(nèi)短風(fēng)險(xiǎn)低”往往是一個(gè)模糊的概念需要把它量化成具體的尺寸閾值。這個(gè)過程中算法工程師要主動(dòng)提供數(shù)據(jù)支持比如給出不同閾值下的漏檢率和誤殺率曲線讓工藝和品質(zhì)部門基于數(shù)據(jù)做決策。實(shí)測(cè)中我發(fā)現(xiàn)誤殺率太高會(huì)直接影響產(chǎn)線效率因?yàn)槊看螆?bào)警都要停機(jī)人工確認(rèn)頻繁的誤報(bào)會(huì)讓操作工產(chǎn)生“狼來了”心理反而可能無視真實(shí)報(bào)警。所以寧可閾值設(shè)置得稍寬松一些優(yōu)先把真正的危險(xiǎn)毛刺全部檢出來然后把可能誤判的邊緣粗糙區(qū)域歸為“警告”級(jí)別讓人工復(fù)核而不是直接判不合格。7.3 備用方案永遠(yuǎn)是工程素養(yǎng)的一部分最后分享一個(gè)實(shí)際教訓(xùn)。有一次項(xiàng)目調(diào)試期間Halcon的license突然到期檢測(cè)程序直接罷工產(chǎn)線停了兩個(gè)小時(shí)。從那以后我在所有Halcon項(xiàng)目里都強(qiáng)制要求客戶配備雙license狗并且在上位機(jī)軟件里加入license到期前7天的預(yù)警提示。這種看似不起眼的細(xì)節(jié)往往決定了你在客戶那里的專業(yè)口碑。另外整個(gè)檢測(cè)系統(tǒng)的備用方案也很重要。最好能實(shí)現(xiàn)雙相機(jī)互為備份主相機(jī)故障時(shí)備用相機(jī)自動(dòng)接替。雖然成本增加了但對(duì)于24小時(shí)不停機(jī)的產(chǎn)線來說系統(tǒng)可用性的價(jià)值遠(yuǎn)超設(shè)備成本。我在方案設(shè)計(jì)階段就把這個(gè)因素考慮進(jìn)去避免后期改造的麻煩。毛刺檢測(cè)這條路技術(shù)難點(diǎn)是可以通過學(xué)習(xí)和實(shí)踐克服的但對(duì)現(xiàn)場(chǎng)的敬畏和對(duì)細(xì)節(jié)的偏執(zhí)才是真正決定項(xiàng)目成敗的東西。希望這篇文章能讓你在自己項(xiàng)目里少走一些彎路。