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}")
运行与验证
- 运行方式:将代码保存为
robot_state_machine.py,执行python robot_state_machine.py - 预期输出(确定性逻辑推导):
初始状态: IDLE [事件] 按钮按下 → 状态: RUNNING 机器人开始运行... [事件] 停止信号 → 状态: IDLE 最终状态: IDLE - 验证通过条件:
- 状态严格按事件转移:IDLE→RUNNING→IDLE
- 非法事件被忽略(如IDLE状态收到STOP不会改变状态)
- 按钮回调函数正确触发状态切换
关键设计说明
- 事件驱动:状态机不主动轮询,而是通过
handle_event()接收外部事件,符合ROS中订阅话题/服务回调的异步模式 - 转移表:使用字典映射状态与事件,新增状态只需扩展字典,无需修改核心逻辑
- ROS集成提示:实际ROS中可将
button_press_callback替换为订阅std_msgs/Bool话题的回调函数,状态机节点在回调中调用handle_event()