ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LabVIEW与正运动控制卡:上位机开发实战指南

LabVIEW与正运动控制卡:上位机开发实战指南 作为一个在工控圈摸爬滚打了十来年的老工程师我这些年用过的上位机开发工具不少从VB6到C#再到LabVIEW都有涉猎。但说实话如果纯粹从“快速上手”和“调试直观”这两个维度来看LabVIEW在运动控制领域的优势依然是独一档的。特别是配合国产正运动控制卡这类硬件把NI的图形化编程和正运动强大的DLL库一结合确实能省下大量开发时间。今天这篇东西我不想写什么官方文档的复读机就想以一个实际干过项目的身份聊聊LabVIEW搭配正运动控制卡做设备上位机的那些事。从选型思路、环境搭建到核心代码逻辑再到我踩过的那些坑一次性说清楚。不管你是刚入门想搞个简单的点位运动还是已经在做多轴插补的复杂设备这篇文章都值得你花几分钟看完至少能让你少走我当年走过的弯路。1. 方案选型为什么是LabVIEW加正运动控制卡1.1 在LabVIEW里做运动控制的几条常见路线很多人一提到LabVIEW做运动控制第一反应就是买NI自家的运动控制卡比如PCI-7344或者现在的C系列模块。但这套方案有一个绕不开的问题贵。一套NI的运动控制硬件加软件授权动辄好几万对于很多中小型设备厂商和独立开发者来说成本压力相当大。而且NI的运动控制生态相对封闭如果你想后期换用别的品牌的伺服或步进驱动器有时候会碰到兼容性上的麻烦灵活性打了折扣。另一条路就是用LabVIEW通过串口或以太网去控制所谓的“独立式运动控制器”比如固高科技的GTS系列、雷赛的DMC系列或者正运动的ZMC系列。这类控制器本身带有运动规划算法上位机只负责发送指令和接收状态。这种方式的优点是开发简单因为运动控制的实时性由控制器硬件保证不占用上位机资源。但我个人觉得独立式控制器的调试便利性不如板卡尤其是当轴数很多、I/O点很多的时候走网络协议总觉得不够直接。第三条路也是我这几年最常采用的就是用LabVIEW调用运动控制卡的DLL动态库也就是“板卡函数库”的模式。正运动控制卡就是这条路线里很有代表性的产品。它的板卡本身不带CPU运动规划全靠上位机的CPU和板卡上的DSP或FPGA协同完成但这恰恰给了开发者极大的自由度。你可以在LabVIEW里用图形化语言去精细控制每一个运动细节而不是被控制器厂家固化的指令集束缚。1.2 正运动控制卡的核心优势在哪选择正运动首先肯定是看中它的性价比。同轴数的PCIe运动控制卡价格能做到国外大牌的几分之一对于预算敏感的项目来说这几乎是决定性的优势。但光便宜不行还得看干活行不行。从技术层面看正运动的运动控制卡有几个我很看重的点。第一它的DLL函数接口设计得比较规范和GALIL、ACS这些老牌厂商的函数风格很像如果你之前用过别的卡上手会非常快。第二它的运动模式很全面点位运动、直线插补、圆弧插补、电子齿轮、电子凸轮这些高级功能都有。第三它的轴通道隔离做得不错抗干扰能力在国产卡里属于第一梯队这对于工厂现场的恶劣电磁环境来说至关重要。我实测下来在带大功率伺服驱动器的设备上它的编码器反馈读数依然很稳没出现过无故丢脉冲的情况。当然选择正运动还有一个隐性好处它的技术支持响应速度确实快。以前用某些进口品牌发个邮件问技术问题往往要等好几天。用正运动的卡直接找技术支持或者逛他们的开发者论坛基本当天就能得到解决思路。对于项目交期紧的人来说这一点真的能救命。2. 开发环境搭建那些文档里没写明白的细节2.1 LabVIEW版本与正运动DLL的位数匹配问题这一步我见过太多新手栽跟头了。正运动控制卡提供的DLL有32位和64位之分而LabVIEW也有32位和64位版本。如果你在64位的Windows系统上装了64位的LabVIEW然后去调用一个32位的DLL系统会直接报错说无法加载动态链接库。反过来也一样。这个问题的根源在于进程位数必须一致。LabVIEW作为一个应用程序它运行时也只能加载同一位数的DLL。64位LabVIEW加载32位DLLWindows的加载器压根不会让它通过。我个人的经验是除非你有非用64位不可的理由比如要处理超大数组、要调用某个只提供64位DLL的第三方库否则在运动控制项目里我建议用32位的LabVIEW配合32位的DLL。理由主要有两个。第一正运动的官方Demo和经典例程大多是围绕32位DLL写的你跟着例程走不容易出幺蛾子。第二很多老的仪器驱动、第三方通信库到现在还只提供32位版本为了长远的兼容性32位环境是更稳妥的。你要是在安装LabVIEW时不小心装成了64位也不用重装系统只需再装一个32位的LabVIEW版本两个版本是可以共存的互不影响。2.2 系统环境准备和安装顺序的讲究安装顺序也有讲究。我的习惯是先装LabVIEW再装正运动的驱动和SDK。因为正运动的安装包在安装过程中会自动探测系统环境变量虽然没有硬性要求必须是这个顺序但按这个顺序来能避免一些莫名其妙的环境变量冲突。具体到LabVIEW 2018或者说其他较新的版本安装时有一个选项叫“NI LabVIEW 2018 Run-Time Engine”这个一定要勾上。另外如果你是在没有网络的环境下安装最好下载完整的离线安装包而不是在线安装器否则它会在安装过程中卡在下载驱动程序的环节进度条半天不动你以为死机了其实它在等网络超时。装完之后我强烈建议你做一件事到正运动的官方网站下载对应型号的“运动控制卡驱动和SDK”安装后找到安装目录下的“dll”文件夹把里面的控制卡DLL文件通常是 zmcaux.dll 或类似命名复制到LabVIEW的项目目录下。这里有一个关键点如果你不复制到项目目录也可以把DLL所在路径加到系统的Path环境变量里。但哪怕你加了环境变量在LabVIEW中调用时偶尔还是会遇到加载失败的情况这可能和LabVIEW的工作目录设置有关。把DLL复制到项目目录是最简单粗暴也最有效的方法因为它确保了LabVIEW在运行时一定能找到这个文件。2.3 在LabVIEW里配置调用库函数节点一切就绪后就要在LabVIEW的框图上配置“调用库函数节点”了也就是CLF这是连接LabVIEW和正运动控制卡的桥梁。右键点击程序框图空白处选择“互连接口”→“调用库函数节点”把这个节点拖出来。双击它在弹出的配置窗口里首先要选择刚才复制过来的DLL文件路径。接下来是配置函数名和参数。这里有一个非常实用的技巧建议使用“在文件中查找函数”的按钮它可以直接扫描DLL文件里导出的所有函数名你选中一个函数后LabVIEW会自动根据函数原型在下面的参数列表里生成对应的输入输出参数类型。这比我手动一个个去核对方括号里的参数要靠谱得多不容易漏掉指针类型的参数。需要注意的是正运动的DLL函数其参数类型以无符号整数、整数和双精度浮点数为主。在配置CLF时要仔细核对函数原型中用的是uint32还是int32。尤其是返回值的类型大多数函数返回的是int32用来表示错误码0代表成功负数代表失败。如果类型配错了轻则得到错误的数值重则导致内存访问冲突直接把LabVIEW搞崩溃前面没保存的代码就全没了。提示配置完成一个CLF节点后一定要先在前面板放几个输入控件试试调用确认返回值是正确的错误码0再继续写后面的复杂逻辑。不要一口气配几十个函数后再统一测试那样出了问题排查成本极高。3. 第一个运动控制Demo电机轴从初始化到转动3.1 连接硬件和轴通道的规划开始写代码之前先要搞清楚你的设备上电机轴是怎么接到控制卡上的。正运动的控制卡比如常见的PCIe系列通常支持多轴控制。每一路轴通道由脉冲输出和方向输出组成连接伺服驱动器的脉冲/方向接口。同时轴通道还包括编码器反馈输入用于接收伺服驱动器或电机后端编码器的反馈信号形成闭环。我第一次用正运动的卡时犯过一个低级错误把伺服的“脉冲”和“方向”信号线接反了导致电机只在单方向运动反向的指令完全不响应。所以这里要特别提醒接线之前一定要看仔细伺服驱动器手册上的端子定义正运动输出的是差分信号还是集电极开路信号要和驱动器的输入端口匹配否则电平不匹配信号根本传不进去。在规划轴地址时也要在头脑中形成一个清晰的映射表。比如1轴是X轴对应控制卡的AXIS1通道2轴是Y轴对应AXIS2通道。这个映射关系最好写到项目文档里避免后续代码写乱了。3.2 初始化流程打开设备、清零、设置运动模式在LabVIEW里一个最基本的运动控制程序初始化流程如下。第一步是调用控制卡的打开设备函数通常是ZAux_Open或类似函数输入设备的连接字符串可能类似“PCI:0”也可能是“ETH:192.168.1.10”这样的网络地址具体取决于你用的是PCIe卡还是以太网口控制器。这会返回一个设备句柄之后的几乎所有操作都要用到这个句柄。第二步是调用复位或清错函数把控制卡上可能残留的报警状态清掉。这一步至关重要。如果你上电后不复位伺服驱动器有时候会因为上次掉电时的非正常状态而锁死报警你后续的使能和运动指令都会被拒绝。第三步是设置每一个使用的轴的脉冲单位。正运动的卡逻辑上使用的是用户单位你可以把它理解为轴的使用单位它与实际脉冲之间有一个比例换算关系。这个比例通过设置“电子齿轮分子”和“电子齿轮分母”来实现。比如你设分子为1000那么轴移动1个单位实际输出1000个脉冲。这个换算原理一定要透彻理解假设你的伺服驱动器电子齿轮比设为1:1电机转一圈需要10000个脉冲而你的丝杠导程是10mm即电机转一圈工作台移动10mm那你希望坐标1代表多少距离如果你希望坐标单位是mm那么轴的脉冲当量即每个脉冲对应的位移就是10mm / 10000脉冲 0.001mm/脉冲也就是1um。此时电子齿轮分子设1分母设1然后在正运动指令中使用“ATABLE”或“UNITS”指令设置轴空间与脉冲的换算比例使目标位置和实际位置都能以mm为单位来显示和编程。这一步是新手最容易困惑的地方因为它涉及设备机械参数和控制卡内部计数单位的对应。我建议在初始化里就加上一段设置单位比例的代码并做好注释说明丝杠导程、驱动器电子齿轮比、电机每转脉冲数这三个参数是什么以及计算过程是怎样的。这样半年后你自己回来维护代码或者同事接手项目都能秒懂。3.3 点位运动从绝对运动到相对运动初始化完成电机就能动了。在正运动控制卡的指令集里点位运动通常用MOVE相对运动和MOVETO绝对运动两个指令来实现。在LabVIEW里就是通过CLF节点调用对应的DLL函数把目标位置和运动速度作为参数传进去。这里我强烈建议封装一个子VI因为点位运动除了目标位置和速度还经常要设置加速度、减速度、加加速度等参数。用一个子VI把这些参数全部打包输入是设备句柄、轴号、目标位置、速度、加减速度输出是错误码。这样在主程序里每次要运动只需要调用这一个子VI填上不同的参数就行代码会干净很多。关于速度参数的单位要和你前面设置的用户单位一致。如果你设定了1个单位1mm那么速度1000就代表1000mm/min这个按需换算。一个常见的陷阱是“运动不执行”。当你调用完点位运动函数后如果返回错误码是0说明指令已经被控制卡接收但此时电机未必真的在动。你需要再调用一个读取轴状态或运动状态的函数确认当前轴的状态是“运动”还是“停止”。要是发现指令发出去了但轴状态一直是停止那基本就是伺服使能没打开或者伺服驱动器报错被锁住了。3.4 回零与限位逻辑安全上电的基本盘任何一个正经的设备都不可能没有回零逻辑和硬限位保护。正运动的控制卡提供了专用回零功能可以配置回零方向、寻找原点开关的速度、以及碰触原点后的脱离速度。在LabVIEW里做回零我的经验是不要只调用一个回零函数就干等它完成。更好的做法是先给轴下发回零运动指令然后在主循环里轮询轴的“回零完成”标志位或轴位置状态。这是因为设备上电后你往往还需要先检测一些安全条件比如急停是否被拍下、安全门是否关闭。如果在回零开始后才检测到这些条件被破坏需要立刻停止运动如果只调一个阻塞式回零函数程序就会卡在等待状态没办法及时响应急停。限位方面正运动卡的硬件限位输入引脚可以直接接到行程开关上。在软件层面我也建议开启软件限位功能。设置软件限位的最大值和最小值后一旦轴的指令位置或反馈位置超出这个范围控制卡会自动停止该轴的运动并报错。这个功能在调试阶段非常有用能避免因为坐标设置错误导致撞机。注意就算开了软件限位硬件限位也绝对不能省。软件限位依赖编码器反馈和程序逻辑正确一旦反馈线松动或者程序跑飞软件限位就是摆设。硬件限位是设备安全的最后一道保险再怎么强调都不为过。4. 进阶功能状态机设计与多轴联动4.1 为什么要在LabVIEW里用状态机很多初学者写LabVIEW运动控制程序习惯用顺序结构一帧一帧地往下执行先回零然后运动到位置A再运动到位置B最后结束。这种思路在调试单个循环时没问题但放到完整的设备程序里就会出现一个大问题你很难在某个运动步骤中停下来去处理“急停”或“暂停”指令因为顺序结构一旦开始执行就会一口气从头跑到尾。状态机的优势就在于“随时可中断、随时可切换”。它把一个设备的运行过程拆分成若干个稳定的状态比如“空闲”、“启动中”、“运行中”、“暂停”、“停止”、“报警”。每一个状态内部只做该状态下该做的事情并通过条件判断来决定下一次循环进入哪个状态。在LabVIEW里写状态机可以用 while 循环配合条件结构来实现。每个循环周期根据当前的“状态枚举量”执行该状态下的逻辑然后根据该逻辑的结果给这个枚举量赋下一个状态的初值。要注意的是运动控制的状态机循环周期不宜太长否则急停响应会有延迟。一般我控制在5到10毫秒左右一个循环既能保证响应速度又不会让CPU占用率过高。4.2 正运动卡状态机的基本实现方式具体到正运动控制卡因为它的函数不是线程安全的官方建议在一个线程里顺序调用。如果状态机设计得好单线程顺序调用完全够用。下面是我常用的状态机状态划分。在“空闲”状态下程序不断扫描UI上的“开始”按钮如果按下就检查各轴的初始状态是否正常比如伺服是否上使能、是否有报警如果都OK就切换到“回零”状态。在“回零”状态下程序调用回零函数然后每轮循环检查回零完成标志。如果完成切换到“自动运行”状态如果中途检测到急停切换到“停止”状态。在“自动运行”状态下程序根据预设的工艺流程逐步下发运动指令。这里会用到一个队列或表格来存储工艺步骤。每完成一步就读取下一步的位置和速度参数继续下发指令。在“暂停”状态下程序调用正运动的暂停运动函数。注意这里有一个细节暂停后电机会减速停止但控制卡内部的目标位置还停留在原来的目标点。如果你此时下发新的绝对运动指令控制卡会认为你还在原来的目标位置导致运动数据冲突。所以最好在恢复运行前先调用一次“设定当前坐标为零或重新取当前位置”的操作比如以当前位置作为参考点。在“报警”状态下程序应该停止所有运动输出弹出清晰的报警窗口并记录当前的轴位置、报警代码和时间戳。这个记录非常重要能帮你在设备跑飞后快速定位故障原因。状态机设计这块如果觉得说起来抽象可以去看一些LabVIEW状态机的经典例子。另外我在网上也看到过不少关于“C#运动控制卡简单状态机”的讨论其实思路是互通的把状态枚举、状态切换条件、状态动作三者分离代码的扩展性和可读性都会大大提升。4.3 实现直线插补与圆弧插补的关键参数理解运动控制卡所谓的“插补”是指同时协调多个轴的运动让它们按照预设的轨迹运动。最典型的就是直线插补MOVE和圆弧插补MOVEARC。在正运动的函数库里直线插补通常需要指定参与插补的轴号列表和每根轴的目标位置控制卡会自动计算速度比例使末端沿直线走。这里有一个非常需要注意的点插补的速度和加减速参数是按合成的轨迹来设定的而不是每个轴单独设定。比如你做一个X轴和Y轴的直线插补X方向走100mmY方向走50mm速度设定为F1000mm/min意味着合成速度是1000mm/min。控制卡会根据合成速度自动计算出X轴和Y轴各自的速度分量。所以你不要尝试去给每个轴单独设定一个速度去匹配那个1000那是算不清楚的也没必要。圆弧插补的参数要更复杂一些要指定圆弧所在平面的两个轴、圆弧的终点坐标、圆心坐标或半径、以及选择顺时针还是逆时针、是优弧还是劣弧。新手很容易在这里懵。我的建议是先在纸上画出轨迹图把起点、终点、圆心标出来然后按实际参数填入。还可以利用正运动自带的上位机调试软件这类软件大多有插补试跑功能你可以先在软件里把轨迹跑通了再把参数搬到LabVIEW里。这个调试软件的界面我强烈建议新手多看看它能实时显示各轴的坐标、速度曲线和I/O状态比自己在LabVIEW里写数据显示程序直观得多。4.4 处理编码器反馈与数据转换4字节转浮点运动控制可不止是发脉冲那么简单。在很多设备上你还得实时读取编码器或光栅尺的反馈值用于精度验证或闭环控制。正运动的卡内部把位置信息以32位有符号整数存储但对于一般用户来说习惯上更愿意看到浮点数形式的坐标值。这里就牵扯到一个经典的LabVIEW问题如何把4字节的数据转换成浮点数。在搜索热词里我看到有人专门问“将4字节数据转换为浮点数IEEE”这个问题很典型。当你通过Modbus TCP或其他通信协议从外部设备读取到4个字节的IEEE 754单精度浮点数据时LabVIEW里是不能直接把这4个字节当成一个数值来看的。你需要从一个“字符串”或“字节数组”中按正确的字节序拼接出一个32位整数再用一个“强制类型转换”或“拆分与重组”的节点把这个32位整数转换为单精度浮点数。实现方式并不复杂先用“索引”功能拆出4个字节然后根据大小端顺序通过“布尔运算”和“位移”组合成一个U32整数最后在“数值”函数面板里找一个“类型转换”函数把它变成Single精度。注意如果设备是大端模式就要反过来。我在做温度采集项目时就踩过这个坑设备输出的是大端字节序用默认的小端转换后温度成了天文数字排查了半天才发现是字节顺序的问题。回到运动控制本身当你要读取正运动卡的当前坐标时DLL函数一般直接返回的就是双精度浮点数不存在这个字节序问题。但如果你读取的是控制卡底层寄存器里的原始计数值32位整数并想在LabVIEW里转成物理单位就要用公式物理位置 原始计数值 / 每圈脉冲数 × 导程。这种转换逻辑建议封装成子VI方便多处调用。5. 常见坑与排查技巧来自实战现场的实录5.1 程序跑着跑着电机停了为什么这是一个让人很崩溃的问题。设备无限循环运行每次跑到几百个循环后某个轴突然不动作了指令发了但不响应控制卡也没报错。重启程序又好了再跑几百个循环又犯病。我遇到这个问题的真正原因是内存泄漏导致的句柄耗尽。我在LabVIEW里用了某个第三方库或者某些未释放引用的节点比如在循环里反复打开文件、反复创建网络连接但没有关闭导致LabVIEW进程的内存不断上涨最终运动控制DLL无法正常分配内部资源运动指令就挂了。排查方法很直接在程序界面上放一个显示“当前内存占用”的控件盯着运行。如果发现内存随循环次数线性增长那基本就是有资源没释放。常见点是在调用DLL时使用了“打开会话”和“关闭会话”形式的函数但关闭会话的代码被放在了条件分支里只在某些条件下执行平时根本走不进去。5.2 脉冲方向对了但坐标时准时不准做运动控制最闹心的就是“丢步”。现象是电机每次走一小段距离后实际位置与目标位置差了那么一丁点并且误差有累积趋势。这个问题的常见原因是加减速曲线设置得太陡。电机在启停瞬间惯性太大超过了伺服驱动器的跟踪能力导致实际接收到的脉冲数和发出去的脉冲数不对等。解决方案是把加速度和减速度调小让电机平缓加速和平缓减速。另外一个原因是脉冲频率太高特别是用软件生成PWM脉冲时如果上位机CPU占用率突增脉冲波形会出现毛刺驱动器偶尔漏检。正运动的卡有硬件脉冲生成器占用CPU极少一般不会出现这个问题。还有一个隐蔽的原因你把脉冲发送模式设置成了CW/CCW正反转脉冲模式但伺服驱动器被设置成了PULSE/DIR脉冲方向模式。两者在某些瞬间会产生不同的计数结果。这一步匹配很关键一定要在初始化时核对清楚。5.3 调试阶段不能忽视的“强制编译”和运行效率在热词里我看到很多人搜“LabVIEW 强制编译”和“LabVIEW运行效率”这和运动控制的实时性有很大关系。LabVIEW的图形化代码在首次运行或修改后都会有一个后台编译过程。如果在运动过程中你突然修改了某个子VI并保存LabVIEW可能需要在运行时重新编译该子VI这会导致当前运动控制循环被阻塞哪怕只是几十毫秒的阻塞对于高速运动来说也可能引发明显顿挫甚至丢脉冲。所以我的经验是调试时把运动控制VI的“允许调试”和“允许运行时自动编译”功能关闭保证首帧编译完成后运行期间不重新编译。对于运动控制主循环我还会把循环周期设置为严格定时并适当降低刷新频率。不相关的数据显示控件不要频繁刷新否则会给主循环带来额外负担。我在实际项目中会把仪表盘如速度曲线、位置曲线的刷新周期设为200ms而核心运动控制状态机保持在5ms周期两者分开跑在不同的循环里互不干扰。注意LabVIEW里的“高优先级”循环设定要放在独立任务里不要让UI事件处理和运动控制抢占同一个线程。不然前面板拖动窗口时可能会导致运动指令响应延迟这在工业现场是很危险的事情。5.4 通信类问题Modbus RTU与中文字符串乱码除了运动控制本身设备上位机往往还肩负着和PLC、传感器通信的任务。我看到热词里有“Modbus RTU 基于LabVIEW”和“中文存入数据库变成乱码的解决方法”这两个问题在我们实际做项目时也常遇到。LabVIEW实现Modbus RTU通常是通过VISA节点串口通信。如果PLC是主站LabVIEW做从站可以用NI的Modbus库但如果你只是用LabVIEW做上位机去读取一个Modbus RTU的传感器或温控器我更推荐直接用VISA读写自己按照Modbus RTU的报文格式去组帧、解析。原因是LabVIEW的Modbus从站库配置起来相对繁琐而且对数据地址的映射处理得不够透明。自己组帧的话能很清楚地在代码里看到哪个寄存器对应哪个物理量。至于中文字符串乱码这个问题几乎总是编码不一致导致的。PLC或仪表发送的是GBK编码或ASCII而LabVIEW里默认显示的是UTF-8或本地代码页。解决办法是在LabVIEW里用“字符串至字节数组转换”拿到原始字节后通过自己写的一个字节转字符串VI强制指定编码格式。在新版本的LabVIEW里有一个“代码页”相关的转换函数可以直接指定从GBK或UTF-8解码。记住一点只要保证数据的编码和解码方式一致乱码问题就绝对不会出现。写在最后的一点个人体会从第一次用LabVIEW控制电机转起来到现在完成一整台多轴设备的软件架构这条路我走了很久。回头看正运动控制卡的资料和例程其实已经很完善了但是官方文档更侧重于函数功能的罗列它不会告诉你“这个参数在工程上到底该怎么配”、“那个报错实际原因是什么”。这些知识只有在现场被设备揍过几回才能真正沉淀成自己的东西。对于刚入行的朋友我建议不要一上来就追求复杂的联动和凸轮把一个轴的回零、限位、点位运动、读反馈做扎实就已经能应对市面上大部分设备的需求了。在此基础上再逐步去研究插补、电子齿轮、状态机你的成长曲线会平稳很多。最后再分享一个小技巧吧正运动的开发论坛和帮助文档里其实隐藏着非常多高质量的应用案例很多老工程师会在上面分享成体系的代码片段。遇到问题先去那里搜一搜很多时候能直接找到一个比自己理解得更合理的实现方案。设备调试遇到瓶颈时不妨暂时放下代码去把这些案例细细读一遍。工控这一行学习的捷径就是站在别人的经验上少走自己摸索的弯路。
返回列表