第十章:Stateflow 状态机

第十章: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

复制代码
集成方式:

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(循环)

搭建步骤:

复制代码
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):多个状态同时激活
✅ 历史节点:记住上次离开时的子状态
✅ 调试:仿真时高亮显示当前激活状态和转移

课后练习

  1. 搭建简单开关状态机:

    • 状态:OFF / ON
    • 转移:按钮按下时切换
    • 输出:LED 状态(0/1)
    • 在 Simulink 中用 Pulse Generator 模拟按钮
  2. 搭建交通灯控制系统:

    • 按照本章示例完整搭建
    • 添加紧急模式:收到 EMERGENCY 信号时,所有灯闪烁黄灯
    • 验证时序是否正确
  3. 搭建电梯控制状态机:

    • 状态:IDLE / MOVING_UP / MOVING_DOWN / DOOR_OPEN
    • 转移:楼层请求、到达目标楼层、开关门
    • 输出:当前楼层、运动方向、门状态
  4. 将电机控制模式管理集成到 Simulink:

    • Stateflow 管理模式
    • Simulink 实现电机动力学模型
    • 测试各种模式切换场景
    • 测试故障注入和恢复
相关推荐
不会C语言的男孩2 天前
第一章:Simulink 基础认知
simulink
WL_GCS5 天前
Level-2 MATLAB S-Function解析
matlab·simulink
可编程芯片开发25 天前
基于PID平滑调整的MPC-RCNC双模式切换控制系统simulink建模与仿真
simulink·mpc·rcnc·pid平滑·mpc-rcnc
可编程芯片开发1 个月前
带三阶参数辨识模块的RCNC扰动抑制闭环伺服控制系统simulink建模与仿真
simulink·参数辨识·rcnc·扰动抑制闭环
2CM_Embed1 个月前
Simulink 仿真加速:配置 MinGW64 编译器并启用加速模式
matlab·simulink·minggw64·仿真加速
隔窗听雨眠1 个月前
Unity与Simulink联合仿真:实现无人机目标追踪系统
unity·无人机·cocos2d·simulink
chhttty1 个月前
《Simulink嵌入式开发实战》新书上市
matlab·simulink
可编程芯片开发1 个月前
基于SMC滑模控制器的永磁同步电机SVPWM矢量控制系统simulink建模与仿真
simulink·svpwm·smc·永磁同步电机·矢量控制·滑模控制器
可编程芯片开发1 个月前
基于DRCNC和双PI控制器的永磁同步电机矢量控制系统simulink建模与仿真
simulink·永磁同步电机·矢量控制·drcnc·双pi控制器