西门子S7-1200 PLC三部十层电梯群控系统:结构化编程与S7通信实战
简介本资源为2021年西门子PLC技能竞赛官方赛题——“三部十层PLC程序”的完整工程文件包面向工业自动化专业学生、PLC初学者及备赛技术人员聚焦梯形图逻辑设计、S7-1500系统架构分层控制与TIA Portal工程实践能力提升。压缩包共130个文件含11个XML项目配置、12个PNG界面截图、7个PDL人机交互页面、11个RPL运行日志及多个BAK/CFG/DB等核心工程备份与数据库文件全面覆盖输入处理、十层逻辑运算、输出执行三大功能模块体现典型电梯控制系统分层建模思路。资源大小63.54MB结构完整、命名规范可直接导入TIA Portal V15.1运行调试附带工程配置说明与版本标识如ap15_1、LT_21_07_21_10_27_30.bak便于理解赛题实现路径与备份机制。已有2808人学习下载是掌握西门子PLC模块化编程、工程管理及多层逻辑优化的优质实操范例。1. 项目背景与核心价值最近在整理硬盘时翻到了一个尘封已久的项目文件夹名字就叫“2021年西门子PLC比赛三部十层PLC程序”。点开一看里面是当年参加一个行业技能竞赛时针对一个模拟“三部十层电梯群控系统”编写的全套西门子S7-1200 PLC程序。这个项目虽然过去几年了但里面涉及的编程思路、结构化设计、故障处理逻辑尤其是多PLC之间的协同与通信到现在看依然非常经典和实用。很多刚接触中大型项目或者对西门子PLC结构化编程感到困惑的朋友常常会问我有没有好的学习案例我觉得这个比赛程序就是一个绝佳的“麻雀虽小五脏俱全”的范本。这个程序要解决的问题很明确模拟一个拥有三台独立电梯A梯、B梯、C梯的十层大楼实现高效的群控调度。目标不仅仅是让每台电梯能独立响应本梯的召唤按钮更重要的是要让三台电梯作为一个整体智能响应大楼各层的公共厅外召唤信号。比如你在5楼按了上行按钮系统需要判断哪台电梯正处于空闲、哪台电梯顺路、哪台电梯响应最快然后将这个召唤任务分配给最合适的那台电梯去执行。这背后涉及到单部电梯的完整运行逻辑、多部电梯之间的数据交换与协调算法以及如何用西门子博途TIA Portal软件进行清晰、可维护的结构化编程。对于PLC学习者而言单纯看手册或简单例程往往难以理解大型程序是如何组织起来的。这个比赛程序恰好展示了如何将一个复杂的控制系统分解成若干个功能明确的块Function Block, FB如何规划数据块DB如何设计高效的通信机制。无论你是想深入理解西门子PLC的SCL结构化文本编程、学习模块化设计思想还是想搞懂S7-1200之间通过S7通信或开放式用户通信如TCP交换数据的实际做法这个项目都能给你带来很多启发。接下来我就把这个程序的“里里外外”拆解一遍分享当时的设计思路、关键代码以及踩过的一些坑。2. 系统整体架构与设计思路拆解面对“三部十层电梯群控”这样一个命题第一步不是打开博途软件直接写梯形图而是进行顶层设计。我们需要把整个控制系统抽象成几个核心的组成部分并确定它们之间的信息流。2.1 硬件与网络拓扑规划当时的比赛指定了控制器为西门子S7-1200系列具体型号为CPU 1214C这意味着我们需要用三台物理PLC来分别控制三部电梯。当然在实际教学中或资源有限时也可以用一台PLC通过仿真或多实例方式来模拟但比赛为了考察分布式控制能力采用了真实的多PLC方案。网络架构是第一个关键点。三台PLC需要实时交换数据例如各自的当前位置、运行方向、目标楼层列表、负载状态以及各楼层的厅外召唤信号。我们选择了最稳定和常见的方案西门子内部的S7通信。通过PROFINET网络将三台S7-1200 PLC组态在同一个项目中利用“组态连接”功能建立S7连接。这种通信方式编程简单只需在两端调用PUT和GET指令即可读写对方PLC的数据块可靠性高特别适合这种中小规模、确定性要求强的数据交换。每台PLC的本地I/O负责连接本梯的核心设备包括轿厢内的楼层按钮10个、开关门按钮、报警按钮以及井道内的楼层平层信号传感器通常为磁簧管或光电开关、上下行限位开关、门机控制输出开门、关门、曳引机控制输出上行、下行、速度还有轿厢的载重检测信号。厅外召唤按钮每层楼的上、下行按钮共18个的输入信号则需要分配给三台PLC中的某一台来集中采集或者更合理的做法是通过分布式I/O如ET200SP采集后由主控PLC通过通信广播给所有电梯PLC。在我们的比赛方案中为了简化硬件和突出逻辑采用了“厅外召唤信号由1号PLCA梯集中采集然后通过通信广播给B梯和C梯PLC”的设计。2.2 软件结构与模块化设计在博途软件中我们坚决采用了结构化编程的方法这是管理复杂逻辑的基石。整个程序被划分为多个层级分明的块组织块OB这是PLC的“入口”。OB1主循环负责调用所有其他功能块OB100启动用于初始化变量OB82诊断错误用于记录硬件故障。我们还使用了OB30等循环中断组织块用于执行需要精确周期调用的任务比如电梯的精确平层控制算法。功能块FB与背景数据块Instance DB这是程序的核心。我们为每部电梯创建了一个主要的控制功能块例如FB_ElevatorControl。这个FB内部封装了一部电梯所有的状态机、逻辑判断和控制输出。每部电梯A, B, C都对应这个FB的一个背景数据块如DB_Elevator_A。这意味着FB_ElevatorControl的代码只有一份但三台电梯各自拥有独立的数据存储空间互不干扰。这是面向对象思想在PLC编程中的体现极大地提高了代码的复用性和可维护性。功能FC用于编写纯功能的、无记忆的例程。例如FC_CalculateDistance用于计算电梯当前位置与目标楼层的距离FC_AssignCall实现了核心的群控调度算法根据一定的策略如最小等待时间、顺向响应等将厅外召唤分配给最合适的电梯。数据块DB用于存储全局或共享数据。我们创建了几个关键的全局数据块DB_GlobalSignals存储所有厅外召唤按钮的状态10层 x 2方向 20个布尔量、系统总急停状态等。DB_Communication定义了用于三台PLC之间通信的数据结构。例如每个电梯PLC都把自己当前的位置、方向、状态空闲/运行/故障、目标楼层列表写入这个DB的特定区域同时从这个DB读取其他两台电梯的对应信息。DB_Communication在1号PLC主站上创建并通过S7通信被2号和3号PLC读写。每个电梯FB的背景DBDB_Elevator_A/B/C则包含了该电梯所有的内部状态变量如当前楼层、运行方向、门状态、内召登记列表、已分配的厅外召列表等。用户自定义数据类型UDT为了管理复杂的数据结构我们定义了UDT_ElevatorStatus这样的类型里面包含了位置、方向、速度、负载等成员。这样在DB或FB接口中直接声明一个UDT_ElevatorStatus类型的变量即可使程序更加清晰。程序执行流程可以概括为上电后各PLC在OB100中初始化自身状态和通信参数。在OB1的每个扫描周期中每台PLC首先读取本地输入本梯内召、平层信号等然后通过S7通信读取DB_Communication获取最新的厅外召唤信号和其他电梯的状态。接着调用FB_ElevatorControl该FB根据当前状态、内召、外召以及其他电梯的状态执行逻辑判断更新自身的状态和目标列表并控制电机和门机动作。最后将自身最新的状态数据写入DB_Communication供其他PLC读取。群控调度算法FC_AssignCall会在主PLC或每台PLC独立计算中周期性执行根据全局信息刷新厅外召唤的分配关系。3. 核心功能块详解与编程要点理解了整体架构我们深入到最核心的FB_ElevatorControl功能块内部。这个FB本质上是一个复杂的状态机。电梯的行为可以归纳为几个典型状态空闲、开门、关门、加速运行、匀速运行、减速运行、精确平层、故障等。3.1 状态机设计与实现我们在FB内部定义了一个E_State的枚举型变量列出所有可能的状态。状态转移的条件是编程的关键。// SCL 语言示例 - 状态枚举和部分转移逻辑 TYPE E_State : ( Idle : // 空闲 DoorOpening : // 开门中 DoorOpen : // 门已开 DoorClosing : // 关门中 Accelerating : // 加速 Running : // 匀速运行 Decelerating : // 减速 Leveling : // 精确平层 Fault : // 故障 ); END_TYPE // 在FB内部 #currentState : E_State; #targetFloor : INT; #doorTimer : TON; // 开门保持定时器 CASE #currentState OF E_State.Idle: // 检查是否有内召或已分配的外召 IF #hasPendingCall THEN // 确定运行方向切换到关门状态 #currentState : E_State.DoorClosing; #doorTimer(IN:FALSE); // 复位开门计时 END_IF; E_State.DoorOpen: // 门完全打开后启动开门保持计时 #doorTimer(IN:TRUE, PT:T#5S); // 保持开门5秒 IF #doorTimer.Q THEN // 时间到无人操作自动关门 #currentState : E_State.DoorClosing; ELSIF #doorCloseButton OR #safetyEdgeObstacle THEN // 按下关门按钮或安全触板被触发立即关门 #currentState : E_State.DoorClosing; END_IF; E_State.Running: // 实时监测当前位置与目标楼层的距离 #distance : ABS(#currentFloor - #targetFloor); IF #distance #decelerationStartDistance THEN // 到达减速点切换到减速状态 #currentState : E_State.Decelerating; // 触发减速曲线输出 ELSIF #currentFloor #nextTargetFloor THEN // 直接到达目标层短距离切换到平层状态 #currentState : E_State.Leveling; END_IF; // ... 其他状态转移 END_CASE;注意状态机的设计要保证完备性和确定性。每个状态下都要考虑所有可能的输入事件并明确转移到哪个状态。避免出现“状态孤岛”或未定义的状态转移。使用CASE语句比一堆IF-ELSEIF更清晰。3.2 召唤登记与定向逻辑这是电梯逻辑的“大脑”。我们需要管理两个列表内召登记列表和外召分配列表。在FB内部我们通常用数组InternalCall[1..10]和AssignedExternalCall[1..10, Up/Down]来表示。当轿厢内按下“5楼”按钮程序需要将InternalCall[5]置位。当厅外5楼上行按钮被按下并且群控算法将该召唤分配给了本梯则AssignedExternalCall[5, Up]被置位。定向逻辑的核心是判断电梯当前应该向上还是向下运行。一个经典的算法是“扫描方向优先”电梯有一个当前运行方向向上、向下、停止。在运行方向上检查所有已登记的、且楼层号大于向上时或小于向下时当前位置的召唤。这些是“顺向召唤”。如果存在顺向召唤则保持原方向前往最近的一个顺向召唤楼层。如果顺向召唤全部完成则检查反方向是否有召唤。如果有则改变方向如果没有则进入空闲状态。这个逻辑需要仔细处理特别是在电梯处于空闲状态收到第一个召唤时需要根据召唤楼层与当前位置的关系来初始确定方向。// 简化版的方向判断函数 (FC) FUNCTION DetermineDirection : INT // 返回 1:上, -1:下, 0:停 VAR_INPUT currentFloor : INT; currentDir : INT; // 当前方向 internalCallArray : ARRAY[1..10] OF BOOL; externalCallArray : ARRAY[1..10, 1..2] OF BOOL; // 1:Up, 2:Down END_VAR VAR_TEMP i : INT; hasCallAbove, hasCallBelow : BOOL; END_VAR hasCallAbove : FALSE; hasCallBelow : FALSE; // 检查所有召唤 FOR i : 1 TO 10 DO IF internalCallArray[i] OR externalCallArray[i, 1] OR externalCallArray[i, 2] THEN IF i currentFloor THEN hasCallAbove : TRUE; END_IF; IF i currentFloor THEN hasCallBelow : TRUE; END_IF; END_IF; END_FOR; // 逻辑判断 IF currentDir 1 THEN // 当前向上 IF hasCallAbove THEN RETURN 1; // 继续向上 ELSIF hasCallBelow THEN RETURN -1; // 反向向下 ELSE RETURN 0; // 停止 END_IF; ELSIF currentDir -1 THEN // 当前向下 IF hasCallBelow THEN RETURN -1; // 继续向下 ELSIF hasCallAbove THEN RETURN 1; // 反向向上 ELSE RETURN 0; // 停止 END_IF; ELSE // 当前停止 IF hasCallAbove AND NOT hasCallBelow THEN RETURN 1; ELSIF hasCallBelow AND NOT hasCallAbove THEN RETURN -1; ELSIF hasCallAbove AND hasCallBelow THEN // 上下都有召唤可优化如响应更近的 RETURN (ABS(currentFloor - NearestCallAbove) ABS(currentFloor - NearestCallBelow)) ? 1 : -1; ELSE RETURN 0; END_IF; END_IF;3.3 群控调度算法实现群控算法是项目的精华它决定了系统的整体效率。我们当时实现了一个相对实用但不算最复杂的算法主要基于最小响应时间预估。算法在FC_AssignCall中实现周期性地例如每100ms执行一次。其基本步骤如下获取全局状态读取DB_Communication中三部电梯的当前位置、方向、状态运行/空闲、当前目标楼层以及各自的召唤列表。遍历未响应的厅外召唤检查DB_GlobalSignals中所有被按下但尚未被分配给任何电梯的厅外召唤按钮。为每个召唤计算候选电梯的响应时间对于每一个未分配召唤例如5楼上行分别计算三部电梯如果响应该召唤预计需要花费的时间。预估时间 电梯完成当前所有已登记任务所需时间 从最后一个任务楼层移动到该召唤楼层的时间。电梯的“任务”包括其内召、已分配的外召。需要模拟电梯按当前方向和逻辑依次服务这些任务的过程才能估算出“完成当前任务所需时间”。这是一个简化的模拟计算。如果电梯当前方向与召唤方向不顺路例如电梯在上行召唤在下方且方向为上其实召唤方向与电梯运行方向相反则响应时间会加上一个“反向代价”因为电梯需要先完成当前方向所有任务再反向运行。分配决策选择预估响应时间最短的那台电梯将这个召唤标记为分配给它。将分配结果写入该电梯在DB_Communication中的“已分配外召列表”区域。重分配检查可选高级功能当某台电梯因故障或长时间等待如门被阻挡时算法可以将其负载的任务重新分配给其他电梯。这个算法在FC中实现时计算量需要控制。我们做了一些简化例如电梯运行时间简化为“楼层差 × 每层运行时间 停靠时间固定值”。在实际比赛中这个算法的效率是重要的评分点。实操心得在编写群控算法时一定要在程序中加入详细的调试输出。例如将每台电梯对每个召唤的预估时间、最终的分配决策都写入一个专门的调试数据块。这样在连接PLC在线调试时可以通过监控表清楚地看到算法的“思考过程”对于排查逻辑错误至关重要。否则面对不合理的电梯调度行为你很难定位是数据采集错了、通信延迟了还是算法计算逻辑有bug。4. 通信配置与数据同步实战多PLC项目的成败一半在于通信。我们采用S7通信以下是具体的配置和编程步骤。4.1 博途中的网络组态在博途项目视图的“网络视图”中添加三台S7-1200 PLC设备例如CPU 1214C。为它们分配不同的IP地址例如PLC_A: 192.168.0.10 PLC_B: 192.168.0.11 PLC_C: 192.168.0.12。确保它们在同一个子网如255.255.255.0。建立S7连接从PLC_A的PROFINET接口拖拽出一条线连接到PLC_B的接口。在弹出的“创建新连接”对话框中选择连接类型为“S7连接”。重复此步骤建立PLC_A到PLC_C的连接。这样PLC_A就作为“客户端/主动方”可以主动向B和C读写数据。如果需要B和C之间也通信或者需要双向主动读写则需要建立更多的连接。在连接属性中可以设置“主动建立连接”的伙伴为PLC_A并记下每个连接的“本地ID”一个16进制的数字如W#16#100这个ID在编程调用通信指令时会用到。4.2 PUT/GET指令编程在PLC_A主站的程序中我们需要周期性地向B和C发送数据写入并从B和C读取数据。向PLC_B发送数据PUT在OB1中调用PUT指令。其参数配置如下REQ: 使用一个始终为TRUE的位如M0.0或一个时钟脉冲如M0.5用时钟存储器位生成来触发每个周期都发送。ID: 填写网络组态中建立的A到B连接的本地ID例如W#16#100。ADDR_1: 指向PLC_A本地要发送的数据区例如P#DB200.DBX0.0 BYTE 20表示从DB200的0.0字节开始共20个字节。SD_1: 与ADDR_1相同例如P#DB200.DBX0.0 BYTE 20。ADDR_2: 指定PLC_B中接收数据的区域例如P#DB300.DBX0.0 BYTE 20。这个地址必须是PLC_B中实际存在的、并且可写的区域通常是在PLC_B中创建好的一个DB如DB_Comm_From_A并确保其属性中“优化的块访问”被取消勾选这样才有固定的绝对地址。DONE/ERROR/STATUS: 连接输出管脚用于监控通信状态可以连接到不同的M位或DB变量以便监控。从PLC_B读取数据GET同样在OB1中调用GET指令。ID: 同样是A到B连接的本地ID。ADDR_1: 指定要读取的PLC_B中的数据区例如P#DB301.DBX0.0 BYTE 20。RD_1: 指向PLC_A本地接收数据的区域例如P#DB201.DBX0.0 BYTE 20。PLC_B和PLC_C作为“服务器/被动方”不需要调用PUT/GET指令。它们只需要创建好对应的DB如DB_Comm_From_A,DB_Comm_To_A并确保数据在这些DB中被正确更新即可。PLC_A会主动来读写。数据同步策略为了减少通信量并保证关键数据的实时性我们定义了固定的通信数据结构。例如一个20字节的数据包前4个字节是电梯A的状态包括当前位置、方向、状态字接着是10个字节的内召列表每个位代表一个楼层最后6个字节是已分配的外召列表。PLC_A在每次扫描周期末尾将本机的这些数据打包写入DB200然后PUT指令会将其发送到B和C的对应DB。同时PLC_A也用GET指令将B和C的对应数据读回到自己的DB201和DB202。这样每台PLC都拥有其他两台电梯数据的“镜像”。关键注意事项S7通信的PUT/GET是异步指令执行需要时间。REQ信号的触发频率不能高于通信处理的速度否则会导致指令排队和通信错误。通常用一个几赫兹的脉冲如100ms来触发就足够了。务必监控ERROR和STATUS位并在程序中做好错误处理例如通信失败时电梯可以降级为单梯独立运行模式避免因通信故障导致整个系统瘫痪。5. 调试技巧与常见问题排查编写完程序只是第一步现场调试才是“大考”。以下是一些从该项目中总结出的宝贵经验。5.1 模拟调试与PLCSIM Advanced在连接真实硬件前强烈建议使用西门子的PLCSIM Advanced进行仿真。它可以仿真多台S7-1200/1500 PLC并支持它们之间的网络通信包括S7通信这对于调试多PLC程序是无价之宝。创建仿真实例在博途的“在线访问”中为项目中的三台PLC分别创建PLCSIM Advanced仿真实例。下载硬件配置和程序分别将硬件配置和程序下载到对应的仿真PLC中。使用仿真表Simulation Table这是最强大的工具。你可以创建一个仿真表为三台PLC的输入点I、中间变量M、数据块DB变量强制赋值或监控。例如你可以手动“按下”某个楼层的厅外召唤按钮强制DB_GlobalSignals.ExternalCall_5_Up为TRUE然后观察三台PLC的内部状态变化、通信数据交换以及最终的电梯调度决策。跟踪程序流结合博途的“监控”和“断点”功能单步执行程序查看状态机的转移是否按预期进行变量值的变化是否正确。5.2 在线监控与变量表连接真实PLC后变量表Watch Table是你的主要眼睛。结构化监控不要把所有变量扔进一个表。建议创建多个变量表如“电梯A状态”、“电梯B状态”、“通信数据区”、“厅外召唤”、“调试信息”等。这样逻辑清晰便于快速定位问题。使用“修改值”功能进行测试在安全的前提下可以通过变量表直接修改DB中的值来模拟输入信号。例如将DB_Elevator_A.CurrentFloor从3直接改为5来测试电梯的平层和开门逻辑。监控通信状态字将每个PUT/GET指令的STATUS字添加到变量表。正常通信时STATUS会有一个固定的值如16#7000。如果出现错误STATUS值会变化根据这个错误代码去查询西门子手册能快速定位是连接配置错误、地址错误还是网络问题。5.3 常见问题与解决方案速查表问题现象可能原因排查步骤与解决方案电梯不响应任何召唤1. 主循环OB1未运行。2. 输入信号未正确映射。3. 初始状态错误。1. 在线查看PLC诊断缓冲区确认无阻止性错误。2. 监控输入映像区I区确认按钮按下时有信号变化。3. 检查OB100中的初始化程序确保状态变量如currentState被正确初始化为“空闲”。单梯运行正常群控失效1. S7通信未建立。2. 通信数据区地址错误或未取消“优化访问”。3. 群控算法FC未被调用或计算错误。1. 在线查看网络视图确认S7连接状态是否为“已建立”。2. 检查通信双方DB的属性确保“优化的块访问”已取消并核对PUT/GET指令中的字节地址是否完全匹配。3. 在群控算法FC中将中间计算结果输出到调试DB在线监控其决策过程。电梯运行方向逻辑混乱1. 召唤登记列表逻辑错误。2. 方向判断函数DetermineDirection有bug。3. 平层信号采集不稳定。1. 监控内召和外召列表数组确认召唤被正确登记和清除。2. 单步调试方向判断函数检查在不同楼层、不同召唤组合下返回值是否符合预期。3. 检查井道平层传感器的安装和信号防抖处理通常用定时器延时确认。通信数据不同步1.PUT/GET指令触发频率不当。2. 通信数据包太大或周期太短导致处理超时。3. 网络负载过高或硬件故障。1. 确保用脉冲触发REQ而不是常通信号。降低触发频率如改为200ms。2. 优化通信数据只传输必要变量减少数据包大小。3. 检查网线、交换机使用Ping命令测试网络连通性和延迟。程序扫描周期过长1. 程序逻辑过于复杂循环过多。2. 通信指令等待时间过长。3. 使用了大量TON等定时器在同一个周期内处理。1. 在博途的“在线与诊断”中查看“循环时间”。优化算法避免在OB1中进行复杂的多层循环计算可考虑将耗时计算移到循环中断OB中。2. 检查通信伙伴PLC是否响应缓慢。3. 确保定时器分布在多个扫描周期内执行。5.4 安全与故障处理逻辑一个健壮的程序必须包含故障处理。我们在项目中实现了以下几层保护硬件故障检测通过OB82诊断中断记录模块缺失、短路等故障并触发系统进入安全状态电梯就近停靠、开门。软件看门狗在OB1中设置一个周期性复位的“生命信号”如M100.0在OB30一个固定周期如100ms的中断中检查这个信号。如果OB1因故卡死生命信号无法更新OB30会检测到超时从而触发故障处理程序。运行超时保护电梯在“运行”状态下如果持续一段时间如超过到达相邻楼层最大可能时间的两倍仍未收到下一个平层信号则判断为“卡梯”或故障立即停止电机尝试开门并报警。通信超时处理在读取其他电梯状态的逻辑中加入时间戳判断。如果某个电梯的数据超过一定时间如2秒没有更新则认为与该电梯通信中断。群控算法将不再考虑该电梯并将其未完成的任务重新分配给其他正常电梯。回过头看这个2021年的比赛项目它不仅仅是一套可运行的PLC代码更是一个完整的中型工业控制系统开发范本。它强迫你从硬件选型、网络规划、软件架构、算法实现一直考虑到调试、故障处理和安全。即使你未来面对的不是电梯而是立体仓库、流水线或者复杂的物料输送系统这套结构化设计、模块化编程、通信协同和状态机控制的思路都是完全相通的。对于想提升西门子PLC编程水平的朋友我的建议是不要只停留在看一定要动手复现。你可以先用PLCSIM Advanced仿真单部电梯把状态机、召唤逻辑调通。然后尝试增加第二部电梯自己设计一个简单的通信数据结构和调度规则。这个过程里遇到的每一个报错、每一个逻辑漏洞都是最宝贵的经验。当你最终能让三台“虚拟电梯”在你的程序指挥下有条不紊地接送“虚拟乘客”时你对PLC编程的理解一定会上升到一个新的层次。本文还有配套的精品资源点击获取