3. 状态机核心语义

Stateflow 的语义并不复杂,但它有一个非常关键的特点:所有图形元素最终都会被翻译成一套确定的执行规则。如果只把状态图当成“画框框和箭头”,很容易在仿真时出现“为什么这个动作没执行”“为什么两个状态同时激活”“为什么默认转移没走我预想的分支”等问题。本节把 Stateflow 的核心语义拆成六个对象——状态、转移、事件、条件、动作、默认转移——并重点讲清它们在一次仿真步中的执行顺序。

一、状态:层次与激活

状态(State)是 Stateflow 最基本的存储与执行单元。一个状态可以包含 entry(进入)、during(驻留)、exit(退出)以及 on event_name(指定事件响应)等动作。状态具有层次性:父状态内部可以嵌套子状态,形成“或(OR)分解”和“与(AND)分解”两种结构。OR 分解下同一时刻只有一个子状态激活;AND 分解下所有并行子状态同时激活。

判断一个状态是否“活着”,看的是它是否处于激活(active)状态。激活状态会持续执行 during 动作,直到发生转移离开。这一点在汽车 ECU 建模中非常实用:例如 PowerMode 父状态下有 OFF、ACC、ON、RUN 四个 OR 子状态,任意时刻只有一个激活,而 RUN 内部又可以 AND 分解出 EngineCtl 与 BodyCtl 两个并行子状态同时运行。

二、转移、事件与条件

转移(Transition)是状态之间的有向连接,它的完整语法是:

事件[条件]{条件动作}/转移动作

其中事件和条件都是可选的,但至少要有一个,否则该转移会在每个仿真步都被触发。事件(Event)分为输入事件、本地事件、输出事件和隐式事件(如 entry、exit、during、tick、after、before、when)。条件(Condition)是一个返回布尔值的表达式,例如 [VehicleSpeed > 30 && BrakePedal == 0]。只有当事件发生且条件为真时,转移才被“使能”。

转移动作(Transition Action)在转移被真正执行时运行一次,常用于更新输出或记录。条件动作(Condition Action)写在方括号之后、斜杠之前,只要条件为真就会执行,即使后续因为优先级没有选中这条转移。这是初学者最容易混淆的地方:条件动作不等于转移动作。

三、默认转移:进入父状态时的入口选择

默认转移(Default Transition)是从父状态边界指向某个子状态的转移,它决定了父状态被激活时,哪个子状态首先进入。默认转移可以带条件,从而形成“条件分支入口”。例如 PowerMode 被激活时,默认转移根据 IgnitionKey 的值决定进入 OFF 还是 ACC。如果没有默认转移,父状态激活后其子状态不会自动激活,这是很多“状态图不动作”问题的根因。

四、执行顺序:一次仿真步里到底发生了什么

Stateflow 在每个仿真步按固定顺序执行,理解这个顺序是排障的关键。核心规则可以概括为:先处理事件,再评估转移,最后执行动作。具体来说,一个仿真步内大致经历:

  1. 更新输入事件与数据;
  2. 从当前激活状态开始,按层次自顶向下评估使能的转移;
  3. 若存在多条使能转移,按转移优先级(图形上越靠上、越靠左优先级越高,也可用数字标签显式指定)选择一条;
  4. 执行源状态的 exit 动作;
  5. 执行转移上的条件动作与转移动作;
  6. 执行目标状态的 entry 动作;
  7. 若目标状态还有子状态,继续执行其默认转移,递归进入;
  8. 本步结束时,激活状态执行 during 动作。

需要特别注意:exit → 转移动作 → entry 是严格顺序,不能颠倒。如果在一个仿真步内发生多次转移(例如经过中间状态),Stateflow 会按“超步(super step)”机制继续处理,直到没有新的使能转移为止,但默认最多执行 1000 次超步,超过会报“超步溢出”错误。

Stateflow 一次仿真步的执行顺序 状态与转移结构 PowerMode(父状态) 默认转移 OFF ACC ON RUN KeyOn[Ign==1] KeyOn[Ign==2] Start[Ready] KeyOff 执行顺序(单步内) ① 更新输入事件与数据 ② 自顶向下评估使能转移 ③ 按优先级选择一条转移 ④ 执行源状态 exit 动作 ⑤ 执行条件动作 + 转移动作 ⑥ 执行目标状态 entry 动作 ⑦ 递归执行默认转移进入子状态 ⑧ 激活状态执行 during 动作

五、一个可复现的示例

假设输入事件 KeyOn 在 t=0.5s 发生,Ign 当前值为 1,Ready 为 false。当前激活状态为 OFF。执行过程如下:

  • t=0.5s 步:事件 KeyOn 到达,评估 OFF→ACC 转移,条件 [Ign==1] 为真,转移使能;
  • 执行 OFF 的 exit 动作(例如 Output = 0);
  • 执行转移动作(例如 Log = 1);
  • 执行 ACC 的 entry 动作(例如 Output = 1);
  • 本步结束,ACC 的 during 动作开始执行。

如果此时 ACC 内部还有子状态,且存在默认转移,则第 7 步会继续递归进入子状态。若没有默认转移,ACC 激活但子状态不激活,during 动作也不会在子状态上执行——这正是“状态进去了但没动作”的典型原因。

排障提示:当状态图行为不符合预期时,优先检查三件事——默认转移是否存在、转移优先级是否被高优先级分支抢占、条件动作与转移动作是否写反。这三类问题覆盖了绝大多数 Stateflow 语义类故障。

六、优先级与超步

转移优先级默认遵循“从上到下、从左到右”,也可以用数字标签显式指定,数字越小优先级越高。当多条转移同时使能时,只有优先级最高的一条会被执行,其余转移的条件动作仍会执行,但转移动作不会执行。超步机制允许在一个仿真步内连续完成多次转移,但要注意避免形成无条件的循环转移,否则会触发超步溢出。

掌握这六个对象及其执行顺序,就掌握了 Stateflow 语义的骨架。后续无论是做模式管理、故障降级还是多任务调度,都可以在这套骨架上进行扩展。