PLC 编程 8 项最佳实践

在西门子 TIA Portal 中编写干净、可维护的 PLC 代码:经过验证的命名约定、FB/FC 结构、NAMUR 告警处理、Git 与仿真测试。

PLC 编程 8 项最佳实践
博途PLC工程智能体 | AI智能体博途网关 | 博途PLC程序知识图谱 | 梯形图转SCL | 自然语言生成梯形图 | 自然语言生成SCL | 逆向生成程序块文档 | 梯形图在线查看 | 博途编程文档MCP | AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI

PLC 编程远不只是把逻辑块串起来。做了 10 多年自动化工程师,我见过无数 PLC 项目——很多都有典型的毛病:

  • 看不懂的变量名,比如 M0.0、DB1.DBX0.0 或 %MW100
  • 编程语言混用——同一个项目里 LAD、FBD、STL、SCL 乱成一锅粥
  • 文档缺失——最初的程序员早就离职了。
你知道吗?可以在TIA Portal中使用 逆向文档插件为PLC程序补上缺失的文档!

在这篇文章里,我分享在西门子 TIA Portal 中编写可维护 PLC 程序最重要的最佳实践。

目标读者

本文面向具备 TIA Portal 基础知识的 PLC 程序员。如果你是初学者,我建议先上一门 IEC 61131-3 基础课程。

1、为什么 PLC 编程需要最佳实践?

1.1 问题:遗留代码

我实践中的典型场景:

设备制造商打来电话:"我们的产线停了,能帮帮忙吗?"

到现场后的残酷现实:

  • 变量名像 M0.0、DB1.DBX0.0、%MW100——没人知道是什么意思
  • LAD、FBD、STL、SCL 在同一个项目里混用
  • 没有注释,没有结构
  • 最初的程序员几年前就离开公司了

排查故障花了远超必要的时间,因为没人看得懂这段代码。

1.2 解决方案:专业化编程

结构化、可维护的 PLC 程序带来明显优势:

  • 描述性变量名让排障更快
  • 新程序员上手时间更短
  • 更好的错误处理带来更少的停机时间

当现有程序正是上面这副模样时

下面这八项实践在新项目里可以从第一天就用起来——在一个已经生长了多年的程序里则需要回头改造,而这很少能在日常工作中顺带完成。结构、命名约定和一个带版本的块库,我会作为定义清楚的工作包来承接,必要时连同现有项目的代码审查一起做:TIA Portal PLC 编程服务。

2、最佳实践 1:始终遵循 IEC 61131-3 标准

IEC 61131-3 是 PLC 编程的国际标准:它统一定义了语言、数据类型和块类型。它的实际价值在于:按标准写出的代码,对下一个程序员来说依然可读。

2.1 五种编程语言概览

标准 (IEC) 西门子 适用场景 局限
LD – 梯形图 KOP 联锁、简单逻辑 超过约 50 个网络后难以维护
FBD – 功能块图 FUP 控制、数据流逻辑 大程序中难以维护
IL – 指令表 AWL 仅遗留代码 2013 年起已弃用
ST – 结构化文本 SCL 算法、计算、库 对纯联锁不够直观
SFC – 顺序功能图 GRAPH 顺序控制 (GRAFCET) 简单逻辑下开销过大

常见误区: SCL 和 ST 是同一种语言——SCL 是西门子对 Structured Text 的实现。把两者分开列等于一种语言数了两次,还通常把 IL 挤掉了。

我的建议

80% 的代码用 SCL/ST(逻辑、计算、顺序控制),只在简单联锁上用 LAD(例如急停逻辑)。控制回路用 FBD,复杂状态机用 SFC。

如果你希望把遗留系统里的LAD转成SCL,可以使用TIA Portal的 LAD2SCL 插件!

2.2 结构化编程:金字塔

PLC 程序结构:从 OB1 经 FC_SystemControl 到功能块和数据块的层级化块架构
OB1 (Main)
  └── FC_SystemControl (Operating modes)
       ├── FB_PackMLStateMachine (Sequential control)
       │    ├── FB_MotorControl (Component)
       │    ├── FB_ValveControl (Component)
       │    └── FB_ConveyorControl (Component)
       └── FC_AlarmHandler (Utility)

