简介:本资源是一套面向自动化工程师与PLC进阶学习者的新能源领域实战案例,聚焦汇川中大型PLC在多轴协同控制中的Codesys编程实现,解决复杂运动控制逻辑设计、实时同步、指针动态寻址及HMI交互集成等核心问题。压缩包共80个文件,含34张界面与逻辑示意图(png)、5个Codesys工程配置文件(opt)、4个XML驱动配置(如JMC_DRIVE_V1.7.xml)、2个完整触摸屏工程(hmiproj)及配套安装程序(InoTouchPad_Setup.exe),另有编译库、动态链接库、日志与数据库等支撑文件,整体259MB,结构清晰,便于模块化学习与工程复用。已有1065人下载学习。读者可直接获取含详细中文注释的多轴控制主程序(支持20+轴)、指针操作范例(用于动态数组与结构体访问)、同步/绝对/相对定位逻辑实现,以及与191触摸屏配套的HMI通信配置与变量映射方案,覆盖从PLC底层控制到上位监控的完整技术链。 在新能源设备这个行当里摸爬滚打久了,你会发现一个很现实的问题:设备工艺越来越复杂,轴数越来越多,从早期的单轴定位到现在的多轴电子齿轮、电子凸轮联动,控制器如果不趁手,项目根本推不动。我最近在做的电池模组PACK线项目里,就是用汇川AM600配合Codesys环境来跑多轴控制逻辑,从最初的四轴电子齿轮联动到后来加入指针做配方数据交换,整个过程踩了不少坑,也沉淀了一套可以复用的练习方法。这篇文章我把完整思路写出来:从为什么选汇川中大型PLC和Codesys,到环境搭建、多轴同步逻辑、指针用法,再到调试中遇到的典型问题,全部摊开讲。适合刚接触Codesys、想做多轴控制练习,或者准备上手汇川AM系列的工程师参考。
1. 为什么用汇川中大型PLC练多轴控制:AM系列与Codesys的组合优势
先说结论:汇川中大型PLC(AM系列)和Codesys的组合,是练多轴控制性价比很高的平台,尤其是AM600这一档,它采用的是标准的Codesys V3.5内核,不是那种魔改到没法移植的封闭环境。
1.1 硬件选型:AM401/402/600怎么选
汇川的中大型PLC可以粗略分成两个梯队:
- AM401系列:入门级中型PLC,主控制器本体内置EtherCAT总线,适合4轴以内的运动控制,CPU主频和内存相对有限,但胜在价格便宜、上手简单。
- AM402系列:可以看成AM401的加强版,支持更大的程序容量和更快的任务周期,适合6到8轴的中型设备。
- AM600系列:真正的旗舰级中型PLC,最多可以带几十个EtherCAT从站,支持复杂运动控制算法,比如多轴插补、凸轮同步、CNC功能。项目里常见的一拖多伺服方案基本都落在这一档。
如果你是拿来练习,我个人建议直接上AM600或者至少AM402。理由很简单:多轴控制的练习重点在同步逻辑和程序架构,如果硬件资源太紧张,轴数一多,任务周期拉长,你会分不清到底是程序写得差还是硬件撑不住,排查起来非常难受。
从实际项目来看,新能源锂电设备里的模组堆叠工位、极耳焊接工位、电芯搬运机械手,主力控制器基本都是AM600搭配IS620N或SV660N伺服,走EtherCAT总线。这个搭配方案在行业内非常成熟,资料也好找。
1.2 Codesys生态的价值:从IEC 61131-3说起
Codesys(CoDeSys)是一个符合IEC 61131-3标准的软PLC开发环境。它不是汇川自家的东西,而是一个开放生态,汇川、倍福、博世力士乐、施耐德等很多厂商都在用或曾经用过这个内核。这就带来一个巨大的优势:你在Codesys上积累的经验,换一个品牌的控制器,大部分知识仍然适用。
具体到汇川的AM系列,它使用的IDE是InoProShop(汇川基于Codesys定制的中文IDE),底层库和标准Codesys高度一致。这意味着:
- 运动控制库文件,比如PLCOpen的MC系列功能块(MC_Power、MC_MoveAbsolute、MC_GearIn),名字和接口都和标准一致,网上搜Codesys的资料可以直接参考。
- ST结构化文本语言完全兼容,写好的代码逻辑、状态机封装,换个平台稍微改改轴配置就能跑。
- 联调时可以直接用Codesys的在线监控功能,方便边调边看轴状态。
很多工程师一上手就急着学指令,我倒是建议先把IEC 61131-3的编程模型吃透。比如任务(Task)的优先级和周期怎么设置、全局变量和局部变量的作用域、功能块实例化和状态保持,这些基本功决定了你后面写的程序是可维护的还是堆砌出来的。
1.3 这个练习项目能练到什么
我给自己设定的练习项目是:用汇川AM600(虚拟轴模式)实现一个新能源模组线的模拟控制,要求如下:
- 4个伺服轴,其中2个轴做电子齿轮同步(模拟传送带和切刀联动)。
- 1个轴做电子凸轮(模拟飞剪或追剪动作)。
- 1个轴做普通点位运动,负责上料搬运。
- 所有轴状态通过一个状态机统一管理。
- 用指针数组实现轴参数的批量初始化和在线修改。
这套练习做完,你对多轴控制的认知会从"一个个轴单独点动"升级到"把轴当成一个协调系统来管理",这个转变在真实项目中非常值钱。
2. 环境搭建与工程配置:先让轴动起来
很多人拿到AM600之后第一件事就是新建工程,然后就开始拖功能块。实际上,环境搭建这步如果没做扎实,后面调试会反复出幺蛾子。
2.1 软件环境和设备描述文件
汇川AM系列用的编程软件是InoProShop,安装之后不要急着开搞,先确认几件事:
- 软件版本和PLC固件版本要匹配。AM600的固件升级后,如果IDE版本太老,可能识别不了新增的功能块或库文件。我遇到过EtherCAT主站扫描不全的情况,最后发现是固件和软件版本差太多。
- 安装伺服驱动器的设备描述文件。不管是IS620N还是SV660N,都需要把对应的ESI文件(EtherCAT Slave Information)放到InoProShop的EtherCAT设备库目录下,否则扫描不到从站。
具体的操作流程是:新建工程时选择对应的PLC型号,然后在左侧设备树里右键点击EtherCAT主站,选择"扫描设备",此时IDE会自动扫描总线上所有从站并生成设备节点。扫描完成后,逐个检查每个伺服轴的PDO映射(过程数据对象),确保位置实际值、目标位置、状态字、控制字这些关键映射存在。
这里有个细节容易被忽略:PDO映射默认可能只包含一部分对象,比如只映射了控制字和状态字,没映射位置实际值。如果后面发现轴在MC_ReadActualPosition里读到的数据始终是0,八成就是PDO映射漏了。
| PDO对象 | 索引 | 方向 | 用途 |
|---|---|---|---|
| 控制字 Controlword | 0x6040 | 主站→从站 | 轴状态机切换 |
| 状态字 Statusword | 0x6041 | 从站→主站 | 读取轴状态 |
| 目标位置 TargetPosition | 0x607A | 主站→从站 | 点位运动目标值 |
| 位置实际值 ActualPosition | 0x6064 | 从站→主站 | 实际位置反馈 |
2.2 EtherCAT总线组态
EtherCAT组态的核心是确保主站任务周期和伺服驱动器期望的周期一致。AM600支持的任务周期至少可以设置到1ms,但实际项目中常用的是1ms或2ms。伺服工作在周期同步位置模式(CSP)时,它的插补周期必须和主站同步,这个参数如果对不上,轴就会报同步错误。
在InoProShop里,每个伺服轴的"扩展参数"里通常有"同步模式"选项,选"DC同步"或"SM同步",建议优先使用DC同步(Distributed Clock),它的时钟同步精度更高,多轴联动时各轴之间的相位差更小。
这里顺带提醒一句:EtherCAT总线的拓扑也影响调试难度。实际项目中尽量用菊花链,避免星型结构,因为星型需要额外的EtherCAT分支器,而且排查断线位置更麻烦。
2.3 标准轴控六件套:使能、回零、点动
配置好轴之后,先别上来就写复杂的多轴逻辑,把每个轴独立跑起来再说。在Codesys运动控制体系里,有一个标准的轴控流程,我习惯叫它"轴控六件套":
- MC_Power:轴使能。注意Enable和RegulatorOn两个信号都要处理,RegulatorOn才是真正把伺服使能给继电器吸合。
- MC_Home:回零。AM系列支持多种回零方式,比如当前限位、原点开关、编码器Z相。
- MC_Stop:停止。需要做急停复位时必须调用。
- MC_MoveAbsolute / MC_MoveRelative:点位运动。Start触发,Done完成位。
- MC_MoveVelocity:连续速度运动。
- MC_ReadAxisError / MC_Reset:读取轴错误和复位。
每个轴都建一个功能块实例,然后手动把伺服抱闸打开、使能、点动反转再正转,确认方向没问题。这一步必须做扎实。多轴项目里如果轴方向没确认好,后面联动时方向反了,撞机就是分分钟的事。
写代码的时候,我习惯在每个轴的功能块实例封装外面再包一层FB_AxisManager,把六个标准功能块全部实例化在这个FB内部,对外暴露接口。后续所有轴都走同一个FB,逻辑就非常统一,排查问题也方便。
3. 多轴控制的核心:电子齿轮耦合与同步逻辑
设备上了多轴之后,最常见的需求就是同步:要么固定比例同步,要么按曲线同步。这就要聊到电子齿轮和电子凸轮。
3.1 电子齿轮原理和机械齿轮的对比
机械齿轮靠物理咬合实现转速和位置的比例关系,主轮转一圈,从轮按齿数比例转固定的圈数。电子齿轮本质是把这个比例关系用软件实现:在运动控制软件里,给从站轴设定一个"跟随"模式,从站轴的位置指令 = 主站轴位置 × 比例系数。
比例系数在Codesys里用RatioNumerator和RatioDenominator两个参数表示,一个分子一个分母。这样设定可以避免小数误差,比如3:2的传动比,分子填3,分母填2,内部计算始终保持高精度。
电子齿轮相比机械齿轮最大的优势是:调整比例不需要换齿轮,一个参数改下去,加减速过程中就能平滑切换。这也是它的核心价值。
3.2 MC_GearIn实现样例与参数拆解
下面是MC_GearIn功能块的一个典型调用示例,我把注释写进去了,方便你对照理解:
// 电子齿轮啮合功能块调用 MC_GearIn_0( Execute := bGearExec, // 上升沿触发啮合 Master := stMainAxis, // 主站轴,也就是主轴 Slave := stSlaveAxis, // 从站轴,跟随轴 RatioNumerator := 2, // 传动比分子:主站转2圈 RatioDenominator := 1, // 传动比分母:从站转1圈 Acceleration := 1000, // 啮合时从站轴的加速度(单位/s²) Deceleration := 1000, // 啮合时从站轴的减速度 Jerk := 0, // 加加速度,0表示不限制 BufferMode := gmcBuffered, // 缓冲模式,让指令排队执行 Done => bGearDone, // 啮合完成输出 Busy => bGearBusy, // 动作执行中输出 Active => bGearActive, // 啮合生效中输出 CommandAborted => bGearAbort, // 动作中断输出 Error => bGearError, // 错误输出 ErrorID => wGearErrorID // 错误代码 );这段代码里,Execute需要用上升沿触发,也就是说你要给它一个从FALSE到TRUE的跳变。很多人第一次用功能块直接给Execute=TRUE,然后轴没反应,就开始怀疑是功能块坏了,其实不是,是触发电平的问题。PLCopen规范里Execute、Enable这些输入默认是边沿触发。
Acceleration参数尤其关键。齿轮啮合不是瞬间完成的,如果Acceleration设得太大,从站轴会猛地加速追赶主站位置,机械冲击巨大。我的一般做法是设为一个较小的值,比如500到1000,等从站轴平稳跟上后再逐步提高速度。
BufferMode参数在复杂运动里很有用。比如我先让轴做一段绝对运动,然后立刻执行齿轮啮合,如果BufferMode设成gmcAborting,新的啮合指令会把之前的运动强制打断,这可能会引起冲击;但如果设成gmcBuffered,啮合动作就会排到当前运动结束之后,逻辑非常安全。
3.3 虚拟主轴方案:同步逻辑更清晰
实际项目里,多轴同步常常不是简单的"从站跟随物理主轴",而是多个轴共同跟随一个虚拟主轴。这个虚拟主轴在程序里是一个软轴,不实际对应任何伺服。
为什么这么设计?因为在很多连续生产线中,真正的主轴信号可能来自编码器(通过高速计数模块读入),也可能来自某个前置工位的伺服。如果直接让多个从站分别跟随这一个主轴,你要管理的主从关系会变成一对多。而虚拟主轴的好处是:所有从站都跟随同一个虚拟参照系,逻辑变成所有轴共用同一条速度曲线。
在Codesys中实现虚拟主轴,可以创建一个"虚拟轴"(Virtual Axis),然后让所有从轴都通过MC_GearIn跟随这个虚拟轴。这样后续如果更换实际主轴来源,只需要改虚拟轴的驱动力来源,不改任何从轴逻辑。
给一个最简单的虚拟轴驱动示例:
// 虚拟主轴:用一路速度指令驱动 MC_MoveVelocity_0( Execute := bRunMaster, // 触发虚拟轴启动 Axis := stVirtualAxis, // 虚拟轴对象 Velocity := rLineSpeed, // 生产线的线速度折算成轴速度 Acceleration := rLineAcc, // 加速时间 Deceleration := rLineAcc, // 减速时间 Direction := gmcPositiveDirection, // 正转 Busy => bMasterBusy, Active => bMasterActive, CommandAborted => bMasterAbort, Error => bMasterError, ErrorID => wMasterErrorID );在虚拟轴运行过程中,所有从轴都通过电子齿轮啮合到它上。生产线的加减速曲线只需要在虚拟轴上做一次,所有从轴自然跟随,不会出现各轴加减速时间不一致导致的飞车或堆积问题。
4. 指针在Codesys中的应用:批量轴控与配方数据交换
聊完多轴,聊指针。为什么多轴控制项目里要牵扯指针?因为轴数一多,参数配置和数据交换的逻辑就变得繁琐。比如10个轴,每个轴的增益参数、回零方式、软件限位,如果逐个赋值,代码又长又乱。指针就是用来解决这类问题的。
4.1 Codesys里指针的基本语法
在Codesys(ST语言)中,指针的基本操作是:
- 声明一个指针变量,比如
pData : POINTER TO INT; - 用ADR(变量)取地址,把变量地址赋值给指针。
- 用指针加
^来访问指针指向的值。
一个快速示例:
PROGRAM PRG_PointerDemo VAR nValue : INT := 100; pValue : POINTER TO INT; nRead : INT; // 添加显示变量方便在线观察 pAddressInfo : STRING; END_VAR pValue := ADR(nValue); // pValue指向nValue的地址 nRead := pValue^; // nRead取出的值是100 pValue^ := 200; // 修改指针指向的值,nValue也变成200这段代码本身很简单,但它体现的是"间接访问内存"的思想。你在一个地方保存数据,在另一个地方通过指针去修改它,不需要频繁传参。
4.2 用指针数组优化多轴参数表
在多轴项目中,我常用的一个模式是:把所有轴的参数块放到一个结构体数组里,然后用指针数组来管理。
举个例子,我定义了一个轴参数结构体:
TYPE ST_AxisParam : STRUCT stAxisName : STRING(32); // 轴名 fHomeVel : LREAL; // 回零低速 fHomeAcc : LREAL; // 回零加速度 eHomeMode : INT; // 回零模式 fSoftwareLimitPos : LREAL; // 正向软限位 fSoftwareLimitNeg : LREAL; // 反向软限位 END_STRUCT END_TYPE然后定义全局轴参数区:
GLOBAL gastAxisParams : ARRAY[1..8] OF ST_AxisParam; // 8个轴的参数块 gpAxisArray : ARRAY[1..8] OF POINTER TO ST_AxisParam; // 指针数组 END_GLOBAL初始化的时候,把指针数组的每一项指向对应轴参数块:
FOR i := 1 TO 8 DO gpAxisArray[i] := ADR(gastAxisParams[i]); END_FOR为什么用指针数组不用直接索引?因为指针数组允许你在运行时动态修改某个轴对应的参数块。比如配方切换时,我只需要把gpAxisArray[2]从指向A配方参数块改成指向B配方参数块,从轴逻辑代码完全不用改。这个方案在设备需要支持多产品型号切换时非常好用。
4.3 指针的坑:类型转换与越界问题
指针虽好,但它是双刃剑。我总结几个踩过的坑:
- 类型不匹配:如果指针声明为
POINTER TO ST_AxisParam,你却用一个ADR(nSimpleValue)赋值,Codesys编译时会提示类型不匹配。这个还算友好,怕的是用了类型强制转换后编译器不报错,但运行时数据完全不对。所以尽量不要用类型转换强制赋值。 - 指针越界:如果一个指针指向数组的第1个元素,你通过
pIndex := 0去遍历时不小心越界了,程序不会立刻报错,但可能把别的变量数据改坏。表现非常难查:某个变量莫名其妙变了,但代码里根本没有对它赋值的语句。 - 悬空指针:如果一个FB实例被删除或进入非活动分支,而你还有个指针指向它的内部变量,就会产生悬空指针。下次访问时可能报错,也可能不报错但拿到垃圾数据。
多轴项目里,处理指针越界有一个很实用的调试技巧:在异常分支里,把当前指针访问的变量地址打印出来或记录到诊断日志。通过比较地址范围,可以快速定位是从哪个数组越界出去的。
再补充一个进阶小技巧:Codesys支持ADR()取地址函数,也支持SIZEOF()获取变量字节数。我在批量上传下载参数时,经常配合使用这两个函数:
// 把整块轴参数区上传到HMI或上位机 pSrc := ADR(gastAxisParams[1]); nByteLen := SIZEOF(gastAxisParams); // 计算整个数组占多少字节 // 然后通过通信功能块把pSrc指向的nByteLen个字节发送出去这样做的好处是:不管结构体怎么扩展,都不用改上传逻辑。只要上位机知道结构体的内存布局,就能正确解析。
5. 从练习项目到产线级程序:结构设计思路
练习项目最容易犯的毛病是"代码全堆在一个PRG里"。4个轴,每个轴一个功能块实例,再加上一堆状态位,一个PRG写到500行,看起来也能跑。但项目一扩大,就会变成灾难。所以我在练习阶段就刻意按产线级标准来组织程序。
5.1 主循环与功能块封装:别把代码全堆在PRG里
我常用的程序结构是三层:
- 第一层:PRG主任务。只做三件事:调用各FB的执行方法、管理全局状态机、做安全逻辑判断。代码量控制在50行以内。
- 第二层:功能块(FB)。每个轴或每个工艺单元一个FB,对外暴露标准接口,内部实现具体逻辑。
- 第三层:功能块内部的方法(Method)。把回零、报警处理、自动运行等子逻辑拆成独立方法。
拿轴管理的例子来说,我会封装一个FB_AxisUnit,它内部实例化MC_Power、MC_Home、MC_MoveAbsolute、MC_ReadActualPosition、MC_ReadAxisError等所有标准功能块,同时把该轴的状态机也封装进去。对外只暴露以下接口:
stAxis : AXIS_REF; // 轴引用 bAutoMode : BOOL; // 自动模式 bJogForward : BOOL; // 正转点动 bJogBackward : BOOL; // 反转点动 bHomeExec : BOOL; // 执行回零 rTargetPosition : LREAL; // 目标位置 bMoveAbsExecute : BOOL; // 启动绝对运动 bReady : BOOL; // 轴就绪 bInPosition : BOOL; // 到位 wErrorID : WORD; // 错误码这样做最直接的好处是:你在主程序里管理10个轴,只需要创建10个FB_AxisUnit实例,然后各自处理它的接口信号,主程序一点都不会乱。
5.2 状态机:报警、暂停、恢复怎么联动
多轴设备的状态管理绝对不能靠互相嵌套的IF-ELSE,要用状态机。我在练习项目里定义了一个设备级状态枚举:
ST_Init(初始化)→ ST_Ready(就绪)→ ST_Running(运行中)→ ST_Paused(暂停)→ ST_Completed(完成)报警状态下,任何状态都可以转到ST_Alarm;报警复位后,回到ST_Ready。
状态机的写法有很多种,我比较喜欢用CASE语句:
CASE eDevState OF ST_Init: // 调用所有轴的初始化逻辑 IF bAllAxisReady THEN eDevState := ST_Ready; END_IF ST_Ready: // 等待启动命令 IF bStartCmd THEN eDevState := ST_Running; END_IF ST_Running: // 运行逻辑:触发各轴动作 // 如果某个轴超时,跳到ST_Alarm IF bAnyAxisFault THEN eDevState := ST_Alarm; END_IF ST_Paused: // 暂停逻辑:所有轴先停止再保持 ST_Alarm: // 报警处理逻辑 IF bResetCmd AND bAllAxisFaultCleared THEN eDevState := ST_Ready; END_IF END_CASE状态机最大的价值是:它强制你把"此时该做什么"和"此时不该做什么"划分清楚。比如在ST_Running状态,如果设备还在等料,你不可能去触发回零动作;有了状态机控制,这种冲突自然就被规避了。
5.3 安全逻辑和限位策略
多轴设备最怕的是轴超行程后撞机,所以软限位逻辑我建议放在三个层面:
- 参数层:每个轴在轴配置里设置软件限位(Software Limit),系统自动监控。
- 逻辑层:在FB_AxisUnit内部,每次下发运动指令前,先检查目标位置是否在安全窗口内,如果超出,直接拒绝执行并给出提示。
- 硬件层:正负硬限位接到伺服驱动器的数字输入端口,通过硬件快速断开使能。
三个层面互相兜底。尤其是逻辑层这个检查,能帮你拦下一大批潜在事故。这段逻辑写起来很简单:
IF (rTargetPosition > rMaxSafePos) OR (rTargetPosition < rMinSafePos) THEN bSafetyLock := TRUE; // 锁住运动启动信号 wSafetyMsg := 16#1001; // 记录安全提示 // 不触发任何运动指令 ELSE bSafetyLock := FALSE; END_IF还要考虑“轴跟随误差过大”的保护。电子齿轮联动时,如果主从轴位置偏差超过设定阈值,说明出现了机构卡滞或丢步,必须立刻停机。这个逻辑在MC_GearIn的Active状态下,用两个轴的MC_ReadActualPosition做差值判断,差值超过阈值就触发报警停止。
6. 调试中的踩坑记录:从虚轴到实轴的教训
练习项目如果全在虚拟轴模式下跑,能帮你验证逻辑,但很多现实问题必须转到实轴上才会暴露。我这部分的经历比较多,挑几个典型的分享。
6.1 固件版本和库版本不一致
有一次项目调试,AM600的EtherCAT主站扫描从站时,偶发扫描不到某个伺服,或者扫描到了但PDO映射错乱。排查了很久,最后发现是PLC固件版本和InoProShop软件版本不匹配导致。PLC固件升级之后,需要到汇川官网重新下载对应版本的设备描述文件,导入到IDE中,再重新扫描。这个问题比较隐蔽,因为不是每次都失败,而是时好时坏。
经验:项目开始之前,统一确认三件事——PLC固件版本、InoProShop版本、伺服驱动器版本,三者的兼容性列表核对一遍,能省很多后期排查时间。
6.2 电子齿轮耦合瞬间的冲击
第一次做电子齿轮时,我直接把RatioNumerator设成2,RatioDenominator设成1,然后在主站轴高速运行时触发啮合。结果从站轴猛地一冲,机械结构差点干废。
根本原因:MC_GearIn在啮合时,默认行为是把从站轴位置强制同步到主站轴位置乘以比例值,这个同步过程需要时间,如果Acceleration设得不够大或者主从偏差太大,从站轴就要用很大加速度去追。
后来我改成这样:先在主站轴静止或低速时触发啮合,然后设置一个合理的Acceleration值,让从站轴平滑跟上。如果产线不允许低速啮合,那就需要先计算主从位置差,用一个中间速度运动把从站轴调整到位,再触发MC_GearIn。这在实际项目中通常叫"预同步"。
预同步的一段示例逻辑:
// 计算当前偏差 rDelta := (rMasterPos * 2.0) - rSlavePos; // 假设比例是2:1 // 如果偏差过大,先做一次相对运动补偿 IF ABS(rDelta) > rSyncTolerance THEN bCompensating := TRUE; // 触发从站轴MC_MoveRelative,目标就是rDelta,速度用较小值 END_IF // 偏差进入允许范围后,再触发MC_GearIn这样操作后,齿轮啮合瞬间的冲击基本可以忽略。这段逻辑你要是直接抄到项目里,注意把比例系数、同步容差做成可调参数,不同工艺要求不一样。
6.3 指针悬空的惨痛教训
指针问题我在4.3里提过,这里说一个具体事故。当时我在一个配方切换功能里用指针数组管理配方数据。配方结构体里有字符串类型字段,我初始化指针数组时,给指针赋的是某个局部变量的地址。结果那个局部变量在功能块执行完之后就失效了,指针指向了栈空间,后续读取全是垃圾数据。
这个错误最坑人的地方是:设备启动的一瞬间看起来正常,因为栈空间里的旧数据还没被覆盖,但运行了一段时间后,某个其他功能块把那段栈空间复用了,配方数据就开始乱跳。
解决方式很简单:指针指向的变量必须是全局变量或功能块内部的保持性变量(PROG内部变量),绝对不能是局部临时变量。如果你需要临时缓存,也应该先声明一个成员变量,再取它的地址。
6.4 用Trace视图和软示波器定位多轴同步问题
最后分享一个调试工具层面的技巧。Codesys有个Trace功能,可以像软示波器一样查看变量曲线,这个在做多轴同步分析时非常好用。
我在做电子齿轮调试时,会把主站轴位置、从站轴位置、两者偏差这三个变量加到Trace里,以1ms周期采样。运行一段时间后查看曲线:如果偏差稳定在一个小范围内波动,说明同步没问题;如果偏差周期性增大或持续扩大,就要检查是不是分子分母设错了、加速阶段没补偿、或者是机械背隙导致的。
Trace功能在InoProShop里通过"设备"→"Trace"进入,支持实时采集和离线分析。配合触发电平,还可以捕捉报警瞬间的数据,这个对分析故障原因非常有用。
再多说一句:抓Trace数据的时候,采样周期不要太快也不要太慢。1ms任务周期,Trace采样设成1ms或2ms比较合适;如果设太慢,快速变化的位置数据会失真;设太快又可能影响PLC实时性。
写在最后
这套练习项目做完,我对汇川中大型PLC和Codesys的认知提升了一个层次。多轴控制不是学几个MC功能块就完事,真正值钱的是如何设计同步逻辑、如何用状态机管理轴组、如何用指针高效处理参数。这些能力从练习项目里培养,成本最低,踩坑也最安全。建议你拿到AM600后,先别急着写完整产线逻辑,按我上面这个结构一步一步来:先单轴动起来,再两轴同步,再加凸轮,最后上指针。每一步都验证扎实,后面接真实项目就会从容很多。
本文还有配套的精品资源,点击获取