This commit is contained in:
Varyngoth
2026-01-28 13:31:49 -04:00
commit 676659e5b9
239 changed files with 7509 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 8 22:59:23 2025
@author: ethan
"""
#! /usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
def main():
grades = {
# mean and standard deviation for
# average population size
# and average speed by grade levels
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
trajectory_file = "SRS_evac.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
spawned_total=0
#============================================================
agent_set = []
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 50
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
spawned_total+=placed_count
#=========================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
max_iterations=20000
max_agents_per_step=50
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
retry=0
max_retry=25
while retry < max_retry:
try:
simulation.add_agent(agent_params)
spawned_total +=1
agents_this_step+=1
break
except Exception as e:
print("Failed: add_agent")
print(f"For: pos={pos}, speed={v0}")
print(f"{e}: Rescheduling...")
retry +=1
if retry >= max_retry:
print(f"\n\nMax Retries:{max_retry}")
break
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
pos[1]+rng.uniform(-0.1,0.1))
agent_params.position =adj_pos
simulation.iterate()
iter_count = simulation.iteration_count()
print(f"\nTime Step: {iter_count}")
print(f"Total Agents: {spawned_total}")
print(f"Platoon: {platoon_agents.items()}")
print("\nSimulation Completed!")
print(f"Total Time Steps: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,349 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Nov 10 01:02:31 2025
@author: ethan
"""
# SPDX-License-Identifier: LGPL-3.0-or-later
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
def main():
grades = {
# mean and standard deviation for
# average population size
# and average speed by grade levels
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
trajectory_file = "SRS_evac.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
#================================================
#================================================
#================================================
agent_set = []
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
#================================================
#================================================
#================================================
#================================================
# Group platoons by entry door first
door_platoons = {}
for platoon_id, platoon_data in platoon_agents.items():
door = platoon_data["Entry Door"]
if door not in door_platoons:
door_platoons[door] = []
door_platoons[door].append((platoon_id, platoon_data))
# Process each door sequentially
agent_set = []
for door_poly, platoon_list in door_platoons.items():
# Calculate total agents for this door
total_agents = sum(data["Platoon Size"] for _, data in platoon_list)
# Generate all positions for this door at once
try:
all_positions = jps.distribute_by_number(
polygon=door_poly,
number_of_agents=total_agents,
distance_to_agents=0.6, # Increased for safety
distance_to_polygon=0.3,
max_iterations=1500
)
# Distribute positions to platoons
position_index = 0
for platoon_id, platoon_data in platoon_list:
platoon_size = platoon_data["Platoon Size"]
platoon_positions = all_positions[position_index:position_index + platoon_size]
position_index += platoon_size
# Create agents for this platoon
spawn_time = float(rng.uniform(5,15) + rng.uniform(0,120))
spawn_time = min(spawn_time, 120)
offset = 0.1
for k, pos in enumerate(platoon_positions):
speed = float(rng.normal(
loc=df["Speed Mean"][platoon_data["Grade Index"]], # You'll need to store grade index
scale=df["Speed Std Dev"][platoon_data["Grade Index"]],
size=1
)[0])
agent = {
"Grade Level": platoon_data["Grade Level"],
"Entry Point": door_poly, # Or door name if you prefer
"Platoon": platoon_id,
"Position": (float(pos[0]), float(pos[1])),
"Speed": speed,
"Spawn Time": float(spawn_time + (k * offset))
}
agent_set.append(agent)
except Exception as e:
print(f"Error generating positions for door: {e}")
# Fallback: use your original per-platoon approach for this door
#================================================
#================================================
#================================================
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
max_iterations=1500
spawned_total=0
max_agents_per_step=25
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
retry=0
max_retry=10
while retry < max_retry:
try:
simulation.add_agent(agent_params)
spawned_total +=1
agents_this_step+=1
break
except Exception as e:
print("Failed: add_agent")
print(f"For: pos={pos}, speed={v0}")
print(f"{e}: Rescheduling...")
retry +=1
if retry >= max_retry:
print(f"\n\nMax Retries:{max_retry}")
break
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
pos[1]+rng.uniform(-0.1,0.1))
agent_params.position =adj_pos
simulation.iterate()
iter_count = simulation.iteration_count()
print(f"Iteration Complete: {iter_count}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,314 @@
"""
Created on Sat Nov 22 14:42:45 2025
@author: ethan
"""
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
from datetime import datetime
def main():
grades = {
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
trajectory_file = f"SRS_evac_{current_time}.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
interval = 50
try:
max_iterations = int(input("Number of steps?"))
if max_iterations<=0:
raise ValueError
except ValueError:
print("Please enter a positive integer")
return
agent_set = []
next_planned_id = 0
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
next_planned_id +=1
agent['PlannedID'] = next_planned_id
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
total_agents = next_planned_id
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
sim_to_planned = {}
max_agents_per_step=25
spawned_total = 0
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
try:
add_result = simulation.add_agent(agent_params)
internal_id = None
if add_result is not None:
try:
internal_id = int(add_result)
except Exception:
internal_id = None
if internal_id is not None:
sim_to_planned[internal_id] = a
print(f"Added planned={a['PlannedID']}")
print(f"internal={internal_id}")
print(f"time={current_time:.2f}")
spawned_total +=1
else:
print(f"Added planned={a['PlannedID']}")
print("(no internal_id returned)")
print(f"time={current_time:.2f}")
agents_this_step+=1
break
except Exception as e:
pending_for_door = sum(
1 for x in pending if (
x['Entry Point']==a["Entry Point"]))
print("\nadd_agent FAILED")
print(f"planned={a['PlannedID']}")
print(f"pos={pos}")
print(f"speed={v0:.2f}")
print(f"time={current_time:.2f}")
print(f"pending_for_door={pending_for_door}")
print(f"\n\nerror={e}")
retry_delay = 0.1
a["Spawn Time"] = current_time+retry_delay
pending.appendleft(a)
break
simulation.iterate()
iter_count = simulation.iteration_count()
if iter_count % interval ==0:
print(f"\nIteration: {iter_count}")
print(f"time={simulation.elapsed_time():.2f}")
print(f"active={simulation.agent_count()}")
print(f"pending={len(pending)}")
print(f"spawned={spawned_total}/{total_agents}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"Total Agents: {total_agents}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,323 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Nov 30 18:48:13 2025
@author: ethan
"""
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
from datetime import datetime
from jupedsim.internal.notebook_utils import animate, read_sqlite_file
import pedpy
def main():
grades = {
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
trajectory_file = f"SRS_evac_{current_time}.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
interval = 50
try:
max_iterations = int(input("Number of steps?"))
if max_iterations<=0:
raise ValueError
except ValueError:
print("Please enter a positive integer")
return
agent_set = []
next_planned_id = 0
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
next_planned_id +=1
agent['PlannedID'] = next_planned_id
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
total_agents = next_planned_id
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
sim_to_planned = {}
max_agents_per_step=25
spawned_total = 0
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
try:
add_result = simulation.add_agent(agent_params)
internal_id = None
if add_result is not None:
try:
internal_id = int(add_result)
except Exception:
internal_id = None
if internal_id is not None:
sim_to_planned[internal_id] = a
print(f"Added planned={a['PlannedID']}")
print(f"internal={internal_id}")
print(f"time={current_time:.2f}")
spawned_total +=1
else:
print(f"Added planned={a['PlannedID']}")
print("(no internal_id returned)")
print(f"time={current_time:.2f}")
agents_this_step+=1
break
except Exception as e:
pending_for_door = sum(
1 for x in pending if (
x['Entry Point']==a["Entry Point"]))
print("\nadd_agent FAILED")
print(f"planned={a['PlannedID']}")
print(f"pos={pos}")
print(f"speed={v0:.2f}")
print(f"time={current_time:.2f}")
print(f"pending_for_door={pending_for_door}")
print(f"\n\nerror={e}")
retry_delay = 0.1
a["Spawn Time"] = current_time+retry_delay
pending.appendleft(a)
break
simulation.iterate()
iter_count = simulation.iteration_count()
if iter_count % interval ==0:
print(f"\nIteration: {iter_count}")
print(f"time={simulation.elapsed_time():.2f}")
print(f"active={simulation.agent_count()}")
print(f"pending={len(pending)}")
print(f"spawned={spawned_total}/{total_agents}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"Total Agents: {total_agents}")
print(f"{trajectory_file = }")
trajectory_data,walkable_area = read_sqlite_file(trajectory_file)
speed=pedpy.compute_individual_speed(traj_data=trajectory_data,frame_step=10)
speed=speed.merge(trajectory_data.data,on=["id","frame"],how="left")
animate(trajectory_data,walkable_area,title_note="Evac",every_nth_frame=5)
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
archive/geo-setup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

96
archive/geometry.py Normal file
View File

@@ -0,0 +1,96 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
def current():
queue = Polygon([
(18.767,9.395),
(15.214,9.395),
(15.214,4.569),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823),
#(26.163,4.569),
#(28.214,5.07),
#(28.214,17.761),
#(25.787,17.761),
])
crosswalk = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
grass = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
plot_polygon(queue,color="blue",add_points=False)
plot_polygon(crosswalk,color="red",add_points=False)
plot_polygon(grass,color="blue",add_points=False)
def new():
new_queue = Polygon([
(19.531,10.306),
(13.88,10.306),
(12.98,9.896),
(12.98,4.569),
(12.98,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823),
#(26.163,4.569),
#(28.214,5.07),
#(28.214,17.761),
#(25.787,17.761),
])
new_crosswalk = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982),
])
new_grass = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
plot_polygon(new_queue,color="blue",add_points=False)
plot_polygon(new_crosswalk,color="red",add_points=False)
plot_polygon(new_grass,color="blue",add_points=False)
return
def spawn():
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
plot_polygon(spawn_area,color="green",add_points=False)
return spawn_area
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
new()
current()
spawn()
plt.show(block=False)
sys.exit(app.exec())

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,557 @@
I'm creating a simulation using JuPedSim to see how pedestrians interact with a crosswalk system. The crosswalk system will have a user-defined "active" interval, and will be activated by agents being within a predefined distance. All agents will spawn in a specified spawn area, and are attempting to get from one side of the road to the other. They may only use the crosswalk to cross, and can only cross when the crosswalk is "active". Below are provided 4 separate files to create the JuPedSim simulation model:
============================
(1.1)
============================
=>geometry_setup.py(file):
class GeometrySetup(current_setup:bool)
spawn:defines the Polygon for the spawn_area
queue:defines the Polygon for the queue_area
trigger:defines the Polygon for the trigger_area
crosswalk:defines the Polygon for the crosswalk_area
grass:defines the Polygon for the grass_area
============================
(1.2)
============================
=>spawning.py(file):
class SpawnManager(spawn_area:Polygon,min_spacing:float):
__init__:
spawn_area: area for valid spawns
min_spacing: minimum spacing between spawn points
spawn_coords: all coordinates for spawning agents
filled_coords: spawn points currently occupied by agents
spawned_agents: set of agent ids already spawned
agent_pos: dictionary which connects agent ids to a spawn point
generate_coords:
uses Poisson disk method with CDTree from scipy.spatial for all spawn points
get_coords:
provides a tuple[float,float] or None based on if there is room to spawn agents
spawn_agent:
provides AgentSetup object or None depending on if there is room to spawn and there are agents left to spawn
unfill_coords:
returns None, used to indicate a spawn point has been made available by an agent moving
get_agent_pos;
returns (0,0) or the spawn coordinates of agents if they are in agent_pos
check_spawn_complete:
checks whether the number of agents spawned is the same as all_agents list, and if all spawn points are empty.
============================
(1.3)
============================
=>agents(folder):
|>agent_data.py(file):
| dictionary with group keys and values
|>agent_setup.py(file):
defines the AgentSetup class and computes list of all_agents:
"""
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
"""
============================
(1.4)
============================
=>crosswalk_setup.py(file):
class CrosswalkStatus(Enum):
INACTIVE
ACTIVE
COOLDOWN
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
__init__(self,config:CrosswalkConfig,current_setup:bool):
update: calls the following methods, then returns get_state()
update_agents_in_trigger
update_state
update_statistics
update_agents_in_trigger: updates what agents are in the trigger_area for the crosswalk
@staticmethod
points_in_area:finding what points lie in a given area
update_state:updates crosswalk state to be COOLDOWN,ACTIVE or INACTIVE based on specified parameters
activate:changes the system to the ACTIVE state
deactivate:changes the system to the COOLDOWN state
update_statistics:updates info on crosswalks if status is ACTIVE
get_state: provides the state based on the current time in relation to the activation time, then updates the system
can_agent_cross: checks whether ACTIVE or INACTIVE to either let agent pass or add agent to waiting group
@property
is_active: provides a boolean response whether the system is ACTIVE or not
get_statistics:provides stats from the system at the current time
============
here is the code:
============================
(2.1) (geometry_setup.py)
============================
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
class GeometrySetup:
def __init__(self,current_setup:bool):
self.current_setup = current_setup
self.spawn()
self.queue()
self.trigger()
self.crosswalk()
self.grass()
def spawn(self)->Polygon:
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
plot_polygon(spawn_area,color="purple",add_points=False)
return spawn_area
def queue(self)->Polygon:
if self.current_setup is True:
queue_area = Polygon([
(18.767,9.395),
(16.924,9.395),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
else:
queue_area = Polygon([
(19.531,10.306),
(15.214,10.306),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
plot_polygon(queue_area,color="blue",add_points=False)
return queue_area
def trigger(self)->Polygon:
if self.current_setup is True:
trigger_area = Polygon([
(15.214,0),
(15.214,9.395),
(16.924,9.395),
(16.924,0)
])
else:
trigger_area = Polygon([
(15.214,10.306),
(15.214,0),
(12.98,0),
(12.98,9.896),
(13.88,10.306)
])
plot_polygon(trigger_area,color="green",add_points=False)
return trigger_area
def crosswalk(self)->Polygon:
if self.current_setup is True:
crosswalk_area = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
else:
crosswalk_area = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982)
])
plot_polygon(crosswalk_area,color="red",add_points=False)
return crosswalk_area
def grass(self)->Polygon:
if self.current_setup is True:
grass_area = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
else:
grass_area = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
plot_polygon(grass_area,color="blue",add_points=False)
return grass_area
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
geo_map = GeometrySetup(False)
plt.show(block=False)
sys.exit(app.exec())
============================
(2.2) (spawning.py)
============================
from shapely.geometry import Polygon,Point
from typing import Tuple, List, Dict, Set
from scipy.spatial import cKDTree
import sys
import math
import numpy as np
import config
sys.path.insert(0,str(config.AGENTS_DIR))
from agents.agent_setup import AgentSetup
class SpawnManager:
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
"""
self.spawn_area: geometry where agents may spawn
self.min_spacing: minimum spacing for spawn_points
self.spawn_coords: all spawn points available
self.filled_coords: spawn points currently filled by agents
self.spawned_agents: all agents already spawned in sim
self.agent_pos: connects agent_id to spawn_index
self.rng: random number generator object
"""
self.spawn_area = spawn_area
self.min_spacing = min_spacing
self.spawn_coords: np.ndarray = np.array([])
self.filled_coords: np.ndarray = np.array([])
self.spawned_agents: set = set()
self.agent_pos:dict={}
self.rng = np.random.default_rng()
def generate_coords(self,max_samples:int=1000)->None:
min_x,min_y,max_x,max_y = self.spawn_area.bounds
points = []
while len(points) ==0:
x = self.rng.uniform(min_x,max_x)
y = self.rng.uniform(min_y,max_y)
if self.spawn_area.contains(Point(x,y)):
points.append([x,y])
for _ in range(max_samples):
idx = self.rng.integers(0,len(points),endpoint=True)
base = points[idx]
for _ in range(25):
angle = self.rng.uniform(0,2*np.pi)
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
x = base[0]+radius*np.cos(angle)
y = base[1]+radius*np.sin(angle)
if not self.spawn_area.contains(Point(x,y)):
continue
if len(points)>0:
tree = cKDTree(points)
distance,_ = tree.query([[x,y]],k=1)
if distance[0]<self.min_spacing:
continue
points.append([x,y])
break
self.spawn_coords = np.array(points)
self.filled_coords = np.zeros(len(points),dtype=bool)
def get_coords(self)->Tuple[float,float]|None:
'''
points = [
pt for pt in self.generate_coords if pt \
not in self.filled_coords.values()
]
return self.rng.choice(points) if points else None
'''
free_idx = np.where(~self.filled_coords)[0]
if len(free_idx) == 0:
return None
idx = self.rng.choice(free_idx)
return tuple(self.spawn_coords[idx])
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
if len(self.spawned_agents) >= len(all_agents):
return None
spawn_point = self.get_coords()
if not spawn_point:
return None
free_agents = [
agent for agent in all_agents \
if agent.id not in self.spawned_agents
]
if not free_agents:
return None
agent = self.rng.choice(free_agents)
self.spawned_agents.add(agent.id)
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
spawn_idx = np.argmin(distances)
self.filled_coords[spawn_idx] = True
self.agent_pos[agent.id] = spawn_idx
return agent
def unfill_coords(self,agent_id:int)->None:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
self.filled_coords[spawn_idx] = False
del self.agent_pos[agent_id]
self.spawned_agents.discard(agent_id)
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
return tuple(self.spawn_coords[spawn_idx])
return (0,0)
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
return (len(self.spawned_agents)<len(all_agents) and\
np.sum(~self.filled_coords) > 0)
============================
(2.3) (agent_setup.py)
============================
from agent_data import grade_data
from dataclasses import dataclass
import numpy as np
from typing import List
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
def AgentConfig()->List[List[AgentSetup],List[int]]:
agent_id = 1
pop_list = []
all_agents = []
for idx,key in enumerate(grade_data.keys()):
grade = grade_data[key]
pop_list.append(grade["Pop Current"])
for i in range(pop_list[idx]):
rng = np.random.default_rng()
agent = AgentSetup(
id=agent_id,
grade=key,
speed=rng.normal(
grade["Speed Mean"],
grade["Speed Std Dev"]
),
radius=grade["Radius"]
)
all_agents.append(agent)
agent_id +=1
return all_agents, pop_list
============================
(2.4) (crosswalk_setup.py)
============================
from dataclasses import dataclass,field
from enum import Enum
from typing import List,Dict,Tuple,Set
from shapely import Polygon,Point
from geometry_setup import GeometrySetup
import shapely
import numpy as np
class CrosswalkStatus(Enum):
INACTIVE = "inactive"
ACTIVE = "active"
COOLDOWN = "cooldown"
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
def __init__(self,config:CrosswalkConfig,current_setup:bool):
self.config = config
self.crosswalk_area = config.crosswalk_area
self.trigger_area = config.trigger_area
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = 0.0
self.current_time = 0.0
self.activation_count = 0
self.agents_in_trigger: Set[int] = set()
self.agents_waiting: Set[int] = set()
self.total_active_time = 0.0
self.agents_served = 0
def update(
self,
agent_pos:Dict[int,Tuple[float,float]],
time_step:float,
current_time:float=None
)->CrosswalkState:
if current_time is not None:
self.current_time = current_time
else:
self.current_time +=time_step
self.update_agents_in_trigger(agent_pos)
self.update_state(time_step)
self.update_statistics(time_step)
return self.get_state()
def update_agents_in_trigger(
self,
agent_pos:Dict[int,Tuple[float,float]]
)->None:
self.agents_in_trigger.clear()
if not agent_pos:
return
agent_ids = list(agent_pos.keys())
positions = np.array(list(agent_pos.values()),dtype=np.float32)
in_trigger = self.points_in_area(positions,self.trigger_area)
for i, agent_id in enumerate(agent_ids):
if in_trigger[i]:
self.agents_in_trigger.add(agent_id)
@staticmethod
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
if len(points) ==0:
return np.array([],dtype=bool)
x,y = points[:,0],points[:,1]
vertices = shapely.get_coordinates(polygon)
n = len(vertices)
inside = np.zeros(len(points),dtype=bool)
j = n-1
for i in range(n):
xi, yi = vertices[i]
xj, yj = vertices[j]
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
inside ^= mask
j = i
return inside
def update_state(self,time_step:float)->None:
elapsed = self.current_time - self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
if elapsed >= self.config.activation_time:
self.deactivate()
elif self.status == CrosswalkStatus.COOLDOWN:
if elapsed >= self.config.cooldown_time:
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = self.current_time
elif self.status ==CrosswalkStatus.INACTIVE:
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
elapsed >= self.config.activation_delay):
self.activate()
def activate(self)->None:
self.status = CrosswalkStatus.ACTIVE
self.state_start_time = self.current_time
self.activation_count +=1
self.agents_served += len(self.agents_waiting)
self.agents_waiting.clear()
def deactivate(self)->None:
self.status = CrosswalkStatus.COOLDOWN
self.state_start_time = self.current_time
def update_statistics(self,time_step:float)->None:
if self.status == CrosswalkStatus.ACTIVE:
self.total_active_time += time_step
def get_state(self)->CrosswalkState:
elapsed = self.current_time-self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
time_remaining = max(0.0,self.config.activation_time-elapsed)
elif self.status == CrosswalkStatus.COOLDOWN:
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
else:
time_remaining = 0.0
return CrosswalkState(
status=self.status,
is_active=(self.status == CrosswalkStatus.ACTIVE),
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
time_remaining=time_remaining,
agents_in_trigger=len(self.agents_in_trigger),
agents_waiting=len(self.agents_waiting),
activation_count = self.activation_count
)
def can_agent_cross(self,agent_id:int)->bool:
if self.status == CrosswalkStatus.ACTIVE:
return True
else:
if agent_id in self.agents_in_trigger:
self.agents_waiting.add(agent_id)
return False
@property
def is_active(self)->bool:
return self.status == CrosswalkStatus.ACTIVE
def get_statistics(self)->Dict[str,float]:
return {
"total_activations":self.activation_count,
"total_active_time":self.total_active_time,
"agents_served":self.agents_served,
"current_agents_in_trigger":len(self.agents_in_trigger),
"current_agents_waiting":len(self.agents_waiting)
}
=========================
The last part which needs to be done is using the JuPedSim module to create a full simulation using all these pieces;

View File

@@ -0,0 +1,690 @@
The text file includes all the files which makeup my simulation configuration. could you please run through them and fix any potential errors, delete any unused or unnecessary variables?
===================
(geometry_setup.py)
===================
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from shapely import Polygon
from shapely.plotting import plot_polygon
from typing import Tuple
class GeometrySetup:
def __init__(self,current_setup:bool):
self.current_setup = current_setup
self.spawn_area = self.spawn()
self.queue_area = self.queue()
self.grass_area = self.grass()
self.crosswalk_area = self.crosswalk()
self.entry_area = self.entry_polygon()
self.exit_area = self.exit_polygon()
def spawn(self)->Polygon:
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
return spawn_area
def queue(self)->Polygon:
if self.current_setup is True:
queue_area = Polygon([
(18.767,9.395),
(16.924,9.395),
(15.214,9.395), #prev trigger area
(15.214,0), # ||
(16.924,0), # ||
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
else:
queue_area = Polygon([
(19.531,10.306),
(15.214,10.306),
(13.88,10.306), #prev trigger area
(12.98,9.896), # ||
(12.98,0), # ||
(15.214,0), #
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
return queue_area
def grass(self)->Polygon:
if self.current_setup is True:
grass_area = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
else:
grass_area = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
return grass_area
def crosswalk(self)->Polygon:
if self.current_setup is True:
crosswalk_area = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
else:
crosswalk_area = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982)
])
return crosswalk_area
def entry_polygon(self)->Polygon:
if self.current_setup is True:
entry_area = Polygon([ # x: 2.9m, y: 3.428m
(15.314,5.268), # dx: 0.1m, dy: 0.8m
(15.314,8.696),
(18.214,8.696),
(18.214,5.268)
])
else:
entry_area = Polygon([ # x: 2.9m, y: 5.6m
(15.98,9.782),
(15.98,4.182),
(13.08,4.182),
(13.08,9.782)
])
return entry_area
def exit_polygon(self)->Polygon:
if self.current_setup is True:
exit_area = Polygon([ # x: 2.9m, y: 3.428m
(1,5.268),
(1,8.696),
(3.9,8.696),
(3.9,5.268)
])
else:
exit_area = Polygon([ # x: 2.9m, y: 5.6m
(3.23,4.182),
(3.23,9.782),
(6.13,9.782),
(6.13,4.182)
])
return exit_area
def plot_all(self)->Tuple[Figure,Axes]:
plot_polygon(self.spawn_area,color="green",add_points=False)
plot_polygon(self.queue_area,color="blue",add_points=False)
plot_polygon(self.grass_area,color="blue",add_points=False)
plot_polygon(self.crosswalk_area,color="red",add_points=False)
plot_polygon(self.entry_area,color="black",add_points=False)
plot_polygon(self.exit_area,color="black",add_points=False)
return
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
GeometrySetup(False).plot_all()
plt.show(block=False)
sys.exit(app.exec())
===================
(agent_setup.py)
===================
from agent_data import grade_data
from dataclasses import dataclass
import numpy as np
from typing import List
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
def AgentConfig()->List[List[AgentSetup],List[int]]:
agent_id = 1
pop_list = []
all_agents = []
for idx,key in enumerate(grade_data.keys()):
grade = grade_data[key]
pop_list.append(grade["Pop Current"])
for i in range(pop_list[idx]):
rng = np.random.default_rng()
agent = AgentSetup(
id=agent_id,
grade=key,
speed=rng.normal(
grade["Speed Mean"],
grade["Speed Std Dev"]
),
radius=grade["Radius"]
)
all_agents.append(agent)
agent_id +=1
return all_agents, pop_list
===================
(crosswalk_setup.py)
===================
from dataclasses import dataclass,field
from enum import Enum
from typing import List,Dict,Tuple,Set
from shapely import Polygon,Point
from geometry_setup import GeometrySetup
import shapely
import numpy as np
class CrosswalkStatus(Enum):
INACTIVE = "inactive"
ACTIVE = "active"
COOLDOWN = "cooldown"
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
def __init__(self,config:CrosswalkConfig,current_setup:bool):
self.config = config
self.crosswalk_area = config.crosswalk_area
self.trigger_area = config.trigger_area
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = 0.0
self.current_time = 0.0
self.activation_count = 0
self.agents_in_trigger: Set[int] = set()
self.agents_waiting: Set[int] = set()
self.total_active_time = 0.0
self.agents_served = 0
def update(
self,
agent_pos:Dict[int,Tuple[float,float]],
time_step:float,
current_time:float=None
)->CrosswalkState:
if current_time is not None:
self.current_time = current_time
else:
self.current_time +=time_step
self.update_agents_in_trigger(agent_pos)
self.update_state(time_step)
self.update_statistics(time_step)
return self.get_state()
def update_agents_in_trigger(
self,
agent_pos:Dict[int,Tuple[float,float]]
)->None:
self.agents_in_trigger.clear()
if not agent_pos:
return
agent_ids = list(agent_pos.keys())
positions = np.array(list(agent_pos.values()),dtype=np.float32)
in_trigger = self.points_in_area(positions,self.trigger_area)
for i, agent_id in enumerate(agent_ids):
if in_trigger[i]:
self.agents_in_trigger.add(agent_id)
@staticmethod
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
if len(points) ==0:
return np.array([],dtype=bool)
x,y = points[:,0],points[:,1]
vertices = shapely.get_coordinates(polygon)
n = len(vertices)
inside = np.zeros(len(points),dtype=bool)
j = n-1
for i in range(n):
xi, yi = vertices[i]
xj, yj = vertices[j]
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
inside ^= mask
j = i
return inside
def update_state(self,time_step:float)->None:
elapsed = self.current_time - self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
if elapsed >= self.config.activation_time:
self.deactivate()
elif self.status == CrosswalkStatus.COOLDOWN:
if elapsed >= self.config.cooldown_time:
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = self.current_time
elif self.status ==CrosswalkStatus.INACTIVE:
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
elapsed >= self.config.activation_delay):
self.activate()
def activate(self)->None:
self.status = CrosswalkStatus.ACTIVE
self.state_start_time = self.current_time
self.activation_count +=1
self.agents_served += len(self.agents_waiting)
self.agents_waiting.clear()
def deactivate(self)->None:
self.status = CrosswalkStatus.COOLDOWN
self.state_start_time = self.current_time
def update_statistics(self,time_step:float)->None:
if self.status == CrosswalkStatus.ACTIVE:
self.total_active_time += time_step
def get_state(self)->CrosswalkState:
elapsed = self.current_time-self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
time_remaining = max(0.0,self.config.activation_time-elapsed)
elif self.status == CrosswalkStatus.COOLDOWN:
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
else:
time_remaining = 0.0
return CrosswalkState(
status=self.status,
is_active=(self.status == CrosswalkStatus.ACTIVE),
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
time_remaining=time_remaining,
agents_in_trigger=len(self.agents_in_trigger),
agents_waiting=len(self.agents_waiting),
activation_count = self.activation_count
)
def can_agent_cross(self,agent_id:int)->bool:
if self.status == CrosswalkStatus.ACTIVE:
return True
else:
if agent_id in self.agents_in_trigger:
self.agents_waiting.add(agent_id)
return False
@property
def is_active(self)->bool:
return self.status == CrosswalkStatus.ACTIVE
def get_statistics(self)->Dict[str,float]:
return {
"total_activations":self.activation_count,
"total_active_time":self.total_active_time,
"agents_served":self.agents_served,
"current_agents_in_trigger":len(self.agents_in_trigger),
"current_agents_waiting":len(self.agents_waiting)
}
===================
(spawning.py)
===================
from shapely.geometry import Polygon,Point
from typing import Tuple, List, Dict, Set
from scipy.spatial import cKDTree
import sys
import math
import numpy as np
import config
sys.path.insert(0,str(config.AGENTS_DIR))
from agents.agent_setup import AgentSetup
class SpawnManager:
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
"""
self.spawn_area: geometry where agents may spawn
self.min_spacing: minimum spacing for spawn_points
self.spawn_coords: all spawn points available
self.filled_coords: spawn points currently filled by agents
self.spawned_agents: all agents already spawned in sim
self.agent_pos: connects agent_id to spawn_index
self.rng: random number generator object
"""
self.spawn_area = spawn_area
self.min_spacing = min_spacing
self.spawn_coords: np.ndarray = np.array([])
self.filled_coords: np.ndarray = np.array([])
self.spawned_agents: set = set()
self.agent_pos:dict={}
self.rng = np.random.default_rng()
def generate_coords(self,max_samples:int=1000)->None:
min_x,min_y,max_x,max_y = self.spawn_area.bounds
points = []
while len(points) ==0:
x = self.rng.uniform(min_x,max_x)
y = self.rng.uniform(min_y,max_y)
if self.spawn_area.contains(Point(x,y)):
points.append([x,y])
for _ in range(max_samples):
idx = self.rng.integers(0,len(points),endpoint=True)
base = points[idx]
for _ in range(25):
angle = self.rng.uniform(0,2*np.pi)
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
x = base[0]+radius*np.cos(angle)
y = base[1]+radius*np.sin(angle)
if not self.spawn_area.contains(Point(x,y)):
continue
if len(points)>0:
tree = cKDTree(points)
distance,_ = tree.query([[x,y]],k=1)
if distance[0]<self.min_spacing:
continue
points.append([x,y])
break
self.spawn_coords = np.array(points)
self.filled_coords = np.zeros(len(points),dtype=bool)
#return points
def get_coords(self)->Tuple[float,float]|None:
'''
points = [
pt for pt in self.generate_coords if pt \
not in self.filled_coords.values()
]
return self.rng.choice(points) if points else None
'''
free_idx = np.where(~self.filled_coords)[0]
if len(free_idx) == 0:
return None
idx = self.rng.choice(free_idx)
return tuple(self.spawn_coords[idx])
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
if len(self.spawned_agents) >= len(all_agents):
return None
spawn_point = self.get_coords()
if not spawn_point:
return None
free_agents = [
agent for agent in all_agents \
if agent.id not in self.spawned_agents
]
if not free_agents:
return None
agent = self.rng.choice(free_agents)
self.spawned_agents.add(agent.id)
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
spawn_idx = np.argmin(distances)
self.filled_coords[spawn_idx] = True
self.agent_pos[agent.id] = spawn_idx
return agent
def unfill_coords(self,agent_id:int)->None:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
self.filled_coords[spawn_idx] = False
del self.agent_pos[agent_id]
self.spawned_agents.discard(agent_id)
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
return tuple(self.spawn_coords[spawn_idx])
return (0,0)
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
return (len(self.spawned_agents)<len(all_agents) and\
np.sum(~self.filled_coords) > 0)
===================
(simulation.py)
===================
from typing import Tuple,List,Dict
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon,Point,GeometryCollection,get_coordinates
from shapely.plotting import plot_polygon
import jupedsim as jps
import numpy as np
import pathlib
from geometry_setup import GeometrySetup
from spawning import SpawnManager
from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig
from agents.agent_setup import AgentConfig
from dataclasses import dataclass
@dataclass
class SimulationSetup:
simulation_time:float=200.0
time_step:float=0.01
max_iterations:int=50000
trajectory_file:str="crosswalk_sim.sqlite"
current_setup:bool=True
trigger_dist:float=1.0
activation_time:float=30.0
cooldown_time:float=10.0
min_agents_activation:int=1
activation_delay:float=2.0
class CrosswalkSimulation:
def __init__(self,config:SimulationSetup):
self.config = config
print("\nInitializing Geometry...")
self.geo = GeometrySetup(self.config.current_setup)
self.spawn_area = self.geo.spawn()
self.queue_area = self.geo.queue()
self.grass_area = self.geo.grass()
self.crosswalk_area = self.geo.crosswalk()
self.entry_area = self.geo.entry_polygon()
self.exit_area = self.geo.exit_polygon()
self.walkable_area = GeometryCollection([
self.spawn_area,
self.queue_area,
self.grass_area,
self.crosswalk_area,
]).unary_union
print("Geometry Configuration Complete!")
print("\nInitializing Spawn Manager...")
self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=0.55)
self.spawn_manager.generate_coords()
print("Spawn Manager Setup Complete!")
print("\nInitializing Crosswalk Controller...")
self.crosswalk_controller = CrosswalkController(
CrosswalkConfig(
trigger_dist=self.config.trigger_dist,
activation_time=self.config.activation_time,
cooldown_time=self.config.cooldown_time,
min_agents_activation=self.config.min_agents_activation,
activation_delay=self.config.activation_delay,
crosswalk_area=self.crosswalk_area,
trigger_area=self.entry_area
),
self.config.current_setup
)
print("Crosswalk Controller Setup Complete!")
print("\nInitializing Agent Setup...")
self.all_agents, pop_list = AgentConfig()
self.total_agents = len(self.all_agents)
print(f"Created {self.total_agents} Agents...")
print("Agent Setup Complete!")
print("\nInitializing JuPedSim Model...")
self.trajectory_file = self.config.trajectory_file
self.simulation = jps.Simulation(
model=jps.CollisionFreeSpeedModel(),
geometry=self.walkable_area,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(self.trajectory_file)
),
dt=self.config.time_step
)
self.journey_setup()
self.jps_agent_ids = {}
# unsure whether these are necessary
#
#self.agent_targets = {}
#self.active_agents = {}
self.iteration_count = 0
self.current_time = 0.0
print(f"\nJuPedSim Model Initialized with:")
print(f"\tTime Step: {self.config.time_step} s")
print(f"\tMax Iterations: {self.config.max_iterations}")
print(f"\tCrosswalk Activation Length: {self.config.activation_time} s")
print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time} s")
def journey_setup(self):
self.queue_waiting_coords = self.waiting_coords(
self.queue_area,self.spawn_manager.min_spacing
)
self.queue_stage_id = self.simulation.add_waiting_set_stage(
self.queue_waiting_coords
)
self.queue_stage = self.simulation.get_stage(self.queue_stage_id)
self.queue_stage.state = jps.WaitingSetState.ACTIVE
self.crosswalk_entry_id = self.simulation.add_waypoint_stage(
self.entry_area,
self.spawn_manager.min_spacing
)
self.crosswalk_exit_id = self.simulation.add_waypoint_stage(
self.exit_area,
self.spawn_manager.min_spacing
)
self.journey = jps.JourneyDescription([
self.queue_stage_id,
self.crosswalk_entry_id,
self.crosswalk_exit_id,
])
self.journey.set_transition_for_stage(
self.queue_stage_id,
jps.Transition.create_fixed_transition(
self.crosswalk_entry_id
))
self.journey.set_transition_for_stage(
self.crosswalk_entry_id,
jps.Transition.create_fixed_transition(
self.crosswalk_exit_id
))
self.journey_id = self.simulation.add_journey(
self.journey)
def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]:
min_x,min_y,max_x,max_y = area.bounds
points = []
x = (min_x+spacing)/2
while x<max_x:
y = (min_y+spacing)/2
while y < max_y:
if area.contains(Point(x, y)):
points.append((x,y))
y += spacing
x += spacing
return points if points else [(min_x+0.5, min_y+0.5)]
def run(self,max_iterations:int):
print("\n\nStarting Simulation")
print("Target Agents: {len(self.all_agents)}")
while self.iteration_count < max_iterations:
if self.spawn_manager.check_spawn_complete(self.all_agents):
new_agent = self.spawn_manager.spawn_agent(self.all_agents)
if new_agent:
jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters(
journey_id=self.journey_id,
stage_id=self.queue_stage_id,
position=self.spawn_manager.get_agent_pos(new_agent.id),
v0=new_agent.speed,
radius=new_agent.radius
)
jps_agent_id = self.simulation.add_agent(jps_agent_params)
self.jps_agent_ids[new_agent.id] = jps_agent_id
# unsure whether this is necessary
#
# self.agent_targets[new_agent.id] = None
print(f"Iter {self.iteration_count:05d}: Spawned agent {new_agent.id} (JPS-ID: {jps_agent_id})")
current_agent_pos = {}
for spawn_id,jps_id in self.jps_agent_ids.items():
agent = self.simulation.agent(jps_id)
current_agent_pos[spawn_id] = (agent.position[0],agent.position[1])
crosswalk_state = self.crosswalk_controller.update(
current_agent_pos,self.config.time_step,self.current_time
)
if crosswalk_state.is_active:
if self.queue_stage.stage != jps.WaitingSetState.INACTIVE:
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
print(f"Iter {self.iteration_count:05d}: Crosswalk ACTIVATED, releasing queue.")
else:
if self.queue_stage.stage != jps.WaitingSetState.ACTIVE:
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
print(f"Iter {self.iteration_count:05d}: Crosswalk INACTIVE, holding agents.")
self.simulation.iterate()
self.current_time += self.config.time_step
self.iteration_count +=1
agents_to_remove = []
for spawn_id, jps_id in list(self.jps_agent_ids.items()):
try:
_ = self.simulation.agent(jps_id)
except RuntimeError:
agents_to_remove.append(spawn_id)
self.spawn_manager.unfill_coords(spawn_id)
for spawn_id in agents_to_remove:
del self.jps_agent_ids[spawn_id]
print(f"Iter {self.iteration_count:05d}: Agent {spawn_id} reached exit and was removed.")
if (len(self.jps_agent_ids) ==0 and not self.spawn_manager.check_spawn_complete(self.all_agents)):
print(f"\nSimulation Completed at Iteration: {self.iteration_count}")
print(f"Total Simulation Time: {self.current_time:.2f} seconds.")
break
print("\n" + "="*50)
print("Simulation finished.")
stats = self.crosswalk_controller.get_statistics()
print(f"Crosswalk was activated {stats['total_activations']} times.")
print(f"Agents served by crosswalk: {stats['agents_served']}")
print(f"Trajectory saved to: {self.trajectory_file.absolute()}")

Binary file not shown.

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"

View File

@@ -0,0 +1,72 @@
import pandas as pd
import numpy as np
from dataclasses import dataclass
grades = {
"Kindergarden":np.array(
[31.43,5.65,1.21,0.24]
),
"Grade 1":np.array(
[32.57,6.27,1.35,0.26]
),
"Grade 2":np.array(
[34.43,6.80,1.42,0.28]
),
"Grade 3":np.array(
[35.43,5.19,1.48,0.23]
),
"Grade 4":np.array(
[34.86,6.77,1.58,0.26]
),
"Grade 5":np.array(
[36.71,7.09,1.59,0.24]
),
"Grade 6":np.array(
[37.71,6.99,1.65,0.24]
),
"Grade 7":np.array(
[40.43,6.02,1.61,0.25]
),
"Grade 8":np.array(
[40.43,5.50,1.66,0.24]
),
"Grade 9":np.array(
[44.14,4.85,1.60,0.24]
),
"Grade 10":np.array(
[46.29,6.29,1.57,0.23]
),
"Grade 11":np.array(
[48.29,3.30,1.51,0.22]
),
"Grade 12":np.array(
[43.71,6.02,1.54,0.23]
)}
df_srs_data=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
@dataclass
class AgentConfig:
id:int
grade:str
door:str
speed:float
radius:float

View File

@@ -0,0 +1,91 @@
import numpy as np
from dataclasses import dataclass
from typing import List, Dict, Optional
from collections import defaultdict
##https://censusatschool.ca/data-results/2017-2018/average-height-by-age/
@dataclass
class AgentConfig:
id:int
grade:str
door:int
speed:float
radius:float
def agent_params():
gr_data = {
# [0]: number of students
# [1]: door number
# [2]: speed mean
# [3]: speed standard deviation
# [4]: radius mean
"Kindergarden":np.array(
[34,0,1.21,0.24,0.407]
),
"Grade 1":np.array(
[26,0,1.35,0.26,0.407]
),
"Grade 2":np.array(
[42,0,1.42,0.28,0.407]
),
"Grade 3":np.array(
[39,0,1.48,0.23,0.407]
),
"Grade 4":np.array(
[30,1,1.58,0.26,0.417]
),
"Grade 5":np.array(
[43,1,1.59,0.24,0.434]
),
"Grade 6":np.array(
[29,1,1.65,0.24,0.454]
),
"Grade 7":np.array(
[45,2,1.61,0.25,0.471]
),
"Grade 8":np.array(
[36,2,1.66,0.24,0.488]
),
"Grade 9":np.array(
[44,2,1.60,0.24,0.500]
),
"Grade 10":np.array(
[36,2,1.57,0.23,0.507]
),
"Grade 11":np.array(
[54,2,1.51,0.22,0.515]
),
"Grade 12":np.array(
[46,2,1.54,0.23,0.520]
)}
agent_id = 1
rng = np.random.default_rng(seed=42)
all_agents = []
gr_agents = []
for grade in gr_data:
for num in range(int(gr_data[grade][0])):
door = gr_data[grade][1]
speed = rng.normal(
loc=gr_data[grade][2],
scale=gr_data[grade][3],
size=1)
radius = gr_data[grade][4]
gr_agents.append(
AgentConfig(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius = radius
))
agent_id += 1
all_agents.append(gr_agents)
gr_agents = []
#for grade in all_agents:
# for agent in grade:
# print(agent)
return all_agents

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"

View File

@@ -0,0 +1,147 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
def geo_current(full_plot:bool=False):
A_crosswalk=Polygon([
(-1,1.499),
(-1,3.327),
(0,3.327),
(11.214, 3.327),
(11.214,1.499),
(0,1.499)
])
B_queue=Polygon([
(11.214, 0),
(22.163, 0),
(22.163, 4.826),
(11.214, 4.826)
])
C_road_adj_path=Polygon([
(21.787,4.826),
(24.214,4.826),
(24.214,40.431),
(29.179,40.431),
(29.179,42.511),
(24.214,42.511),
(21.787,42.511)
])
D_path_k_3=Polygon([
(26.45,42.511),
(26.45,52.84),
(26.45,53.84),
(29.179,53.84),
(29.179,52.84),
(29.179,42.511)
])
E_path_4_6=Polygon([
(29.179,42.511),
(54.351,42.511),
(60.406,48.842),
(60.406,51.22),
(60.406,52.22),
(63.49,52.22),
(63.49,51.22),
(63.49,47.866),
(56.381,40.431),
(29.179,40.431)
])
F_path_7_12=Polygon([
(22.163, 0),
(39.227, 5.516),
(39.631, 4.267),
(39.939,3.315),
(45.099,4.983),
(44.792,5.935),
(43.169,10.954),
(24.214,4.826),
(22.163,4.826)
])
G_extended_queue=Polygon([
(11.214,0),
(12.924,0),
(12.924,-4.569),
(11.214,-4.569)
])
H_angled_path=Polygon([
(21.787,13.192),
(21.787,10.527),
(17,4.826),
(14.767,4.826)
])
enter_k_3=Polygon([
(26.45,52.84),
(29.179,52.84),
(29.179,53.84),
(26.45,53.84)
])
enter_4_6=Polygon([
(60.406,51.22),
(60.406,52.22),
(63.49,52.22),
(63.49,51.22)
])
enter_7_12=Polygon([
(39.631, 4.267),
(39.939,3.315),
(45.099,4.983),
(44.792,5.935)
])
exit_polygon=Polygon([
(0,1.499),
(0,3.327),
(-1,3.327),
(-1,1.499)
])
geometry = (
A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path)
)
doors = [
enter_k_3,
enter_4_6,
enter_7_12,
exit_polygon
]
if full_plot is False:
plot_polygon(A_crosswalk,color="black",add_points=False)
plot_polygon(B_queue,color="black",add_points=False)
plot_polygon(C_road_adj_path, color="blue",add_points=False)
plot_polygon(D_path_k_3, color="blue",add_points=False)
plot_polygon(E_path_4_6, color="blue",add_points=False)
plot_polygon(F_path_7_12, color="blue",add_points=False)
plot_polygon(G_extended_queue, color="black",add_points=False)
plot_polygon(H_angled_path, color="black",add_points=False)
plot_polygon(enter_k_3, color="darkgreen",add_points=False)
plot_polygon(enter_4_6, color="darkgreen",add_points=False)
plot_polygon(enter_7_12, color="darkgreen",add_points=False)
plot_polygon(exit_polygon, color="orangered",add_points=False)
else:
plot_polygon(geometry,color="blue",add_points=False)
plot_polygon(enter_k_3,color="red",add_points=False)
plot_polygon(enter_4_6,color="red",add_points=False)
plot_polygon(enter_7_12,color="red",add_points=False)
return geometry, doors
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
geometry,doors = geo_current(True)
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,71 @@
"""
E.Drake - ENGN 2220
Jan 21, 2026
TESTING GEOMETRY
"""
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
#print(str(config.GEO_DIR))
sys.path.insert(0,str(config.GEO_DIR))
sys.path.insert(0,str(config.PATH_DIR))
from geo_current import geo_current
import jupedsim as jps
from matplotlib.patches import Circle
def main_loop():
geometry,doors = geo_current(full_plot = True)
dk_3, d4_6, d7_12, d_exit = doors
model = jps.CollisionFreeSpeedModel()
sim = jps.Simulation(model=model,geometry=geometry)
exit_id = sim.add_exit_stage(d_exit)
journey = jps.JourneyDescription([exit_id])
journey_id = sim.add_journey(journey)
total_sim_time = 60.0
doorways = {
0: dk_3,
1: d4_6,
2: d7_12,
}
# Spawn times for each door (seconds)
spawn_schedule = {
"door_1": [0.0, 5.0, 10.0], # Agents at t=0, 5, 10
"door_2": [2.0, 7.0],
"door_3": [3.0],
}
events = []
for door_name, times in spawn_schedule.items():
for t in times:
events.append((t, doors[door_name]))
events.sort(key=lambda x: x[0])
event_index = 0
while sim.elapsed_time() < total_sim_time:
current_time = sim.elapsed_time()
# Process all events whose time has come
while event_index < len(events) and events[event_index][0] <= current_time:
_, door_pos = events[event_index]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=door_pos,
journey_id=journey_id,
stage_id=exit_id,
radius=0.2,
)
sim.add_agent(agent_params)
event_index += 1
sim.iterate()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_loop()
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,386 @@
import jupedsim as jps
import shapely
from dataclasses import dataclass
from typing import List, Dict, Tuple
import numpy as np
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
sys.path.insert(0,str(config.GEO_DIR))
from geo_current import geo_current
@dataclass
class AgentSetup:
id:int
grade:str
door:int
speed:float
radius:float
spawn:Tuple[float,float]
@dataclass
class SimSetup:
doorways:Dict[int,shapely.Polygon]
grades:Dict[str,Dict]
min_spacing:float=0.6
total_sim_time:float=200.0
door_capacity:int=10
walkable_area:shapely.Polygon
exit_area:shapely.Polygon
class EvacSim:
def __init__(self,setup:SimSetup):
self.setup = setup
self.all_agents = []
self.all_spawn_events = []
self.simulation = None
self.exit_id = None
self.doorway_system = {}
def run(self):
self.all_agents = self.agent_params()
self.setup_sim_env()
self.spawn_events = self.get_spawn_events()
self.run_sim()
def agent_params(self)->List[AgentSetup]:
agent_id = 1
rng = np.random.default_rng(seed=42)
all_agents = []
for grade in self.setup.grades.keys():
spawn_time = rng.uniform(0.0,115.0)
self.setup.grades[grade]["Spawn Time"] = spawn_time
gr_agent_num = int(self.setup.grades[grade]["Pop Current"])
door = int(self.setup.grades[grade]["Door"])
current_agent = 0
for num in range(gr_agent_num):
speed = rng.normal(
loc=self.setup.grades[grade]["Speed Mean"],
scale=self.setup.grades[grade]["Speed Std Dev"],
size=1)
radius = self.setup.grades[grade]["Radius"]
new_agent = AgentSetup(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius = radius,
)
all_agents.append(new_agent)
agent_id += 1
current_agent += 1
return all_agents
def setup_sim_env(self):
walkable_area = self.setup.walkable_area
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(
model=model,geometry=walkable_area)
self.exit_id = self.simulation.add_exit_stage(
self.setup.exit_polygon)
def doorway_system(self, door_id: int, door_polygon: shapely.Polygon):
def get_waiting_area(self,door_polygon:shapely.Polygon)->shapely.Polygon:
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
waiting_area = get_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
self.doorway_info[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
for door_id, door_polygon in self.setup.doorways.items():
self.doorway_system(door_id, door_polygon)
def get_spawn_events(self)->List[Dict]:
events = []
agents_by_grade = {}
def get_spawn_point(self,door:int,num_points:int)->List[Tuple[float,float]]:
polygon = self.setup.doorways[door]
min_x,min_y,max_x,max_y = polygon.bounds
points = []
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.setup.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
for agent in self.all_agents:
for grade_name, grade_info in self.setup.grades.items():
if agent.door == grade_info["Door"]:
if grade_name not in agents_by_grade:
agents_by_grade[grade_name] = []
agents_by_grade[grade_name].append(agent)
break
for grade_name, grade_info in self.setup.grades.items():
door_id = grade_info["Door"]
spawn_time = grade_info["Spawn Time"]
grade_agents = agents_by_group.get(grade_name, [])
if not grade_agents:
continue
door_polygon = self.setup.doorways[door_id]
spawn_positions = self.get_spawn_point(
door_polygon,
len(group_agents))
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent": agent,
"position": position,
"grade": grade_name,
"door": door_id
})
events.sort(key=lambda x: x["time"])
return events
def run_sim(self):
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
print("\nStarting Simulation Loop...")
print(f"Total Simulation Time: {self.config.total_simulation_time}s")
print(f"Door Capacity: {self.config.door_capacity} agents per door")
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
self._process_spawn_events(
current_time,
event_index,
spawned_event_indices,
agents_in_door_area)
while (event_index < len(self.spawn_events) and \
self.spawn_events[event_index]["time"] <= current_time and \
event_index in spawned_event_indices):
event_index += 1
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def process_spawn_events(
self,
current_time: float,
event_index: int,
spawned_events: set,
agents_in_door_area: Dict
):
while (event_idx < len(self.spawn_events) and \
self.spawn_events[event_idx]["time"] <= current_time and \
event_idx not in spawned_events):
event = self.spawn_events[event_idx]
door_id = event["door"]
agent = event["agent"]
if agents_in_door_area[door_id] < self.setup.door_capacity:
self.spawn_agent(event,door_id,agent)
agents_in_door_area[door_id] += 1
spawned_events.add(event_idx)
event_index += 1
def spawn_agent(self,event:Dict,door_id:int,agent:AgentSetup):
journey_id = self.doorway_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.doorway_system[door_id]["waiting_set_id"],
radius=agent.radius,
v0=agent.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
def start_sim_run():
print("Evacuation Simulation")
print("-" * 40)
geometry,[door0,door1,door2,exit_door] = geo_current(full_plot = True)
door_polygons = {
1: door0,
2: door1,
3: door2
}
grade_data = {
"Kindergarden":{
"Door":0,
"Pop Current":34,
"Pop Mean":31.43,
"Pop Std Dev":5.65,
"Speed Mean":1.21,
"Speed Std Dev":0.24,
"Radius":0.407,
"Spawn Time":None
},
"Grade 1":{
"Door":0,
"Pop Current":26,
"Pop Mean":32.57,
"Pop Std Dev":6.27,
"Speed Mean":1.35,
"Speed Std Dev":0.26,
"Radius":0.407,
"Spawn Time":None
},
"Grade 2":{
"Door":0,
"Pop Current":42,
"Pop Mean":34.43,
"Pop Std Dev":6.80,
"Speed Mean":1.42,
"Speed Std Dev":0.28,
"Radius":0.407,
"Spawn Time":None
},
"Grade 3":{
"Door":0,
"Pop Current":39,
"Pop Mean":35.43,
"Pop Std Dev":5.19,
"Speed Mean":1.48,
"Speed Std Dev":0.23,
"Radius":0.407,
"Spawn Time":None
},
"Grade 4":{
"Door":1,
"Pop Current":30,
"Pop Mean":34.86,
"Pop Std Dev":6.77,
"Speed Mean":1.58,
"Speed Std Dev":0.26,
"Radius":0.417,
"Spawn Time":None
},
"Grade 5":{
"Door":1,
"Pop Current":43,
"Pop Mean":36.71,
"Pop Std Dev":7.09,
"Speed Mean":1.59,
"Speed Std Dev":0.24,
"Radius":0.434,
"Spawn Time":None
},
"Grade 6":{
"Door":1,
"Pop Current":29,
"Pop Mean":37.71,
"Pop Std Dev":6.99,
"Speed Mean":1.65,
"Speed Std Dev":0.24,
"Radius":0.454,
"Spawn Time":None
},
"Grade 7":{
"Door":2,
"Pop Current":45,
"Pop Mean":40.43,
"Pop Std Dev":6.02,
"Speed Mean":1.61,
"Speed Std Dev":0.25,
"Radius":0.471,
"Spawn Time":None
},
"Grade 8":{
"Door":2,
"Pop Current":36,
"Pop Mean":40.43,
"Pop Std Dev":5.50,
"Speed Mean":1.66,
"Speed Std Dev":0.24,
"Radius":0.488,
"Spawn Time":None
},
"Grade 9":{
"Door":2,
"Pop Current":44,
"Pop Mean":44.14,
"Pop Std Dev":4.85,
"Speed Mean":1.60,
"Speed Std Dev":0.24,
"Radius":0.500,
"Spawn Time":None
},
"Grade 10":{
"Door":2,
"Pop Current":36,
"Pop Mean":46.29,
"Pop Std Dev":6.29,
"Speed Mean":1.57,
"Speed Std Dev":0.23,
"Radius":0.507,
"Spawn Time":None
},
"Grade 11":{
"Door":2,
"Pop Current":54,
"Pop Mean":48.29,
"Pop Std Dev":3.30,
"Speed Mean":1.51,
"Speed Std Dev":0.22,
"Radius":0.515,
"Spawn Time":None
},
"Grade 12":{
"Door":2,
"Pop Current":46,
"Pop Mean":43.71,
"Pop Std Dev":6.02,
"Speed Mean":1.54,
"Speed Std Dev":0.23,
"Radius":0.520,
"Spawn Time":None
}}
config = SimSetup(
doorways=door_polygons,
grades=grade_data,
total_simulation_time=180.0,
door_capacity=10,
walkable_area=geometry,
exit_area=exit_door
)
sim = EvacSim(config)
return sim.run()
if __name__ == "__main__":
simulation = start_sim_run()
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,388 @@
import jupedsim as jps
import shapely
import random
from dataclasses import dataclass
from typing import List, Dict, Tuple, Optional
import numpy as np
@dataclass
class AgentConfig:
id: int
grade: str
door: int
speed: float
radius: float
@dataclass
class SimulationConfig:
door_polygons: Dict[int, shapely.Polygon]
groups: Dict[str, Dict]
total_simulation_time: float = 300.0
door_capacity: int = 10
min_spacing: float = 0.6
"""
def __post_init__(self):
'''Set default walkable area and exit polygon if not provided.'''
if self.walkable_area_coords is None:
self.walkable_area_coords = [(0, 0), (50, 0), (50, 30), (0, 30)]
if self.exit_polygon_coords is None:
self.exit_polygon_coords = [(45, 10), (48, 10), (48, 20), (45, 20)]
"""
class PedestrianSimulation:
"""
Main class for running pedestrian simulations with door queues and scheduled spawning.
Architecture Flow:
1. Configuration Setup
2. Agent Configuration Generation
3. Geometry Preparation (Walkable area, Doors, Exit)
4. Door System Setup (Waiting areas, Queues, Journeys)
5. Spawn Event Precomputation
6. Simulation Execution with Dynamic Spawning
7. Results Analysis/Visualization
"""
def __init__(self, config: SimulationConfig):
self.config = config
self.simulation = None
self.door_systems = {}
self.all_agents = []
self.spawn_events = []
self.exit_id = None
def run(self) -> jps.Simulation:
"""
Main orchestrator: Run the complete simulation.
Steps:
1. Create agent configurations
2. Setup simulation environment
3. Precompute spawn events
4. Execute simulation with dynamic spawning
5. Return completed simulation object
"""
print("=" * 60)
print("PEDESTRIAN SIMULATION STARTING")
print("=" * 60)
self.all_agents = self._create_agent_configurations()
print(f"Created {len(self.all_agents)} agent configurations")
self._setup_simulation_environment()
print("Simulation environment setup complete")
self.spawn_events = self._precompute_spawn_events()
print(f"Precomputed {len(self.spawn_events)} spawn events")
self._execute_simulation()
print("Simulation execution complete")
return self.simulation
def _create_agent_configurations(self) -> List[AgentConfig]:
"""Create AgentConfig objects for all agents in all groups."""
all_agents = []
agent_id = 0
for group_name, group_info in self.config.groups.items():
door = group_info["door"]
size = group_info["size"]
for i in range(size):
grade = random.choice(["A", "B", "C", "D", "F"])
speed = random.uniform(1.0, 1.5) # m/s
radius = random.uniform(0.2, 0.3) # meters
all_agents.append(AgentConfig(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius=radius
))
agent_id += 1
return all_agents
# includes id, grade, door, speed, and radius
def _generate_spawn_points(self, polygon: shapely.Polygon,num_points: int) -> List[Tuple[float, float]]:
"""Generate non-overlapping spawn points within a polygon."""
points = []
min_x, min_y, max_x, max_y = polygon.bounds
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.config.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
# get list of spawn point tuples to provide to agents
def _create_waiting_area(self, door_polygon: shapely.Polygon) -> shapely.Polygon:
"""Create a waiting area adjacent to the door polygon."""
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
def _setup_simulation_environment(self):
"""Setup the simulation with door queues and waiting areas."""
# Create walkable area geometry
walkable_area = shapely.Polygon(self.config.walkable_area_coords)
# Create model and simulation
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(model=model, geometry=walkable_area)
# Define exit zone
exit_polygon = shapely.Polygon(self.config.exit_polygon_coords)
self.exit_id = self.simulation.add_exit_stage(exit_polygon)
# Create door systems
for door_id, door_polygon in self.config.door_polygons.items():
self._setup_door_system(door_id, door_polygon)
def _setup_door_system(self, door_id: int, door_polygon: shapely.Polygon):
"""Setup queue system for a specific door."""
# Create waiting area
waiting_area = self._create_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
# Create queue waypoints
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
# Create journey
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
# Store door system
self.door_systems[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
def _precompute_spawn_events(self) -> List[Dict]:
"""Precompute all spawn events with positions and agent configurations."""
events = []
# Group agents by their assigned group
agents_by_group = {}
for agent in self.all_agents:
for group_name, group_info in self.config.groups.items():
if agent.door == group_info["door"]:
if group_name not in agents_by_group:
agents_by_group[group_name] = []
agents_by_group[group_name].append(agent)
break
# Create events for each group
for group_name, group_info in self.config.groups.items():
door_id = group_info["door"]
spawn_time = group_info["spawn_time"]
group_agents = agents_by_group.get(group_name, [])
if not group_agents:
continue
# Generate spawn positions
door_polygon = self.config.door_polygons[door_id]
spawn_positions = self._generate_spawn_points(
door_polygon,
len(group_agents)
)
# Create events
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent_config": agent,
"position": position,
"group": group_name,
"door": door_id
})
# Sort events by time
events.sort(key=lambda x: x["time"])
return events
def _execute_simulation(self):
"""Execute the simulation with dynamic spawning."""
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
'''
print("\nStarting simulation loop...")
print(f"Total simulation time: {self.config.total_simulation_time}s")
print(f"Door capacity: {self.config.door_capacity} agents per door")
'''
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
# Process spawn events
self._process_spawn_events(current_time, event_index, spawned_event_indices,
agents_in_door_area)
# Update event index
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index in spawned_event_indices):
event_index += 1
# Iterate simulation
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def _process_spawn_events(self, current_time: float, event_index: int,
spawned_event_indices: set, agents_in_door_area: Dict):
"""Process all spawn events that should occur at the current time."""
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index not in spawned_event_indices):
event = self.spawn_events[event_index]
door_id = event["door"]
agent_config = event["agent_config"]
# Check door capacity
if agents_in_door_area[door_id] < self.config.door_capacity:
self._spawn_agent(event, door_id, agent_config)
agents_in_door_area[door_id] += 1
spawned_event_indices.add(event_index)
# Move to next event
event_index += 1
def _spawn_agent(self, event: Dict, door_id: int, agent_config: AgentConfig):
"""Spawn a single agent into the simulation."""
journey_id = self.door_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.door_systems[door_id]["waiting_set_id"],
radius=agent_config.radius,
v0=agent_config.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
# Example usage function
def create_and_run_simulation() -> PedestrianSimulation:
"""
Example function to create and run a complete simulation.
Returns:
PedestrianSimulation: The completed simulation object
"""
# Define door polygons
door_polygons = {
1: shapely.Polygon([(5, 5), (10, 5), (10, 15), (5, 15)]),
2: shapely.Polygon([(20, 5), (25, 5), (25, 15), (20, 15)]),
3: shapely.Polygon([(35, 5), (40, 5), (40, 15), (35, 15)]),
}
# Define groups (example with 4 groups, extend to 13 as needed)
groups = {
"group_1": {"door": 1, "spawn_time": 0.0, "size": 40},
"group_2": {"door": 2, "spawn_time": 5.0, "size": 35},
"group_3": {"door": 3, "spawn_time": 10.0, "size": 30},
"group_4": {"door": 1, "spawn_time": 15.0, "size": 25},
# Add 9 more groups to reach 13 total
}
# Create simulation configuration
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=200.0, # Adjust as needed
door_capacity=10,
min_spacing=0.6
)
# Create and run simulation
sim_runner = PedestrianSimulation(config)
simulation = sim_runner.run()
return sim_runner
# Quick execution function
def run_simulation_quickstart():
"""Quickstart function for running a basic simulation."""
print("Pedestrian Simulation Quickstart")
print("-" * 40)
# You can modify these parameters
door_polygons = {
1: shapely.Polygon([(2, 2), (6, 2), (6, 8), (2, 8)]),
2: shapely.Polygon([(10, 2), (14, 2), (14, 8), (10, 8)]),
3: shapely.Polygon([(18, 2), (22, 2), (22, 8), (18, 8)]),
}
groups = {
"class_a": {"door": 1, "spawn_time": 0.0, "size": 30},
"class_b": {"door": 2, "spawn_time": 10.0, "size": 25},
"class_c": {"door": 3, "spawn_time": 20.0, "size": 20},
}
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=100.0,
door_capacity=8
)
sim = PedestrianSimulation(config)
return sim.run()
if __name__ == "__main__":
# Option 1: Use the example function
# sim_runner = create_and_run_simulation()
# Option 2: Use quickstart for testing
simulation = run_simulation_quickstart()
# You can now analyze the simulation results
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")
# Additional analysis can be added here

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"