:位置設(shè)定與顯示操作詳解)
簡介面向C#開發(fā)者和機(jī)器視覺入門者的一套C#與HALCON視覺檢測項(xiàng)目案例圍繞位置設(shè)定界面與顯示操作演示如何借助HALCON的.NET接口將圖像抓取、特征提取、模板匹配、幾何測量等能力集成到WPF桌面應(yīng)用中。項(xiàng)目從相機(jī)連接與實(shí)時取像到控件布局、參數(shù)輸入、事件驅(qū)動流程、數(shù)據(jù)綁定、多線程界面刷新和異常調(diào)試基本覆蓋了視覺檢測操作界面的完整開發(fā)鏈路。壓縮包共73個文件包含cs源碼、bmp圖像資源、resx與resources資源配置、xml與dat數(shù)據(jù)文件、exe和dll可執(zhí)行及運(yùn)行庫、sln工程與配置文件整體約3.92MB目錄結(jié)構(gòu)清晰便于按模塊對照研讀。已有546人學(xué)習(xí)下載尤其適合正在構(gòu)建視覺定位/顯示界面、希望上手C#與HALCON聯(lián)動開發(fā)的讀者。從界面設(shè)計到HALCON算子調(diào)用均有源碼級參考可幫助理解高級編程語言與專業(yè)視覺庫在實(shí)際項(xiàng)目中的結(jié)合方式。1. C# 與 Halcon 的視覺檢測項(xiàng)目位置設(shè)定界面和顯示操作要先于算法落地車間里換產(chǎn)品型號時操作工每天按得最多的不是算法參數(shù)頁而是位置設(shè)定界面。視覺檢測上位機(jī)項(xiàng)目里算法能不能檢出來要跑完整個流程才知道但操作工換產(chǎn)品時拉一拉檢測框、對準(zhǔn)一下基準(zhǔn)點(diǎn)、看一眼圖像上疊加的 ROI 和結(jié)果這個動作每調(diào)一次就要發(fā)生一次。標(biāo)題里的兩個詞落到工程上就是用 C# 把 Halcon 窗口嵌進(jìn)上位機(jī)給人提供“在圖像上示教位置、存配方、實(shí)時看結(jié)果”的能力。位置設(shè)定界面解決的是“換產(chǎn)品怎么快速改”矩形 ROI 拉一拉、基準(zhǔn)十字叉移一移、世界坐標(biāo)自動換算、配方一鍵保存。顯示操作解決的是“改了怎么看得見”圖像縮放平移、檢測結(jié)果疊加、OK/NG 反饋?zhàn)尨翱诶锏漠嬅婧彤a(chǎn)線現(xiàn)場對得上。兩者合在一起是 C# 上位機(jī)視覺項(xiàng)目的骨架后面的標(biāo)定、手眼映射、halcon 測量、缺陷檢測全都掛在這套骨架上跑。這篇按做視覺集成最常見的落地路徑來寫適合正在用 C# winform/wpf 搭上位機(jī)、準(zhǔn)備把 Halcon 集成進(jìn)來的開發(fā)也適合要設(shè)計示教流程的調(diào)試工程師。2. 位置設(shè)定的數(shù)據(jù)模型Halcon 像素坐標(biāo)、世界坐標(biāo)與配方序列化2.1 Halcon 的 (Row, Col) 坐標(biāo)系和界面上習(xí)慣的 (X, Y) 差在哪接觸 Halcon 的 C# 開發(fā)幾乎都會在同一個地方栽跟頭Halcon 圖像坐標(biāo)系里第一個坐標(biāo)是 Row行從上往下第二個是 Column列從左往右原點(diǎn)在左上角。而界面和機(jī)器人領(lǐng)域習(xí)慣的是 X 向右、Y 向上的笛卡爾坐標(biāo)。把 Halcon 返回的 row 當(dāng)成 Y、col 當(dāng)成 X方向是反的想恢復(fù)成日常習(xí)慣還要再把 Y 取負(fù)。位置設(shè)定界面如果在這個換算上出錯后續(xù)標(biāo)定和手眼映射會全部跟著偏。另外要區(qū)分“控件坐標(biāo)”和“圖像坐標(biāo)”。HSmartWindowControl 的鼠標(biāo)事件給的是控件像素位置一旦圖像被縮放或平移過兩個坐標(biāo)系就不是等比例關(guān)系。顯示操作里任何“鼠標(biāo)點(diǎn)哪里圖像對應(yīng)哪里”的需求都要先走一次坐標(biāo)系換算// 把控件上的鼠標(biāo)位置換算成當(dāng)前圖像上的 (row, col) HOperatorSet.ConvertCoordinates( hSmartWindowControl.HalconWindow, new HTuple(e.Y), new HTuple(e.X), window, out HTuple imgRow, out HTuple imgCol, image); // 狀態(tài)欄給操作員看的坐標(biāo)轉(zhuǎn)成笛卡爾習(xí)慣 // X 取 colY 取 row注意 Halcon 里 Y 軸向下 string tip $X {imgCol.D:F1}, Y {imgRow.D:F1};這段代碼的邏輯ConvertCoordinates 接受窗口坐標(biāo)from 寫 window、to 寫 image算子內(nèi)部會結(jié)合當(dāng)前 ImagePart 完成縮放和平移的反算輸入順序永遠(yuǎn)先 row 后 col。參數(shù)里 e.Y、e.X 是 HMouseEventArgs 的控件坐標(biāo)window 和 image 是最常用的一組坐標(biāo)系枚舉也可以換成 display 看顯示緩沖區(qū)的坐標(biāo)。換算結(jié)果和鼠標(biāo)位置差一截時優(yōu)先檢查 ImagePart 是否還是舊尺寸。2.2 一個“位置”記錄應(yīng)該存哪些字段位置設(shè)定界面里的一條記錄絕不是一對坐標(biāo)這么簡單。以平面定位加區(qū)域檢測的常見場景為例一條位置要同時回答三個問題檢測目標(biāo)在哪塊區(qū)域、基準(zhǔn)點(diǎn)在哪、機(jī)械手要去哪。因此數(shù)據(jù)結(jié)構(gòu)按三組職責(zé)設(shè)計直接對照采集、標(biāo)定、顯示三個階段的數(shù)據(jù)流public class VisionPosition { public string Name { get; set; } // 位置名如 Pos_A、吸嘴1 public string RecipeName { get; set; } // 所屬配方/產(chǎn)品型號 // 圖像坐標(biāo)Halcon 習(xí)慣 row 在前 public double Row { get; set; } public double Column { get; set; } // 世界坐標(biāo)平面標(biāo)定后的物理坐標(biāo)單位 mm public double WorldX { get; set; } public double WorldY { get; set; } public double Angle { get; set; } // 弧度系統(tǒng)內(nèi)統(tǒng)一用弧度 // ROI 幾何矩形用兩角點(diǎn)圓用中心半徑 public double RoiRow1 { get; set; } public double RoiCol1 { get; set; } public double RoiRow2 { get; set; } public double RoiCol2 { get; set; } public double RoiRadius { get; set; } public bool Enabled { get; set; } true; }字段拆開的理由是圖像坐標(biāo)負(fù)責(zé)“算法在哪找”世界坐標(biāo)負(fù)責(zé)“機(jī)器往哪動”ROI 負(fù)責(zé)“界面上畫什么形狀”。三者經(jīng)常聯(lián)動變化例如產(chǎn)品整體轉(zhuǎn)了 30 度ROI 跟著轉(zhuǎn)但基準(zhǔn)點(diǎn)相對治具的世界坐標(biāo)不變把三類字段放在同一個類里后續(xù)做表格綁定、配方切換、顯示高亮都會順手很多。2.2.1 為什么顯示層還要一份 Rectangle2顯示操作里常用 DispRectangle2 畫帶角度的框它接收的是中心行、中心列、角度、半高、半寬和 RoiRow1 這種兩角點(diǎn)表達(dá)不一樣。工程上的常見做法是存儲一律用兩角點(diǎn)示教最直觀渲染時用中心點(diǎn)和半高半寬重新生成 Rectangle2 參數(shù)。兩套表達(dá)之間換算很簡單但必須在數(shù)據(jù)邊界上統(tǒng)一成一種存儲格式避免界面上同時存在“存的是一套、畫的是另一套”的情況。2.3 配方文件的序列化方案JSON 為主Halcon 對象單獨(dú)落盤位置參數(shù)就是產(chǎn)品配方的核心。保存格式的選擇直接影響產(chǎn)線換型效率下面是三種常見方案的對比方案可讀性版本兼容中文支持適用場景JSON好可直接手改好缺字段給默認(rèn)值好UTF-8推薦主方案winform/wpf 直接解析XML中標(biāo)簽多好好客戶或 MES 指定接口Halcon 的 .tup / .mat差全是數(shù)值一般差只放標(biāo)定矩陣、模板句柄推薦組合是配方主體用 JSONHalcon 特有的數(shù)據(jù)HomMat2D 標(biāo)定矩陣、測量模板單獨(dú)導(dǎo)成 .mat 或 .tup 文件配方里只存路徑。HRegion、HImage、HMeasure 這類 Halcon 對象不能直接 JSON 序列化邊界上要轉(zhuǎn)成幾何數(shù)值或 HTuple這正是上一節(jié)把 VisionPosition 設(shè)計成純數(shù)值類的原因。// 配方保存把位置列表整份寫成 UTF-8 的 JSON public static void SavePositions(string path, ListVisionPosition list) { var opt new JsonSerializerOptions { WriteIndented true }; string json JsonSerializer.Serialize(list, opt); File.WriteAllText(path, json, Encoding.UTF8); } // 配方加載文件不存在或解析失敗都返回空列表 public static ListVisionPosition LoadPositions(string path) { if (!File.Exists(path)) return new ListVisionPosition(); try { string json File.ReadAllText(path, Encoding.UTF8); return JsonSerializer.DeserializeListVisionPosition(json) ?? new ListVisionPosition(); } catch (JsonException) { return new ListVisionPosition(); } }這兩段代碼的邏輯WriteIndented 讓配方文件可以直接交給非技術(shù)同事肉眼比對顯式傳 Encoding.UTF8 避免 Windows 默認(rèn)代碼頁把中文位置名讀成亂碼反序列化失敗返回空列表而不是往上拋異常是“界面不能因?yàn)橐粋€壞配方就起不來”的防御習(xí)慣。參數(shù)說明JsonSerializerOptions 來自 System.Text.JsonWriteIndented 控制是否格式化縮進(jìn)catch JsonException 只接解析錯誤File.Exists 擋掉文件缺失分支。3. 在 C# 工程里集成 Halcon 窗口從 HSmartWindowControl 到顯示操作3.1 控件選型HWindowControl 和 HSmartWindowControl 怎么選Halcon 的 .NET 接口里有兩個常用窗口控件。HWindowControl 是老牌的 WinForms 控件縮放、平移、重繪全部要自己接管HSmartWindowControl 是面向交互場景設(shè)計的控件內(nèi)置滾輪縮放、左鍵平移和自適應(yīng)顯示還提供 HMouseDown、HMouseMove、HMouseWheel 這一整套事件。WPF 工程對應(yīng)使用 HSmartWindowControlWPF事件模型和 WinForms 版保持一致。能力HWindowControlHSmartWindowControl滾輪縮放手寫內(nèi)置圖像平移手寫內(nèi)置鼠標(biāo)事件有需自換算有坐標(biāo)換算配套繪圖調(diào)用Draw* 阻塞式Draw* 阻塞式可配合事件自繪顯示自適應(yīng)手寫 SetPartSetFullImagePart 一步到位新項(xiàng)目直接選 HSmartWindowControl只有老項(xiàng)目遷移才需要繼續(xù)維護(hù) HWindowControl。理由很直接視覺檢測調(diào)試時放大看缺陷邊緣、平移看 ROI 與工件相對位置是高頻操作滾輪以鼠標(biāo)為中心縮放自己寫有一定的 ImagePart 換算量不值得重復(fù)造。3.2 最小可運(yùn)行加載圖片并用 HSmartWindowControl 顯示把 halcondotnet.dll 加到工程引用、工具箱里拖入 HSmartWindowControl 之后加載一張本地圖片顯示出來的代碼只需四行private void btnLoadImage_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() ! DialogResult.OK) return; _currentImage?.Dispose(); _currentImage new HImage(openFileDialog1.FileName); // 讀入圖像 // 把 ImagePart 設(shè)為整幅圖窗口按控件尺寸自適應(yīng)縮放 hSmartWindowControl.SetFullImagePart(_currentImage); hSmartWindowControl.HalconWindow.DispObj(_currentImage); RedrawOverlays(); // 把已有的 ROI、十字線重新疊加上去 }SetFullImagePart 的作用是讓窗口一次性看到整幅圖像隨后滾輪縮放、拖拽平移都以這幅圖像為基準(zhǔn)DispObj 把 HImage 內(nèi)部持有的 HObject 畫到窗口上。如果是相機(jī)取流把這四行里的 new HImage 換成相機(jī) Grab 返回值即可。老版本 Halcon 沒有 SetFullImagePart 時等價寫法是手動 SetPart(0, 0, height-1, width-1)。3.2.1 ImagePart 是什么ImagePart 是 Halcon 窗口當(dāng)前可見的圖像區(qū)域本質(zhì)是四個邊界值 (row1, col1, row2, col2)??s放和平移改變的就是這個區(qū)域顯示操作里的坐標(biāo)換算也以它為基準(zhǔn)。HSmartWindowControl 把這塊封裝掉了但理解 ImagePart 仍然重要凡是“圖像上畫框、鼠標(biāo)點(diǎn)擊命中、窗口里截圖”這三類需求最終都要回到 ImagePart 的數(shù)值上。3.3 顯示操作里高頻出現(xiàn)的設(shè)置項(xiàng)顏色、線寬、十字線、文字位置設(shè)定界面上除了底圖還要畫 ROI、中心點(diǎn)、匹配框、OK/NG 文字。這些東西全部走同一套模式先用設(shè)置算子定義樣式再調(diào)用 Disp 系列算子畫上去HWindow hWin hSmartWindowControl.HalconWindow; // 畫一個矩形 ROI綠色邊框、線寬 2、只描邊不填充 hWin.SetColor(green); hWin.SetLineWidth(2); hWin.SetDraw(margin); // margin 描邊f(xié)ill 填充 hWin.DispRectangle2(pos.Row, pos.Column, pos.Angle, 60, 40); // 在基準(zhǔn)位置畫十字線 hWin.SetColor(red); hWin.SetLineWidth(1); hWin.DispCross(pos.Row, pos.Column, 25, pos.Angle);DispRectangle2 的參數(shù)順序是中心行、中心列、角度、半高、半寬角度為 0 時退化為普通矩形DispCross 的第 4 個參數(shù)是十字線偏角與位置角度保持一致畫出來的十字才跟著產(chǎn)品方向轉(zhuǎn)。這里的 pos 就是第 2 章 VisionPosition 的一條記錄。執(zhí)行順序上必須先 DispObj 底圖、再畫疊加內(nèi)容后畫的圖形始終蓋在圖像上方位置設(shè)定界面才能保證 ROI 永遠(yuǎn)清晰可見。中文文字是顯示操作里最典型的坑。Halcon 自帶的默認(rèn)字體不包含中文字形直接用 DispText 輸出“檢測結(jié)果: OK”會變成方框。常見做法是用 SetTfont 把字體指到系統(tǒng)已安裝的中文字體XLFD 是固定 14 段的字體描述串// 讓 DispText 支持中文指定系統(tǒng)中文字體字段用 * 通配 hWin.SetTfont(-*-Microsoft YaHei-Medium-R-Normal--14-*-*-*-*-*-*-*); hWin.DispText(檢測結(jié)果: OK, window, 15, 15, black, lime, box);提示DispText 的坐標(biāo)系統(tǒng)參數(shù)有兩檔window 是相對窗口定位縮放時文字不動image 是相對圖像定位文字跟隨圖像移動。位置設(shè)定界面的結(jié)果文字一般用 window。如果 SetTfont 的 XLFD 串在你們版本上一直報 invalid font最省事的替代方案是Halcon 窗口內(nèi)只顯示 OK、NG、數(shù)值這類 ASCII 內(nèi)容中文標(biāo)題放到窗體自身的 Label 或狀態(tài)欄里。很多量產(chǎn)項(xiàng)目的界面就是這么分的。3.4 窗體 Resize 后的重繪順序HSmartWindowControl 在窗體尺寸變化時會自動調(diào)整顯示區(qū)域但窗口緩沖區(qū)的重建會把上次畫上去的疊加圖形沖掉表現(xiàn)為“縮放窗口后 ROI 不見了”。處理方法是在 SizeChanged 里按固定順序重畫private void hSmartWindowControl_SizeChanged(object sender, EventArgs e) { if (_currentImage null) return; hSmartWindowControl.HalconWindow.DispObj(_currentImage); // 先底圖 RedrawOverlays(); // 后疊加 }順序和 3.3 里說的完全一致。RedrawOverlays 內(nèi)部遍歷位置列表逐個畫 ROI這個函數(shù)在位置列表增刪、選中項(xiàng)切換、檢測完成三個時機(jī)都要調(diào)用是整個顯示操作里復(fù)用率最高的入口值得單獨(dú)抽出來維護(hù)。4. 位置設(shè)定界面的交互實(shí)現(xiàn)畫 ROI、位置列表聯(lián)動與測量喂參4.1 畫 ROI 的兩種實(shí)現(xiàn)DrawRectangle1 阻塞式與鼠標(biāo)事件自繪示教 ROI 是位置設(shè)定界面的核心動作。Halcon 提供了 Draw 系列算子DrawRectangle1、DrawCircle、DrawPoint 都能在窗口上交互取數(shù)也有基于鼠標(biāo)事件的自繪方案。兩者對比如下方式代碼量實(shí)時預(yù)覽與縮放兼容適用場景DrawRectangle1 阻塞式少無窗口自動處理點(diǎn)按鈕后拉一個框的簡單示教鼠標(biāo)事件自繪多可做需自己換坐標(biāo)拖動過程要看測量值/尺寸阻塞式是入門最快的寫法鼠標(biāo)十字光標(biāo)由 Halcon 窗口自己處理用戶拖完矩形松手算子才返回private void btnTeachRoi_Click(object sender, EventArgs e) { HWindow hWin hSmartWindowControl.HalconWindow; HTuple r1, c1, r2, c2; // 在當(dāng)前顯示的圖像上拖一個矩形松手后返回兩角點(diǎn) hWin.DrawRectangle1(out r1, out c1, out r2, out c2); var pos _positions[_currentIndex]; pos.RoiRow1 r1.D; pos.RoiCol1 c1.D; pos.RoiRow2 r2.D; pos.RoiCol2 c2.D; RedrawOverlays(); }注意DrawRectangle1 這類 Draw 算子是阻塞調(diào)用期間窗體其他控件不可用。不要把它放到采集線程里執(zhí)行否則窗口停在等待鼠標(biāo)狀態(tài)取流直接卡死。缺點(diǎn)是繪制期間窗體其他按鈕全部不可用也沒法在拖動過程中實(shí)時看到測量結(jié)果。需要實(shí)時反饋的產(chǎn)品用鼠標(biāo)事件自繪private bool _drawing; private double _sRow, _sCol; // 拖動起點(diǎn)圖像坐標(biāo) private double _eRow, _eCol; // 拖動終點(diǎn)圖像坐標(biāo) private void hSmart_HMouseDown(object sender, HMouseEventArgs e) { if (_editMode ! EditMode.DrawRoi) return; _drawing true; // 窗口坐標(biāo)必須換成圖像坐標(biāo)否則縮放后畫歪 ConvertWindowToImage(e.X, e.Y, out _sRow, out _sCol); _eRow _sRow; _eCol _sCol; } private void hSmart_HMouseMove(object sender, HMouseEventArgs e) { if (!_drawing) return; HWindow hWin hSmartWindowControl.HalconWindow; // 第一步用白色粗線把上一次的框“擦掉” hWin.SetColor(white); hWin.SetLineWidth(5); hWin.DispRectangle1(_sRow, _sCol, _eRow, _eCol); // 第二步按最新鼠標(biāo)位置畫紅色新框 ConvertWindowToImage(e.X, e.Y, out _eRow, out _eCol); hWin.SetColor(red); hWin.SetLineWidth(2); hWin.DispRectangle1(_sRow, _sCol, _eRow, _eCol); } private void ConvertWindowToImage(int winX, int winY, out double imgRow, out double imgCol) { // Halcon 窗口坐標(biāo)行 Y列 X HOperatorSet.ConvertCoordinates(hSmartWindowControl.HalconWindow, new HTuple(winY), new HTuple(winX), window, out HTuple ir, out HTuple ic, image); imgRow ir.D; imgCol ic.D; }自繪方案的邏輯是“先擦后畫”Halcon 窗口沒有 XOR 模式用白色粗線重畫一次舊矩形把它蓋掉再畫新矩形鼠標(biāo)移動時視覺上就是跟手拖拽。參數(shù)說明_editMode 是自定義枚舉用來區(qū)分當(dāng)前是窗口平移模式還是畫框模式避免和 HSmartWindowControl 自帶的左鍵平移手勢沖突進(jìn)入畫框模式時把平移禁止掉退出時恢復(fù)這是工程上的常見約定。4.2 位置列表和圖像聯(lián)動選中一條記錄就高亮對應(yīng)框光能畫框還不夠位置設(shè)定界面要有“位置清單”。用 DataGridView 綁定位置列表選中行變化時觸發(fā)重繪把選中位置的框變成高亮紅色其余位置保持綠色private void dgvPositions_SelectionChanged(object sender, EventArgs e) { if (dgvPositions.CurrentRow?.DataBoundItem is not VisionPosition sel) return; RedrawOverlays(sel); } private void RedrawOverlays(VisionPosition highlight null) { HWindow hWin hSmartWindowControl.HalconWindow; if (_currentImage ! null) hWin.DispObj(_currentImage); // 先重畫底圖 foreach (var p in _positions) { bool isSel (p highlight); hWin.SetColor(isSel ? red : green); // 選中紅色高亮 hWin.SetLineWidth(isSel ? 3 : 1); hWin.DispRectangle2(p.Row, p.Column, p.Angle, 60, 40); hWin.DispCross(p.Row, p.Column, 25, p.Angle); } }這段代碼的邏輯SelectionChanged 里先從 CurrentRow 的 DataBoundItem 解出位置對象解不出就返回重繪函數(shù)每次都先刷底圖再遍歷全部位置選中項(xiàng)用紅色粗線、其他用綠色細(xì)線。注意 RedrawOverlays 是 3.4 里說的那個重繪入口這里的 highlight 參數(shù)讓同一函數(shù)同時服務(wù)列表聯(lián)動和檢測完成刷新。4.2.1 數(shù)據(jù)源用 BindingList 而不是 ListDataGridView 的 DataSource 直接綁 List 時新增、刪除、修改字段后表格不會自動刷新還要手動 ResetBindings。常見做法是綁 BindingListVisionPosition集合變化會自動通知表格刷新而 VisionPosition 里的屬性通知則用 INotifyPropertyChanged 實(shí)現(xiàn)。位置設(shè)定界面里的“新增一條、改個角度、刪一條”這些操作都建立在可感知的集合上。4.3 位置設(shè)定之后的下游把坐標(biāo)喂給測量算子和缺陷檢測位置設(shè)定不是終點(diǎn)設(shè)定好的坐標(biāo)要喂給 Halcon 的檢測算子。最典型的是把位置的角度和范圍交給一維測量工具測邊緣對的距離這也是 c# 上位機(jī)視覺項(xiàng)目里“位置”最有價值的用法// 用位置記錄里的中心點(diǎn)和角度創(chuàng)建測量對象 HMeasure mm new HMeasure(pos.Row, pos.Column, pos.Angle, 1.0, 30, all, all); HTuple rowEdge, colEdge, amp, dist; mm.MeasurePos(_currentImage, 1.0, true, all, all, out rowEdge, out colEdge, out amp, out dist); // 在界面上把測量到的邊緣點(diǎn)用十字畫出來操作員肉眼核對 HWindow hWin hSmartWindowControl.HalconWindow; for (int i 0; i rowEdge.Length; i) { hWin.SetColor(orange); hWin.DispCross(rowEdge.DArr[i], colEdge.DArr[i], 15, pos.Angle); }HMeasure 構(gòu)造參數(shù)依次是中心行、中心列、角度、平滑系數(shù)、邊緣閾值、邊緣選取方式和過渡方向測量矩形沿 pos.Angle 方向張開。MeasurePos 返回的 rowEdge、colEdge 是找到的邊緣點(diǎn)amp 是邊緣幅度dist 是邊緣對距離把邊緣點(diǎn)以十字線形式畫回圖像是顯示操作里最直觀的驗(yàn)證手段。同理缺陷檢測里把 ROI 區(qū)域傳給 Blob 分析或深度學(xué)習(xí)分類器之前區(qū)域的位置和角度同樣來自這里。這套“示教位置 - 存配方 - 啟動時載入 - 喂給算子 - 結(jié)果顯示”的閉環(huán)就是位置設(shè)定界面的完整職責(zé)。5. 循環(huán)數(shù)據(jù)采集下 UI 刷新卡頓排查與 Halcon 窗口內(nèi)容自檢5.1 循環(huán)數(shù)據(jù)采集下 UI 刷新卡頓限流 BeginInvoke 組合相機(jī)連續(xù)取流時在采集線程里直接調(diào) DispObj 是界面卡頓的最常見原因。Halcon 窗口只能在 UI 線程繪制跨線程調(diào)用輕則掉幀、重則報錯而每次都 BeginInvoke 不做限流高頻取流會把 UI 消息隊(duì)列塞滿表現(xiàn)為鼠標(biāo)拖不動、數(shù)據(jù)表格假死。標(biāo)準(zhǔn)做法是采集線程只管取幀UI 刷新單獨(dú)限流private readonly Stopwatch _uiClock new Stopwatch(); private bool _running; private HImage _latest; private void AcquisitionLoop() { _uiClock.Restart(); while (_running) { _latest _camera.GrabImage(); // 采集線程取幀 // 界面刷新限流40ms 一幀約 25fps if (_uiClock.ElapsedMilliseconds 40) { _uiClock.Restart(); BeginInvoke((Action)(() DisplayFrame(_latest))); } // 檢測算法也放這個線程和 UI 隔離 } } private void DisplayFrame(HImage frame) { hSmartWindowControl.HalconWindow.DispObj(frame); RedrawOverlays(); }BeginInvoke 是異步投遞采集線程不會因?yàn)榻缑胬L制慢而停頓限流讓界面刷新頻率固定不受采集幀率波動影響。40ms 這個值適合大多數(shù)產(chǎn)線節(jié)拍需要看高速運(yùn)動的物體時改為 16ms再快就沒有意義了人眼在 25fps 以上基本感知不到提升。還卡就查 RedrawOverlays 是不是每次都在 DispObj 整幅底圖這一步是顯示開銷的大頭。5.2 把 Halcon 窗口內(nèi)容導(dǎo)出 PNG遠(yuǎn)程定位顯示問題現(xiàn)場反饋“界面顯示不對”時截圖往往比口頭描述可靠。Halcon 的 DumpWindowImage 能把窗口當(dāng)前內(nèi)容連疊加圖形一起導(dǎo)出成圖片這個能力在遠(yuǎn)程調(diào)試工位尤其有用// 把 Halcon 窗口當(dāng)前顯示內(nèi)容含所有疊加導(dǎo)出為 PNG HOperatorSet.DumpWindowImage(out HImage dump, hSmartWindowControl.HalconWindow); dump.WriteImage(png, 0, D:\debug\window_snapshot.png); dump.Dispose();DumpWindowImage 導(dǎo)出的分辨率等于 ImagePart 對應(yīng)的圖像尺寸而不是控件像素尺寸如果導(dǎo)出的圖片和現(xiàn)場肉眼看的不一樣先確認(rèn)是不是 ImagePart 與控件尺寸不一致導(dǎo)致。把導(dǎo)出功能綁到快捷鍵或右鍵菜單上讓操作員就地生成現(xiàn)場畫面遠(yuǎn)程調(diào)試能少一半來回確認(rèn)。最后給一個低成本的自檢流程新位置設(shè)定界面做完可以照這個順序驗(yàn)收加載一張帶明顯角點(diǎn)的測試圖新增三個不同角度的位置并逐個畫 ROI按第 2 章的格式保存配方重啟程序再加載確認(rèn)所有框的位置、角度、十字線和高亮色都和保存前一致。這個流程同時覆蓋了坐標(biāo)換算、序列化、重繪三條鏈路視覺項(xiàng)目里最容易出問題的三個環(huán)節(jié)一次驗(yàn)完。本文還有配套的精品資源點(diǎn)擊獲取