用正運動控制卡:從DLL封裝到插補運動實戰(zhàn))
1. 為什么是運動控制卡而不是又一臺PLC很多人拿到一個自動化設(shè)備的活第一反應(yīng)就是PLC。這很自然PLC在邏輯控制、順序控制、環(huán)境適應(yīng)性上的優(yōu)勢確實明顯而且電氣工程師普遍熟悉。但一旦涉及多軸聯(lián)動高速插補軌跡規(guī)劃這些詞PLC的局限就暴露出來了首先高端PLC的定位模塊或運動控制模塊價格不便宜而且大多是封閉生態(tài)編程風格和通用上位機開發(fā)完全是兩套邏輯其次PLC的軌跡規(guī)劃能力通常比較弱圓弧插補、電子凸輪、連續(xù)軌跡前瞻這些功能很多中低端PLC做起來非常別扭。我的習慣是設(shè)備里如果只有氣缸、電機起停、簡單的兩三個伺服定位那PLC完全夠了一旦出現(xiàn)四軸以上、或者要求連續(xù)軌跡加工、或者需要和視覺系統(tǒng)高頻交互的場景我就會考慮上位機運動控制卡這套架構(gòu)。以正運動控制卡為例它本質(zhì)上是一張PCI或PCIe接口的板卡板載高性能DSP或ARM處理器專門負責實時性要求極高的插補運算、位置閉環(huán)、IO掃描。而上位機也就是工控機里的LabVIEW程序只負責人機交互、邏輯調(diào)度和數(shù)據(jù)處理。這樣分工的好處很明顯實時任務(wù)由控制卡獨立完成不占用操作系統(tǒng)資源Windows系統(tǒng)哪怕偶爾卡頓軸的運動軌跡也不會亂。還有一個容易被忽略的點。正運動控制卡的指令集是Basic語法也就是大家常說的運動控制器Basic指令它提供了一種在控制器里直接編寫運動腳本的能力。這意味著有些對實時性要求極高的邏輯可以直接下載到控制卡里離線執(zhí)行完全脫離上位機。這個特性在做一些需要脫機運行的設(shè)備時非常實用。2. LabVIEW調(diào)用控制卡的三條典型路線正運動控制卡的官方開發(fā)包里有DLL動態(tài)庫這是Windows環(huán)境下最直接的調(diào)用方式也是我最初上手時的路線。LabVIEW通過調(diào)用庫函數(shù)節(jié)點Call Library Function Node簡稱CLFN可以直接加載DLL里的導出函數(shù)實現(xiàn)指令下發(fā)。這個過程并不復雜但有幾個關(guān)鍵細節(jié)很容易踩坑后面我會專門展開。2.1 三種通信方式的取舍通信方式實時性開發(fā)效率適用場景DLL函數(shù)調(diào)用高μs級中需要封裝CLFN絕大多數(shù)通用場合網(wǎng)絡(luò)通訊TCP/Modbus中ms級高用現(xiàn)成TCP節(jié)點遠程監(jiān)控、跨工位數(shù)據(jù)采集控制器離線腳本最高由控制卡獨立運行低需學Basic指令脫機運行、高速插補剛開始做LabVIEW上位機的朋友我建議優(yōu)先走DLL路線因為這是官方支持和測試最充分的路徑后續(xù)做二次開發(fā)、功能升級也都圍繞這套DLL展開。網(wǎng)絡(luò)通訊適合做遠程看板、數(shù)據(jù)采集這類非實時應(yīng)用。離線腳本屬于進階玩法等DLL路線跑通后再研究也不遲。2.2 CLFN節(jié)點配置時最容易錯的細節(jié)調(diào)用庫函數(shù)節(jié)點的配置界面看起來選項很多但真正需要關(guān)心的就幾個函數(shù)名、調(diào)用約定、參數(shù)類型和返回值。正運動的DLL大部分函數(shù)是__stdcall調(diào)用約定返回值是一個表示指令狀態(tài)的整數(shù)通常0表示成功非0表示錯誤碼參數(shù)則根據(jù)具體指令不同而變化。我第一次封裝時犯過的錯是把所有的參數(shù)都設(shè)成了I32有符號32位整數(shù)結(jié)果字符串類型的軸名稱參數(shù)傳進去全是亂碼。后來才意識到字符串參數(shù)在CLFN里需要用C String Pointer格式傳入。如果你的控制卡指令里有浮點參數(shù)比如速度值、坐標值還得在CLFN里明確設(shè)置成數(shù)值或者浮點格式否則精度會丟失。正常來說每封裝一個DLL函數(shù)在LabVIEW里會生成一個VI虛擬儀器。這個VI就是后續(xù)你在程序框圖中反復調(diào)用的基礎(chǔ)模塊建議把這類VI統(tǒng)一放到一個自定義控件庫或子面板里方便復用。3. 環(huán)境準備從驅(qū)動安裝到動態(tài)庫封裝這一節(jié)寫給第一次接觸這套方案的朋友跳過這里的代價往往是后面調(diào)試時浪費大量時間。3.1 驅(qū)動和開發(fā)包版本對齊正運動的驅(qū)動和控制卡固件版本、開發(fā)包版本之間存在對應(yīng)關(guān)系。很多人拿到一張新卡直接裝最新版驅(qū)動結(jié)果發(fā)現(xiàn)老設(shè)備上的程序跑不了。尤其注意如果你用的開發(fā)包是某個特定版本控制卡固件版本必須匹配。這里有個笨辦法但很有效安裝好驅(qū)動后用官方調(diào)試軟件連接控制卡先看固件版本號和DLL版本號是否一致再開始編程。這一步確認好了后面能省掉很多莫名其妙的坑。LabVIEW版本的選擇上我建議盡量用較新的穩(wěn)定版比如LabVIEW 2018及以后。因為新版對CLFN和事件結(jié)構(gòu)的支持更穩(wěn)定而且網(wǎng)上能搜到的封裝實例和工具箱也更多。注意一個細節(jié)控制卡的DLL有些是32位的如果你的LabVIEW是64位版本兩者會存在位數(shù)不匹配的問題。解決方案就是在64位系統(tǒng)上裝32位版本的LabVIEW或者向控制卡廠家確認是否有64位DLL。這一點非常關(guān)鍵。3.2 DLL封裝的標準流程在LabVIEW中封裝一個DLL函數(shù)標準流程如下在程序框圖中拖入一個調(diào)用庫函數(shù)節(jié)點雙擊打開配置窗口。在函數(shù)選項卡中選擇DLL路徑填入函數(shù)名選擇調(diào)用約定為__stdcall。在參數(shù)選項卡中逐個配置參數(shù)類型整數(shù)用I32浮點用單精度或雙精度字符串用C String Pointer。配置返回值為I32代表錯誤碼。保存為子VI輸入輸出建立連線板接線端。反復測試該子VI確保指令下發(fā)成功后再往上搭建業(yè)務(wù)邏輯。注意正運動控制卡的指令集里同一功能往往有指令參數(shù)和返回值指令兩種形式。例如設(shè)置脈沖當量既可以通過一個帶參數(shù)的指令完成也可以通過查詢類指令獲取當前值。封裝時不要把兩種形式搞混建議每個函數(shù)獨立封裝成獨立VI命名清晰方便后面查閱。如果覺得手動封裝太繁瑣可以用官方提供的封裝工具或參考例程。正運動的開發(fā)包里通常自帶LabVIEW例程找到后直接拿來改比自己從頭封裝效率高得多。但還是建議先手動封裝一兩遍理解底層原理后面調(diào)試才不至于抓瞎。4. 運動邏輯是怎么跑起來的核心指令與狀態(tài)流轉(zhuǎn)封裝好基礎(chǔ)VI后真正的運動邏輯就開始了。很多新手在這里容易懵控制卡的指令那么多到底哪些是必用的4.1 一套最小可用的指令集以我的經(jīng)驗LabVIEW上位機控制正運動卡核心指令可以精簡到下面幾類連接與初始化打開控制卡如ZAux_Open、復位如ZAux_Reset。程序啟動時先執(zhí)行這兩步確??刂瓶ㄌ幱谝阎獱顟B(tài)。參數(shù)配置設(shè)置脈沖當量、加速度、減速度、速度、回零方向、軟限位等。這類指令在設(shè)備啟動后、運動開始前統(tǒng)一配置。點位運動單軸絕對運動MOVE、單軸相對運動MOVED、多軸直線插補MOVE或LINE類指令。這是最常用的指令組完成90%的定位需求?;亓悴僮骰卦cHOME或機械回零不同控制卡命名有差異但功能類似。狀態(tài)查詢讀取當前軸位置、運動狀態(tài)運動中/停止、IO狀態(tài)、報警狀態(tài)。這是上位機做狀態(tài)刷新和邏輯判斷的基礎(chǔ)。下面是一段偽代碼風格的邏輯示意展示LabVIEW程序框圖中如何組織一次完整的單軸定位// 偽代碼演示邏輯流程 1. 初始化控制卡 2. 設(shè)置軸0參數(shù)速度1000加速度5000減速度5000 3. 執(zhí)行回零 4. 等待回零完成循環(huán)查詢軸狀態(tài)直到停止 5. 下發(fā)絕對定位指令移動到位置100000 6. 等待運動完成 7. 讀取當前位置核對是否到位對應(yīng)到LabVIEW程序框圖其實就是幾個子VI的串聯(lián)加上While循環(huán)做狀態(tài)等待。這里的核心技巧是等待運動完成的循環(huán)一定要加延時典型值10~20ms避免CPU空閑占用過高同時保證狀態(tài)查詢的穩(wěn)定性。4.2 回零和限位的優(yōu)先級問題回零是設(shè)備調(diào)試的第一步也是最容易出問題的地方。正運動控制卡支持多種回零方式常見的是找原點開關(guān)找Z相的組合方式。常規(guī)邏輯是先以較快的速度朝回零方向運動碰到原點開關(guān)后減速然后低速找編碼器Z相脈沖最后停在固定位置。這個過程中必須把限位信號的處理優(yōu)先級放在所有運動邏輯的最前面??刂瓶ǖ南尬惠斎胍坏┯|發(fā)軸必須立即停止。這是硬件級的安全保障不能指望上位機軟件來處理——萬一Windows卡死了軟件壓根反應(yīng)不過來。所以配置時一定要確保硬限位信號接到了控制卡的專用限位輸入口而不僅僅是在LabVIEW里做了軟限位判斷。軟限位只負責正常情況下防呆硬限位才是保護設(shè)備的最后一道防線。4.3 狀態(tài)機的設(shè)計從簡單到可擴展熱搜詞里有一條c#正運動運動控制卡簡單狀態(tài)機實現(xiàn)方式可見狀態(tài)機是繞不開的話題。在LabVIEW里做狀態(tài)機常用的模式是事件結(jié)構(gòu)狀態(tài)枚舉條件結(jié)構(gòu)這是一套比較經(jīng)典的組合。我一般會這樣組織狀態(tài)機狀態(tài)狀態(tài)碼動作IDLE0等待用戶指令或觸發(fā)信號HOMING1執(zhí)行回零流程狀態(tài)輪詢READY2回零完成等待運動指令MOVING3正在執(zhí)行運動實時刷新位置PAUSED4暫停運動緊急停止后ERROR5碰到限位、報警、超時等異常處理狀態(tài)機的好處是邏輯清晰、擴展方便。比如后續(xù)增加連續(xù)軌跡加工狀態(tài)只需要在枚舉里加一個狀態(tài)然后在條件結(jié)構(gòu)中增加對應(yīng)分支即可。相比用一堆布爾量加大量條件判斷的寫法狀態(tài)機讓程序結(jié)構(gòu)一目了然調(diào)試和排查問題的難度大幅降低。LabVIEW里實現(xiàn)狀態(tài)機的做法是在While循環(huán)里放一個移位寄存器保存當前狀態(tài)循環(huán)體內(nèi)用條件結(jié)構(gòu)根據(jù)當前狀態(tài)執(zhí)行對應(yīng)分支并在分支結(jié)束前將下一狀態(tài)寫入移位寄存器。這樣循環(huán)每跑一次程序就從一個狀態(tài)流轉(zhuǎn)到下一個狀態(tài)清晰且穩(wěn)定。這里分享一個心得不要把狀態(tài)判斷寫得過于復雜。狀態(tài)機的核心價值在于當前做什么是明確的接下來做什么是可預(yù)期的。如果狀態(tài)分支里的條件太多可以考慮拆分子狀態(tài)機讓每個狀態(tài)只負責一件具體的事。5. 崩潰現(xiàn)場三次典型踩坑記錄5.1 回零時軸往反方向猛跑第一次調(diào)試時明明配置了回零方向軸卻直接沖向了和預(yù)期相反的方向差點撞上機械結(jié)構(gòu)。排查了很久最后發(fā)現(xiàn)是參數(shù)設(shè)置的問題控制卡的方向參數(shù)是二進制數(shù)而我在LabVIEW里把方向值配置成了相反的那個數(shù)。這件事給我的啟發(fā)是回零調(diào)試時務(wù)必先斷開電機的使能用手轉(zhuǎn)動電機來觀察編碼器方向是否和軟件配置一致確認無誤后再做低速回零然后才逐步提高速度。更穩(wěn)妥的做法是調(diào)試階段在LabVIEW里加一個總使能開關(guān)在程序界面做明顯的急停按鈕方便隨時切斷動力。5.2 CLFN參數(shù)類型不匹配導致運動指令失效封裝MOVE指令時位置、速度這些參數(shù)都是浮點數(shù)但我一開始全部按I32配置了。結(jié)果就是指令下發(fā)后控制卡一直返回錯誤代碼軸紋絲不動。查了好久才意識到是DLL函數(shù)的參數(shù)類型配置錯了。這個問題的排查思路可以這樣當指令返回錯誤碼時先去控制卡的錯誤碼對照表里查看具體含義。正運動的錯誤碼體系比較完整每個碼都對應(yīng)明確的錯誤原因。對照后發(fā)現(xiàn)錯誤碼指示的參數(shù)類型不合法再回頭檢查CLFN配置問題很快就定位了。5.3 32位DLL與64位LabVIEW的死亡組合有一次在64位系統(tǒng)上裝了64位LabVIEW結(jié)果所有DLL函數(shù)調(diào)用都返回內(nèi)存訪問沖突之類的錯誤。研究一番后發(fā)現(xiàn)正運動控制卡的DLL在當時的版本只提供32位版本64位LabVIEW在加載32位DLL時會出現(xiàn)兼容性問題。后來折中的方案是安裝32位版LabVIEW重新封裝DLL一切恢復正常。所以如果你準備用正運動控制卡配合LabVIEW開發(fā)安裝軟件前最好先確認DLL位數(shù)再決定LabVIEW版本位數(shù)。這種問題的排查成本很高提前避免是最好。6. 插補與連續(xù)軌跡脫離單軸運動的進階排版前面的內(nèi)容主要圍繞點位運動展開。點位運動的特點是起點到終點之間只關(guān)注最終位置中間軌跡不關(guān)心。但很多設(shè)備的需求遠不止這個切割機要走圓弧、點膠機要連續(xù)走S型曲線、視覺引導要把相機給出的坐標轉(zhuǎn)換成多軸的連續(xù)路徑。這些都需要用到插補能力。6.1 直線插補和圓弧插補的調(diào)用邏輯正運動控制卡支持多軸直線插補和圓弧插補。調(diào)用邏輯其實很直接下發(fā)一條插補指令指定參與插補的軸號、目標坐標、速度等參數(shù)即可。以兩軸直線插補為例// 偽代碼 直線插補(軸0, 軸1, 目標X, 目標Y, 速度, 加速度)控制卡內(nèi)部會把這條指令解析為兩軸聯(lián)動的位置數(shù)據(jù)按插補周期實時輸出脈沖。整個過程對LabVIEW程序來說是透明的上位機只需要等待插補完成狀態(tài)即可。圓弧插補類似只是參數(shù)變成了圓心坐標、半徑、方向等。關(guān)鍵是理解一點插補運算是在控制卡內(nèi)部完成的上位機只負責下發(fā)參數(shù)。這也是運動控制卡區(qū)別于普通脈沖輸出模塊的核心能力。6.2 連續(xù)軌跡的前瞻機制連續(xù)軌跡也叫小線段連續(xù)加工是更進階的功能。設(shè)備要加工的輪廓通常是一堆密集的小線段如果每段都等停止后再走下一段效率極低。連續(xù)軌跡功能就是讓控制卡在到達線段終點之前就開始規(guī)劃下一段運動實現(xiàn)平滑過渡。正運動控制卡的連續(xù)軌跡通常需要將多個目標點預(yù)先寫入緩沖區(qū)然后依次執(zhí)行。LabVIEW端通過一個循環(huán)下發(fā)點列集合把當前的位置點不斷追加到控制卡的軌跡緩沖區(qū)。這段邏輯要特別注意緩沖區(qū)的深度管理避免緩沖區(qū)被填滿后程序還在盲寫。我的建議是連續(xù)軌跡的調(diào)試要分步驗證先用10個點以內(nèi)的簡單輪廓跑通確認軌跡和預(yù)期一致再逐漸增加點數(shù)和速度。一開始就上幾千個點的復雜輪廓出問題時很難定位是哪個坐標導致的路徑偏差。6.3 運動狀態(tài)刷新避免盲飛插補和連續(xù)軌跡執(zhí)行期間上位機的狀態(tài)刷新依然要做。通常用一個定時循環(huán)比如50ms周期不斷讀取當前坐標和運動狀態(tài)顯示在界面上。這里有一個細節(jié)讀取坐標指令比較耗時間不建議在程序的每個循環(huán)里都讀所有軸。可以做一個需要時讀取的設(shè)計比如只在界面刷新周期或者運動完成時觸發(fā)坐標讀取降低總線的通信壓力。7. 上位機架構(gòu)的穩(wěn)定性交互、看門狗與異常恢復運動控制應(yīng)用里上位機程序的穩(wěn)定性比功能性更重要。一套設(shè)備運轉(zhuǎn)起來上位機死機或卡頓的后果輕則產(chǎn)品報廢重則安全事故。所以架構(gòu)設(shè)計上要提前考慮三層防護。7.1 用戶界面與運動邏輯的分離LabVIEW天然是多線程、并行運行的。寫上位機程序時我會刻意把UI線程、運動控制線程、狀態(tài)刷新線程分開。UI線程負責按鈕、數(shù)值輸入、顯示刷新運動控制線程負責指令下發(fā)和邏輯判斷狀態(tài)刷新線程單獨跑一個更慢的循環(huán)讀取IO和坐標。這樣做的好處是UI卡頓不會影響運動指令的執(zhí)行狀態(tài)刷新慢一點不會影響運動精度。如果所有功能混在一個循環(huán)里UI一旦卡住運動邏輯也跟著停擺這是絕對不能接受的。7.2 軟件看門狗控制卡本身有硬件看門狗但是檢測的是控制卡自己是否正常工作。上位機這邊需要做的是通信看門狗定時檢查上位機和控制卡之間的通信是否正常如果連續(xù)幾次通信超時或返回錯誤就認為鏈路異常執(zhí)行急停。LabVIEW里實現(xiàn)通信看門狗很簡單在狀態(tài)刷新線程里加一個錯誤計數(shù)器連續(xù)N次通信失敗就觸發(fā)急停。這個N不宜過小避免偶發(fā)通信抖動就誤動作。7.3 運動前的自檢流程我寫的每一套運動控制程序都會在啟動時做一個自檢流程初始化控制卡、讀取固件版本、檢查急停信號是否釋放、檢查伺服驅(qū)動器的使能和報警狀態(tài)、檢查各軸是否在軟限位范圍內(nèi)。自檢不通過程序就不進入運動狀態(tài)同時把未通過原因顯示在界面上。這個流程看似簡單但在實際設(shè)備上能攔截掉大量半路出問題的現(xiàn)場故障。因為你永遠無法保證設(shè)備每一次上電所有硬件都處于正常狀態(tài)。自檢就是把這些不確定性在上電階段就暴露出來。8. 我的習慣和主觀建議做運動控制這幾年有個深切的體會硬件選型和架構(gòu)設(shè)計決定了項目的上限而細節(jié)處理和調(diào)試經(jīng)驗決定了項目的下限。選型層面正運動控制卡是我用過性價比很高的方案。原因有幾個指令集簡潔且覆蓋面廣從基礎(chǔ)點位到復雜插補都有對應(yīng)的指令學習曲線不算陡。開發(fā)包支持多語言DLL接口標準化不管是LabVIEW、C#、Python都能方便調(diào)用??刂瓶ū旧愍毩⑦\行能力很強脫機腳本適合做一些嵌入式場景。知識生態(tài)相對開放網(wǎng)上能找到大量例程和討論少走很多彎路。細節(jié)層面我自己的習慣是所有程序里增加固定的數(shù)據(jù)記錄功能把關(guān)鍵指令和狀態(tài)變化寫入日志文件?,F(xiàn)場出問題時先翻日志定位大概時間點再針對性地排查效率高很多。運動參數(shù)速度、加速度、位置不在程序里硬編碼全部做成界面輸入項或配置文件。這樣現(xiàn)場調(diào)試時不需要改代碼直接改界面的數(shù)值就能試運行。哪怕只是單軸的簡單應(yīng)用我也建議按初始化—自檢—回零—運動—停止的標準流程來組織程序。流程規(guī)范了換項目、換機型時絕大部分邏輯可以直接復用。如果你是第一次用運動控制卡配合LabVIEW做上位機我的建議是先不要著急寫業(yè)務(wù)邏輯先把控制卡的指令集中最基礎(chǔ)的幾個功能跑通比如連接、回零、點位運動、讀取位置。把這條鏈路打通了再逐步往上加需求?;A(chǔ)鏈路不穩(wěn)定的情況下后續(xù)所有開發(fā)都會是在沙灘上蓋樓。最后再分享一個小技巧封裝DLL子VI時記得把所有與指令相關(guān)的參數(shù)都暴露出來作為輸入哪怕有些參數(shù)當前用不上。因為設(shè)備的運動需求經(jīng)常會變?nèi)f一后來需要調(diào)節(jié)某個參數(shù)直接在前面板改就行了不用重新連導線。采過幾次坑之后我深刻體會到前期多花一點時間做規(guī)范的封裝后期調(diào)試能節(jié)省的時間遠超過你預(yù)期。