4、状态动作与转移动作:entry、during、exit动作的编写与执行顺序,转移动作的触发时机
在Stateflow中,状态机之所以能够描述复杂的逻辑行为,核心在于其动作(Action)机制。动作是附着在状态或转移上的代码片段,它们决定了系统在特定时刻执行何种操作。初学者往往容易混淆状态动作与转移动作的区别,导致逻辑执行顺序混乱,甚至产生难以排查的时序Bug。本章将深入剖析entry(进入)、during(持续)、exit(退出)三种状态动作的编写方法与执行顺序,并详细讲解转移动作的触发时机及其与状态动作的优先级关系。理解这些底层机制,是设计可靠控制逻辑的基石。
上图清晰地展示了从状态A转移到状态B时,一个完整时间步内的动作执行顺序。接下来,我们将从原理、方法、案例和误区四个维度展开详细讲解。
4.1 核心原理:状态动作的生命周期与执行语义
Stateflow中的每个状态都有其独立的生命周期。状态动作就是在这个生命周期不同阶段挂接的代码。理解生命周期是掌握动作编写的前提。
- entry 动作(进入动作):当状态从非激活变为激活时执行。它只执行一次,通常用于初始化该状态所需的局部变量、置位输出信号、开启定时器或记录进入时间戳。例如,在电机控制中,进入“加速”状态时,需要将目标转速变量赋值并启动PID控制器。
- during 动作(持续动作):只要状态保持激活,在每个时间步都会执行。它是状态机的“心脏”,用于执行持续的控制算法、读取传感器数据、更新输出或进行条件判断。例如,在“匀速运行”状态中,during动作会持续计算速度误差并输出PWM占空比。
- exit 动作(退出动作):当状态从激活变为非激活时执行。它也只执行一次,通常用于清理资源、保存状态参数、关闭执行器或发出状态离开的指示信号。例如,退出“急停”状态时,需要清除故障标志位。
这三个动作与状态的关系可以用一个简单的类比来理解:entry 是“进门时开灯”,during 是“在房间里持续工作”,exit 是“出门时关灯”。
执行顺序铁律:在一个时间步内,如果状态保持激活,则只执行 during 动作。只有当发生转移时,才会依次执行 exit(源状态)→ 转移动作 → entry(目标状态)→ during(目标状态)。
4.2 判断方法与操作流程:如何正确编写与放置动作
在实际建模中,工程师经常面临“这段代码该放在entry还是during里”的困惑。以下提供一套判断流程,帮助您做出正确决策。
- 判断执行频率:该操作是需要只执行一次,还是每个时间步都执行?只执行一次的放在
entry或exit;需要持续执行的放在during。 - 判断执行时机:该操作是在进入状态时必须完成,还是在离开状态时必须完成?进入时的初始化放在
entry,离开时的清理放在exit。 - 判断是否与转移条件相关:如果某段代码只有在特定转移条件成立时才执行,且与源状态或目标状态的内部逻辑无关,则应将其放在转移动作中。
- 检查依赖关系:如果目标状态的
entry动作依赖于转移动作中计算出的变量值,那么该计算必须放在转移动作中,且转移动作的执行顺序在entry之前。
在Stateflow编辑器中,双击状态图形,即可在属性对话框中看到 entry、during、exit 三个标签页,分别编写对应的代码。转移动作则写在转移连线上的事件标签位置,格式为 事件[条件]{转移动作}。
| 动作类型 | 执行次数 | 典型用途 | 编写位置 |
|---|---|---|---|
| entry | 1次(进入时) | 初始化局部变量、置位输出、启动定时器 | 状态属性 → entry 标签 |
| during | 每次时间步 | 控制算法、实时数据采集、输出刷新 | 状态属性 → during 标签 |
| exit | 1次(退出时) | 保存参数、复位输出、关闭设备 | 状态属性 → exit 标签 |
| 转移动作 | 1次(转移发生时) | 记录转移事件、计算中间变量、触发一次性操作 | 转移连线 → 动作标签 |
4.3 案例拆解:汽车自动空调的温度调节控制逻辑
为了更直观地理解上述概念,我们以一个汽车自动空调温度调节系统为例进行拆解。该系统需要根据车内温度与设定温度的差值,控制鼓风机风速和压缩机状态。
背景与约束:车辆启动后,空调系统上电。设定目标温度为24°C。系统需要快速降温,但又要避免压缩机频繁启停。控制周期为100ms。
状态划分:
- IDLE(待机):空调关闭,无输出。
- COOLING(制冷):压缩机开启,鼓风机高速运转。
- MAINTAIN(维持):压缩机间歇工作,鼓风机低速运转。
动作编写:
// 状态: COOLING
entry:
compressor_on = TRUE; // 开启压缩机
fan_speed = HIGH; // 鼓风机高速
start_time = get_tick(); // 记录进入时刻
during:
temp_error = current_temp - target_temp; // 计算温差
if (temp_error < 2.0)
{
// 温差小于2度,准备切换到维持状态
// 注意:这里不直接切换,而是通过转移条件判断
}
// 持续监测温度并调整风速
if (temp_error > 5.0) fan_speed = HIGH;
else if (temp_error > 3.0) fan_speed = MEDIUM;
exit:
compressor_on = FALSE; // 关闭压缩机
fan_speed = LOW; // 鼓风机降速
last_run_duration = get_tick() - start_time; // 记录本次运行时长
转移动作:
// 从 COOLING 转移到 MAINTAIN
COOLING [temp_error < 2.0] { transition_count++; log_event("Cooling to Maintain"); }
分析判断过程:当车内温度从30°C降至26°C时,temp_error 为2°C,满足转移条件。此时,在一个时间步内,Stateflow先执行 COOLING.exit(关闭压缩机、记录时长),然后执行转移动作(计数加1),最后执行 MAINTAIN.entry(设置维持模式参数)。如果温度回升,则从 MAINTAIN 转移回 COOLING,同样遵循 exit → 转移动作 → entry 的顺序。
结果对比与复盘:如果错误地将 compressor_on = FALSE 写在 during 动作中,那么只要状态处于 COOLING,压缩机就会在每次时间步被关闭,导致系统无法制冷。通过这个案例,可以清晰地看到 exit 动作对于资源释放和状态清理的重要性。
风险提示:在 during 动作中,应避免编写可能阻塞或耗时过长的代码,例如复杂的浮点运算或无限循环。这会拖慢整个状态机的执行周期,导致控制实时性下降。
4.4 常见误区与总结复盘
在实际工程中,以下三个误区最为常见,需要特别警惕。
- 误区一:在 during 中重复初始化。有些工程师习惯在
during里对变量赋值,导致每次时间步都重置变量,使得累加器或计数器永远无法正常工作。正确做法是:初始化放在entry,更新放在during。 - 误区二:混淆转移动作与 during 动作。转移动作只在转移发生时执行一次,而
during在每个时间步都执行。如果希望某个操作在状态激活期间持续生效,应放在during;如果只是想在切换瞬间做一件事,应放在转移动作中。 - 误区三:忽略 exit 动作的清理作用。在状态切换时,如果不在
exit中复位输出,可能会导致执行器在错误的状态下继续运行。例如,从“急停”状态退出时,必须清除急停标志,否则系统会一直处于急停锁定状态。
总结复盘:本章的核心知识点可以归纳为“一个顺序、两个频率、三个动作”。一个顺序是指 exit → 转移动作 → entry → during 的严格执行顺序;两个频率是指 entry/exit 只执行一次,而 during 每个时间步都执行;三个动作分别对应状态的进入、持续和退出阶段。掌握这些基础,您就能构建出逻辑清晰、行为可预测的状态机模型。在下一节中,我们将进一步探讨转移条件的优先级与事件广播机制,敬请期待。