mirror of
https://github.com/ZGCA-Forge/Elevator.git
synced 2025-12-15 13:34:44 +00:00
Compare commits
4 Commits
4b60359894
...
83459923e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83459923e8 | ||
|
|
b4b99daead | ||
|
|
d44ba8b6cd | ||
|
|
71e8f2a451 |
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.0.8
|
current_version = 0.0.9
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
tag_name = v{new_version}
|
tag_name = v{new_version}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
# ================================
|
# ================================
|
||||||
# Python-related files
|
# Python-related files
|
||||||
# ================================
|
# ================================
|
||||||
|
elevator_saga/traffic/test_cases.py
|
||||||
|
|
||||||
# Compiled Python files
|
# Compiled Python files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ Response format:
|
|||||||
"passengers": [101, 102],
|
"passengers": [101, 102],
|
||||||
"max_capacity": 10,
|
"max_capacity": 10,
|
||||||
"run_status": "constant_speed",
|
"run_status": "constant_speed",
|
||||||
|
"energy_consumed": 38.5,
|
||||||
|
"energy_rate": 1.0,
|
||||||
"..."
|
"..."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -81,7 +83,8 @@ Response format:
|
|||||||
"avg_wait": 15.2,
|
"avg_wait": 15.2,
|
||||||
"p95_wait": 30.0,
|
"p95_wait": 30.0,
|
||||||
"avg_system": 25.5,
|
"avg_system": 25.5,
|
||||||
"p95_system": 45.0
|
"p95_system": 45.0,
|
||||||
|
"total_energy_consumption": 156.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ Features
|
|||||||
|
|
||||||
🔌 **Client-Server Model**: Separate simulation server from control logic for clean architecture
|
🔌 **Client-Server Model**: Separate simulation server from control logic for clean architecture
|
||||||
|
|
||||||
📊 **Performance Metrics**: Track wait times, system times, and completion rates
|
📊 **Performance Metrics**: Track wait times, system times, completion rates, and energy consumption
|
||||||
|
|
||||||
|
⚡ **Energy Tracking**: Monitor and optimize energy consumption with configurable per-elevator energy rates
|
||||||
|
|
||||||
🎯 **Flexible Control**: Implement your own algorithms using a simple controller interface
|
🎯 **Flexible Control**: Implement your own algorithms using a simple controller interface
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ Complete state information for an elevator:
|
|||||||
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
||||||
passenger_destinations: Dict[int, int] = {} # passenger_id -> floor
|
passenger_destinations: Dict[int, int] = {} # passenger_id -> floor
|
||||||
energy_consumed: float = 0.0
|
energy_consumed: float = 0.0
|
||||||
|
energy_rate: float = 1.0 # Energy consumption rate per tick
|
||||||
last_update_tick: int = 0
|
last_update_tick: int = 0
|
||||||
|
|
||||||
Key Properties:
|
Key Properties:
|
||||||
@@ -142,6 +143,11 @@ Key Properties:
|
|||||||
- ``pressed_floors``: List of destination floors for current passengers
|
- ``pressed_floors``: List of destination floors for current passengers
|
||||||
- ``load_factor``: Current load as fraction of capacity (0.0 to 1.0)
|
- ``load_factor``: Current load as fraction of capacity (0.0 to 1.0)
|
||||||
|
|
||||||
|
Energy Tracking:
|
||||||
|
|
||||||
|
- ``energy_consumed``: Total energy consumed by this elevator during the simulation
|
||||||
|
- ``energy_rate``: Energy consumption rate per tick when moving (default: 1.0). Can be customized in traffic configuration files to simulate different elevator types (e.g., older elevators with higher rates, newer energy-efficient elevators with lower rates)
|
||||||
|
|
||||||
FloorState
|
FloorState
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
@@ -261,11 +267,16 @@ Tracks simulation performance:
|
|||||||
p95_floor_wait_time: float = 0.0 # 95th percentile
|
p95_floor_wait_time: float = 0.0 # 95th percentile
|
||||||
average_arrival_wait_time: float = 0.0
|
average_arrival_wait_time: float = 0.0
|
||||||
p95_arrival_wait_time: float = 0.0 # 95th percentile
|
p95_arrival_wait_time: float = 0.0 # 95th percentile
|
||||||
|
total_energy_consumption: float = 0.0 # Total energy consumed by all elevators
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
|
|
||||||
- ``completion_rate``: Fraction of passengers completed (0.0 to 1.0)
|
- ``completion_rate``: Fraction of passengers completed (0.0 to 1.0)
|
||||||
|
|
||||||
|
Energy Metrics:
|
||||||
|
|
||||||
|
- ``total_energy_consumption``: Sum of energy consumed by all elevators in the system. Each elevator consumes ``energy_rate`` units of energy per tick when moving.
|
||||||
|
|
||||||
API Models
|
API Models
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -345,3 +356,75 @@ All models support JSON serialization:
|
|||||||
restored = ElevatorState.from_dict(data)
|
restored = ElevatorState.from_dict(data)
|
||||||
|
|
||||||
This enables seamless transmission over HTTP between client and server.
|
This enables seamless transmission over HTTP between client and server.
|
||||||
|
|
||||||
|
Energy System
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Overview
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
The energy system tracks energy consumption of elevators to help optimize control algorithms for both passenger service and energy efficiency.
|
||||||
|
|
||||||
|
How Energy Works
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**Energy Consumption:**
|
||||||
|
|
||||||
|
- Each elevator has an ``energy_rate`` attribute (default: 1.0)
|
||||||
|
- When an elevator moves (any tick where it's not stopped), it consumes energy equal to its ``energy_rate``
|
||||||
|
- Energy consumption is independent of speed, direction, or load
|
||||||
|
- Total system energy is the sum of all individual elevator energy consumption
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
|
||||||
|
Energy rates are configured in traffic JSON files via the ``elevator_energy_rates`` field:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"building": {
|
||||||
|
"floors": 10,
|
||||||
|
"elevators": 3,
|
||||||
|
"elevator_capacity": 10,
|
||||||
|
"elevator_energy_rates": [1.0, 1.0, 1.2],
|
||||||
|
"scenario": "custom_scenario",
|
||||||
|
"duration": 600
|
||||||
|
},
|
||||||
|
"traffic": []
|
||||||
|
}
|
||||||
|
|
||||||
|
In this example, elevators 0 and 1 have standard energy rates (1.0), while elevator 2 consumes 20% more energy (1.2), perhaps representing an older or less efficient unit.
|
||||||
|
|
||||||
|
**Use Cases:**
|
||||||
|
|
||||||
|
1. **Algorithm Optimization**: Balance passenger wait times against energy consumption
|
||||||
|
2. **Heterogeneous Fleets**: Model buildings with elevators of different ages/efficiencies
|
||||||
|
3. **Cost Analysis**: Evaluate the energy cost of different control strategies
|
||||||
|
4. **Green Building Simulation**: Optimize for minimal energy while maintaining service quality
|
||||||
|
|
||||||
|
Example Usage
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Get current state
|
||||||
|
state = api_client.get_state()
|
||||||
|
|
||||||
|
# Check individual elevator energy
|
||||||
|
for elevator in state.elevators:
|
||||||
|
print(f"Elevator {elevator.id}: {elevator.energy_consumed} units consumed")
|
||||||
|
print(f" Energy rate: {elevator.energy_rate} units/tick")
|
||||||
|
|
||||||
|
# Check total system energy
|
||||||
|
metrics = state.metrics
|
||||||
|
print(f"Total system energy: {metrics.total_energy_consumption} units")
|
||||||
|
print(f"Completed passengers: {metrics.completed_passengers}")
|
||||||
|
|
||||||
|
# Calculate energy per passenger
|
||||||
|
if metrics.completed_passengers > 0:
|
||||||
|
energy_per_passenger = metrics.total_energy_consumption / metrics.completed_passengers
|
||||||
|
print(f"Energy per passenger: {energy_per_passenger:.2f} units")
|
||||||
|
|
||||||
|
**Default Behavior:**
|
||||||
|
|
||||||
|
If ``elevator_energy_rates`` is not specified in the traffic file, all elevators default to an energy rate of 1.0, ensuring backward compatibility with existing traffic files.
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ A Python implementation of the Elevator Saga game with event-driven architecture
|
|||||||
realistic elevator dispatch algorithm development and testing.
|
realistic elevator dispatch algorithm development and testing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "0.0.8"
|
__version__ = "0.0.9"
|
||||||
__author__ = "ZGCA Team"
|
__author__ = "ZGCA Team"
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ class ElevatorState(SerializableModel):
|
|||||||
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
||||||
passenger_destinations: Dict[int, int] = field(default_factory=dict) # 乘客ID -> 目的地楼层映射
|
passenger_destinations: Dict[int, int] = field(default_factory=dict) # 乘客ID -> 目的地楼层映射
|
||||||
energy_consumed: float = 0.0
|
energy_consumed: float = 0.0
|
||||||
|
energy_rate: float = 1.0 # 能耗率:每tick消耗的能量单位
|
||||||
last_update_tick: int = 0
|
last_update_tick: int = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -337,7 +338,7 @@ class PerformanceMetrics(SerializableModel):
|
|||||||
p95_floor_wait_time: float = 0.0
|
p95_floor_wait_time: float = 0.0
|
||||||
average_arrival_wait_time: float = 0.0
|
average_arrival_wait_time: float = 0.0
|
||||||
p95_arrival_wait_time: float = 0.0
|
p95_arrival_wait_time: float = 0.0
|
||||||
# total_energy_consumption: float = 0.0
|
total_energy_consumption: float = 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def completion_rate(self) -> float:
|
def completion_rate(self) -> float:
|
||||||
@@ -346,13 +347,6 @@ class PerformanceMetrics(SerializableModel):
|
|||||||
return 0.0
|
return 0.0
|
||||||
return self.completed_passengers / self.total_passengers
|
return self.completed_passengers / self.total_passengers
|
||||||
|
|
||||||
# @property
|
|
||||||
# def energy_per_passenger(self) -> float:
|
|
||||||
# """每位乘客能耗"""
|
|
||||||
# if self.completed_passengers == 0:
|
|
||||||
# return 0.0
|
|
||||||
# return self.total_energy_consumption / self.completed_passengers
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SimulationState(SerializableModel):
|
class SimulationState(SerializableModel):
|
||||||
|
|||||||
@@ -180,6 +180,14 @@ class ElevatorSimulation:
|
|||||||
building_config["elevators"], building_config["floors"], building_config["elevator_capacity"]
|
building_config["elevators"], building_config["floors"], building_config["elevator_capacity"]
|
||||||
)
|
)
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
# 设置电梯能耗率
|
||||||
|
elevator_energy_rates = building_config.get("elevator_energy_rates", [1.0] * building_config["elevators"])
|
||||||
|
for i, elevator in enumerate(self.state.elevators):
|
||||||
|
if i < len(elevator_energy_rates):
|
||||||
|
elevator.energy_rate = elevator_energy_rates[i]
|
||||||
|
server_debug_log(f"电梯 E{elevator.id} 能耗率设置为: {elevator.energy_rate}")
|
||||||
|
|
||||||
self.max_duration_ticks = building_config["duration"]
|
self.max_duration_ticks = building_config["duration"]
|
||||||
traffic_data: list[Dict[str, Any]] = file_data["traffic"]
|
traffic_data: list[Dict[str, Any]] = file_data["traffic"]
|
||||||
traffic_data.sort(key=lambda t: cast(int, t["tick"]))
|
traffic_data.sort(key=lambda t: cast(int, t["tick"]))
|
||||||
@@ -380,8 +388,12 @@ class ElevatorSimulation:
|
|||||||
old_position = elevator.position.current_floor_float
|
old_position = elevator.position.current_floor_float
|
||||||
if elevator.target_floor_direction == Direction.UP:
|
if elevator.target_floor_direction == Direction.UP:
|
||||||
new_floor = elevator.position.floor_up_position_add(movement_speed)
|
new_floor = elevator.position.floor_up_position_add(movement_speed)
|
||||||
|
# 电梯移动时增加能耗,每tick增加电梯的能耗率
|
||||||
|
elevator.energy_consumed += elevator.energy_rate
|
||||||
elif elevator.target_floor_direction == Direction.DOWN:
|
elif elevator.target_floor_direction == Direction.DOWN:
|
||||||
new_floor = elevator.position.floor_up_position_add(-movement_speed)
|
new_floor = elevator.position.floor_up_position_add(-movement_speed)
|
||||||
|
# 电梯移动时增加能耗,每tick增加电梯的能耗率
|
||||||
|
elevator.energy_consumed += elevator.energy_rate
|
||||||
else:
|
else:
|
||||||
# 之前的状态已经是到站了,清空上一次到站的方向
|
# 之前的状态已经是到站了,清空上一次到站的方向
|
||||||
pass
|
pass
|
||||||
@@ -435,7 +447,6 @@ class ElevatorSimulation:
|
|||||||
self._emit_event(
|
self._emit_event(
|
||||||
EventType.STOPPED_AT_FLOOR, {"elevator": elevator.id, "floor": new_floor, "reason": "move_reached"}
|
EventType.STOPPED_AT_FLOOR, {"elevator": elevator.id, "floor": new_floor, "reason": "move_reached"}
|
||||||
)
|
)
|
||||||
# elevator.energy_consumed += abs(direction * elevator.speed_pre_tick) * 0.5
|
|
||||||
|
|
||||||
def _process_elevator_stops(self) -> None:
|
def _process_elevator_stops(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -553,6 +564,10 @@ class ElevatorSimulation:
|
|||||||
completed = [p for p in self.state.passengers.values() if p.status == PassengerStatus.COMPLETED]
|
completed = [p for p in self.state.passengers.values() if p.status == PassengerStatus.COMPLETED]
|
||||||
|
|
||||||
total_passengers = len(self.state.passengers)
|
total_passengers = len(self.state.passengers)
|
||||||
|
|
||||||
|
# 计算总能耗
|
||||||
|
total_energy = sum(elevator.energy_consumed for elevator in self.state.elevators)
|
||||||
|
|
||||||
if not completed:
|
if not completed:
|
||||||
return PerformanceMetrics(
|
return PerformanceMetrics(
|
||||||
completed_passengers=0,
|
completed_passengers=0,
|
||||||
@@ -561,6 +576,7 @@ class ElevatorSimulation:
|
|||||||
p95_floor_wait_time=0,
|
p95_floor_wait_time=0,
|
||||||
average_arrival_wait_time=0,
|
average_arrival_wait_time=0,
|
||||||
p95_arrival_wait_time=0,
|
p95_arrival_wait_time=0,
|
||||||
|
total_energy_consumption=total_energy,
|
||||||
)
|
)
|
||||||
|
|
||||||
floor_wait_times = [float(p.floor_wait_time) for p in completed]
|
floor_wait_times = [float(p.floor_wait_time) for p in completed]
|
||||||
@@ -586,6 +602,7 @@ class ElevatorSimulation:
|
|||||||
p95_floor_wait_time=average_excluding_top_percent(floor_wait_times, 5),
|
p95_floor_wait_time=average_excluding_top_percent(floor_wait_times, 5),
|
||||||
average_arrival_wait_time=sum(arrival_wait_times) / len(arrival_wait_times) if arrival_wait_times else 0,
|
average_arrival_wait_time=sum(arrival_wait_times) / len(arrival_wait_times) if arrival_wait_times else 0,
|
||||||
p95_arrival_wait_time=average_excluding_top_percent(arrival_wait_times, 5),
|
p95_arrival_wait_time=average_excluding_top_percent(arrival_wait_times, 5),
|
||||||
|
total_energy_consumption=total_energy,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_events(self, since_tick: int = 0) -> List[SimulationEvent]:
|
def get_events(self, since_tick: int = 0) -> List[SimulationEvent]:
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ def generate_fire_evacuation_traffic(
|
|||||||
for floor in range(1, floors):
|
for floor in range(1, floors):
|
||||||
# 每层随机数量的人需要疏散
|
# 每层随机数量的人需要疏散
|
||||||
num_people = random.randint(people_per_floor[0], people_per_floor[1])
|
num_people = random.randint(people_per_floor[0], people_per_floor[1])
|
||||||
for i in range(num_people):
|
for _ in range(num_people):
|
||||||
# 在10个tick内陆续到达,模拟疏散的紧急性
|
# 在10个tick内陆续到达,模拟疏散的紧急性
|
||||||
arrival_tick = alarm_tick + random.randint(0, min(10, duration - alarm_tick - 1))
|
arrival_tick = alarm_tick + random.randint(0, min(10, duration - alarm_tick - 1))
|
||||||
if arrival_tick < duration:
|
if arrival_tick < duration:
|
||||||
@@ -791,10 +791,12 @@ def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str]
|
|||||||
traffic_data = generator_func(**generator_params)
|
traffic_data = generator_func(**generator_params)
|
||||||
|
|
||||||
# 准备building配置
|
# 准备building配置
|
||||||
|
num_elevators = params["elevators"]
|
||||||
building_config = {
|
building_config = {
|
||||||
"floors": params["floors"],
|
"floors": params["floors"],
|
||||||
"elevators": params["elevators"],
|
"elevators": num_elevators,
|
||||||
"elevator_capacity": params["elevator_capacity"],
|
"elevator_capacity": params["elevator_capacity"],
|
||||||
|
"elevator_energy_rates": [1.0] * num_elevators, # 每台电梯的能耗率,默认为1.0
|
||||||
"scenario": scenario,
|
"scenario": scenario,
|
||||||
"scale": scale,
|
"scale": scale,
|
||||||
"description": f"{config['description']} ({scale}规模)",
|
"description": f"{config['description']} ({scale}规模)",
|
||||||
@@ -835,7 +837,7 @@ def generate_scaled_traffic_files(
|
|||||||
if custom_building:
|
if custom_building:
|
||||||
floors = custom_building.get("floors", BUILDING_SCALES[scale]["floors"][0])
|
floors = custom_building.get("floors", BUILDING_SCALES[scale]["floors"][0])
|
||||||
elevators = custom_building.get("elevators", BUILDING_SCALES[scale]["elevators"][0])
|
elevators = custom_building.get("elevators", BUILDING_SCALES[scale]["elevators"][0])
|
||||||
elevator_capacity = custom_building.get("capacity", BUILDING_SCALES[scale]["capacity"][0])
|
_elevator_capacity = custom_building.get("capacity", BUILDING_SCALES[scale]["capacity"][0])
|
||||||
|
|
||||||
# 重新确定规模
|
# 重新确定规模
|
||||||
detected_scale = determine_building_scale(floors, elevators)
|
detected_scale = determine_building_scale(floors, elevators)
|
||||||
|
|||||||
Reference in New Issue
Block a user