
顺序功能图SFC这套东西我第一次在现场真正用起来是被一条装配线逼出来的。当时那条线上十几个工位每个工位都有气缸、伺服、传感器用梯形图顺着写下去程序写到八百多行的时候我自己都看不懂了——改一个动作要顺着中间继电器爬半天售后打电话过来描述故障我得让他把程序打印出来一行行对。后来把主流程改成顺序功能图SFC重写同样的功能压到三百多行在线监控的时候哪一步亮着、卡在哪一步一目了然。这就是顺序功能图SFC的价值它把“设备现在走到哪了”这件事从程序员的脑子里的隐式状态变成了图纸上看得见的显式状态。这篇文章面向的是已经会用梯形图写点逻辑、但一遇到长流程就头大的电气工程师、设备调试人员和自动化专业的学生我会把 SFC 的元素语义、分支结构怎么选、步和转换怎么写、怎样落到实际程序里、以及调试时那些坑一次讲透。看完你至少能做到两件事给一台多工位设备画出规范的 SFC并且把它变成能跑的程序。1. 顺序功能图到底在描述什么1.1 它不是流程图是带执行语义的状态机很多人第一次看 SFC会觉得“这不就是流程图吗”。差别在于流程图是画给人看的画错了顶多沟通有歧义SFC 是画给控制器执行的每一个符号都有严格语义画错一个转换条件设备就会在错误的时刻动起来。严格来说SFC 是有限状态机FSM的一种图形化表达**步Step**对应状态**转换Transition**对应状态转移条件**动作Action**对应状态下的输出有向连线规定了转移方向。它在 IEC 61131-3 里被列为与梯形图、功能块图、指令表、结构文本并列的一种编程语言但它的定位很特殊——它更像是一种“流程骨架语言”负责描述先后顺序与并发关系具体某个动作内部的数值计算、PID 调节、报警判断仍然交给结构文本或梯形图去做。这一点是我吃过亏才想明白的。早些年我试图用 SFC 把温度 PID 也塞进去结果发现 PID 需要每个扫描周期都无条件执行而 SFC 里的动作只在步激活期间才生效两者天然错位。后来我的固定做法是SFC 只管流程的“走”与“停”连续量控制全部放到独立的功能块里由 SFC 输出一个使能位去调用它。这样职责清晰改流程的时候不会碰到控制参数调参数的时候不会动到流程。再往下拆SFC 之所以在流程型设备上比梯形图顺手核心原因是它天然解决了“互锁”这个问题。梯形图里两个动作互斥你得靠中间继电器互相串联常闭点来保证SFC 里两个相邻步在同一时刻只允许有一个激活这是语言层面的规则不需要你写任何互锁代码。这条规则叫“步的激活唯一性”它顺带把双线圈问题也消掉了——同一个输出线圈理论上不会被两个同时激活的步去驱动除非你自己在并行分支里犯了错。1.2 五个基本元素缺一不可要画出能跑的东西五个元素必须都搞清楚我用一张表把它们对照着说。元素图形表示语义要点常见误用步单线框矩形框内写步号或步名表示流程中的一个稳定状态激活期间其下挂动作生效把“等待条件成立”写成空转步导致流程卡死无提示初始步双线框矩形上电初始化后应被置为激活的唯一入口步上电没置位程序起来所有步全灭转换步之间的短横线旁边写条件条件由假变真的那一瞬触发步切换条件写成恒真一步闪过根本看不见有向连线竖直或带箭头的线定义流程走向上下顺序有严格方向从下往上画靠软件自动纠向图变乱动作步右侧的矩形框内写动作名与限定符限定符决定动作在步激活期间是持续、脉冲还是保持用 N 型驱动需要保持的输出步一结束输出就掉这里要重点提醒的是动作限定符它是 SFC 里最容易被忽略、也最容易出事故的部分。限定符决定了这个动作怎么执行写得不对轻则输出抖动重则设备误动作。1.3 动作限定符这才是 SFC 的细节所在常见的限定符有这么几个我把它们的行为和适用场景整理了一下限定符行为典型用途我的使用建议N非存储型步激活期间持续为真步结束自动清零气缸伸出、电机运行、指示灯默认首选绝大多数动作都用它S置位步激活时置位步结束后仍保持需要跨步保持的状态位、模式标志必须有配对的 R 复位否则早晚出问题R复位步激活时复位清除上一步的保持输出常和 S 成对出现注意执行顺序P脉冲步激活瞬间产生一个扫描周期的脉冲触发一次计数、下发一条指令别用来驱动机械动作脉冲太短执行器反应不过来D延时步激活后延时设定时间才生效延时确认、稳定后动作延时时间要大于最慢那个信号的最长响应L时间限制步激活期间动作有效超过设定时间强制结束限时动作、防超时配合超时报警用别只限时不报警SD / DS / SL存储型延时组合复杂时序新手别碰先用基本限定符把逻辑写清楚我个人的经验是新手阶段只用 N加上少量 S/R其余一律不碰。用 N 能把 90% 的流程写对剩下 10% 里的大半靠 S/R 处理。延时优先放在转换条件里做别放在动作限定符里——转换条件里的延时你能在线看到计时器当前值动作限定符里的延时在很多环境里是黑盒出问题很难查。2. 结构设计分支怎么选方案怎么定2.1 单序列、选择序列、并行序列的判定方法SFC 的结构一共就三类判定方法其实很简单我一般问三个问题第一个问题流程是不是一条道走到黑如果是用单序列从上到下一条线最简单也最不容易出错。典型场景是单工位的上下料、一台压机的完整循环、一次清洗流程。第二个问题同一个时刻是不是只在几个分支中选一个走如果是用选择序列也就是分支处画多条横线每条线前面挂一个转换条件谁的条件先成立走谁。典型场景是自动/手动模式分流、合格与不合格品分流、多配方选择。选择序列的关键是转换条件的互斥性如果两个条件同时成立不同环境的处理不一样有的走上分支有的报错非常危险。我的做法是给分支条件加优先级靠程序顺序保证同时在设计文档里写清楚“同时成立时走哪条”。第三个问题是不是几个动作必须同时开始、全部完成才能往下走如果是用并行序列分支处的转换条件只有一个条件成立后所有分支同时激活汇合处要等所有分支都到达才继续。典型场景是双工位同时加工、多轴联动、一边送料一边吹气。并行序列有个坑我必须说清楚并行分支激活后同时有多个步在激活状态如果你习惯性地用“步标志位直接驱动输出线圈”这时候不会有问题因为输出本来就该同时为真但如果你在汇合处用 S 保持型动作很容易忘记复位导致上一轮的输出残留在下一轮。我遇到过一次双工位并行压装汇合后回到初始步其中一个工位的气缸保持伸出状态没退回结果下一轮上料直接撞上。原因就是那个气缸用了 S 限定符汇合后没写 R。2.2 和梯形图、结构文本怎么分工选型这件事我的判断标准是“这个需求的主体是什么形状”。需求特征首选方案理由步骤清晰、顺序固定、工位多SFC流程可视化在线监控直观改流程成本低纯组合逻辑、联锁、报警梯形图调试直观老电工也能看懂交接友好大量数值计算、配方换算、通信组包结构文本循环、数组、字符串处理方便代码短连续量闭环控制功能块封装好参数稳定不随流程启停一台设备既有长流程又有复杂计算SFC ST 混用SFC 做骨架ST 做动作内部实现我在一个多工位装配项目里就是这么干的SFC 描述整机的三十多个步每一步调一个 ST 写的功能块或者置一个指令位PID 温控、扭矩曲线判定、条码校验全部在功能块里。这个组合的好处是改工艺流程的时候只动 SFC改算法的时候只动功能块两边互不干扰而且分工之后每个文件的长度都控制在两三百行以内接手的人看得懂。还有一个现实考量是交接成本。纯 ST 或者纯指令表写的流程程序功能上一点不差但换个人接手他得先在脑子里把代码跑一遍才能知道设备走到哪了。SFC 的好处是在线监控的时候画面上直接高亮当前激活的步售后人员在电话里描述“卡在 S35 那步”你翻开图纸就知道是哪一段。这个价值在设备出厂几年后尤其明显。3. 核心细节步、转换与初始化怎么写才不出事3.1 步的划分粒度与编号规则步划得太粗一个步里塞了五六个动作出问题定位不到划得太细七十多个步图纸一大张维护也累。我的经验值是一个步对应一个“可以被观察到的物理状态”比如“夹紧到位”是一个步“压头下行”是一个步“保压中”是一个步。一般单工位设备的步数在 8 到 25 之间比较舒服整线主流程控制在 30 到 60 步超过这个量级就该考虑拆成主流程加子流程宏步了。编号我强烈建议跳号编号比如 10、20、30、40中间留空。为什么因为设备改型是常态第一版做完客户说这里要加个吹气动作你要在步 20 和步 30 之间插一步如果连续编号你得把后面所有步号全部改一遍图纸、注释、程序、HMI 画面对应关系全得跟着动改一次错一次。跳号编号之后插入步 25 就行后面的步号一个都不用动。这个习惯我从第三个项目开始坚持到现在省下来的返工时间多得数不清。另外每个步都必须有中文注释注释里写清楚这个步在等什么、动作是什么、异常时应该看哪个传感器。别嫌麻烦设备交付两年后你自己都记不住 S47 是干嘛的。3.2 转换条件的三个铁律转换条件是 SFC 里唯一决定“什么时候往下走”的东西写错了就是灾难。三条铁律第一转换条件必须是“确认型”的不能是“推断型”的。什么叫推断型比如“气缸伸出指令发出后延时 1 秒认为气缸已伸出”。这种写法在现场能用但一旦气压不足、节流阀被调过、冬天油粘度变了它就在错误的时刻往下走。正确写法是“气缸伸出到位传感器为真且延时 200ms 确认信号稳定”。延时不是用来代替传感器的是用来滤掉抖动的。第二转换条件里不能出现持续变化的模拟量做唯一判据。比如“压力大于 5MPa 就进入下一步”压力在临界点附近波动的时候流程就会在当前步和下一步之间反复跳。必须加窗口或者加稳定时间。我的做法是压力达到阈值后用 200ms 到 500ms 的稳定窗口确认具体取值看工艺节拍允许多大牺牲。第三转换条件的信号保持时间必须大于扫描周期。这条是纯技术细节但很要命。假设控制器扫描周期是 8ms如果一个到位信号只保持 5ms那么本次扫描没读到、下次扫描它已经消失了转换条件永远判定不成立流程就卡死在那里。工程上的做法是让信号最短保持时间大于3 到 5 个扫描周期8ms 扫描的话至少要保证 25ms 以上实际设计时留到 50ms 更稳。反过来如果信号是用高速计数器捕捉的窄脉冲就不能直接做转换条件要先在程序里用一个自锁位把它保持住。3.3 初始化和回原点最容易翻车的地方上电之后程序必须做的第一件事是把所有步标志位清零然后把初始步置为激活。这个顺序不能反——先清后置如果反了你先置初始步再清全部初始步也被清掉了程序起来所有步全灭操作员按启动没反应现场就开始怀疑人生。还有一个必须处理的是回原点。设备中途急停、报警、断电恢复之后不能直接跳回原来那一步继续跑因为机械状态已经变了。标准做法是设计一条独立的“回原点序列”把每个执行机构按安全顺序退回原位全部到位确认之后才允许进入自动流程。回原点序列本身也可以用 SFC 写用选择序列从主流程分出来做完再汇合回去。我要特别强调急停回路必须是硬接线的不能进 SFC。SFC 是软件流程软件会跑飞、会死循环、会有人改错。安全功能必须由独立的安全继电器或者安全控制器实现SFC 里的“急停”只是一个状态位用来触发流程中止和报警不是安全功能的实现手段。这条是底线我见过把急停串在 PLC 程序里的做法绝对不能容忍。4. 实操给一台装配工位写完整的 SFC4.1 需求梳理与步划分拿一个模拟项目来说某装配工位动作顺序是——托盘到位 → 定位销伸出锁紧 → 压头下行压装 → 保压 2 秒 → 压头返回 → 检测高度 → 判定合格/不合格 → 定位销退回 → 托盘放行。不合格品要走单独的分支报警并等待人工确认。先把步划分出来我用跳号编号S10 初始步等待启动所有输出复位S20 托盘到位等待等托盘到位传感器S30 定位锁紧定位销伸出等到位S40 压头下行压头下行等到下位S50 保压压力达标后计时 2 秒S60 压头返回压头返回等到上位S70 高度检测检测并判定S80 合格放行定位销退回托盘放行S90 不合格处理报警等待人工确认后返回初始步S100 异常/中止步任何时刻报警都跳到这里这里就能看出 SFC 的好处了十个步一张图工艺顺序、分支条件全在上面。换个人来看五分钟能明白整台设备在干什么。4.2 转换条件与联锁设计接下来给每个转换写条件。这一步是真正费脑子的地方我的做法是拿张纸把每个转换列出来然后在后面追问三个问题这个条件成立时机械真的允许动吗这个条件会不会误判这个条件不成立时我能不能知道为什么转换条件补充联锁说明S10→S20启动按钮 自动模式 无报警安全门闭合、气压正常缺一个都不许进流程S20→S30托盘到位传感器 ON持续 100ms托盘类型识别正确防止托盘经过时误触发S30→S40定位销到位传感器 ON锁紧压力确认没锁紧不许压S40→S50压头下位传感器 ON压力达到下限位置加压力双确认S50→S60保压计时完成压力全程不低于下限保压期间掉压要报警S60→S70压头上位传感器 ON无异常没回到位不许检测S70→S80检测结果合格—选择序列分支S70→S90检测结果不合格—另一分支S80→S10托盘离开传感器 ON—回到初始步S90→S10人工确认按钮 不合格品已取走—必须人工介入这张表就是程序设计的依据写完这张表程序基本就写完一半了。而且它同时是调试手册——现场卡在哪一步翻这张表看对应条件逐个确认信号效率比盲猜高得多。4.3 从图到程序步标志位的落地写法不同编程环境的 SFC 实现方式不一样有的支持直接画 SFC 并自动生成代码有的是用指令或者位变量手工实现。我这里给一套用位变量手工实现的通用写法好处是不依赖特定环境任何支持结构文本的地方都能跑而且每一步都看得见调试最直接。先定义步标志位(* 步标志位定义 *) bStep10 : BOOL; (* 初始步等待启动 *) bStep20 : BOOL; (* 等待托盘到位 *) bStep30 : BOOL; (* 定位锁紧 *) bStep40 : BOOL; (* 压头下行 *) bStep50 : BOOL; (* 保压 *) bStep60 : BOOL; (* 压头返回 *) bStep70 : BOOL; (* 高度检测 *) bStep80 : BOOL; (* 合格放行 *) bStep90 : BOOL; (* 不合格处理 *) bStepErr : BOOL; (* 异常中止步 *)初始化程序放在一个每个扫描周期都执行的任务里只在上电首个周期运行一次(* 上电初始化先全清再置初始步 *) IF bFirstScan THEN bStep10 : FALSE; bStep20 : FALSE; (* ... 其余全部 FALSE ... *) bStepErr : FALSE; bStep10 : TRUE; (* 最后置初始步 *) bAutoMode : FALSE; (* 上电不自动进自动模式 *) END_IF;然后每个转换写一段标准的“转移”逻辑。这里有个细节转换判断放在动作输出之后、且所有转移在同一个扫描周期内互斥执行。为了简单可靠我用 IF-ELSIF 链或者按顺序写 IF 并保证前步立即清零。写法如下(* S10 - S20 *) IF bStep10 AND bStartBtn AND bAutoMode AND NOT bAlarm AND bDoorClosed AND bAirOK THEN bStep10 : FALSE; bStep20 : TRUE; END_IF; (* S20 - S30到位信号 100ms 确认 *) IF bStep20 AND bPalletInPos AND bPalletTypeOK THEN bTOn20(IN : TRUE, PT : T#100MS); IF bTOn20.Q THEN bTOn20(IN : FALSE); (* 复位定时器为下次准备 *) bStep20 : FALSE; bStep30 : TRUE; END_IF; ELSE bTOn20(IN : FALSE); END_IF;动作输出部分用当前激活的步去驱动用 N 型语义步结束自动掉(* 动作输出N 型步激活期间有效 *) bPinExtendCmd : bStep30; (* 定位销伸出 *) bPressDownCmd : bStep40; (* 压头下行 *) bPressHoldCmd : bStep50; (* 保压液压站保持压力 *) bPressUpCmd : bStep60; (* 压头返回 *) bPinRetractCmd: bStep80; (* 定位销退回 *) bPalletRelease: bStep80; (* 托盘放行 *)这套写法的好处是非常透明你在监控画面里能同时看到十个步标志位哪个是 TRUE 一目了然任何一个转换条件不满足你盯着那一行就能看出是哪个信号没来。缺点是代码比直接画 SFC 长但因为都是模板化的复制粘贴改一改就行。我实际用下来十个步的流程半小时能写完加上注释。并行分支要多说一句。并行分支激活的时候同时置多个步为 TRUE这时候立刻就会有两个动作同时输出这是对的但汇合的时候必须等所有分支都到齐写法是(* 并行分支汇合两个分支都到达才继续 *) IF (bStep80 OR bStep85) AND bStepAllBranchDone THEN bStep80 : FALSE; bStep85 : FALSE; bStep90 : TRUE; END_IF;注意这里不能写成IF bStep80 AND bStep85因为并行汇合的真实含义是“各分支都完成”而不是“两个标志位同时为真”后者在你加了等待步之后就不成立了。4.4 调试现场记录程序写完第一次上电调试我的固定流程是四步。第一步强制表逐个验证转换条件。手动把每个传感器触发一遍看着监控画面上对应的输入点和转换条件表达式确认它真的变成了你期望的值。这一步能抓出 80% 的接线错误和地址配置错误。第二步单步模式走流程。用一个“单步允许”开关每按一次启动按钮只走一个转换。观察每一步的机械动作是否和设计一致动作之间有没有干涉。这个时候人必须站在设备旁边手放在急停上。第三步空跑连续循环。不放工件让设备连续跑二十个循环看有没有哪一步偶尔卡住、有没有步标志位残留。这一步最容易暴露定时器没复位、S 型动作没复位的毛病。第四步带载跑重点看异常分支。故意制造不合格品、故意中途按暂停、故意断开某个传感器验证异常分支能不能正确进入和退出。异常分支是最容易写完就不测的部分也是最容易在客户现场翻车的部分。我那次的记录是这样的节选一下当时踩的坑S50 保压步压力达到下限后开始计时但保压过程中压力有一次短暂掉到下限以下程序里没有处理结果产品判定通过但实际压力不足。后来改成在保压步里加了一个“压力全程不低于下限”的检测位掉压立即报警转异常步。这个改动在标准文档里根本不会写只有实际跑过才知道。5. 常见问题与排查实录5.1 现象对照速查表现场遇到的 SFC 问题翻来覆去就那么几种我按“现象—可能原因—排查动作”整理成表出问题的时候直接对号入座。现象高概率原因排查动作流程卡在某步不动转换条件里有常假信号打开该转换的表达式逐个信号看实时值重点查传感器是否有输入、地址是否写错流程在某两步之间来回跳转换条件在临界值抖动给该条件加滤波时间或稳定窗口看信号是否抖动两个步同时激活并行汇合处理不当或用了 S 型动作没复位检查汇合逻辑检查所有 S/P 型动作是否有对应复位输出抖动气缸啪啪响动作被两个步同时驱动或转换条件抖动查是否有双线圈查转换条件稳定性上电后按启动没反应初始步没置位或初始化顺序写反看初始步标志位是否为 TRUE检查是否先置位后清零单步调试时下一步闪一下就跳走交替执行标志互锁缺失单步模式与自动模式加互锁且单步只允许一个转换生效回初始步后设备还带着动作保持型输出未复位在回初始步的转换里追加复位所有保持型输出扫描周期变长节拍变慢SFC 展开后指令量增大或动作里塞了耗时运算把耗时计算移出步动作放到独立任务里按时钟执行报警后恢复直接跳回原来那步缺少回原点序列报警恢复一律进回原点不能直接续跑某个步只在特定条件下卡住转换条件里用了模拟量单一判据改为阈值稳定时间双判据5.2 几条从坑里捡回来的经验关于“空步”。有时候需要一个“什么都不做只等条件”的步比如等上游来料。这种步在图上画出来是个空框容易被误认为是漏画。我的做法是给它一个明确的名字和注释比如“S45 等待上游来料最长 30 秒超时报警”并且一定要配超时分支。没有超时保护的等待步是现场“设备莫名其妙停着不动”的第一大来源。关于在线修改。很多环境支持运行中修改 SFC方便是真方便风险也是真大。运行中改步结构可能会出现某个步标志位既不在旧逻辑里也不在新逻辑里卡成孤儿状态。我的规矩是只允许改动作内容和转换条件不允许在运行中增删步。增删步必须停机重新下载走一遍初始化。关于步标志位的命名。别用 S1、S2、S3用 S10、S20 跳号并且命名里带上功能比如S30_PinLock。半年后你会在心里感谢自己。命名混乱的项目后期维护成本至少翻一倍。关于 HMI 上的流程显示。我给每个项目都会在触摸屏上做一个简单的流程状态显示直接显示“当前步号 步名称 已停留时间”。这个投入不到半天但对售后的价值巨大——操作工能直接说出“停在第 40 步压头下行这里”而不是“机器不动了”。已停留时间这个字段尤其有用正常步停留 2 秒现在显示 45 秒问题范围立刻缩小。关于转移的扫描顺序。不同环境对“动作执行”和“转换评估”的先后顺序处理不一样有的先算动作再判转换有的反过来这会带来一个扫描周期的相位差。如果你的设备节拍要求很紧比如几十毫秒级的动作衔接一定要翻手册确认清楚。我遇到过一个高速贴装的项目就是因为这个相位差导致取料和放料的时间窗口差了 10ms成品率掉了一个点查了两天才定位到。6. SFC 的适用边界与延伸用法顺序功能图SFC最适合的场景本质上是“状态数量有限、状态转移明确、需要人能看懂”的流程。反过来如果一个流程的状态高度依赖连续量的组合判断或者状态数量会动态增长那 SFC 反而是负担。举个例子一个按配方动态生成工序的配料系统工步数量随配方变化这时候硬用 SFC 画几十个分支图会乱成一团不如用一张工序表存在数组里用结构文本按表驱动执行SFC 只保留外面的“启动—执行—完成”三个大状态。这种“外层 SFC 保骨架、内层表驱动保灵活”的混搭我在好几个柔性设备上都用过效果比纯 SFC 或者纯表驱动都好。跳出 PLC 的范畴SFC 的这套思路其实在很多地方都能直接搬。测试设备的测试序列一个用例一个步通过条件就是转换异常就是分支机器人工作站的动作编排取料、移动、放置、返回天生就是单序列甚至连上位机的业务流程编排都可以借用“步 转换 动作”的模型把每个业务动作封装成一个可执行单元用状态位串起来好处和 PLC 里完全一样——流程可见、卡点可查、改动局部化。我后来在一个上位机项目里就这么干的把原来用一堆嵌套回调写的业务流程改成显式的状态推进出问题时日志里直接打印当前状态名排查效率提升非常明显。最后分享一个我一直在用的做法每个项目都保留一份 SFC 图纸作为交付物和电气原理图放在一起。这不是为了应付检查是因为这张图是整台设备逻辑的最高层级说明。三年后设备要大修或者改造第一个该翻的就是它。程序可以重写这张图上的工艺流程是重写不了的资产。顺序功能图这套东西说白了就是把设备的状态说清楚。你把步划清楚把转换条件确认清楚把保持型动作的复位想清楚剩下的都是体力活。真正难的不是画图是想明白每一步“凭什么往下走”——这个“凭什么”就是你对工艺的理解。