简介:面向工业自动化工程师与三菱PLC编程人员,这份压缩包提供了基于ST语言编写的FX5U轴控制FB块模板,将原点复归、点动、定位运动等常用轴控逻辑封装为可复用模块,适用于多轴伺服/步进系统开发。通过实例化同一FB块即可扩展轴数,有效减少重复编程工作量,提升调试效率。包内共8个文件,整体990KB,包含1个txt示例代码、1个HTML使用说明以及6张JPG示意图,可辅助理解FB块的配置流程与实际效果。目前已有1071人学习浏览,适合具备基础PLC知识、正在推进多轴运动控制项目的工程师参考。该模板直接给出ST语言源码与调用说明,结合图片拆解原点复归、点动、定位运动的关键参数设置,帮助读者快速搭建轴控制框架,规避常见配置误区,是多轴设备程序结构化设计的实用参考资料。
1. 三菱FX5U轴FB封装:用ST把原点复归、点动、定位一次写成通用块
第一次在FX5U上写多轴程序时,我把原点复归、点动、绝对定位三套逻辑在梯形图里复制粘贴了四遍,每加一个轴就多三十几个网络,改一句速度要翻四个地方。后来我把整套逻辑重写成ST语言的FB块,用GX Works3直接在ST里定义输入输出和各轴参数,把原点复归、点动、定位运动封装成一个轴FB块;多轴重复时只要复制实例,不需要重复编轴控制逻辑。这篇文章不讲花哨的算法,就讲我在FX5U内置定位上跑过的这个轴FB块:接口怎么定、ST代码怎么拆、多轴怎么配、哪里容易翻车。适合正在给设备写多轴定位、被重复梯形图折磨的程序员直接照着改。
2. FX5U内置定位与FB设计:先定接口,再写逻辑
2.1 FX5U内置定位的轴号与脉冲输出
FX5U主打的是CPU内置定位,不需要额外插定位模块,高速脉冲输出由晶体管输出点担任,轴号按0、1、2、3的顺序编排。我第一次用的时候就踩了轴号从0开始的坑:设备图纸上写“1轴Y0”,程序里我理所当然地填了K1,结果定位指令一直不执行,GX Works3监控里指令触发位为ON但当前值不动,查手册才知道轴号是K0。所以设计FB时,轴号千万不要直接从HMI读数塞进去,最好在FB内部做一个“轴号缓冲区”,让外部传0、1、2这样的纯数字,再由内部映射到对应脉冲点。
FB封装之前有一个前提:得先明白FX5U每条定位指令的“触发条件”。和伺服驱动器里那种运行信号不同,FX5U的DRVZ、DRVI、DRVA这类指令一般要求边沿触发,也就是信号从OFF变ON的那一瞬间才有效。如果用一个位置开关一直给信号,FB一次定位做完之后,这个信号还停在ON,那么下一次再让FB跑,指令不会重新接收。所以FB内部不能直接拿外部按钮信号去触指令,必须自己做上升沿检测,把边沿脉冲和原信号分开处理。
2.2 FB接口:输入、输出和内部变量怎么划分
这个FB块我起名叫FB_AXIS,接口定义是经过三个项目迭代出来的:
FUNCTION_BLOCK FB_AXIS VAR_INPUT bEnable : BOOL; // 总使能,OFF时所有运动命令无效 bHome : BOOL; // 原点复归请求 bJogFwd : BOOL; // 正向点动,按住走,松开停 bJogRev : BOOL; // 反向点动 bGo : BOOL; // 定位启动请求 bAbsMode : BOOL; // TRUE=绝对定位,FALSE=增量定位 iAxisNo : UINT; // 轴号:0,1,2,3... uiHomeSpd : DINT; // 原点复归高速 uiCreepSpd: DINT; // 原点复归爬行速度 uiJogSpd : DINT; // 点动速度 diSpd : DINT; // 定位速度 diPos : DINT; // 目标位置 END_VAR VAR_OUTPUT bBusy : BOOL; // 运动执行中 bDone : BOOL; // 定位完成/回零完成 bAlarm : BOOL; // 轴报警 diCurPos : DINT; // 当前值 wErrCode : WORD; // 错误代码 END_VAR VAR bPrevHome : BOOL; bPrevGo : BOOL; bCmdOn : BOOL; eCmdKind : WORD; END_VAR接口设计上我有三个原则:第一,所有输入信号都用BOOL或者DINT的普通变量,不让外部程序直接给定位指令传结构体,因为HMI数据写入和另一个PLC的通信都要操作普通标签,太复杂的外部结构体会让联调很难查;第二,速度单位统一用Hz,位移统一用脉冲数,不在FB内部做毫米到脉冲的换算,换算放到上层,因为不同轴的丝杆螺距和电子齿轮比不一样,FB只管“多少脉冲、多少速度”,换算逻辑留在设备参数页;第三,错误代码用WORD输出而不是只给BOOL,方便上位机直接查故障码。
要注意的是输入输出变量里的DINT长度:FX5U的定位寄存器按32位处理,位置值超过正负20亿脉冲就会溢出,FB里最好不要用INT,统一用DINT,HMI写入也要对应32位格式。
2.3 边沿检测与运动状态机
FB内部真正的难点,是外部那些按钮信号都是电平信号,而定位指令需要边沿信号。“上升沿”在梯形图里一句话的事,到了ST里就得自己造一个沿记忆:
IF bHome AND NOT bPrevHome THEN bCmdOn := TRUE; eCmdKind := CMD_HOME; END_IF; bPrevHome := bHome; IF bGo AND NOT bPrevGo THEN IF bBusy THEN bCmdOn := FALSE; ELSE bCmdOn := TRUE; eCmdKind := CMD_POS; END_IF; END_IF; bPrevGo := bGo;这段代码的逻辑是:bHome从OFF变ON时记录一次原点复归命令;bGo从OFF变ON时记录一次定位命令,但如果FB已经在忙,就把启动命令清掉,避免后面的指令把正在跑的动作打断。bPrevHome和bPrevGo就是上一扫描周期留下的影子变量,这是ST语言里替代上升沿最通用的做法。
参数说明:eCmdKind保存命令类型,我用CMD_HOME、CMD_JOG、CMD_POS三个常量来区分;具体常量的值可以定义为内部静态变量,或直接写“0”、“1”但不推荐直接写魔法数。常见的做法是在FB里留一个枚举声明,GX Works3的ST支持枚举,可以避免命令类型写错。
这里把边沿检测放到每个扫描周期的开头,后面的运动逻辑都以bCmdOn和eCmdKind为准,这样同一个FB里无论以后加多少种命令,都不会出现多处同时触发指令的冲突。
3. ST语言核心逻辑:原点复归、点动、定位的顺序与互斥
3.1 原点复归分支:高速找原点,低速爬行确认
原点复归按FX5U的DRVZ系列做法,基本套路是先以高速向原点方向走,碰到原点信号后减速,再以爬行速度找原点。我一般不让驱动器自己回零,而是让FB直接接管脉冲输出,因为FX5U内置定位的DRVZ指令本身就支持回零速度、爬行速度两个参数,外部要做的只是把速度传对:
CASE eCmdKind OF CMD_HOME: bBusy := TRUE; bDone := FALSE; // 调用原点复归指令,iAxisNo为轴号 DRVZ(iAxisNo, uiHomeSpd, uiCreepSpd); eCmdKind := CMD_NONE; bCmdOn := FALSE; CMD_JOG: bBusy := TRUE; bDone := FALSE; JOGZ(iAxisNo, uiJogSpd); eCmdKind := CMD_NONE; bCmdOn := FALSE; CMD_POS: bBusy := TRUE; bDone := FALSE; IF bAbsMode THEN DRVA(iAxisNo, diPos, diSpd); ELSE DRVI(iAxisNo, diPos, diSpd); END_IF; eCmdKind := CMD_NONE; bCmdOn := FALSE; END_CASE;这里我给的是一个简化版的命令分发,每个指令执行前先把bBusy置位,做完这段后清掉命令标志,确保同一个扫描周期内只启动一个动作。要注意DRVZ、DRVI、DRVA这类指令的助记符在GX Works3的FX5U ST程序里是可以直接调用的,但不同固件版本的参数个数和排列有差异,我工程里这套写法的顺序是轴号、位置、速度,你复现的时候先按自己软件里的指令帮助对齐一遍。
3.2 点动分支:按住走、松开停,还要跟定位互斥
点动和定位最大的冲突是:点动手一直按着,结果定位命令也来了,两个动作都想抢脉冲输出。我的做法是首先在命令采集阶段就判断,点动信号只在FB空闲时才能进入命令队列;进入运动状态以后,点动松开就马上停止,但不打断正在跑的定位。
先列点动时的处理逻辑:
// 点动命令按上升沿进入,松开后由内部检测停止 IF (bJogFwd OR bJogRev) AND NOT bBusy THEN bCmdOn := TRUE; eCmdKind := CMD_JOG; END_IF; // 点动中的停止:检测到点动按钮都松开,或状态异常 IF bBusy AND eCmdKind = CMD_JOG THEN IF NOT bJogFwd AND NOT bJogRev THEN // 停止点动指令 JOG_STOP(iAxisNo); bBusy := FALSE; bDone := TRUE; END_IF; END_IF;这里JOG_STOP是我在工程里自己封装的一个停止函数,实际做的是把对应轴的脉冲输出停止位置ON。很多新人在ST里面写点动,喜欢一直用JOGZ指令,其实JOGZ一旦触发后并不会自己松开,必须由程序给出停止信号让它停下来,所以点动分支里“松开按钮”本身就是一个停止动作。
参数说明:点动速度uiJogSpd我建议设成比定位速度低的数值,通常在50%左右,因为手动操作时操作员要盯着机械位置,速度太高容易撞机。还有一点,点动正反转两个信号不要同时为ON,我在FB接口里没有做强制互斥,而是交给外部梯形图去保证,理由是现场设备HMI上都做了按钮互锁;如果你担心操作员乱按,可以在FB内部再加一个IF判定,两个都为ON时直接忽略正转。
3.3 定位分支:绝对和增量到底什么时候用
定位分支看bAbsMode:绝对定位DRVA要求当前值有意义,所以必须先知道机械原点在哪里,否则位置坐标系是错的;增量定位DRVI相对当前点走一段,不用原点也能跑,适合推料、步进这类只关心相对位移的机构。
我一般会在设备手动模式下让操作员先回一次原点,再切自动模式运行绝对定位。FB里处理得比较粗暴:如果bAbsMode为TRUE而当前值寄存器里已经是个很大的随机数,说明这台设备还没有回过原点,FB输出bAlarm并拒绝执行DRVA。这一步很关键,不然机械手第一次自动运行就可能直接撞到硬限位。实现方式如下:
IF bAbsMode THEN // 用当前值寄存器判断是否回零过,绝对值大于环境值时报警 IF diCurPos < -100000 OR diCurPos > 100000 THEN bAlarm := TRUE; wErrCode := 16#4021; bCmdOn := FALSE; ELSE DRVA(iAxisNo, diPos, diSpd); END_IF; ELSE DRVI(iAxisNo, diPos, diSpd); END_IF;这段把绝对定位的安全检查放进了FB,不依赖调用方的自觉。判断阈值100000脉冲要根据设备实际量程改,我写过一台设备丝杆行程120mm,螺距5mm,步进细分2000,一个来回就是480000脉冲,阈值设到100000会因为原点偏了一点就误报警;所以这个阈值别写死,做成FB内部参数比较合适。
3.4 运动完成和报警汇总
定位指令执行之后,FX5U会通过特殊继电器表示“定位结束”。FB里不能只在指令下发那一刻置位bDone,因为脉冲没走完之前设备还在运动。我的做法是每个扫描周期读一次“轴运动中”标志,从运动变为停止的下降沿才算完成:
// 读取轴的执行状态(不同型号寄存器地址不同,这里只写访问逻辑) xInMotion := READ_AXIS_BUSY(iAxisNo); // 完成信号:从运动中变成停止的下降沿 IF bPrevMotion AND NOT xInMotion THEN bDone := TRUE; bBusy := FALSE; END_IF; bPrevMotion := xInMotion; // 报警汇总:报警时强制退出运动状态 IF bAlarm THEN bBusy := FALSE; END_IF;这里我把READ_AXIS_BUSY写成一个占位函数,实际工程里需要查看FX5U的轴状态地址表,也可以直接用特殊继电器和寄存器。建议在调试时先用监控表确认自己轴上“运动结束”和“定位结束”两个标志,然后用正确的标志位来产生bDone,不同CPU和固件对这两个标志的定义不完全一致。
4. 多轴复用:实例化、参数映射和HMI对接
4.1 创建多个FB实例
FB写完后,复用就是“复制实例”。在GX Works3全局标签或程序内声明中,写四个实例:
VAR stAxis0 : FB_AXIS; // 1号轴,Y0输出 stAxis1 : FB_AXIS; // 2号轴,Y1输出 stAxis2 : FB_AXIS; // 3号轴,Y2输出 stAxis3 : FB_AXIS; // 4号轴,Y3输出 END_VAR每个实例拥有独立的输入输出变量和内部变量。程序里调用方式一模一样:
stAxis0( bEnable := xAxis0Enable, bHome := xAxis0Home, bJogFwd := xAxis0JogFwd, bJogRev := xAxis0JogRev, bGo := xAxis0Go, bAbsMode := xAxis0AbsMode, iAxisNo := 0, uiHomeSpd := 50000, uiCreepSpd := 1000, uiJogSpd := 10000, diSpd := 20000, diPos := iAxis0TargetPos );关键点是iAxisNo在外部赋值,而不是FB内部写死。如果你发现FB代码里出现“0”这种轴号常量,建议立刻改成输入参数。理由很简单:复制实例时手一抖把0号轴的实例参数填成1,两轴就会抢同一个脉冲输出,程序编译不报错,设备动作完全乱套。
4.2 每个轴的参数映射表
多轴FB最容易乱的就是参数和轴对应关系。我把每台设备的轴参数集中放在一个结构体或全局标签区域里,并用注释的方式在代码里留轴参数表。
| 实例名 | 轴号 | 脉冲输出 | 原点传感器 | 正限位 | 负限位 |
|---|---|---|---|---|---|
| stAxis0 | 0 | Y000 | X002 | X003 | X004 |
| stAxis1 | 1 | Y001 | X005 | X006 | X007 |
| stAxis2 | 2 | Y002 | X010 | X011 | X012 |
这张表不是为了写文档,而是为了在GX Works3里做全局标签绑定的时候不出错。我一般先把这张表放在工程注释里,然后把脉冲输出点通过GX Works3的定位参数配置窗口分配到Y0/Y1/Y2,再启动FB实例。比如轴1实际用Y0输出,但全局标签绑定到Y1,程序编译后运行时会发现指令都没触发,排查半天才看到标签映射错了。
4.3 HMI联动
HMI和FB的对接,最忌讳的是HMI直接读写FB内部的变量。FB内部变量对HMI是不可见的,需要把要显示和控制的数据引到全局标签去:比如手自动切换、速度写入、当前位置显示、报警状态。我一般做一组中间标签,在FB调用处做一次拷贝:
// 全局显示标签 g_Axis0CurPos := stAxis0.diCurPos; g_Axis0Busy := stAxis0.bBusy; g_Axis0Done := stAxis0.bDone; g_Axis0Alarm := stAxis0.bAlarm;我不建议HMI通过请求式通信直接在画面刷新上千次每秒的当前值,即使FX5U的以太网读写速度够快,也要克制刷新频率。当前值这种变量通常设定每200ms刷新一次,按钮和模式切换这类变化不频繁的信号按需写入。在FB输出上还有一个细节:bDone是脉冲信号,FB跑完就消失,HMI如果刷新周期太长看不到,建议把bDone做成“完成脉冲保持”并加一个清除条件,或者让HMI读取后由画面触发清除。
5. FX5U轴FB调试避坑:五个常见坑从现象到原因到解决
5.1 轴号从0开始还是从1开始
现象:FB实例启动后定位指令的触发位已经为ON,但对应脉冲输出点完全没有脉冲,当前位置一点不动。排查GX Works3的定位监视画面,发现目标位置和当前位置都没有变化。
原因:FX5U内置定位轴号从0开始,第一个轴是K0,不是K1。我在初版工程里按习惯填了1,结果指令一直发给“1号轴”,但设备上实际只有0号轴被初始化。GX Works3的定位参数区也是从0号开始,所以指令找不到对应的运行通道。
解决:把iAxisNo统一改为0开始的编号,并在FB接口注释里写清楚“0为第一轴”,同时建议在HMI的画面上把1号轴显示成“Axis 0”而不是“Axis 1”,让操作员也习惯这个编号法。如果现场人员只看设备铭牌上的1号轴,最好让程序里的注释和HMI标签名显式对应设备编号。
5.2 完成信号不刷掉,第二次定位没反应
现象:设备第一次自动定位正常,再一次触发定位时,FB的bGo有了上升沿,但定位指令不执行,位置停在原地。
原因:第一次定位完成后bDone一直为ON,调用方程序里没有清掉bDone,而我的FB里面在收到新命令时看到bDone还置着,就把整个使能条件给拦下来了。这个锅一半在调用方的梯形图,一半在FB接口设计没有做完成自复位。
解决:我在FB内部增加了一个“完成信号看门狗”逻辑,命令执行完成后,bDone置位,但在某个输入条件不满足时自动清除;同时要求调用方必须用一个沿或按钮信号来启动定位,不要用自锁信号去置bGo。从那以后我每写一个FB,都会检查所有输出信号是否存在“不依赖外部清除”的自复位可能。
5.3 ST类型不匹配:WORD、INT、DINT的显式转换
现象:把FB编译后,调用处报“类型不一致”的编译错误,或者传入的定位速度参数运行时比设定值大很多。
原因:ST语言是强类型语言,梯形图里自动进行的DINT到INT转换在ST里不会自动发生。尤其输入标签来自HMI,HMI写入类型经常是INT16,而FB要求的diSpd是DINT,直接接上去编译不通过。
解决:在调用处加显式转换,例如:
stAxis0( diSpd := DINT_TO_DINT(HMI_Axis0Spd), diPos := INT_TO_DINT(HMI_Axis0Pos) );GX Works3的ST里有很多类似的转换函数。在编写FB内部时,也要注意字变量和位变量的区别,常用的是WORD_TO_UINT、UINT_TO_DINT这种显式写法,别用隐式转换去赌编译器。
5.4 多轴同时触发:脉冲输出点冲突
现象:两个轴FB同时启动点动或定位,其中一个轴没有动作,另一个轴速度明显不对,而且监控到两个FB的bBusy都在TRUE。
原因:把两个轴配置到了同一个脉冲输出点。最常见的错误是在GX Works3的定位参数里,1号轴和2号轴都选用了Y0作为脉冲输出。代码层面是独立的FB,硬件层却是同一个输出,脉冲通道冲突。
解决:先核对参数映射表,确保每个轴的脉冲点不同,然后再检查代码。如果硬件上确实不够用,就要考虑加扩展定位模块,不能靠软件去复用同一个输出点。在调试时我一般会把每个轴的脉冲点先做成常量并在FB里做一次输出点“互斥校验”,发现两个实例传入了相同轴号立即输出报警。
5.5 原点复归后位置偏差
现象:每次设备回原点后再做绝对定位,目标位置和实际机构位置都有固定偏差,偏差值和原点复归的爬行速度有关。
原因:原点复归分高速和爬行两段,如果爬行速度设置太高,机械在碰到原点传感器后还没来得及停下来,脉冲数已经多走了一段。不同机械的惯性不一样,这个值需要现场试验。
解决:把爬行速度放低并留出可调整的余地。我一般先设成高速的十分之一,然后手动回原点一次,量机械位置偏差,再微调爬行速度直到偏差一致。另外,原点传感器信号最好做成上升沿触发而不是常闭触点,不然可能产生两次触发,导致Z相信号对不上。
6. 验证与进阶:让每一根轴都能在30分钟内验收完
FB写好之后,光编译通过不算完。我习惯在GX Works3的监控表里做一个“轴的验收清单”,把每根轴按顺序过一遍:原点复归一次、正向点动、反向点动、增量定位5000脉冲、绝对定位回到原点、报警断开一次。每个动作都要观察bBusy和bDone的时序变化,bBusy上升沿和bDone下降沿之间的时间就是实际定位耗时。
进阶做法是增加一个轴参数结构体,把原点速度、爬行速度、定位速度、软限位全部放进结构体,而不是散落在多个输入参数里。这样FB实例可以简化成“一个结构体加几个BOOL”,后续维护时开设备参数页就能改速度,不用翻程序。我在第三个版本里把接口改成了这样:
VAR_INPUT bEnable : BOOL; bHome : BOOL; bGo : BOOL; stParam : ST_AXIS_PARAM; // 轴参数结构体 END_VAR此外,验证时可以用模拟运行模式,把脉冲输出点和原点信号用内部软元件替代,先不接伺服驱动器跑一遍状态逻辑。这样可以确认FB内部沿检测、互斥、完成信号都正确,再上真实机械。不要跳过这一步,直接在真机上测试,撞机的概率很高。
最后还有一个习惯,我每次发布多轴设备程序之前,都会在GX Works3的注释里把每根轴的映射和参数更新日期写一遍,就当是给未来的自己留的后悔药:程序里所有和轴有关的常量必须以这张表为准,谁改了丝杆螺距,就得同步更新参数表和替换实例的输入速度。从那以后我每建立一个新轴FB程序,都强制自己把第一件事从“先写逻辑”改成“先填参数表”,再写逻辑,希望帮到你。
本文还有配套的精品资源,点击获取