1. 项目概述:工业伺服控制中的ST+FB架构实战
在工业自动化领域,伺服控制系统的可靠性直接决定生产线能否稳定运行。三菱FX5U系列PLC凭借其出色的运动控制性能和友好的开发环境,已成为中小型自动化设备的首选控制器。但真正决定项目成败的,往往是程序架构的设计水平——糟糕的伺服程序轻则导致调试周期延长,重则引发设备异常停机,这也是为什么业内常说"伺服程序写得好,下班回家早;写得烂,现场调试能让人原地爆炸"。
我使用的ST(结构化文本)结合FB(功能块)的编程范式,经过三年六条生产线的实战检验,形成了这套高可靠性的伺服控制模板。与传统梯形图编程相比,ST语言更适合处理复杂的数学运算和逻辑判断,而FB则能将重复使用的功能模块化,显著提升代码复用率。举个例子,在包装机械的定位控制中,同一个FB功能块可以被不同工位的伺服轴调用,只需修改参数配置,就能实现多轴协同运动,这种架构在设备升级改造时尤其省时省力。
2. 硬件配置与参数映射
2.1 轴参数配置功能块解析
伺服系统的机械参数转换是许多新手容易栽跟头的地方。我们来看核心的AxisConfig功能块实现:
st复制FUNCTION_BLOCK AxisConfig
VAR_INPUT
iAxisNo: INT; // 轴编号(1-4对应FX5U的4轴脉冲输出)
iGearRatio: REAL; // 减速比(例如10:1减速机输入0.1)
END_VAR
VAR_OUTPUT
oAccTime: TIME; // 计算后的加速时间(单位:ms)
oDecTime: TIME; // 计算后的减速时间(单位:ms)
END_VAR
VAR
rPulsePerMM: REAL := 1000.0; // 每毫米脉冲数(根据编码器分辨率计算)
END_VAR
// 核心计算公式
oAccTime := T#500MS * (1.0 / iGearRatio); // 基础时间×减速比倒数
oDecTime := oAccTime * 1.2; // 减速时间比加速长20%防止过冲
这个功能块的关键在于:
- 使用TIME类型直接处理时间参数,避免传统D寄存器方案的单位混淆问题
- 减速比采用倒数运算(1.0/iGearRatio),正确处理了减速机传动关系
- 加减速时间按1.2:1比例设置,这是经过多次实测得出的防抖动经验值
实际应用中发现,当减速比大于20:1时,建议在功能块内增加下限保护:
oAccTime := MAX(T#100MS, oAccTime);防止时间过短导致驱动器报警。
2.2 硬件接线与PLC配置
FX5U的脉冲输出端口配置需要与程序严格对应:
| 程序轴号 | 物理端口 | 伺服驱动器接收端 | 推荐线径 |
|---|---|---|---|
| 1 | Y0/Y1 | PP/NP | 0.75mm² |
| 2 | Y2/Y3 | PP/NP | 0.75mm² |
| 3 | Y4/Y5 | PP/NP | 0.75mm² |
| 4 | Y6/Y7 | PP/NP | 0.75mm² |
常见坑点:
- 屏蔽线接地不良会导致脉冲丢失(建议驱动器侧单点接地)
- 输出类型必须设置为"集电极开路"(漏型输出)
- 脉冲频率超过200kHz时需要缩短接线距离(<3米)
3. 运动控制状态机设计
3.1 主状态机流程
伺服控制的核心是状态机设计,下面展示经过优化的七段式结构:
st复制CASE stMotionState OF
0: // 初始化
IF bAxisEnabled THEN
stMotionState := 10;
END_IF;
10: // 回原点
IF NOT AxisHome(bAxis1) THEN
AlarmCode := 1001;
stMotionState := 90;
ELSE
stMotionState := 20;
END_IF;
20: // 待机状态
IF diStart THEN
stMotionState := 30;
ELSIF diManualMode THEN
stMotionState := 60;
END_IF;
30: // 自动运行
AxisMove(bAxis1, rTargetPos, rSpeed);
stMotionState := 40;
40: // 运动监控
IF AxisInPosition(bAxis1) THEN
stMotionState := 50;
ELSIF diEmergencyStop THEN
AxisStop(bAxis1);
stMotionState := 90;
ELSIF NOT bMotionTimeout THEN // 新增超时判断
AlarmCode := 1002;
stMotionState := 90;
END_IF;
50: // 工序完成
stMotionState := 20;
60: // 手动模式
JogControl();
90: // 异常处理
AlarmProcessing();
END_CASE;
状态机优化要点:
- 增加初始化状态(0)确保上电后不会立即动作
- 分离自动/手动模式路径(20→30与20→60)
- 新增运动超时检测(bMotionTimeout)
- 所有状态转换都有明确的条件判断
3.2 超时保护实现
在状态40中提到的超时检测是这样实现的:
st复制// 在全局变量区定义
VAR
tonMotionTimer: TON;
bMotionTimeout: BOOL;
END_VAR
// 在状态40激活时启动计时器
tonMotionTimer(IN:= (stMotionState = 40), PT:= T#5S);
// 超时判断逻辑
bMotionTimeout := NOT tonMotionTimer.Q;
这个5秒超时设置是根据实际工况调整的:
- 对于行程<500mm的场合,可缩短至2秒
- 长行程(>2m)建议延长至10秒
- 特殊工况可通过HMI参数调整
4. 报警处理与安全机制
4.1 增强型报警功能块
原始版本的自锁逻辑在实际使用中发现需要增加报警分级:
st复制FUNCTION_BLOCK AlarmHandler
VAR_INPUT
iErrorCode: INT; // 错误代码(0=无错误)
iErrorLevel: INT; // 错误等级(1-警告 2-严重)
iResetSignal: BOOL; // 复位信号(上升沿触发)
END_VAR
VAR_OUTPUT
oActiveAlarms: WORD; // 当前活跃报警位图
END_VAR
VAR
aAlarmHistory: ARRAY[1..16] OF INT; // 报警历史记录
nHistoryIndex: INT := 1;
END_VAR
// 报警锁存逻辑
IF iErrorCode <> 0 THEN
// 按等级处理
CASE iErrorLevel OF
1: oActiveAlarms.0 := TRUE; // 位0表示警告
2: oActiveAlarms.1 := TRUE; // 位1表示严重错误
END_CASE;
// 记录历史(环形缓冲区)
aAlarmHistory[nHistoryIndex] := iErrorCode;
nHistoryIndex := nHistoryIndex MOD 16 + 1;
END_IF;
// 复位逻辑(需持续1秒以上)
IF iResetSignal THEN
oActiveAlarms := 16#0000;
END_IF;
改进点包括:
- 增加错误等级区分(警告/严重)
- 采用位图方式记录多个同时发生的报警
- 添加环形缓冲区记录历史报警
- 复位信号需要持续1秒以上(防误触)
4.2 急停处理最佳实践
伺服系统的急停处理有特殊要求:
st复制// 急停响应程序
IF diEmergencyStop THEN
// 立即停止脉冲输出(特殊指令)
PLSV K0 K0 Y0; // Y0对应轴1的脉冲口
// 触发动态制动
DO_Brake := TRUE;
// 记录急停事件
AlarmHandler(iErrorCode:=9001, iErrorLevel:=2);
// 切断伺服使能
bServoOn := FALSE;
END_IF;
关键注意事项:
- 必须使用PLSV指令立即停止脉冲(普通AXISSTOP有延迟)
- 动态制动信号应在5ms内触发
- 急停后必须手动复位才能重新上使能
- 建议在HMI上增加急停次数统计
5. 手动模式与调试技巧
5.1 带速度斜坡的JOG控制
原始代码中的速度斜坡算法可以进一步优化:
st复制// 改进版JOG控制
FUNCTION JogControl: BOOL
VAR_INPUT
bJogFwd: BOOL; // 正转按钮
bJogRev: BOOL; // 反转按钮
rMaxSpeed: REAL; // 最大速度(单位:mm/s)
END_VAR
VAR
rSpeedStep: REAL := rMaxSpeed / 10.0; // 速度阶梯
rRampTime: TIME := T#200MS; // 斜坡时间
tonRamp: TON; // 斜坡计时器
END_VAR
// 速度斜坡生成
IF bJogFwd OR bJogRev THEN
tonRamp(IN:=TRUE, PT:=rRampTime);
IF tonRamp.Q THEN
// 每200ms增加一个速度阶梯
rCurrentSpeed := LIMIT(
MIN:=0.0,
MAX:=rMaxSpeed,
IN:=rCurrentSpeed + rSpeedStep * (BOOL_TO_INT(bJogFwd) - BOOL_TO_INT(bJogRev))
);
tonRamp(IN:=FALSE); // 重置计时器
END_IF;
ELSE
// 平滑减速到零
rCurrentSpeed := 0.0;
END_IF;
// 执行运动
AxisJog(bAxis1, rCurrentSpeed);
这个改进版的特点:
- 采用阶梯式加速而非线性斜坡,更符合操作习惯
- 速度变化幅度与最大速度自动适配
- 松开按钮时立即平滑停止
- 正反转切换无冲击
5.2 调试诊断功能实现
在FB中添加调试接口能大幅提升效率:
st复制FUNCTION_BLOCK AxisDebug
VAR_INPUT
iAxisNo: INT; // 轴号
bEnable: BOOL; // 调试使能
diManualOverride: BOOL; // 手动干预信号
END_VAR
VAR_OUTPUT
oActualPos: REAL; // 实际位置(mm)
oCmdPos: REAL; // 指令位置(mm)
oFollowingError: REAL; // 跟随误差
END_VAR
// 获取实时数据
IF bEnable THEN
oActualPos := _GET_AXIS_POS(iAxisNo);
oCmdPos := _GET_CMD_POS(iAxisNo);
oFollowingError := oCmdPos - oActualPos;
// 手动位置干预(调试用)
IF diManualOverride THEN
_SET_AXIS_POS(iAxisNo, oActualPos);
END_IF;
END_IF;
调试技巧:
- 跟随误差>0.1mm时需要检查机械传动
- 指令位置与实际位置差值持续增大可能表示丢脉冲
- 手动干预后必须重新回零
- 建议在HMI上绘制位置-时间曲线
6. 工艺配方与生产数据管理
6.1 配方功能块增强设计
原始配方功能块可以扩展为支持参数组:
st复制FUNCTION_BLOCK RecipeManager
VAR_INPUT
iRecipeNo: INT; // 配方编号
bLoadRequest: BOOL; // 加载请求
END_VAR
VAR_OUTPUT
oRecipeLoaded: BOOL; // 配方加载完成
oRecipeName: STRING[20]; // 配方名称
END_VAR
VAR
stCurrentRecipe: RecipeStruct; // 当前配方数据
aRecipes: ARRAY[1..50] OF RecipeStruct; // 配方数据库
END_VAR
// 配方数据结构
TYPE RecipeStruct :
STRUCT
sName: STRING[20]; // 配方名称
rSpeed: REAL; // 工作速度
rPos1: REAL; // 位置1
rPos2: REAL; // 位置2
rAccTime: TIME; // 加速时间
END_STRUCT
END_TYPE
// 配方加载逻辑
IF bLoadRequest THEN
IF (iRecipeNo >= 1) AND (iRecipeNo <= 50) THEN
stCurrentRecipe := aRecipes[iRecipeNo];
oRecipeName := stCurrentRecipe.sName;
oRecipeLoaded := TRUE;
ELSE
oRecipeLoaded := FALSE;
END_IF;
END_IF;
高级功能实现:
- 配方数据掉电保持(使用FX5U的文件寄存器)
- 通过RS485导入/导出配方(需配套PC工具)
- 配方版本控制(添加CRC校验)
- 参数越限报警(在加载时检查数值范围)
6.2 生产数据统计实现
在状态机中嵌入生产统计功能:
st复制// 在全局变量区
VAR
nTotalCount: UDINT; // 总产量
nGoodCount: UDINT; // 良品数
stProductionData: ProductionData; // 生产数据
END_VAR
// 数据结构
TYPE ProductionData :
STRUCT
rCycleTime: REAL; // 循环时间(秒)
rEfficiency: REAL; // 设备综合效率
nErrorTimes: UINT; // 异常次数
END_STRUCT
END_TYPE
// 在状态50(工序完成)添加:
nTotalCount := nTotalCount + 1;
IF bQualityOK THEN
nGoodCount := nGoodCount + 1;
END_IF;
// 计算循环时间(需在程序开始时记录启动时间)
stProductionData.rCycleTime := TIME_TO_REAL(tCycleTimer) / 1000.0;
// OEE计算(假设理论周期为5秒)
stProductionData.rEfficiency :=
(nGoodCount / nTotalCount) * // 合格率
(5.0 / stProductionData.rCycleTime) * // 性能率
(1.0 - (stProductionData.nErrorTimes * 60.0 / 86400.0)); // 时间利用率
数据管理建议:
- 每日数据自动存入CSV文件(需扩展存储卡)
- 关键参数设置SPC控制图
- 异常记录关联报警代码
- 通过MQTT上传至MES系统(需扩展通信模块)
7. 项目移植与维护要点
7.1 跨设备移植检查清单
当将此模板应用到新设备时,必须检查以下项目:
-
硬件配置验证
- 确认PLC型号是否为FX5U-32MT/ES(脉冲输出点数差异)
- 检查伺服驱动器型号及参数(三菱MR-JE与J4参数不同)
- 核实编码器分辨率(17位与20位配置不同)
-
参数调整项
st复制// 必须修改的参数(示例) VAR CONSTANT cGearRatio: REAL := 10.0; // 实际减速比 cPulsePerRev: DINT := 10000; // 每转脉冲数 cMachineStroke: REAL := 1000.0; // 设备行程(mm) END_VAR -
IO映射检查
- 原点/限位开关的输入点编号
- 伺服使能/报警的输出点分配
- 急停信号的输入滤波时间(建议20ms)
7.2 长期维护建议
经过多个项目验证,以下维护策略能显著提升系统可靠性:
-
定期检查项目
- 每月备份程序及参数(使用GX Works3的工程归档功能)
- 每季度检查电池电压(FX5U内置电池寿命约5年)
- 每年紧固一次接线端子(特别是脉冲输出线)
-
故障预警指标
监测参数 警告阈值 危险阈值 应对措施 跟随误差 ±0.1mm ±0.3mm 检查联轴器或导轨 电机温度 60℃ 75℃ 改善散热或降低负载 脉冲丢失次数 10次/班 50次/班 检查接线或增加滤波器 -
程序升级策略
- 修改FB接口时保持向下兼容
- 使用版本控制(如Git)管理工程文件
- 重大修改前先在模拟器测试(GX Simulator3)
这套架构最值得称道的不是某个具体功能实现,而是建立了一套完整的伺服控制工程规范。从变量命名(di_输入/do_输出前缀)到注释标准(每个FB头部包含修改记录),再到错误处理流程,形成了可复用的知识体系。当新工程师接手项目时,通过代码本身就能理解90%的设计意图——这才是工业程序最珍贵的价值。