2. 产线状态机的基本概念
在产线控制程序里,状态机不是一种“高级写法”,而是一种把设备行为讲清楚的组织方式。它把“设备现在处于什么情况”“什么条件下允许改变”“改变时具体做什么”拆成几个明确的概念。只要这几个概念定义清楚,程序逻辑就能从大量 if/else 中解放出来,变成可读、可查、可维护的结构。
本节围绕五个核心概念展开:状态(State)、事件(Event)、转移(Transition)、动作(Action)、守卫条件(Guard)。理解它们之间的关系,是后续编写自动/手动模式管理、故障恢复和节拍控制的基础。
一、状态:设备“此刻在做什么”
状态描述的是设备在一个相对稳定阶段中的行为特征。它不是某一个输出点的通断,而是一组输出、内部标志和等待条件的集合。例如一条包装产线,可以定义如下状态:
- Idle(待机):电机停止,气缸复位,等待启动命令。
- Homing(回零):各轴依次寻找原点,完成后进入 Ready。
- Ready(就绪):原点已建立,允许启动自动循环。
- Running(运行):按节拍执行送料、压装、检测、下料。
- Paused(暂停):循环停在安全位置,输出保持,等待恢复。
- Fault(故障):出现急停、超时或安全门打开,输出进入安全状态。
状态划分要遵循两个原则:一是互斥,同一时刻只能处于一个状态;二是可观察,每个状态应有明确的输出表现或 HMI 指示,便于操作员判断。状态名建议使用英文或拼音缩写,并在 HMI 上显示中文注释,避免现场误解。
二、事件:推动状态改变的外部或内部信号
事件是状态机“被唤醒”的触发源。它可以是按钮按下、传感器到位、定时器超时、报警复位,也可以是上位机下发的命令。事件本身不决定“能不能转”,它只是“提出申请”。例如:
StartBtn:启动按钮上升沿。PartAtPos:工件到位光电信号。CycleDone:本循环完成标志。FaultReset:故障复位按钮。Timeout_5s:某动作超过 5 秒未完成。
在 PLC 中,事件通常用上升沿指令提取,避免电平持续期间被反复触发。例如三菱的 LDP、西门子的 P_TRIG,或统一写成 R_TRIG 功能块。事件应尽量“短促”,只在一个扫描周期内有效,防止状态机被同一事件连续推动。
三、转移:从当前状态到下一状态的有向连接
转移是状态之间的“箭头”。它由源状态 + 事件 + 守卫条件共同决定,指向目标状态。没有事件,状态不会自己改变;没有守卫条件,事件来了也可能被拒绝。转移的典型表达是:
Running --[StopBtn AND Guard_SafePos]--> Paused
Running --[FaultActive]-----------------> Fault
Fault --[FaultReset AND Guard_NoAlarm]--> Idle
注意转移是“瞬时”的:一旦条件满足,状态机在一个扫描周期内完成源状态退出和目标状态进入。因此转移条件必须用边沿或一次性标志,不能用电平长期保持,否则会出现“刚进目标状态又被同一条件推走”的抖动。
四、动作:进入、退出或停留时执行的具体操作
动作是状态机真正“干活”的部分。按执行时机可分为三类:
- 进入动作(Entry Action):进入某状态时执行一次。例如进入 Running 时启动主电机、打开允许输出。
- 退出动作(Exit Action):离开某状态时执行一次。例如退出 Running 时停止主电机、关闭使能。
- 停留动作(Do Action):状态保持期间每个扫描周期执行。例如 Running 中根据节拍依次触发送料、压装、检测。
动作要尽量“短”和“确定”。如果某个动作需要等待,应把它拆成独立状态,而不是在动作里写延时循环。例如“压装并保压 2 秒”应拆为 Pressing 和 Holding 两个状态,用定时器事件转移,这样 HMI 能显示当前阶段,故障时也能定位到具体步骤。
五、守卫条件:转移的“门禁”
守卫条件是一个布尔表达式,只有为真时,事件才能触发转移。它回答的是“现在允许转吗”。守卫条件通常包括:
- 安全条件:安全门关闭、急停未触发、气压正常。
- 位置条件:气缸已缩回、轴已到安全位。
- 模式条件:当前处于自动模式、手动模式或维护模式。
- 互锁条件:另一工位未占用、下游未满料。
守卫条件与事件的区别可以用一句话概括:事件是“什么时候”,守卫是“可不可以”。例如 StartBtn 是事件,Mode == Auto AND Guard_Safe 是守卫。两者同时满足,转移才发生。
六、状态机在产线控制中的典型表达方式
在 PLC 程序中,状态机最常用的表达方式是整数状态变量 + CASE 语句。用一个 INT 或 DINT 变量保存当前状态编号,每个扫描周期先处理转移条件,再根据新状态执行对应动作。典型结构如下:
// 状态编号定义
// 0=Idle, 1=Homing, 2=Ready, 3=Running, 4=Paused, 5=Fault
CASE #State OF
0: // Idle
IF #StartBtn AND #Guard_Safe THEN
#State := 1; // 转 Homing
END_IF;
1: // Homing
// 回零动作
IF #HomingDone THEN
#State := 2;
END_IF;
3: // Running
// 节拍动作
IF #StopBtn AND #Guard_SafePos THEN
#State := 4;
END_IF;
IF #FaultActive THEN
#State := 5;
END_IF;
5: // Fault
// 输出安全状态
IF #FaultReset AND #Guard_NoAlarm THEN
#State := 0;
END_IF;
END_CASE;
这种写法的好处是:状态一目了然,转移条件集中,动作与状态绑定。调试时只需观察 State 变量,就能知道设备卡在哪一步。HMI 上也可以直接用状态编号显示中文状态名,操作员不需要理解程序内部结构。
另一种常见表达是状态图(State Diagram),用于设计阶段和文档交付。它用圆圈表示状态、箭头表示转移、箭头旁标注事件和守卫条件。对于多工位、多模式产线,状态图能提前暴露“死锁”“漏转移”“守卫冲突”等问题,比直接写代码更高效。
七、一个容易混淆的点:状态与模式的区别
状态机管理的是“设备当前处于哪个阶段”,而模式管理的是“设备按哪套规则运行”。自动模式和手动模式可以共用同一套状态机,但守卫条件和动作内容不同。例如手动模式下,StartBtn 可能直接触发某个气缸动作,而不进入 Running 状态;自动模式下,StartBtn 才推动 Idle→Homing→Ready→Running 的完整链路。
因此,在程序里通常把“模式”作为守卫条件的一部分,而不是把模式本身当成状态。例如:
IF #Mode = 1 AND #StartBtn AND #Guard_Safe THEN
#State := 1; // 仅自动模式允许进入回零
END_IF;
这样状态机保持单一职责,模式切换不会打乱状态编号,后续增加“维护模式”“单步模式”时也只需扩展守卫条件,不必重写整个状态结构。
总结来看,状态、事件、转移、动作、守卫条件这五个概念构成了状态机的完整语义:状态回答“现在在哪”,事件回答“什么时候动”,守卫回答“能不能动”,转移回答“往哪动”,动作回答“动了做什么”。把这五件事分别写清楚,产线控制程序就会从“能跑”走向“可读、可查、可维护”。