第十章:Stateflow 状态机
10.1 Stateflow 基础
10.1.1 什么是 Stateflow
Stateflow 是 Simulink 的扩展工具箱,用于设计和仿真有限状态机(FSM)和流程图。
Stateflow 解决的问题:
纯 Simulink 擅长:
连续/离散数学运算
信号处理
控制算法
Stateflow 擅长:
逻辑判断和状态切换
事件驱动的行为
模式管理
安全监控逻辑
典型应用:
汽车变速箱控制(P/R/N/D档位切换)
交通灯控制(红/黄/绿状态循环)
电机控制模式(启动/运行/停止/故障)
通信协议状态机(握手/传输/断开)
飞行控制模式(起飞/巡航/降落/紧急)
10.1.2 有限状态机概念
有限状态机(FSM)由以下要素组成:
① 状态集合 S = {S1, S2, S3, ...}
系统在任意时刻处于且仅处于一个状态
② 输入事件集合 E = {e1, e2, e3, ...}
触发状态转移的事件
③ 转移函数 δ: S × E → S
在状态S收到事件e后,转移到新状态
④ 初始状态 S0
系统启动时的初始状态
⑤ 输出函数
在特定状态或转移时产生的输出
示例:简单开关状态机
状态:{OFF, ON}
事件:{press_button}
转移:
OFF + press_button → ON
ON + press_button → OFF
初始状态:OFF
10.1.3 Stateflow 与 Simulink 的关系
集成方式:
Simulink 模型
└── Chart(Stateflow图表)
├── 状态(States)
├── 转移(Transitions)
├── 事件(Events)
└── 数据(Data)
Stateflow Chart 作为 Simulink 的一个模块存在:
输入端口:接收 Simulink 信号(触发事件或数据)
输出端口:向 Simulink 输出数据
数据流向:
Simulink 信号 ──→ Stateflow Chart ──→ Simulink 信号
(连续/离散信号) (逻辑状态机) (控制信号/状态输出)
10.2 Stateflow 基本元素
10.2.1 状态(State)
状态表示系统的一种工作模式或条件。
状态的图形表示:
┌─────────────────────────────┐
│ StateName │
├─────────────────────────────┤
│ entry: 进入时执行的动作 │
│ during: 每个时间步执行的动作 │
│ exit: 离开时执行的动作 │
└─────────────────────────────┘
状态类型:
普通状态(AND/OR):
OR 状态:同一层级只有一个激活
AND 状态:同一层级同时激活(并行状态)
超状态(Superstate):
包含子状态的状态
实现层次化状态机
历史节点(History Junction):
记住上次离开超状态时的子状态
重新进入超状态时恢复到历史子状态
状态动作(Actions):
entry(进入动作):
进入该状态时执行一次
用于初始化、复位操作
例:entry: speed = 0; led = ON;
during(持续动作):
在该状态中每个时间步执行
用于持续计算或监控
例:during: temperature = read_sensor();
exit(退出动作):
离开该状态时执行一次
用于清理、保存操作
例:exit: save_data(); alarm = OFF;
on event_name(事件动作):
收到特定事件时执行
例:on BUTTON_PRESS: count = count + 1;
10.2.2 转移(Transition)
转移定义了状态之间的切换条件和动作。
转移的图形表示:
[源状态] ──────────────────────────→ [目标状态]
条件[guard]/动作{action}
转移语法:
event[condition]{action}
event:触发转移的事件(可省略)
[condition]:转移条件(布尔表达式)
{action}:转移时执行的动作
示例:
TIMEOUT[speed > 0]{brake = ON}
→ 收到 TIMEOUT 事件,且速度大于0时,
执行 brake=ON,然后转移到目标状态
默认转移(Default Transition):
没有源状态的转移
用于指定初始状态
图形:实心圆点 ●──→ [初始状态]
转移优先级:
当多个转移同时满足条件时:
优先级由转移的编号决定(数字越小优先级越高)
可以在转移属性中设置优先级
最佳实践:
避免多个转移同时满足条件
使用互斥条件确保唯一性
10.2.3 事件(Event)
事件是触发状态转移的信号。
事件类型:
① 输入事件(Input Event):
来自 Simulink 的触发信号
在 Chart 输入端口显示为触发端口
② 输出事件(Output Event):
Stateflow 向 Simulink 发送的事件
可以触发 Simulink 中的触发子系统
③ 局部事件(Local Event):
Chart 内部使用的事件
用于状态间的内部通信
④ 时间事件(Temporal Event):
基于时间的事件
after(n, sec):n秒后触发
after(n, tick):n个时间步后触发
every(n, sec):每n秒触发一次
示例:
after(5, sec):5秒后触发
after(10, tick):10个仿真步后触发
every(1, sec):每秒触发一次
10.2.4 数据(Data)
Stateflow 中的数据用于存储和传递数值。
数据作用域:
Local(局部):
只在 Chart 内部使用
不与 Simulink 交换
Input(输入):
从 Simulink 接收数据
对应 Chart 的输入端口
Output(输出):
向 Simulink 发送数据
对应 Chart 的输出端口
Parameter(参数):
常量参数,不随时间变化
从 MATLAB 工作区获取
Constant(常数):
固定不变的值
数据类型:
double / single / int8 / uint8 / boolean / ...
与 Simulink 数据类型一致
10.2.5 动作(Action)
动作是在状态或转移中执行的代码。
支持的语法:
赋值:
x = 5;
y = x + 1;
flag = true;
条件语句:
if x > 0
y = x;
else
y = -x;
end
循环:
for i = 1:10
sum = sum + i;
end
函数调用:
y = my_function(x);
[a, b] = another_function(x, y);
MATLAB 函数(部分支持):
y = abs(x);
y = sqrt(x);
y = mod(x, 10);
发送事件:
send(EVENT_NAME);
广播事件:
send(EVENT_NAME, target_state);
10.3 搭建状态机
10.3.1 创建 Chart
方法1:从 Library Browser 添加
Library Browser → Stateflow → Chart
拖拽到 Simulink 模型画布
方法2:从菜单创建
Simulink 模型中:
菜单 Diagram → New → Chart
方法3:命令行
add_block('stateflow/Chart', 'mymodel/MyChart')
Chart 属性配置:
双击 Chart 进入编辑界面
右键 Chart → Properties:
General 标签:
Action Language:
MATLAB(推荐,语法熟悉)
C(性能更高,代码生成)
Update method:
Inherited(继承 Simulink 采样时间)
Discrete(指定采样时间)
Continuous(连续更新)
Enable super step semantics:
允许在一个时间步内执行多次转移
10.3.2 添加状态和转移
添加状态:
方法1:工具栏
Chart 编辑器工具栏 → 状态图标(矩形)→ 点击画布
方法2:右键菜单
在画布空白处右键 → Add State
方法3:快捷键
按住 S 键 + 点击画布
配置状态:
双击状态 → 输入状态名称
在状态内部输入动作代码:
entry: 初始化代码
during: 持续执行代码
exit: 退出代码
添加转移:
方法1:拖拽
鼠标悬停在源状态边缘
出现蓝色箭头时拖拽到目标状态
方法2:工具栏
选择转移工具 → 点击源状态 → 点击目标状态
配置转移:
双击转移线 → 输入转移标签
格式:event[condition]{action}
例:TIMEOUT[speed > 0]{brake = ON}
添加默认转移(指定初始状态):
方法:
工具栏选择默认转移工具
点击目标状态(初始状态)
→ 生成从实心圆点到初始状态的箭头
每个层级必须有且只有一个默认转移
10.3.3 配置转移条件
转移条件语法示例:
简单条件:
[speed > 100]
[temperature >= 80]
[flag == true]
复合条件:
[speed > 100 && temperature < 50]
[mode == 1 || mode == 2]
[~alarm_active]
时间条件:
[after(5, sec)] → 5秒后
[after(100, tick)] → 100个时间步后
[every(1, sec)] → 每秒(用于周期性转移)
事件+条件:
BUTTON_PRESS[count > 3]
→ 收到 BUTTON_PRESS 事件且 count > 3 时转移
无条件转移(立即转移):
不写任何标签
→ 进入源状态后立即转移到目标状态
(通常用于连接节点)
10.3.4 仿真调试
Stateflow 调试工具:
运行仿真时,Chart 编辑器实时显示:
当前激活的状态(高亮显示)
已执行的转移(箭头高亮)
数据的当前值
调试功能:
断点:右键状态/转移 → Set Breakpoint
单步执行:仿真暂停后,逐步执行
数据监视:查看变量当前值
常见调试问题:
问题1:状态机不转移
检查:转移条件是否正确
检查:事件是否正确触发
检查:数据类型是否匹配
问题2:状态机在两个状态间振荡
原因:转移条件互相满足,来回切换
解决:添加时间延迟或计数器防抖
问题3:初始状态不正确
检查:默认转移是否指向正确的初始状态
检查:entry 动作中的初始化是否正确
10.4 实战:交通灯控制系统
10.4.1 需求分析
交通灯控制需求:
状态:
RED(红灯):持续 30 秒
GREEN(绿灯):持续 25 秒
YELLOW(黄灯):持续 5 秒
转移规则:
RED → GREEN:红灯持续 30 秒后
GREEN → YELLOW:绿灯持续 25 秒后
YELLOW → RED:黄灯持续 5 秒后
输出:
red_light:boolean(红灯开关)
green_light:boolean(绿灯开关)
yellow_light:boolean(黄灯开关)
countdown:double(倒计时秒数)
初始状态:RED
10.4.2 状态机设计
状态机图:
●(默认转移)
│
↓
┌─────────────────────────────────────┐
│ RED │
│ entry: red=1; green=0; yellow=0; │
│ countdown=30; │
│ during: countdown=countdown-dt; │
└─────────────────────────────────────┘
│
│ [after(30, sec)]
↓
┌─────────────────────────────────────┐
│ GREEN │
│ entry: red=0; green=1; yellow=0; │
│ countdown=25; │
│ during: countdown=countdown-dt; │
└─────────────────────────────────────┘
│
│ [after(25, sec)]
↓
┌─────────────────────────────────────┐
│ YELLOW │
│ entry: red=0; green=0; yellow=1; │
│ countdown=5; │
│ during: countdown=countdown-dt; │
└─────────────────────────────────────┘
│
│ [after(5, sec)]
└──────────────────────────────────→ RED(循环)
10.4.3 Simulink 集成
搭建步骤:
Step 1:新建 Simulink 模型
Step 2:添加 Stateflow Chart 模块
Library Browser → Stateflow → Chart
Step 3:配置 Chart 输出端口
在 Chart 中定义输出数据:
右键画布 → Add Data → Output
添加:red_light, green_light, yellow_light, countdown
Step 4:在 Chart 内部搭建状态机
添加三个状态:RED, GREEN, YELLOW
添加默认转移到 RED
添加三条转移(使用 after() 时间条件)
在每个状态的 entry 动作中设置灯光输出
Step 5:连接 Simulink 模块
Chart 输出端口 → Display 模块(显示倒计时)
Chart 输出端口 → Scope(显示灯光状态)
Step 6:配置仿真
Stop Time = 120s(观察两个完整周期)
Solver = Fixed-step, Step size = 1s
Chart 内部代码:
RED 状态:
entry:
red_light = true;
green_light = false;
yellow_light = false;
countdown = 30;
GREEN 状态:
entry:
red_light = false;
green_light = true;
yellow_light = false;
countdown = 25;
YELLOW 状态:
entry:
red_light = false;
green_light = false;
yellow_light = true;
countdown = 5;
转移条件:
RED → GREEN:[after(30, sec)]
GREEN → YELLOW:[after(25, sec)]
YELLOW → RED:[after(5, sec)]
10.4.4 仿真验证
matlab
%% 交通灯仿真验证脚本
% 运行仿真
simOut = sim('traffic_light', 'StopTime', '120');
t = simOut.tout;
% 获取输出数据
red = simOut.logsout.getElement('red_light').Values.Data;
green = simOut.logsout.getElement('green_light').Values.Data;
yellow = simOut.logsout.getElement('yellow_light').Values.Data;
% 绘图
figure;
subplot(3,1,1);
stairs(t, double(red), 'r-', 'LineWidth', 2);
ylabel('红灯'); ylim([-0.1 1.1]); grid on;
title('交通灯控制仿真');
subplot(3,1,2);
stairs(t, double(green), 'g-', 'LineWidth', 2);
ylabel('绿灯'); ylim([-0.1 1.1]); grid on;
subplot(3,1,3);
stairs(t, double(yellow), 'Color', [1 0.7 0], 'LineWidth', 2);
ylabel('黄灯'); ylim([-0.1 1.1]); grid on;
xlabel('时间 (s)');
% 验证时序
fprintf('=== 交通灯时序验证 ===\n');
% 找红灯开始时间
red_starts = t(diff([0; double(red)]) > 0);
fprintf('红灯开始时刻:');
fprintf('%.0f ', red_starts(1:min(3,end)));
fprintf('...\n');
% 验证周期
if length(red_starts) >= 2
period = red_starts(2) - red_starts(1);
fprintf('交通灯周期:%.0f 秒(期望:60秒)\n', period);
end
综合实战:电机控制模式管理
系统描述:
工业电机控制模式管理:
模式(状态):
IDLE(空闲):电机停止,等待命令
STARTING(启动):电机加速到目标转速
RUNNING(运行):电机以目标转速运行
STOPPING(停止):电机减速到零
FAULT(故障):检测到故障,紧急停机
转移条件:
IDLE → STARTING:收到 START 命令
STARTING → RUNNING:转速达到目标值(speed >= target_speed)
RUNNING → STOPPING:收到 STOP 命令
STOPPING → IDLE:转速降到零(speed <= 0)
任意状态 → FAULT:检测到故障(fault_detected == true)
FAULT → IDLE:故障清除(fault_cleared == true)
输出:
motor_enable:boolean(电机使能)
speed_setpoint:double(转速设定值)
status_code:int8(状态码:0=空闲,1=启动,2=运行,3=停止,4=故障)
状态机代码:
IDLE 状态:
entry:
motor_enable = false;
speed_setpoint = 0;
status_code = 0;
STARTING 状态:
entry:
motor_enable = true;
speed_setpoint = target_speed;
status_code = 1;
RUNNING 状态:
entry:
status_code = 2;
during:
% 监控运行状态
if speed < target_speed * 0.9
% 转速下降,可能有负载变化
speed_setpoint = target_speed * 1.1; % 适当提高设定值
else
speed_setpoint = target_speed;
end
STOPPING 状态:
entry:
speed_setpoint = 0;
status_code = 3;
exit:
motor_enable = false;
FAULT 状态:
entry:
motor_enable = false;
speed_setpoint = 0;
status_code = 4;
alarm = true;
exit:
alarm = false;
转移:
IDLE → STARTING:START[~fault_detected]
STARTING → RUNNING:[speed >= target_speed * 0.95]
RUNNING → STOPPING:STOP
STOPPING → IDLE:[speed <= 1.0]
IDLE → FAULT:[fault_detected]
STARTING → FAULT:[fault_detected]
RUNNING → FAULT:[fault_detected]
STOPPING → FAULT:[fault_detected]
FAULT → IDLE:[fault_cleared]
本章小结
本章核心要点:
✅ Stateflow 用于设计逻辑状态机,补充 Simulink 的数学计算能力
✅ 状态机三要素:状态(State)/ 转移(Transition)/ 动作(Action)
✅ 状态动作:entry(进入时)/ during(持续)/ exit(退出时)
✅ 转移语法:event[condition]{action}
✅ 时间条件:after(n, sec) / every(n, sec)
✅ 默认转移:指定初始状态(实心圆点箭头)
✅ 层次化状态机:超状态包含子状态,实现复杂逻辑
✅ 并行状态(AND):多个状态同时激活
✅ 历史节点:记住上次离开时的子状态
✅ 调试:仿真时高亮显示当前激活状态和转移
课后练习
-
搭建简单开关状态机:
- 状态:OFF / ON
- 转移:按钮按下时切换
- 输出:LED 状态(0/1)
- 在 Simulink 中用 Pulse Generator 模拟按钮
-
搭建交通灯控制系统:
- 按照本章示例完整搭建
- 添加紧急模式:收到 EMERGENCY 信号时,所有灯闪烁黄灯
- 验证时序是否正确
-
搭建电梯控制状态机:
- 状态:IDLE / MOVING_UP / MOVING_DOWN / DOOR_OPEN
- 转移:楼层请求、到达目标楼层、开关门
- 输出:当前楼层、运动方向、门状态
-
将电机控制模式管理集成到 Simulink:
- Stateflow 管理模式
- Simulink 实现电机动力学模型
- 测试各种模式切换场景
- 测试故障注入和恢复