1. 电梯控制系统状态机建模
任务目标
使用有限状态机(FSM)为单部电梯建立控制模型,覆盖停止(Idle)、运行(Moving)、开门(DoorOpen)、关门(DoorClosed)四个核心状态,并处理来自楼层的外部请求与电梯内部的楼层选择请求。模型需正确响应请求、按方向运行、到站停靠并开关门。
状态定义与转移逻辑
| 当前状态 | 触发事件 | 条件 | 下一状态 | 动作 |
|---|---|---|---|---|
| Idle | request(floor) | floor > currentFloor | Moving | 方向=向上,加入请求队列 |
| Idle | request(floor) | floor < currentFloor | Moving | 方向=向下,加入请求队列 |
| Idle | request(floor) | floor == currentFloor | DoorOpen | 开门 |
| Moving | arrived(floor) | floor == 目标楼层 | Idle | 停止,清除该请求 |
| DoorOpen | timeout(3s) | — | DoorClosed | 关门 |
| DoorClosed | request(floor) | floor > currentFloor | Moving | 方向=向上 |
| DoorClosed | request(floor) | floor < currentFloor | Moving | 方向=向下 |
关键完整代码(Python)
from enum import Enum
from collections import deque
class State(Enum):
IDLE = "idle"
MOVING = "moving"
DOOR_OPEN = "door_open"
DOOR_CLOSED = "door_closed"
class ElevatorFSM:
def __init__(self, total_floors=10):
self.state = State.IDLE
self.current_floor = 1
self.direction = None # "up" / "down"
self.total_floors = total_floors
self.requests = set() # 待处理的楼层请求
self.log = []
def _log(self, msg):
self.log.append(msg)
def request(self, floor):
"""外部或内部请求到达指定楼层"""
if floor < 1 or floor > self.total_floors:
raise ValueError(f"无效楼层: {floor}")
self.requests.add(floor)
self._handle_request()
def _handle_request(self):
if self.state == State.IDLE:
if self.current_floor in self.requests:
self.requests.remove(self.current_floor)
self.state = State.DOOR_OPEN
self._log(f"到达请求楼层 {self.current_floor},开门")
else:
self._start_moving()
elif self.state == State.DOOR_CLOSED:
self._start_moving()
def _start_moving(self):
if not self.requests:
return
# 选择方向:优先当前方向,否则按最近请求决定
if self.direction is None:
if any(f > self.current_floor for f in self.requests):
self.direction = "up"
else:
self.direction = "down"
self.state = State.MOVING
self._log(f"开始运行,方向={self.direction},当前楼层={self.current_floor}")
def step(self):
"""模拟电梯运行一个时间单位(移动一层)"""
if self.state != State.MOVING:
return
if self.direction == "up":
self.current_floor += 1
else:
self.current_floor -= 1
self._log(f"运行至楼层 {self.current_floor}")
# 检查是否到达某个请求楼层
if self.current_floor in self.requests:
self.requests.remove(self.current_floor)
self.state = State.IDLE
self.direction = None
self._log(f"到达楼层 {self.current_floor},停止")
# 若还有同向请求,继续运行;否则开门
if self.requests:
self._start_moving()
else:
self.state = State.DOOR_OPEN
self._log("开门")
def door_timeout(self):
"""模拟3秒超时后关门"""
if self.state == State.DOOR_OPEN:
self.state = State.DOOR_CLOSED
self._log("关门")
if self.requests:
self._start_moving()
def run_until_idle(self, max_steps=100):
"""持续运行直到无请求且门关闭"""
steps = 0
while steps < max_steps:
if self.state == State.IDLE and not self.requests:
break
if self.state == State.MOVING:
self.step()
elif self.state == State.DOOR_OPEN:
self.door_timeout()
elif self.state == State.DOOR_CLOSED and self.requests:
self._start_moving()
steps += 1
return self.log
运行与验证
# 实例化并模拟:初始1楼,请求5楼和2楼
elev = ElevatorFSM(total_floors=10)
logs = elev.run_until_idle() # 空转,无请求
# 添加请求并运行
elev = ElevatorFSM(total_floors=10)
elev.request(5)
elev.request(2)
logs = elev.run_until_idle()
# 打印运行日志
for line in logs:
print(line)
# 验证断言
assert elev.current_floor == 2, f"最终应在2楼,实际在{elev.current_floor}"
assert elev.state == State.DOOR_OPEN, "最终应停在开门状态(等待超时关门)"
print("状态机验证通过")
验证方法与判定标准
- 运行方式:将上述代码保存为
elevator_fsm.py,在Python 3.8+环境中执行python elevator_fsm.py。 - 预期输出(确定性逻辑推导):日志依次显示“开始运行,方向=up”→“运行至楼层2”→“运行至楼层3”→“运行至楼层4”→“运行至楼层5”→“到达楼层5,停止”→“开门”→“关门”→“开始运行,方向=down”→“运行至楼层4”→“运行至楼层3”→“运行至楼层2”→“到达楼层2,停止”→“开门”。最终
current_floor == 2,状态为DOOR_OPEN。 - 通过条件:程序无异常退出,断言全部通过,且日志顺序与上述预期一致。若请求楼层包含当前楼层(如1楼请求1楼),状态应直接从IDLE转为DOOR_OPEN。
常见问题
- 方向选择策略:本实现采用“当前方向优先”的简化策略,未实现电梯调度中的“顺路接单”优化。若需完整SCAN/LOOK算法,需在
_start_moving中增加对同向请求的筛选。 - 门超时:实际系统中门超时后需检测障碍物,本模型以
door_timeout()模拟3秒定时器触发,未加入传感器输入。 - 多请求同时到达:
requests使用set去重,同一楼层多次请求只处理一次,符合实际电梯逻辑。