Codesys 工控项目实战:从工程搭建到总线排障与交付 Codesys 这套开发环境我从第一次装到电脑里到现在摸爬滚打也有好几年了。它不是那种装完就能写两行代码点运行的小工具而是一整套 IEC 61131-3 的编程平台加运行时同一套工程能跑到不同厂商的控制器上同一门 ST 语言能写逻辑、能配总线、能拉可视化、能做运动控制。这些年我用它在包装线、分选设备、老化测试台这些场景里落地过项目也被它折磨过——版本对不上、库冲突、下载卡住、掉电保持莫名清空坑一个没落下。这篇东西是我自己的使用记录从选型逻辑、工程骨架搭建、总线对接、代码写法到排障实录和版本管理一条线捋下来。适合刚上手 Codesys 的工控同行看也适合带着某品牌 PLC 经验想转过来的朋友参考我不打算讲手册里有的东西主要讲手册里不写、但现场真会碰上的那些。下面这套流程按“从零建一个能跑起来的工程”的顺序走你可以对着一路抄。1. 选型前的判断这套平台到底适合什么活儿1.1 我遇到过的真实麻烦三台设备四种软件几年前接过一条包装产线主控是某品牌中型 PLC两台伺服来自另一个品牌视觉系统自带一套配置软件上位触摸屏又是第四家。结果是改一个联动逻辑我要在三个软件里对着三份变量表逐个比对地址改错一个字的概率高得离谱。更麻烦的是备件——现场那台主控停产了换型号就得把整份工程重写一遍变量名、地址映射、通讯块全都得动。那会儿我就下定决心下一个项目一定要把“逻辑”和“硬件”拆开。Codesys 这套东西的价值就在这里它把 IEC 61131-3 标准的编程环境和运行时Runtime做了解耦。我写的是标准 ST、LD、FBD落到哪个厂商的控制器上只需要换设备描述文件、重新编译下载。同一套程序结构从 ARM 小盒子迁到 x86 工控机改动量可以压到很小。这个特性在备件停产、多机型复用的场景里省下来的时间是按周算的。注意解耦不等于免改。厂商对标准库的扩展、IO 映射方式、甚至任务调度的默认参数都可能不同。换平台一定要预留联调时间别指望“拷过去就完事”。1.2 什么活儿适合它什么活儿别硬塞我的判断标准很朴素看三条场景特征是否推荐原因多机型、小批量、逻辑复用多强烈推荐标准语言写一遍换设备只换底层需要复杂算法浮点、数组、结构体、字符串处理推荐ST 的表达能力远强于梯形图写状态机和算法舒服单机简单控制、几路开关量不太必要学习成本高于收益小 PLC 梯形图更快对实时性要求极高的高速同步微秒级谨慎要看具体运行时的调度能力和硬件支持别想当然完全依赖某品牌专有功能块谨慎迁到 Codesys 反而是给自己找麻烦上表最后一条是我踩过的有个项目用了某品牌专有的凸轮功能块迁到 Codesys 之后发现要自己重写凸轮表虽然原理不难但那一周真挺煎熬。所以选型之前先把“这套逻辑里有多少是依赖厂商专有功能的”数一遍超过三成就得重新权衡。1.3 上手前必须搞清的三件事版本、授权、目标支持这三件事搞不清楚后面全是无效劳动。第一是版本。Codesys 的版本跨度很大同一个工程在不同大版本之间打开会提示升级升级之后基本回不去。我吃过一次教训现场调试用新版打开工程做了几个改动回办公室用旧版打不开只能把新版的安装包拷到所有电脑上重装。现在的规矩是——工程建立时就定死版本号写进项目文档第一页谁都不许私自升。第二是授权。运行时一般是按设备授权开发环境免费。上设备之前确认清楚授权状态别等到了现场才发现跑在试用模式两小时一停那画面我见过一次客户就在旁边站着。第三是目标设备支持度。不是所有控制器都支持全部功能有的不支持可视化有的运动控制库要单独授权有的 EtherCAT 只支持部分从站。买硬件之前问清楚“这个型号的 Codesys 运行时支持哪些特性”比事后返工便宜太多。2. 工程骨架怎么搭设备树、POU 与任务配置2.1 新建工程与设备树第一步走错后面全歪新建工程时向导会让我选设备和编程语言。这一步很多人随手点确定后面就一直在还债。我的做法是先建一个最小可运行工程只放一个空程序和主任务编译下载确认能跑再往上加东西。这个“基线工程”要存一份模板后面每个新项目都从它复制。设备树的结构大致是设备节点控制器→ 应用Application→ 任务配置、程序、库管理器、IO 映射。层级本身不复杂但有个细节容易忽略应用Application是可以有多个的。多应用适合把“设备控制”和“数据服务”分开各自下载、各自复位互不干扰。我一般会给诊断和数据上报单独开一个应用好处是主控逻辑在跑的时候重启诊断应用不影响生产。提示模板工程里先把常用的库、常用的功能块、标准的变量命名前缀都放好。复制模板可能多花五分钟从头建工程可能多花半天。2.2 PLC_PRG 不是垃圾桶POU 的拆分原则新手最常见的问题是什么都往PLC_PRG里塞一个程序写了两千行全局变量满天飞。等要改需求根本不敢动。我的拆分原则是按“职责”切不按“设备”切得太碎也不按“功能”堆在一起按工艺单元拆程序PRG进料、分选、包装、出料各一个程序各自内部闭环。按可复用逻辑拆功能块FB气缸动作、伺服定位、报警处理、配方切换、滤波、超时检测全做成 FB参数化。按数据聚合拆结构体DUT一个工位的所有状态量塞进一个结构体传参时只传一个引用不用传二十个参数。诊断单独一层所有设备状态、报警码、通讯质量统一汇总供 HMI 和上位读取。这么拆之后一个典型项目大概二三十个 POU每个都不长改哪儿心里有数。反过来如果一个 POU 超过 300 行我会强迫自己看看是不是能再切一刀。2.3 任务配置扫描周期怎么定看门狗怎么设任务配置是新手上手最容易糊弄、现场最容易出问题的地方。我的经验值是任务类型典型周期说明主逻辑任务4~10 ms常规逻辑控制够用就行别盲目追小运动/高速IO任务1~2 ms与总线周期对齐独立任务通讯/数据处理20~100 msModbus 轮询、上位数据打包诊断/看护100~500 ms心跳、故障汇总、日志周期不是越小越好。周期从 10 ms 压到 2 msCPU 负载翻几倍换来的控制精度对绝大多数产线毫无意义因为机械惯性和气缸响应本身就是几十毫秒级。反过来如果一个本该 4 ms 的任务你配了 50 ms位置环会抖、高速计数会丢那时候再改就晚了。看门狗Watchdog的设置我一般按任务周期的 4~5 倍给。比如主任务 4 ms看门狗设 16~20 ms。设太小偶发的一个耗时循环就复位设太大真死机了也发现不了。另外要记住看门狗超时的默认行为是让控制器进停止状态量产设备上这个行为要提前跟客户对齐因为一旦触发就是全线停机得让人知道怎么恢复。2.4 变量声明命名、掉电保持与常量变量命名我用匈牙利前缀加英文不用拼音——拼音缩写过三个月自己都认不出来。前缀规范大致这样x布尔i整型r实型s字符串t时间e枚举st结构体a数组。全局变量加G_前缀功能块内部变量不加。输入_In、输出_Out功能块引脚上看得明白。掉电保持分两种这个一定要分清Retain保持变量普通保持程序重新下载在线修改时可能被重置具体行为看运行时实现。Persistent持久变量独立存储区下载、复位都不清适合存校准参数、累计产量、设备序列号这类“绝不能丢”的数据。我见过最典型的事故是把配方参数全放在 Retain 里一次在线修改下载参数全变默认值产线跑出来一批废品。从那以后我的规矩是——校准值、配方、计数器一律 Persistent普通状态量用 Retain 都嫌多。常量用VAR_GLOBAL CONSTANT不要写魔法数字。一个 3.14159 散落在二十个地方改起来就想哭。3. 现场总线对接EtherCAT 与 Modbus 的实操细节3.1 EtherCAT 从站配置别急着点“扫描设备”EtherCAT 的配置流程大概是这样添加主站 → 导入从站 ESI 文件 → 扫站或手动添加 → 配置过程数据PDO→ 映射到变量 → 设置分布式时钟。新手最容易犯的错是直接点“扫描设备”扫出来一堆站然后过程数据默认映射能跑就完事。问题在于默认 PDO 往往包含一堆用不上的对象白白占带宽和内存而且顺序不一定适合你。我的做法是先扫一遍确认站顺序和型号然后手动整理 PDO只保留真正要读写的那几项映射到自己定义的全局结构体里把自动生成的变量表全部删掉。分布式时钟DC这块我的建议是伺服轴和需要严格同步的从站开 DC普通 IO 模块可以不开。全开也不是不行但配置复杂度和排障成本会上升一旦某个从站不支持 DC 或者同步有问题整条链路的抖动会让位置精度莫名其妙地漂。注意EtherCAT 站的物理顺序必须和工程里的顺序一致中间少一个站、多一个站都会导致链路起不来。现场加装一个 IO 模块工程里必须同步改这一点要写进设备变更流程。3.2 Modbus 通讯的坑地址偏移和字节序Modbus 这块我几乎每个项目都要重新确认一遍两件事因为它不像 EtherCAT 那样有统一描述文件。第一件是地址偏移。不同厂家说的“40001”和“0”是不是一回事必须看手册里的说明——有的文档写的是寄存器编号从 1 开始有的写的是协议地址从 0 开始。我在一个电表项目上就栽过手册写“读取 0001H 开始的 10 个寄存器”我按 1 写实际要读 0结果读回来全是错位数据查了两小时。第二件是字节序。一个 32 位浮点数在 Modbus 里占两个寄存器谁在前谁在后厂家说了算。常见四种排列ABCD、CDAB、BADC、DCBA。我一般会先读一个已知固定值比如设备序列号或版本号拿四种排列各试一次看哪个能对上记进文档。这个“试”的过程五分钟猜的过程可能一整天。另外Modbus 轮询别放在主任务里同步阻塞。我的做法是单开一个 20~50 ms 的任务用状态机做非阻塞轮询发请求 → 等响应 → 校验 → 下一个站。超时和重试次数都要设正常情况下每站超时 200~500 ms重试 2 次连续失败就置通讯故障位让 HMI 报警而不是傻等。3.3 伺服轴参数换算电子齿轮比这一步必须算清楚运动控制这块最容易出问题的是单位换算。以一根丝杠为例导程 10 mm减速比 1:5电机转 5 圈丝杠转 1 圈编码器分辨率 23 位 8388608 脉冲/转。先算一个脉冲对应的位移一个脉冲位移 10 mm / (8388608 × 5) 10 / 41943040 ≈ 2.384 × 10⁻⁷ mm如果我希望程序里用的单位是 0.001 mm也就是 1 μm那么一个用户单位需要多少脉冲脉冲/单位 8388608 × 5 / 10000 4194.304不是整数。这就意味着直接设整数电子齿轮比会有累积误差跑一百万个单位就偏出几百微米。解决办法有两个一是在轴配置里用小数齿轮比如果运行时支持二是把用户单位改成 0.01 mm 或者直接按脉冲编程。我一般倾向第一个参数里把 4194.304 原样填进去别自己四舍五入成 4194。提示算完齿轮比一定要做一次“走 100 mm 量实际位移”的验证别信计算值。机械上的间隙、皮带打滑、联轴器松动算得再准也白搭。4. 代码怎么写状态机、功能块与库管理4.1 状态机用 CASE别用一堆 IF 嵌套ST 语言里写顺控流程标准写法就是枚举加 CASE。我所有设备动作都长这样TYPE E_StationState : ( IDLE : 0, HOMING : 10, READY : 20, RUNNING : 30, PAUSING : 40, FAULT : 90 ); END_TYPE CASE eState OF E_StationState.IDLE: xOut_Ready : FALSE; IF xIn_Start AND NOT xFault THEN eState : E_StationState.HOMING; END_IF E_StationState.HOMING: fbHome(bExecute : TRUE); IF fbHome.bDone THEN fbHome(bExecute : FALSE); eState : E_StationState.READY; ELSIF fbHome.bError THEN iErrCode : fbHome.nErrorID; eState : E_StationState.FAULT; END_IF E_StationState.READY: xOut_Ready : TRUE; IF xIn_Start THEN eState : E_StationState.RUNNING; END_IF E_StationState.RUNNING: // 工艺流程 IF xIn_Stop THEN eState : E_StationState.PAUSING; ELSIF xFault THEN eState : E_StationState.FAULT; END_IF E_StationState.FAULT: xOut_Ready : FALSE; IF xIn_Reset AND NOT xFault THEN iErrCode : 0; eState : E_StationState.IDLE; END_IF END_CASE几个规矩我一直守状态值用显式赋值不用默认递增方便在线监视和日志记录每个状态里只做一件事所有状态跳转必须在同一周期内完成判断不能跨周期遗留标志位故障状态单独一个值不要藏在 RUNNING 里。4.2 一个可复用的功能块把重复劳动收干气缸动作是我所有项目里重复最多的逻辑所以我把它做成标准 FB全公司项目复用FUNCTION_BLOCK FB_Cylinder VAR_INPUT xCmd : BOOL; // 动作命令 tExtendTime : TIME : T#3S; // 伸出超时 tRetractTime: TIME : T#3S; // 缩回超时 xExtendSensor : BOOL; // 伸出到位 xRetractSensor: BOOL; // 缩回到位 END_VAR VAR_OUTPUT xOut : BOOL; // 电磁阀输出 xBusy : BOOL; xDone : BOOL; xError : BOOL; iErrCode : INT; END_VAR VAR eState : E_CylState; tonTimeout : TON; END_VAR内部逻辑就是一个小状态机命令来了先开阀、启动超时定时器、等到位信号、到位后置 Done超时没到位就置 Error 并关阀。关键点是超时时间做成参数因为不同工位的气缸行程差别很大硬编码在 FB 里等于白做。这个 FB 我用了几年中间加过两个改进一是加“到位信号防抖”传感器抖动导致的误判太常见二是加“手动模式强制动作”的旁路方便调试。这些都是从现场反馈里长出来的不是一开始就想到的。4.3 库管理版本锁定比功能丰富更重要Codesys 的库管理器是我又爱又恨的地方。爱的是标准库、运动库、通讯库一装就有省了大量工作恨的是库版本冲突——尤其是多人协作或者升级开发环境之后。典型症状是打开工程提示“库未找到”或者“版本不匹配”编译一堆红色波浪线指示某个功能块不存在。处理办法在库管理器里看每个库的“有效版本”和“引用版本”把不一致的统一到同一版本然后右键“保存库快照”或者把库随工程一起存放。我的规矩是项目启动时确定库版本写进文档不允许单独升级某个库要升就整体评估交付时把用到的第三方库.library文件随工程一起打包别指望甲方电脑上装了。提示如果工程里有自己写的库切记在所有引用它的 POU 都编译通过之后再更新库顺序错了容易出现“引用了旧版本”的幽灵问题重启开发环境才好。5. 排障实录现场最常见的几类问题5.1 通信问题速查现象最可能原因处置扫描不到设备网卡选错 / IP 段不符 / 防火墙逐项排查网卡、IP、关闭干扰下载卡在 30%目标在运行态 / 授权异常停止运行再下载检查授权EtherCAT 起不来站顺序不符 / 少站 / 线缆问题对站序逐个短接排查Modbus 数据错位地址偏移或字节序读已知值验证四种排列偶发通讯超时轮询阻塞主任务 / 网络干扰拆分任务加超时重试HMI 读不到变量变量未勾选“网络可见”在变量属性里开放访问最后一条我提一下因为太多人卡在这。Codesys 里变量默认不一定对 HMI 或上位开放需要在变量声明或符号配置里勾选相应属性否则通讯库读回来全是零或者报无效句柄。5.2 编译与下载类问题“编译通过但下载报错”八成是设备描述文件版本和目标固件不匹配。解决办法是取目标设备的实际固件版本装对应的设备描述包然后重新建工程不是改是新建把 POU 拷过去。这个操作麻烦但比重装系统快。“变量在线修改不生效”检查是不是在“在线修改”模式下改了声明区。声明区的改动往往需要重新下载强制修改只能改变量值不能改结构。我一般的做法结构改动就停止、下载、重启不要试图热改省的那两分钟不够排查。“程序跑飞看门狗复位”先看任务的实际执行时间在线监视里有如果某次执行时间飙到周期以上基本就是死循环或阻塞调用。常见的是 While 循环没退出条件、字符串拼接、库函数内部等待。这类问题只能用“二分注释法”定位把程序一段段注释掉跑看哪段触发。5.3 运行期异常与保持数据丢失掉电保持数据丢失我总结出三种原因变量类型不对用了 Retain 而不是 Persistent下载即清。保持区容量超限声明的保持变量超过运行时分配的存储空间超出的部分静默失效。这个最坑没有任何报错。电源掉电时序断电太快运行时来不及把数据写回存储。这个问题要靠硬件解决加 UPS 或者选带掉电保持电容的控制器。第二种我遇到过一个项目累计用了两百多个保持变量现场反馈“偶尔参数变默认值”查了两天才发现保持区超了。后来养成的习惯是——保持变量一律集中在单独的结构体里方便统计数量也方便整体导出备份。另外我给所有关键保持数据加了一个“校验字”在结构体末尾放一个由其他字段算出来的校验值上电时校验一次不匹配就报警而不是静默使用默认值。这个做法在量产设备上非常有用能提前发现问题而不是等跑了半天才发现参数不对。6. 工程管理与交付让下一个人能接手6.1 版本管理用 Git 管 Codesys 工程Codesys 工程是二进制/项目文件格式直接 Git 会有一堆无法比对的问题。我的做法是主工程文件纳入版本管理但每次提交必须写清楚改了什么关键的 POU、DUT、GVL 导出成纯文本.txt或.st单独存一份作为可读备份和 diff 依据每次重大改动打 tag对应现场的软件版本号现场调试用的临时改动单独建分支别把调试代码混进主干。这套做法牺牲了一点便利但带来的好处很实在现场出问题能立刻定位到“上次改了什么”。我经历过一次客户说某动作偶尔不执行翻提交记录发现三天前一次改动把某个条件的边沿触发改成了电平触发五分钟定位。6.2 交付清单别只交一个工程文件交付的时候我一般准备这几样交付物内容为什么必须有工程源文件完整工程 库文件后续维护的基础版本说明版本号、日期、改动点出事时能对上号变量表IO 点表、通讯地址表、保持变量清单接线和对接上位靠它参数记录伺服齿轮比、轴参数、通讯参数换件重配靠它调试记录遇到的问题与处理方式下一个人的救命稻草授权信息运行时授权状态与位置换机、扩容要用最后一项特别容易被忽略。我见过现场换了一台控制器发现没授权等了一周。所以在交付文档里写清楚授权是绑在哪台设备上、怎么转移比什么都实在。调试记录这份东西我建议用最朴素的方式写——时间、现象、做了什么事、结果。不用多正式但一定要写。因为现场调试那几天脑子是高度紧张的很多东西当时觉得“记住了”一周后全忘。提示每次现场调试结束趁热把当天改过的参数、动过的逻辑记下来同步到工程注释里。等三个月后返修这些注释就是唯一的线索。这套流程跑下来一个中等规模项目从选型到交付大概两三个月工程文件本身可能只占三成工作量剩下的全是现场对接、排障、文档。Codesys 帮我省下的主要是“换平台重写”的成本但省不了“把事做扎实”的成本。我个人的体会是用这套平台最值钱的能力不是会写 ST而是知道哪些地方该留余量、哪些参数该做成配置、哪些数据必须持久化——这些东西任何一本手册都不会写只能靠一个个项目攒出来。