规则: 每一层只承担一项明确的职责。

3、最佳实践 2:建立命名约定

3.1 问题:变量名混乱

反面例子(来自实践):

// ❌ BAD
M0.0    // What is this?
DB1.DBX0.0   // Which motor?
%MW100  // Temperature? Pressure?

正面例子:

// ✅ GOOD
bMotorPumpMainRun : BOOL;  // Main pump motor running
iTemperatureTank1 : INT;   // Tank 1 temperature in °C
rPressureSetpoint : REAL;  // Pressure setpoint in bar

3.2 标准化前缀(匈牙利命名法)

前缀 数据类型 示例
b BOOL bMotorRun, bAlarmActive
i INT iCounter, iTemperature
r REAL rSpeed, rPressure
s STRING sAlarmText, sRecipeName
dt DATE_TIME dtStartTime, dtLastMaintenance
t TIME tDelayStart, tCycleTime
udi UDINT udiPartCounter, `udiTotalProduction

3.3 带前缀的输入/输出

// Inputs: ix (Input X)
ixMotorFeedbackRun : BOOL AT %I0.0;  // "x" = Bool
iwTemperatureSensor : WORD AT %IW0;   // "w" = Word

// Outputs: qx (Output X)
qxMotorStart : BOOL AT %Q0.0;
qwValvePosition : WORD AT %QW0;

一致性至上: 和团队敲定一种约定并写进文档。

工具支持

西门子 TIA Portal v18+ 提供 Code Quality Check,可自动检查命名约定。

4、最佳实践 3:正确使用功能块与函数

4.1 功能块 (FB) —— 有状态

什么时候用?

  • 有状态的部件(电机、阀门、输送机)
  • 告警处理
  • 状态机

示例:电机控制

FUNCTION_BLOCK FB_MotorControl
VAR_INPUT
  bStart : BOOL;        // Start command
  bStop : BOOL;         // Stop command
  bFeedback : BOOL;     // Motor running feedback
END_VAR

VAR_OUTPUT
  qxStart : BOOL;       // Motor start output
  bRunning : BOOL;      // Status: Motor running
  bAlarm : BOOL;        // Fault active
END_VAR

VAR
  tDelayStart : TON;    // Start delay
  tTimeoutFeedback : TON; // Feedback timeout
  bAlarmLatched : BOOL; // Latched fault
END_VAR

// Program logic here...
END_FUNCTION_BLOCK

调用:

// In OB1 or FC
dbMotorPumpMain(
  bStart := bSystemRun AND NOT bEmergencyStop,
  bStop := bSystemStop OR bEmergencyStop,
  bFeedback := ixMotorPumpMainFeedback
);
qxMotorPumpMainStart := dbMotorPumpMain.qxStart;

4.2 函数 (FC) —— 无状态

什么时候用?

  • 无状态的计算
  • 量程换算
  • 数学运算
  • 工具函数

示例:温度量程换算

FUNCTION FC_ScaleTemperature : REAL
VAR_INPUT
  iwRawValue : INT;  // Raw sensor value (0-27648)
  rMinTemp : REAL;   // Minimum temperature (e.g., -50°C)
  rMaxTemp : REAL;   // Maximum temperature (e.g., +150°C)
END_VAR

// Scaling 0-27648 → rMinTemp to rMaxTemp
FC_ScaleTemperature := rMinTemp +
  (REAL#iwRawValue / 27648.0) * (rMaxTemp - rMinTemp);
END_FUNCTION

调用:

rTemperatureTank1 := FC_ScaleTemperature(
  iwRawValue := iwTempSensor1,
  rMinTemp := -50.0,
  rMaxTemp := 150.0
);

经验法则

调用之间有状态(记忆)吗?→ FB 是计算或工具函数?→ FC

5、最佳实践 4:按 NAMUR NE 107 做错误处理

NAMUR NE 107 是过程自动化中自监控与诊断的行业标准,在机械工程领域也已普及。

5.1 四种状态

NAMUR NE 107 诊断状态:正常运行、需要维护、功能检查、故障

5.2 用 FB_AlarmHandler 实现

FUNCTION_BLOCK FB_AlarmHandler
VAR_INPUT
  bTrigger : BOOL;          // Alarm trigger
  eAlarmLevel : INT;        // 1=Info, 2=Warning, 3=Error, 4=Critical
  sAlarmText : STRING[80];  // Alarm text
END_VAR

VAR_OUTPUT
  bAlarmActive : BOOL;      // Alarm is active
  bAckRequired : BOOL;      // Acknowledgment required
END_VAR

VAR
  bAlarmLatched : BOOL;     // Latched alarm
  dtTimestamp : DATE_TIME;  // Alarm timestamp
END_VAR

// Logic: Latch alarm, set timestamp, acknowledge if needed
IF bTrigger AND NOT bAlarmLatched THEN
  bAlarmLatched := TRUE;
  dtTimestamp := CURRENT_TIMESTAMP();
  bAlarmActive := TRUE;

  // Logging to HMI/SCADA
  // ...
END_IF;

bAckRequired := bAlarmLatched AND eAlarmLevel >= 3;
END_FUNCTION_BLOCK

最佳实践: 一个中央告警管理器收集所有告警,并打包发送给 HMI。

注意

每条告警都需要唯一 ID(例如 ALM-001),并在文档中有对应说明。

6、最佳实践 5:用 Git 做版本控制

是的,Git 对 PLC 代码同样管用!从 TIA Portal v18 起,集成变得简单多了。

6.1 配置:TIA Portal + Git

1. 启用 Multiuser Engineering

TIA Portal → Project → Properties → Multiuser Engineering → Activate

2. 以 Git 友好的格式保存项目

# .gitignore for TIA Portal
*.ap18
*.zap18
__OPNB*
*.bak

3. 初始化 Git 仓库

git init
git add .
git commit -m "Initial commit: TIA Portal project setup"

6.2 PLC 项目的分支策略

main (Production code)
  ├── develop (Development)
  │    ├── feature/motor-control-update
  │    ├── feature/add-safety-logic
  │    └── bugfix/alarm-text-typo
  └── hotfix/critical-emergency-stop-bug

工作流:

  1. 创建特性分支:git checkout -b feature/new-conveyor-logic
  2. 提交更改:git commit -m "Add conveyor start delay 2s"
  3. 创建拉取请求(GitHub、GitLab)
  4. 由第二位程序员做代码评审
  5. 合并进 develop
  6. 在开发 PLC 上测试
  7. 发布分支 → main

优势

  • 团队协作不丢数据
  • 历史版本随时可取回
  • 不同特性可并行开发
  • 自动备份(GitHub、GitLab Cloud)

7、最佳实践 6:注释与文档

7.1 代码注释:三行规则

反例:

// ❌ Obvious, adds nothing
bMotorRun := TRUE;  // Start motor

正例:

// ✅ Explains WHY, not WHAT
// 2s delay due to pressure build-up in hydraulic circuit
// (See requirement REQ-HYD-012)
tDelayStart(IN := bStartRequest, PT := T#2S);
qxMotorStart := tDelayStart.Q;

7.2 文档标准

每个 FB/FC 都要有:

(*
  Name: FB_MotorControl
  Version: 1.2.0
  Author: David Prybisch
  Date: 2025-11-22

  Description:
    Standardized motor control with start delay,
    feedback monitoring and fault latching.

  Change History:
    v1.2.0 - 2025-11-22 - Added timeout monitoring
    v1.1.0 - 2025-10-15 - NAMUR alarm integration
    v1.0.0 - 2025-09-01 - Initial version
*)
FUNCTION_BLOCK FB_MotorControl
// ...
END_FUNCTION_BLOCK

系统架构用 UML 图:

用 PlantUML 或 Microsoft Visio 之类的工具把整体架构可视化。

8、最佳实践 7:测试与仿真

8.1 用 PLCSIM 仿真

西门子 PLCSIM Advanced 允许在没有物理硬件的情况下测试 PLC 程序:

工作流:

  1. 在 TIA Portal 中开发 PLC 程序
  2. 启动 PLCSIM Advanced 并加载项目
  3. 通过监控表设置并检查变量
  4. 系统性地测试顺序控制

优势:

  • 尽早发现错误——在办公室而不是客户现场
  • 新程序员可以放心测试
  • 调试前先验证更改

小技巧

用 TIA Portal 的监控表系统性地强制置位输入/输出,检查程序行为。

9、最佳实践 8:性能优化

9.1 控制 CPU 占用率

常见的性能杀手:

  1. 定时器/计数器太多 → 改用数组而不是单个变量
  2. 实时循环里的字符串操作 → 挪到低优先级任务
  3. OB1 里的复杂计算 → 改用循环中断 (OB35)

示例:基于数组的定时器管理

// ❌ BAD: 100 individual timers
VAR
  tMotor1 : TON;
  tMotor2 : TON;
  // ... 98 more timers
END_VAR

// ✅ GOOD: Array of timers
VAR
  atMotorTimers : ARRAY[1..100] OF TON;
END_VAR

// Call in loop
FOR i := 1 TO 100 DO
  atMotorTimers[i](IN := abMotorStart[i], PT := T#2S);
  aqxMotorStart[i] := atMotorTimers[i].Q;
END_FOR;

9.2 用多重实例优化内存

问题: 每个 FB 实例都消耗自己的 DB(实例 DB)。

解决方案: 对重复出现的块使用多重实例。

// FB_ConveyorLine contains 10x FB_MotorControl as multi-instance
FUNCTION_BLOCK FB_ConveyorLine
VAR
  Motor1 : FB_MotorControl;  // Multi-instance
  Motor2 : FB_MotorControl;
  // ...
END_VAR

// Create only ONE instance of FB_ConveyorLine
dbConveyorLine : FB_ConveyorLine;  // Saves 90% memory vs. 10 separate FBs

10、干净的代码在排障时能省多少

上面每一条最佳实践,最迟在产线停机、必须顶着压力找故障时回本。结构化工作的程序员不是漫无目的地"找"故障——而是逐层收敛:症状 → 受影响的块 → 信号路径 → 根因。当每个工艺段都封装在自己名副其实的 FB 里,你立刻就知道该查哪里,而不必在 OB1 的几千个网络里刨。

这就是前几章 FC/FB 分离、命名约定和清晰层级的实际回报:它们把几个小时的猜测变成几分钟的定向排查。

深入: 完整方法——从告警视图到块的五个步骤、合适的 TIA Portal 工具、为什么强制置位几乎从来不是答案、以及大多数问题背后的五类故障——见专栏文章 PLC 故障排查:5 步找到根因。

11、结论:专业的 PLC 编程物有所值

对结构化、可维护 PLC 程序的投入有多重回报:

  • 描述性变量名和清晰结构让排障更快
  • 新程序员上手时间更短
  • 更好的错误处理带来更少的停机时间
  • 全生命周期内维护更省力

11.1 下一个 PLC 项目的检查清单

  • 命名约定已与团队商定并成文
  • 层级化程序结构已规划(UML 图)
  • 遵循 IEC 61131-3 标准
  • 已按 NAMUR NE 107 实现告警管理
  • Git 仓库已建立、.gitignore 已配置
  • PLCSIM 仿真已准备就绪
  • 代码评审流程已建立
  • 文档(FB 头、系统架构)已编写

下一步

从小处着手:拿一个现有项目,按这些最佳实践重构一个 FB。投入的时间(2-4 小时)会在下一次排障时回本。

12、信息图:PLC 最佳实践一览

PLC 编程最佳实践信息图:结构化、可维护 PLC 程序的检查清单

原文链接: PLC Programming: 8 Best Practices for Maintainable Code

汇智网翻译整理,转载请标明出处