實戰(zhàn):VS2015、Qt5.9與Halcon20組合解析)
前段時間需要維護一套多相機缺陷檢測的系統(tǒng)打開工程一看編譯環(huán)境是VS2015、Qt5.9和Halcon20的組合。放在今天來看這套搭配多少有點“老”但完整跑過項目、處理完產(chǎn)線上的各種幺蛾子之后我反而越來越覺得這三個版本湊在一起不是偶然而是產(chǎn)線機器視覺設(shè)備里非常務(wù)實的一種解法。這篇文章就圍繞這套“VS2015 Qt5.9 Halcon20”的多個相機缺陷檢測源碼來講。我會把系統(tǒng)架構(gòu)、算法流程、工程坑點、打包部署整個鏈路都復(fù)盤一遍適合正在做視覺檢測設(shè)備、或者拿到一套老代碼不知道從哪下手的工程師參考。里面提到的思路和踩坑記錄都是實際項目里的真東西不是demo級別的玩具代碼。1. 這個“奇妙組合”是怎么被逼出來的1.1 需求端為什么一臺設(shè)備要裝多個相機我接觸的這套源碼對應(yīng)的是一臺外觀缺陷檢測設(shè)備。產(chǎn)品是一個方形結(jié)構(gòu)件需要檢測上下表面和兩個側(cè)面四個工位各裝一個相機。檢測目標包括劃痕、臟污、壓傷、缺料、異物標準還不完全相同——上下表面用明場看大面積臟污側(cè)面用低角度光抓劃痕其中一個面還要求檢測局部微小凸點。多相機不是簡單的“多買幾個攝像頭”它帶來三個層面的問題多路圖像要并發(fā)采集幀率還不能掉得太難看每個相機的檢測參數(shù)、ROI區(qū)域、打光條件都不一樣必須能獨立配置產(chǎn)品在流水線上移動同一個產(chǎn)品不同工位拍出來的圖要能關(guān)聯(lián)到一起便于判定最終OK/NG。這些需求如果軟件架構(gòu)設(shè)計得不好后面每個環(huán)節(jié)都會冒問題。所以先明確一點多相機檢測系統(tǒng)的核心難點不在某個算法有多高級而在多路圖像怎么組織、怎么調(diào)度、怎么管理各自的參數(shù)。1.2 版本選型不是念舊是“剛好每一步都不得不這么做”選定VS2015、Qt5.9、Halcon20這個組合很多人第一反應(yīng)是“怎么還用這么老的版本”。我在項目里也反復(fù)權(quán)衡過最后堅持下來是有實際原因的。先看VS2015。產(chǎn)線工控機很多時候是客戶指定的系統(tǒng)環(huán)境陳舊是常態(tài)。有些型號的工控機出廠自帶的老版運行庫、PLC通信庫、IO卡驅(qū)動就是基于v140工具集編譯的。我們?nèi)绻昧薞S2022或者更新工具鏈編譯出的exe一上工控機就容易缺VCRUNTIME140.dll、MSVCP140.dll。VS2015的v140工具集生成的目標文件在兼容性上非常穩(wěn)尤其配合Halcon和相機SDK這種吃C運行時很敏感的庫少掉很多“運行時沖突”的麻煩。再看Qt5.9。這個版本是Qt官方的LTS長期支持版本雖然老但它在老舊顯卡驅(qū)動的工控機上顯示非常穩(wěn)定不依賴太新的OpenGL特性。VS2015對應(yīng)Qt5.9的MSVC2015_64套件編出來的程序能直接嵌進VS里調(diào)試不需要像MinGW那樣處理鏈接庫格式不一致的問題。如果你用MinGW版Qt去鏈Halcon的MSVC版lib一堆無法解析的外部符號非常折磨人。最后是Halcon20。與其說是選Halcon20不如說是選它的多相機接口穩(wěn)定性和并發(fā)支持。比它更老的Halcon 13、17在GigE Vision多路取流上偶爾會掉幀Halcon20在20.11之后對多線程的算子支持明顯改善而且還能直接兼容VS2015。也就是說在這個組合里編譯器是舊但能跑界面庫是舊但穩(wěn)視覺庫是新但兼容三者正好懟到一塊去了。1.3 Halcon20的安裝與授權(quán)細節(jié)這一步栽過很多次關(guān)于Halcon20安裝我先給一個比較穩(wěn)的版本Halcon 20.11 Progress。安裝的時候有幾個坑值得記一下。第一位數(shù)必須對齊。小學三年級都知道但實際項目里總有同事在這個上翻車。相機SDK是64位的那Halcon就必須裝64位VS工程也必須選x64平臺。Halcon安裝目錄下會分x64-win64和x86-win32子目錄裝完以后別裝錯。三處都是64位才不會出現(xiàn)鏈接階段signature不匹配的問題。第二環(huán)境變量。正常安裝時安裝程序會自動把HALCONROOT指向安裝目錄同時把C:\Program Files\MVTec\HALCON-20.11\bin\x64-win64寫進PATH。但很多工控機是用的鏡像部署系統(tǒng)環(huán)境變量被精簡掉Halcon的DLL找不到程序一啟動就報錯。我建議在開發(fā)機上專門打開環(huán)境變量確認一下HALCONROOTC:\Program Files\MVTec\HALCON-20.11 HALCONARCHx64-win64 PATH...\bin\x64-win64;...\bin\x64-win64\dotnet第三license。開發(fā)調(diào)試階段用試用license或正常采購的開發(fā)授權(quán)正式部署時用runtime license。常見問題是把開發(fā)版license直接丟到產(chǎn)線電腦上結(jié)果到期后相機全鎖很難堪。還有一個小細節(jié)如果同一臺機器裝過多個Halcon版本秋水一樣的PATH變量會把版本搞混插件裝完還是提示找不到hdevengine.dll。裝完之后建議先用Halcon自帶的例程驗證相機是否都能正常取流。examples\cpp下有現(xiàn)成的open_framegrabber例子直接改成對應(yīng)相機IP地址跑通了再往Qt工程里集成。這個步驟能幫你排除相機、網(wǎng)卡、IP設(shè)置、防火墻等底層因素不然到最后你會發(fā)現(xiàn)Qt工程里報錯本質(zhì)上跟Qt一點關(guān)系都沒有。2. 多相機采集與任務(wù)流水線不是“多開幾個線程”那么簡單2.1 采集層設(shè)計用Halcon還是相機SDK多相機采集第一件事是決定用Halcon的接口直接取圖還是用相機廠商SDK。我在這套源碼里兩條路都走過給你說下對比。如果相機支持GigE Vision或者USB3 Vision標準協(xié)議直接用Halcon的GrabImageAsync是最省力的。好處很明顯抓出來的圖像直接是HImage對象后續(xù)threshold、connection這些算子直接調(diào)用不需要做內(nèi)存拷貝Halcon內(nèi)部對多路相機采集有獨立buffer管理一臺相機一路handle不容易相互干擾代碼量小不用維護每家廠商不同的SDK回調(diào)。但某些場景下必須用廠商SDK比如相機私有的觸發(fā)模式、多相機硬同步、GPIO控制光源、某些特定型號的相機沒有走標準協(xié)議。這種情況就老老實實拿SDK回調(diào)里的圖像buffer用GenBuffer或者GenImage1包成HObject再進處理管線。我這套系統(tǒng)里主流相機走的是Halcon GigEVision接口只有帶硬觸發(fā)聯(lián)動的關(guān)鍵工位加了一個廠商SDK輔助控制。整體邏輯就是能用標準協(xié)議就用Halcon必須私有控制才引SDK。2.2 一個相機一個采集線程 檢測線程池多相機采集最容易踩的坑是“一鍋燉”——在UI線程里循環(huán)反復(fù)抓四個相機的圖像然后發(fā)現(xiàn)界面卡頓、圖像掉幀、CPU占用還爆滿。正確的做法是把采集和界面徹底分離。我給這套源碼設(shè)計的是這樣一套結(jié)構(gòu)每個相機一個獨立的采集線程std::thread內(nèi)部循環(huán)調(diào)GrabImageAsync抓到圖像后塞進一個線程安全的任務(wù)隊列帶容量上限避免內(nèi)存爆掉檢測線程從隊列里取圖跑Halcon算法得出OK/NG結(jié)果和缺陷坐標檢測結(jié)果通過信號槽機制發(fā)回主界面主界面只負責顯示和操作交互。代碼骨架大致長這樣// 采集線程內(nèi) HTuple hv_AcqHandle; OpenFramegrabber(GigEVision, 0, 0, 0, 0, 0, 0, default, -1, default, -1, false, default, cameraIP, 0, -1, hv_AcqHandle); GrabImageStart(hv_AcqHandle, -1); while (bRunning !bStopRequested) { HImage image; image.GrabImageAsync(hv_AcqHandle, -1); if (queue.size() MAX_QUEUE_LENGTH) { queue.push(std::move(image)); } QThread::msleep(2); } CloseFramegrabber(hv_AcqHandle);// 檢測線程內(nèi) try { HImage image; if (queue.try_pop(image, 200)) { DetectorResult result detector.run(image); emit resultReady(cameraId, result); } } catch (HException e) { emit errorOccurred(cameraId, QString(e.ErrorMessage().Text())); }這里有個細節(jié)隊列長度一定要設(shè)上限。如果檢測速度跟不上采集速度隊列無限增長程序會在一分鐘之內(nèi)吃光所有內(nèi)存。我一般設(shè)100到200幀上限滿了就丟新幀寧可少檢一幀不可讓程序崩掉。2.3 多相機參數(shù)與工件ID聯(lián)動管理多個相機的檢測參數(shù)絕不能寫死在代碼里。原因很簡單產(chǎn)品換型、光源調(diào)整、相機角度微調(diào)這些在產(chǎn)線現(xiàn)場時刻都在發(fā)生總不能為了改一個曝光值重新編譯一次程序。我的做法是每個相機一個配置段全部丟到一個JSON文件里統(tǒng)一管理。內(nèi)容包含相機IP、曝光時間、增益、觸發(fā)模式ROI區(qū)域坐標每一類缺陷算法的使能開關(guān)和閾值參數(shù)檢測結(jié)果的保存路徑、是否存NG圖。換產(chǎn)品的時候界面上下拉選擇產(chǎn)品型號程序自動把所有相機參數(shù)加載進來。另外多相機檢測一定要解決“同一產(chǎn)品多工位結(jié)果關(guān)聯(lián)”的問題。我這邊用條碼或者產(chǎn)品流水號做主鍵每個相機檢測完成后把結(jié)果回寫到產(chǎn)品記錄里等所有工位都檢測完匯總規(guī)則再觸發(fā)最終判斷。這樣才不會有“一個面NG了但另外幾個面OK設(shè)備卻不報警”的低級問題。3. Halcon缺陷檢測三板斧打光、分割、特征篩選3.1 打光先行圖像質(zhì)量決定了算法的天花板說句得罪人的話很多團隊做缺陷檢測遇到算法不穩(wěn)定第一反應(yīng)是瘋狂調(diào)Halcon算子參數(shù)但真正的問題往往是打光就不對。我舉幾個實際例子。檢測金屬表面劃痕如果用普通的環(huán)形LED照明劃痕方向不固定反光時有時無。后面改成多角度低角度光源讓結(jié)構(gòu)光從側(cè)面掠過表面劃痕散射光明顯增強黑白對比一下就出來了。檢測凸點或者顆粒用暗場照明背景壓暗顆粒邊緣散射光變成亮點閾值分割極其輕松。所以在這套源碼里我特別強調(diào)一個原則**算法的任務(wù)是穩(wěn)定提取特征不是替光源擦屁股。**拿到圖像先不要急著寫代碼先調(diào)光源角度、亮度、相機曝光讓缺陷和非缺陷區(qū)域的灰度差值保持在比較明顯的水平上。圖像質(zhì)量不好后面一切算子都是白搭。3.2 缺陷分割不同缺陷對應(yīng)不同的算子套路在Halcon里做缺陷檢測總的核心思想是“把缺陷區(qū)域從背景中剝離出來”。針對不同缺陷類型我總結(jié)了四種最實用的套路劃痕與淺色臟污適合用局部動態(tài)閾值dyn_threshold。先對原圖做均值濾波或高斯平滑得到一張背景估計圖再用原圖和背景圖做差超過動態(tài)閾值的像素就是疑似缺陷。HObject smoothed, diffRegion; MeanImage(image, smoothed, 21, 21); DynThreshold(image, smoothed, diffRegion, 5, dark);凸點凹點用中值濾波差分。中值濾波對邊緣保持比較好原圖減掉中值結(jié)果后非邊緣區(qū)域的微小灰度突變會被放大凸點凹點直接暴露。規(guī)則紋理表面的異常頻域濾波更有效。先做FFT用濾波器把周期性紋理的頻段抑制掉再反變換回來紋理變成均勻背景l(fā)ocal defect才顯露出來。這個比空域卷積更高效。大面積臟污或整體灰階不均先做背景校正??梢杂靡粋€大尺度的高斯濾波作為背景估計然后原圖像素灰度除以背景灰度再乘一個標準值把光照不均的影響抹平最后閾值分割。分割完了通常還要做一輪形態(tài)學清理。connection把連通域打散opening_circle去掉細小的毛刺噪聲closing_circle填補缺陷內(nèi)部的小孔洞。3.3 特征篩選壓誤檢的關(guān)鍵一步分割出來的區(qū)域并不全是缺陷。金屬反光、灰塵、正常結(jié)構(gòu)邊緣、螺紋輪廓這些都有可能被分割出來。這時候就要用select_shape、select_gray這類特征篩選算子。我個人經(jīng)驗里最有效的幾個特征組合是面積area太小的面積基本是噪點可以過濾寬高比width/height真正的劃痕長寬比很高灰塵接近圓形圓形度circularity污點通常較圓劃痕通常細長灰度均值和標準差區(qū)域亮度與原圖對應(yīng)位置的灰階差異可以區(qū)分真實缺陷和反光假點。舉個例子一段疑似劃痕區(qū)域面積只有5個像素寬高比卻很大這時候不能直接判NG否則正常的微小氧化點都會報警。我會再加一條約束區(qū)域的灰度均值要比周圍背景暗多少以上或者劃痕的長度必須超過某個像素值。把這些規(guī)則組合起來誤檢率能壓下來一個數(shù)量級。HObject selected; SelectShape(connected, selected, area, and, 20, 99999); SelectShape(selected, selected, width, and, 3, 99999); SelectShape(selected, selected, height, and, 3, 99999); SelectGray(selected, selected, image, mean, and, 0, 128);調(diào)參這件事我不建議對著實時視頻盲調(diào)而是先收集一批有代表性的樣本圖比如100張正常品和50張已知NG品離線跑一遍算法把所有候選區(qū)域的面積、寬高比、灰度均值統(tǒng)計成一張表再根據(jù)分布去定閾值。這樣做出來的參數(shù)可解釋、可復(fù)現(xiàn)而不是“差不多調(diào)到不誤報為止”。4. VS2015與Qt5.9工程里的坑從環(huán)境變量到內(nèi)存釋放4.1 環(huán)境變量與“HALCONROOT找不到”的排查鏈路這套源碼交付后最容易出現(xiàn)的第一類報錯是啟動時彈窗找不到hdevengine.dll或找不到halcon.dll或者“無法定位程序輸入點”。排查順序不要亂。先確認開發(fā)機能正常跑再拿到工控機或者同事的機器上。常見原因沒有安裝Halcon runtime或者PATH里沒有bin目錄halconcpp.dll存在但依賴的hdevengine.dll不在同目錄系統(tǒng)里同時裝了新老HalconPATH順序被改壞程序加載到另一個版本的DLL。解決方式把Halcon的bin目錄顯式加到PATH并且確認HALCONROOT指向正確版本。這類問題在視覺項目里出現(xiàn)頻率非常高我甚至見過有同事把整個Halcon的lib文件夾拷到exe目錄以此解決DLL依賴雖然丑但當年確實能救急。反倒是VS2015安裝包丟失或者損壞這類問題多數(shù)情況下是Windows Installer緩存C:\ProgramData\Package Cache被清理過導(dǎo)致的碰到就建議直接修復(fù)安裝VS2015并保證磁盤空間充足離線ISO用官方鏡像。4.2 Qt VS Tools 與 moc、uic 生成鏈問題VS2015下用Qt開發(fā)需要裝Qt VS Tools插件。這個插件負責調(diào)用moc元對象編譯器、uic界面編譯器、rcc資源編譯器。實際使用中最常見的坑是新增了一個類里面寫了Q_OBJECT但moc沒有自動重新生成編譯報“未定義的虛表”或者“無法解析的外部符號”改了UI文件界面不生效因為uic沒有重新運行Debug和Release混用鏈接報一堆LNK2038之類。我的經(jīng)驗是每次從Git上拉完代碼先做一次“全部清理”然后在Qt VS Tools菜單里強制重新運行moc。另外一個被忽視的點是字符集Halcon20的HTuple默認寬字符處理Qt默認UTF-8如果項目設(shè)置了多字節(jié)字符集混合使用時容易在字符串傳遞上出亂碼和截斷建議在工程屬性里統(tǒng)一使用Unicode字符集。4.3 圖像對象、內(nèi)存與相機釋放的順序敏感多相機程序的崩潰重災(zāi)區(qū)十個里面有八個跟釋放順序有關(guān)。我整理過一條明確的釋放原則先停采集線程等待線程join退出再釋放任務(wù)隊列里所有未處理的HImage最后再調(diào)用CloseFramegrabber關(guān)閉相機。如果先關(guān)了相機句柄采集線程還在下一次GrabImageAsync里程序就直接訪問沖突。如果是用了廠商SDK順序變成先停止采集回調(diào)再釋放圖像buffer再卸載SDK庫最后關(guān)UI窗口。另外Halcon的HObject是引用計數(shù)類型多線程共享同一個HObject時如果一邊讀取一邊析構(gòu)會觸發(fā)計數(shù)錯亂。所以我在源碼里有一條硬性規(guī)定檢測線程拿到的是圖像的一個獨立拷貝或者檢測完成后立刻把對象回收絕不跨線程共享同一個HObject實例。還有一個小坑某些工控機顯卡驅(qū)動對Qt5.9的渲染支持不完整圖像顯示控件頻繁刷新會閃爍這個不是Halcon的問題解決辦法是改用QPixmap離線繪制后一次性貼圖不要在paintEvent里實時做格式轉(zhuǎn)換。5. 性能優(yōu)化與產(chǎn)線穩(wěn)定性斷線重連和誤檢閉環(huán)5.1 相機斷線重連產(chǎn)線穩(wěn)定性最容易翻車的點一套外觀檢測設(shè)備算法再準確相機只要掉線一次整個工位就停擺。GigE相機最怕網(wǎng)線松動、工控機網(wǎng)卡休眠、供電波動。為此我在源碼里加了三重保障采集線程內(nèi)對每次抓圖設(shè)置超時超過5秒沒有新幀就判定斷線斷線后自動進入重連流程重新OpenFramegrabber最多重試10次每次間隔1秒界面狀態(tài)欄實時顯示相機連接狀態(tài)斷開標紅恢復(fù)標綠。重連時有個細節(jié)有些相機在重連前必須調(diào)用一下OpenFramegrabber中的reset參數(shù)或者先釋放舊句柄再重新獲取直接復(fù)用句柄多半會一直失敗。為了保證UI在重連期間不卡死重連邏輯放在獨立線程里跑彈窗提示放主界面。這套機制上線之后現(xiàn)場報障率明顯下降。5.2 誤檢統(tǒng)計與調(diào)參閉環(huán)用數(shù)據(jù)代替感覺我在做這套源碼時給程序內(nèi)置了一個“離線回放模式”。現(xiàn)場采集的圖像全部按日期、相機ID、產(chǎn)品型號分類保存然后可以在調(diào)試界面上回放任意一批圖邊調(diào)整算法參數(shù)邊看效果。這里面最核心的方法是“NG留樣”。每張判NG的圖強制保存原圖、預(yù)處理結(jié)果圖、缺陷分割圖這樣積累一段時間后可以回放分析有哪些NG是真正的缺陷有哪些是誤檢。我經(jīng)常對同事說誤檢不是靠智商消滅的是靠樣本量消滅的。你手里的NG樣本越多參數(shù)就能調(diào)得越干凈。同時OK品也要抽一部分保存專門用來驗證“調(diào)完參數(shù)后是否把正常件誤殺了”。這兩類樣本要分開目錄管理每次算法改動都跑一遍全量回放對比誤檢率和漏檢率全部通過才更新到產(chǎn)線版本。5.3 性能分析先量化再談優(yōu)化多相機系統(tǒng)經(jīng)常出現(xiàn)“CPU占用高但不知道卡在哪”的情況。我習慣用QElapsedTimer給每一段處理計時采集耗時GrabImageAsync返回時間算法耗時從HImage傳入到缺陷結(jié)果輸出結(jié)果存儲耗時寫文件、寫數(shù)據(jù)庫UI刷新耗時。把四個數(shù)據(jù)打到同一個日志文件里一跑便知瓶頸在哪。如果算法耗時長優(yōu)先看是否用多線程并行算子Halcon里可以通過set_system(thread_num, n)開啟部分算子的內(nèi)置并行也可以把多相機檢測任務(wù)分到線程池每個相機一個檢測線程充分利用多核。我有個很深的體會真正拖慢系統(tǒng)的往往是存儲環(huán)節(jié)。如果每張圖都同步寫高清原圖到機械硬盤一個產(chǎn)品多幾個面整條線速度就被IO拖死了。正確做法是圖像保存全部走異步隊列NG圖必存、OK圖抽存檢測線程絕不等磁盤寫完成再繼續(xù)。6. 部署收尾從開發(fā)機到工控機打包清單與運行環(huán)境6.1 Qt打包與Halcon運行時合并到了交付階段最怕的就是開發(fā)機上能跑拷到工控機上各種缺DLL。Qt這邊有現(xiàn)成工具windeployqt --release --compiler-runtime app.exe這條命令會把Qt5.9運行庫、VS2015的C運行庫都收集到exe同目錄。注意一定要加--compiler-runtime不然目標機器上沒裝VS2015 Redistributable的話照樣報缺少VCRUNTIME140.dll。Halcon這邊則要手動拷貝runtime最低限度包括halcon.dllhalconcpp.dllhdevengine.dll對應(yīng)的runtime license文件如果還用了Halcon的擴展或第三方庫一并拷入。不要為了省事把整個Halcon安裝目錄復(fù)制過去授權(quán)容易出問題文件又多又雜。另外license文件要放到Halcon的license目錄下或者在代碼里顯式指定路徑這個不配好工控機上打開相機就會報license錯誤。之前有同事從WinForm項目轉(zhuǎn)過來問Qt怎么打包安裝程序。其實思路是一樣的WinForm可以用安裝項目或InstallShieldQt照樣可以用Inno Setup寫腳本把整個exe目錄打成Setup包。差別只是Qt多了一步windeployqt其他沒有本質(zhì)區(qū)別。6.2 工控機運行環(huán)境標準化產(chǎn)線工控機的環(huán)境一定要標準化否則同一個exe在這個工廠能跑到另一個工廠就癱瘓。我在交付時固定了一份運行環(huán)境清單操作系統(tǒng)Win10企業(yè)版LTSC或者Win7 SP1前提是相機SDK還支持關(guān)閉Windows自動更新防止半夜自動重啟把產(chǎn)線搞停電源設(shè)置為高性能模式關(guān)閉屏幕睡眠和硬盤休眠防火墻對相機網(wǎng)卡網(wǎng)段放行或直接關(guān)閉公網(wǎng)連接相機網(wǎng)卡禁用節(jié)能模式這個我踩過網(wǎng)卡一節(jié)能GigE相機掉幀掉得懷疑人生。這些步驟看著基礎(chǔ)但每一項都是在產(chǎn)線上真金白銀換來的經(jīng)驗。機器視覺設(shè)備是7x24小時跑的任何“意外”其實都是必然提前在環(huán)境層面堵死比事后救火省心得多。6.3 源碼與參數(shù)管理的一點體會最后說回標題里的“多個相機缺陷檢測源碼”本身。這套源碼我維護了大半年最大的感觸是多相機系統(tǒng)的代碼本身不復(fù)雜復(fù)雜的是參數(shù)、配置和依賴的版本管理。我的做法是所有相機參數(shù)、檢測參數(shù)、產(chǎn)品型號配置全部放JSON獨立文件絕不混進二進制每個版本的Halcon runtime、相機SDK、Qt庫版本記錄在README里鎖死依賴Git分支按產(chǎn)品型號區(qū)分改參數(shù)直接改配置文件不重新編譯exe。這套實踐下來現(xiàn)場調(diào)試的效率高了不少。很多時候客戶反饋問題遠程要一份配置文件就能復(fù)現(xiàn)不用反復(fù)傳exe。如果你也要在產(chǎn)線上搭一套多相機缺陷檢測系統(tǒng)我的建議是別糾結(jié)版本新舊先把環(huán)境變量、相機釋放順序、打包依賴這三件事用紙列清楚能少熬很多個夜。VS2015、Qt5.9和Halcon20這個組合在把多個相機缺陷檢測源碼從開發(fā)機遷到工控機這件事上確實是我試過的搭配里最省心的一版。