西门子S7-1200码垛机程序结构详解与联调实战经验
从现场调试间走出来的时候我习惯把最后一版带注释的程序归档到网盘备注写下“结构清楚注释齐全可复用”。这几个字看起来简单实际上是我做过的十几个码垛机项目里最想留给下一任工程师的东西。西门子1200做立体库码垛机控制说难不难说简单也真不简单——难的不是那一两个运动控制指令难的是整套程序拆下来能不能让下一个接手的人半小时看懂、两天改完、三周跑通现场。这篇就把我反复打磨过的1200码垛机程序和立体库码垛机程序结构掰开聊从硬件选型、FB划分、坐标换算到联调踩坑全是我在真实项目里验证过的东西适合正在做码垛机、堆垛机、库前输送线项目的电气工程师也适合准备用S7-1200入行运动控制的新手。1. 先说结论这套程序到底解决的是哪一类问题码垛机或者说立体库码垛机本质上是一台三轴运动设备X轴负责水平行走Y轴负责货叉升降Z轴负责货叉前后伸缩再配合库前的输送线、托盘工位和仓库管理系统WMS的手势交互。很多第一次做这类项目的工程师容易把它想得太玄觉得“码垛”一定涉及复杂的路径规划、防碰撞算法。真到现场你会发现90%的码垛逻辑都是规则堆叠也就是固定垛型的坐标换算加一点点顺序判断真正让设备跑不起来的往往不是算法而是程序结构混乱、注释缺失、轴状态没有闭环、握手信号没做超时处理。我这套程序就是用S7-1200作为主控把立体库码垛机的全部动作拆解成可复用的功能块。X、Y、Z三轴全部走Profinet连接伺服驱动器用西门子标准的定位功能块做点到点运动控制不需要PLC写复杂的PID或插补算法。程序能做到“结构清楚明了”核心是我把每个功能边界切得很干净手动模式归手动自动流程归自动轴控制归轴控制垛型数据归数据。你打开项目文件夹看到OB1里的调用顺序就能像看目录一样知道整套设备怎么跑。注释方面我做到了块头注释、网络注释、变量注释三级覆盖尤其是轴参数里的减速比、行程范围、回零方向这种“魔鬼细节”全部写进了UDT的注释里。这套东西不是某个特定机型的专用程序我后面换过托盘尺寸、换过货叉行程、换过伺服功率都是只改数据块和垛型参数不碰逻辑块这也是我敢说“可复用”的原因。2. 硬件选型与程序架构我为什么这么切分2.1 为什么是S7-1200而不是200 Smart或1500很多朋友问过我一个问题码垛机这种设备用S7-200 SMART不也能做吗脉冲控制三台伺服成本低一大截。确实能但有一个很实际的门槛S7-200 SMART的PN通信能力弱跟WMS做以太网对接、跟伺服驱动器走Profinet都会很别扭而且它的数据块管理方式和SCL编程支持都比较初级程序一旦超过几百行后期维护很痛苦。反过来如果你选S7-1500性能是绰绰有余但CPU加机架的硬件成本直接翻倍对于码垛机这种逻辑为主、运动为辅助的设备来说属实没必要。S7-1200正好卡在中间。CPU 1215C自带双网口交换机可以一路接WMS、一路接伺服Profinet网络硬件上省一个交换机运动控制方面支持轴工艺对象和FB284这种标准功能块调伺服不用自己拼报文编程上支持SCL语言垛型坐标换算、数组遍历这类逻辑写起来非常顺手。我用得最多的是CPU 1215C DC/DC/DC输入输出点大概是X/Y/Z三轴的限位、原点、抱闸反馈用掉十几个DI输送线光电、托盘检测、按钮指示灯用掉十几个DI/DO总点数在40点左右1215C自带的DI/DO足够覆盖偶尔有个别信号我才会挂一个小远程IO模块。2.2 伺服方案与FB284的搭配逻辑三轴伺服我统一选了带Profinet接口的V90伺服报文配置用的是EPOS模式下的标准报文111。为什么不用脉冲方式原因有两个。第一Profinet方式省掉了一大把高速输出点X、Y、Z三台伺服如果走脉冲至少要三个高速输出点加三路脉冲方向控制硬件接线绕来绕去排查故障的时候非常煎熬。第二V90走Profinet之后伺服报警、当前位置、跟随误差这些诊断数据可以直接读回PLC无论是HMI报警还是远程运维都方便得多。运动控制我全部通过FB284功能块来实现。FB284是西门子库里的SINA_POS专门用于S7-1200/1500通过Profinet控制V90做定位。我一般把它封装成一个自己的轴功能块叫FB_AXIS内部把模式选择、速度给定、加减速时间、使能、回零、故障复位全部处理好外面只暴露几个BOOL和REAL接口。这样做的好处非常明显自动流程里调用轴的时候不用每次面对一堆杂乱的引脚只需给“目标位置”“速度”“启动”三个信号轴就会自己走完全部过程。2.3 程序结构OB1只做调度业务全在FB里这套程序在结构上我把它们切成了六个部分对应六个功能块加一个主组织块功能块职责关键输出OB100初始化加载垛型参数和轴参数轴使能状态、数据块初值OB1主循环按周期调用各FB调用顺序即运行逻辑FB_MANUAL手动/点动模式维护用单轴点动、抱闸控制FB_AUTO自动流程状态机取货、码垛、送库的流程步号FB_AXIS单轴封装内部调FB284位置到达、轴空闲、轴故障FB_STACKING垛型计算与码放顺序每层目标坐标FB_ALARM报警集中处理弹窗信息、故障复位请求OB1里的调用顺序我固定为读输入信号、调用FB_MANUAL或FB_AUTO由手自动切换决定、调用FB_AXIS处理轴指令、调用FB_ALARM刷新报警、最后写输出信号。这个顺序说白了就是“采集—逻辑—执行—反馈”的循环。如果把逻辑块和数据块揉在一起今天加一个信号明天加一个延时程序很快变成一锅粥。我把所有轴参数、垛型参数、中间变量分别放在独立的DB里例如DB_AXIS_PARAM只放三轴的行程、速度、回零方向DB_STACK_PARAM只放托盘尺寸、箱尺寸、每层数量。数据归类清楚程序结构自然就清楚了。一个很容易忽略的地方DB的“优化访问”属性。我建议轴参数DB和垛型参数DB都勾选“优化访问”但是通过符号访问方式如果现场有人用老版本的编程习惯去按地址强制读写数据块会发现自己找不到偏移地址这其实是好事——逼着所有人按符号走避免程序里到处飘着DB1.DBX0.0这类看不懂的地址。3. 码垛控制的核心坐标体系、垛型参数和状态流转3.1 坐标体系必须先定“三个点”码垛机的坐标计算如果不先定基准编到后面一定会乱。我每次做项目都强制要求自己先画一张坐标定义草图标清楚三个关键点机械原点、取货安全高度、码垛区域的绝对零点。机械原点是设备回零后各轴编码器计数的起始位置一般定义在X轴左端限位、Y轴最高位、Z轴缩回位。取货安全高度指货叉在输送线上方或货架层格内移动时不会撞到货物的高度这组数值必须实测出来不能拍脑袋否则货叉前进后退时最容易刮到托盘或者货物。码垛区域的绝对零点是整个垛型的基准坐标通常定义在码垛托盘工位靠近X轴零点的一个角点。所有“第几行第几列第几层”的目标坐标全部由这个基准点加上偏移量计算而来不采用绝对坐标逐点存储的方式。这样做的原因是后期换型能力极强托盘尺寸一换只改基准点的坐标和偏移量不用重新示教几百个点。3.2 垛型参数用UDT和数组管理垛型参数我是用UDT加数组实现的。UDT_STACK_LEVEL里面定义了X偏移数组、Y偏移数组、Z层高数组和每层的箱数。DB_STACK_PARAM里放一个UDT_STACK_LEVEL的三维数组第一维是层号第二维是行号第三维是列号。整个定义看起来不复杂但非常直观——你要修改第3层第2行第4列的位置直接打开数据块找对应数组元素就行不用去翻几十页的梯形图。坐标换算的核心逻辑用SCL写非常顺手。以一个5层、每层3行2列的垛型为例程序每码完一箱先计算出当前步对应的层、行、列再通过基准坐标加偏移得到目标位置// 计算当前目标坐标 // Layer当前层RowInLayer当前层内的行ColInLayer当前层内的列 #TargetX : #BaseX #StackDB.XOffset[#Layer, #RowInLayer, #ColInLayer]; #TargetY : #BaseY #StackDB.YOffset[#Layer, #RowInLayer, #ColInLayer]; #TargetZ : #BaseZ - #StackDB.ZHeight[#Layer]; // 送达后计数 #DoneBoxCount : #DoneBoxCount 1; // 判断是否该切换列、行、层 IF #ColInLayer #MaxCol THEN #ColInLayer : #ColInLayer 1; ELSIF #RowInLayer #MaxRow THEN #RowInLayer : #RowInLayer 1; #ColInLayer : 1; ELSE #Layer : #Layer 1; #RowInLayer : 1; #ColInLayer : 1; END_IF;这段代码是整个码垛控制的中枢。为什么用层、行、列三级循环而不是一个扁平计数器加一堆取余运算因为现场调试的时候人最习惯看到的是“现在码到第3层第2行第1列”而不是“当前已计数到第13箱”。数据可视化程度越高调试效率越高。段注释里我也会写清楚当前坐标对应机械位置多少方便后面机械工程师对照。3.3 状态机取货—行走—升降—放货—返回自动码垛的状态流转我是用步号加CASE语句实现的。常规定义是S0空闲、S1取货下降、S2货叉进、S3货叉升、S4货叉退、S5行走至目标格、S6货叉进、S7放货下降、S8货叉退、S9返回安全位。这套步号从开始到最后几乎没有大变过因为它完整覆盖了码垛机的核心动作循环。步与步之间的转移条件全部写成独立的网络谁要看某一步为什么卡住直接看对应网络的注释就知道前置条件是什么。比如S2到S3的转移条件是“货叉前进到位信号已置位并且升降轴当前在安全高度以下”这就避免了货叉还没完全伸进托盘底下就抬升导致顶翻货物的危险。每次现场客户说设备动作不对我第一件事不是去猜而是打开变量监控表看当前步号卡在第几步再顺着那一步的条件逐个排查十分钟内基本能锁定问题。3.4 轴联锁必须写进逻辑而不是全靠机械限位码垛机最怕的就是X轴还在走Z轴货叉已经伸出去一撞就是大事故。所以我在FB_AXIS内部强制做了三层联锁第一层是HMI使能总开关第二层是各轴的正负限位硬信号第三层是程序内的位置区间软互锁。软互锁的逻辑是Z轴只有在X轴位于取货位或放货位窗口内才允许前进Y轴只有在Z轴完全缩回时才允许下降到低于某一高度。软互锁的参数在DB_AXIS_PARAM里全部做成可调实数比如“Z轴允许伸出时X轴的左极限”和“右极限”。不要问我为什么不做死在程序里——每个现场的货架尺寸不一样货叉在哪个窗口内才安全只有现场调出来才是最准的。做死在程序里换个现场就得改逻辑容易改出Bug。全部参数化不同项目直接改DB逻辑块一个字节都不用动。4. “注释齐全”的落地套路从全局DB到每一行4.1 注释的四个层级写注释不难难的是让注释匹配真实程序而不是变成装饰品。我给自己定了一个注释规范总共四个层级写注释的时间和写代码的时间差不多但收益绝对值得。第一层是块头注释。每个FB、OB、DB的最上方都有一段文字写明这个块的用途、涉及哪些轴、哪个项目用过、作者和修改日期。第二层是网络注释。每段逻辑不管多简单都要写一句“这一行在干什么”。第三层是变量注释。所有临时变量、静态变量、全局DB变量的注释必须写清楚物理含义和单位。第四层是异常说明。现场处理过的故障我会把现象和根因记在对应的网络注释后面这层注释看起来最“多余”但下一回同类问题再发生时它能帮你省掉一整天的排查时间。举一个实例。DB_AXIS_PARAM里的X轴速度变量普通写法是给个名字加一个“速度”注释。我的写法是“X轴行走速度单位mm/s软限幅值需小于机械最大速度的80%V90参数P2000的分子必须与此值一致。”可能有人会觉得啰嗦但正是这种“啰嗦”让一个从没接触过这个项目的工程师也能放心地改参数而不是改完不知道影响哪里。4.2 命名规范让变量自己会说话程序里我全面用了“前缀物理量后缀”的命名方式。BOOL信号用“bXxx”REAL信号用“rXxx”INT用“iXxx”轴相关用“xAxis_”“yAxis_”“zAxis_”前缀WMS对接信号用“wms_”前缀。这样变量监控表一拉出来哪种类型、属于哪台轴、是给谁的信号一目了然。变量命名的功夫在编程之前就做完了每当新加一个信号我会先花十秒钟确认它应该归到哪个前缀体系里而不是随便起名扔在主程序里。这套规则推行下来程序里几乎不会出现“m1”“temp3”这种看三遍都不知道是干嘛的变量。注释齐全加命名规范两个东西配合起来程序的可读性才会真正上台阶。我在前一个立体库码垛机项目里和A同学配合调试他不需要我问任何问题光看程序就能判断出某个轴当前位置异常是因为回零方向配置反了。当时我就意识到注释和命名的价值不只是在交付时好看它直接决定一个团队能不能并行干活、一个项目能不能从有人守着变成放养式稳跑。4.3 报警与注释联动报警这块我用了一个小技巧FB_ALARM里每一条报警都有一个对应的注释字符串而且和HMI上的中文提示文本严格同步。例如轴故障报警的触发条件是FB_AXIS输出的“轴故障”信号程序在处理报警的同时会把故障代码、目标位置、实际位置、速度设定值全部抓到诊断缓冲区并写入网络注释里。后期如果客户打电话过来说设备报警了我只要远程打开DB看最近几条诊断记录基本就能还原现场发生了什么。这个习惯是在一次现场故障中逼出来的。当时设备在凌晨三点报警停机现场值班人员只说了句“屏幕上有红色弹窗”我远程拿到CPU诊断缓冲区才发现是Z轴追随误差超限原因是机械装配时货叉滑块松动导致阻力异常。从那以后报警不带上下文信息的程序我一律视为不合格。5. 立体库对接联调中踩过的三个典型坑5.1 WMS握手信号没有做超时处理立体库码垛机基本都要和WMS或更高一级调度系统做联锁。最常见的方式是PLC和WMS之间交换一组BOOL信号WMS发“允许取货”PLC回“取货完成”WMS再发“允许放货”。听起来很简单但有一个致命问题——WMS程序是谁写的你根本控制不了对方的通信节点一旦卡死握手信号可能一直保持上一次的状态PLC如果没有超时判断就会一直傻等整个码垛线停摆。我的做法是给每一条握手信号加定时校验。比如等待WMS“允许放货”信号超过设定时间默认60秒参数可调仍未收到PLC置位“WMS超时报警”自动流程停下来并输出当前等待状态机的步号。同时将握手的时序状态写入诊断DB方便事后追查是WMS没发还是网络延迟。这个坑我不只踩过一次第一次没有超时保护半夜两点客户打电话说全线停了我远程一看WMS侧程序跑飞了PLC还在傻等。加了这个机制之后就算再遇到WMS卡死至少设备能给出明确报警而不是默默停机。5.2 Profinet断线后轴的“静默失控”Profinet通信在多层立体库环境中容易受到干扰尤其是电缆走线和电机动力线绑在一起太久的情况下偶发断线不可避免。断线本身不可怕可怕的是断线后PLC仍然把运动指令发给轴轴已经收不到但设备看起来“还在运行”。我踩过最痛的一次坑X轴正在快速行走Profinet瞬间断线FB284实际没有收到使能但PLC侧的逻辑还在按“行走中”的状态推进等到通信恢复轴从断线前的实际位置跳到新目标位置直接撞到货架立柱幸好速度不快没酿成大事故。那次之后我在每根轴的FB_AXIS里强制加了一个通信监控每50毫秒读一次伺服驱动器的实际位置和状态字如果连续三个周期读不到数据立即置位轴故障并触发急停同时将“通信丢失”写入诊断缓冲区。这个逻辑不加的话Profinet网络偶发抖动看起来是小问题但放在码垛机上就是大风险。整台设备几米长的行程一两秒失去位置反馈后果可能是几十万的机械损伤。5.3 垛型数组越界导致的“第N层超限”有一次现场调试堆垛机每次码到第3层第2行时就报“Y轴超限”重新回零后再跑又是同一个位置报警。按经验第一反应是机械行程不够或限位开关松动但拆开检查一切正常Y轴距离硬限位还有一百多毫米余量。后来我关掉自动流程手动单步执行发现“超限”只出现在自动码垛流程里。这才想到去查垛型数组的定义。结果发现DB_STACK_PARAM的ZHeight数组定义成了3层但程序计算到第4层时才去访问ZHeight[4]数组越界读到了一个随机的巨大值导致目标位置超出软限位触发报警。问题根子在数组长度和层数定义不匹配——我改了DB里的层数定义忘了同步修改数组上界。这个坑很有代表性。后来我把所有垛型数组的上界统一由“最大层数”常量控制并且在OB100初始化时加了一个自检遍历所有数组元素如果发现非法值负数、大于行程的离谱值就直接停机报警不等到程序运行到那一层才暴露。从那次起类似问题再没有在调试现场出现过。6. 拿到这套程序做移植时我建议你重点改哪里6.1 轴参数量程与减速比必须先核对程序移植到新设备上第一件事不是跑自动流程而是把DB_AXIS_PARAM里的每一项参数重新核对一遍。减速比、丝杠导程、编码器分辨率、行程范围、回零方向任何一个错了轴动作都会和HMI显示的目标位置对不上。我遇到过最隐蔽的问题减速比从原来的15改成了20程序里没同步更新结果X轴定位每次都偏一点累积起来越来越明显乍看像丢步实际上是位置换算基础就错了。核对方法建议用“慢速点动百分表打表”的方式手动把轴走100mm用百分表实测到位位移是不是100mm误差过大就检查伺服电子齿轮比和PLC轴参数的量程是否一致。这一步一定不要省我在新设备调试时至少检查两遍。6.2 垛型换型测试比跑通一种垛型更重要很多工程师觉得能跑通一种垛型就算成功了。但实际上码垛机项目的完美验收标准是不同托盘尺寸、不同箱体尺寸、不同层数都能通过切换配方参数实现且不需要修改一个逻辑字。前面说的UDT加数组方案就是为了这个目标。换型测试我建议做三次第一次用小箱小托盘验证坐标偏置方向是否正确第二次用大箱大托盘验证软限位是否会在极限位置正确触发第三次用奇数层数验证层数切换时Z轴高度计算是否正确。三次全过了这套程序才算真正完成了“移植”。如果只跑一种垛型就直接交付总有一天换型时会出问题而那时候再改程序成本高得多。6.3 注释也要跟着程序一起“移植”这不是玩笑。我见到过不少程序逻辑块换了新设备原来的注释却原封不动留着比如项目名还是旧的、作者还是之前的同事、轴参数注释里写的是旧丝杠导程。这样的注释不但没帮助反而会误导后来的人。我的习惯是每次移植完成后花半天时间把所有注释过一遍凡是和当前设备不一致的全部改掉。块头注释里的项目信息、修改日期、设备型号都要刷新。这套程序之所以能在十几个项目里稳定复用始终维持“结构清楚明了注释齐全”的状态靠的就是这种每次移植都不带包袱的做法。别人拿到手的第一眼感觉就会像打开一本排版清晰、标注完整的技术手册而不是读一份加密过的草稿。最后顺手说一句与程序无关但同等重要的经验码垛机程序写完后我在每个轴的功能块里都保留了一个“抱闸手动测试”接口。设备长期停机后抱闸容易出现粘滞或者未完全打开的状态这个接口能让维护人员不跑自动流程就单轴测试抱闸是否正常释放。多数程序都不会留这个口子但它能让你在现场维护时少拆十几次防护罩。