4. 状态机中的事件驱动:按钮触发启动

任务目标

实现一个基于事件驱动的状态机,模拟机器人通过外部按钮事件从待机(IDLE)状态切换到运行(RUNNING)状态。本案例聚焦状态机的事件响应机制,不涉及具体机器人运动控制。

核心实现

使用Python标准库实现一个轻量级状态机,包含两个状态和两个事件(按钮按下、停止)。状态转移规则:IDLE状态下收到BUTTON_PRESS事件进入RUNNING;RUNNING状态下收到STOP事件回到IDLE。按钮事件通过外部函数注入,模拟真实硬件中断。

# robot_state_machine.py
import time
from enum import Enum, auto

class State(Enum):
    IDLE = auto()
    RUNNING = auto()

class Event(Enum):
    BUTTON_PRESS = auto()
    STOP = auto()

class RobotStateMachine:
    def __init__(self):
        self.state = State.IDLE
        self._running = False
        self._transitions = {
            State.IDLE: {Event.BUTTON_PRESS: self._start_running},
            State.RUNNING: {Event.STOP: self._stop_running}
        }

    def _start_running(self):
        self.state = State.RUNNING
        print(f"[事件] 按钮按下 → 状态: {self.state.name}")

    def _stop_running(self):
        self.state = State.IDLE
        print(f"[事件] 停止信号 → 状态: {self.state.name}")

    def handle_event(self, event: Event):
        """外部事件入口,非法事件忽略"""
        handler = self._transitions.get(self.state, {}).get(event)
        if handler:
            handler()
        else:
            print(f"[忽略] 当前状态 {self.state.name} 不响应 {event.name}")

    def run(self):
        """模拟机器人运行循环(仅演示状态切换)"""
        self._running = True
        print("机器人开始运行...")
        time.sleep(0.5)  # 模拟运行过程
        self._running = False

# 模拟外部按钮事件
def button_press_callback(sm: RobotStateMachine):
    """外部中断回调:模拟物理按钮按下"""
    sm.handle_event(Event.BUTTON_PRESS)

if __name__ == "__main__":
    sm = RobotStateMachine()
    print(f"初始状态: {sm.state.name}")

    # 模拟按钮按下
    button_press_callback(sm)
    # 此时状态应为 RUNNING,执行运行逻辑
    if sm.state == State.RUNNING:
        sm.run()

    # 发送停止事件
    sm.handle_event(Event.STOP)
    print(f"最终状态: {sm.state.name}")

运行与验证

  1. 运行方式:将代码保存为robot_state_machine.py,执行python robot_state_machine.py
  2. 预期输出(确定性逻辑推导):
    初始状态: IDLE
    [事件] 按钮按下 → 状态: RUNNING
    机器人开始运行...
    [事件] 停止信号 → 状态: IDLE
    最终状态: IDLE
  3. 验证通过条件:
    • 状态严格按事件转移:IDLE→RUNNING→IDLE
    • 非法事件被忽略(如IDLE状态收到STOP不会改变状态)
    • 按钮回调函数正确触发状态切换

关键设计说明

  • 事件驱动:状态机不主动轮询,而是通过handle_event()接收外部事件,符合ROS中订阅话题/服务回调的异步模式
  • 转移表:使用字典映射状态与事件,新增状态只需扩展字典,无需修改核心逻辑
  • ROS集成提示:实际ROS中可将button_press_callback替换为订阅std_msgs/Bool话题的回调函数,状态机节点在回调中调用handle_event()
IDLE 待机 RUNNING 运行 BUTTON_PRESS STOP 外部按钮回调 运行中可接收停止