Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions examples/scenarios/Mcity_corner_case_generation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
simulation_module: "my_simulations"
simulation_class: "CarSimulation"
parameters:
num_cars: 10
simulation_time: 500

# Simulation type: Choose between "safetest_mcity_av" or "safetest_mcity"
simulation_type: "safetest_mcity_av"


environment:
# Module containing the environment class
module: "terasim_nde_nade.envs" # Use "terasim_nde_nade.envs.safetest_nade" for non-AV simulations
# Environment class name
class: "SafeTestNADE" # Use "SafeTestNADE" for non-AV simulations
parameters:
# Vehicle factory class
vehicle_factory: "terasim_nde_nade.vehicle.nde_vehicle_factory.NDEVehicleFactory"
# Info extractor class
info_extractor: "terasim.logger.infoextractor.InfoExtractor"
log_flag: true
warmup_time_lb: 100 # Lower bound for warmup time
warmup_time_ub: 500 # Upper bound for warmup time
run_time: 500 # Simulation run time (use 30 for non-AV simulations)

MOBIL_lc_flag: True
stochastic_acc_flag: False
drive_rule: "righthand"

adversity_sampling_probability: 0.0
adversity_cfg:
vehicle:
roundabout_cutin:
_target_: terasim_nde_nade.adversity.vehicles.lanechange_adversity.LanechangeAdversity
_convert_: 'all'
location: 'roundabout'
ego_type: 'vehicle'
probability: 2.3123152310029683e-05
predicted_collision_type: "roundabout_cutin"

# roundabout_rearend:
# _target_: terasim_nde_nade.adversity.vehicles.leader_adversity.LeaderAdversity
# _convert_: 'all'
# location: 'roundabout'
# ego_type: 'vehicle'
# probability: 1.8383121748678325e-10
# predicted_collision_type: "roundabout_rearend"

roundabout_fail_to_yield:
_target_: terasim_nde_nade.adversity.vehicles.trafficrule_adversity.TrafficRuleAdversity
_convert_: 'all'
location: 'roundabout'
ego_type: 'vehicle'
probability: 0.0005025411072000003
predicted_collision_type: "roundabout_fail_to_yield"

# roundabout_neglect_conflict_lead:
# _target_: terasim_nde_nade.adversity.roundabout_cutin_adversity.RoundaboutCutinAdversity
# _convert_: 'all'
# location: 'roundabout'
# ego_type: 'vehicle'
# probability: 0.00027119028871789013

highway_cutin:
_target_: terasim_nde_nade.adversity.vehicles.lanechange_adversity.LanechangeAdversity
_convert_: 'all'
location: 'highway'
ego_type: 'vehicle'
probability: 3.1996225726187704e-05
predicted_collision_type: "highway_cutin"

# highway_rearend:
# _target_: terasim_nde_nade.adversity.vehicles.leader_adversity.LeaderAdversity
# _convert_: 'all'
# location: 'highway'
# ego_type: 'vehicle'
# probability: 0 # 0.8783674048511999
# predicted_collision_type: "highway_rearend"

intersection_cutin:
_target_: terasim_nde_nade.adversity.vehicles.lanechange_adversity.LanechangeAdversity
_convert_: 'all'
location: 'intersection'
ego_type: 'vehicle'
probability: 6.694592721399203e-05
predicted_collision_type: "intersection_cutin"

# intersection_rearend:
# _target_: terasim_nde_nade.adversity.vehicles.leader_adversity.LeaderAdversity
# _convert_: 'all'
# location: 'intersection'
# ego_type: 'vehicle'
# probability: 0.00017365940078885788
# predicted_collision_type: "intersection_rearend"

intersection_headon:
_target_: terasim_nde_nade.adversity.vehicles.headon_adversity.HeadonAdversity
_convert_: 'all'
location: 'intersection'
ego_type: 'vehicle'
probability: 1.0663168272664859e-08
predicted_collision_type: "intersection_headon"

intersection_tfl:
_target_: terasim_nde_nade.adversity.vehicles.trafficrule_adversity.TrafficRuleAdversity
_convert_: 'all'
location: 'intersection'
ego_type: 'vehicle'
probability: 0.010141902545438387
predicted_collision_type: "intersection_tfl"

# intersection_neglect_conflict_lead:
# _target_: terasim_nde_nade.adversity.roundabout_cutin_adversity.RoundaboutCutinAdversity
# _convert_: 'all'
# location: 'roundabout'
# ego_type: 'vehicle'
# probability: 0.015050715723004704

vulnerable_road_user:
jaywalking:
_target_: terasim_nde_nade.adversity.vru.jaywalking_adversity.JaywalkingAdversity
_convert_: 'all'
location: 'crosswalk'
ego_type: 'vulnerable_road_user'
# probability: 0.001
probability: 0.0
predicted_collision_type: "intersection_jaywalking"

runningredlight:
_target_: terasim_nde_nade.adversity.vru.runningredlight_adversity.RunningRedLightAdversity
_convert_: 'all'
location: 'crosswalk'
ego_type: 'vulnerable_road_user'
# probability: 0.001
probability: 0.0
predicted_collision_type: "intersection_runningredlight"

stopcrossing:
_target_: terasim_nde_nade.adversity.vru.stopcrossing_adversity.StopCrossingAdversity
_convert_: 'all'
location: 'crosswalk'
ego_type: 'vulnerable_road_user'
# probability: 0.001
probability: 0.0
predicted_collision_type: "intersection_stopcrossing"

AV_cfg:
route: ["EG_35_1_14", "EG_1_3_1", "EG_1_3_1.61", "EG_1_3_1.136", "EG_34_1_24", "EG_34_1_3", "gneE0", "EG_4_1_1", "EG_10_1_1", "EG_15_1_17", "EG_15_1_1", "EG_16_45_1", "EG_16_23_1", "EG_17_1_1", "EG_14_2_1", "EG_9_1_1", "EG_21_1_1", "EG_21_1_5", "EG_20_1_11", "EG_29_1_1", "EG_35_1_14"] # list of SUMO edges (example for Mcity)
type: "NDE_URBAN"
cache_radius: 100
control_radius: 100
warmup_control:
enabled: true # Enable AV warmup (false keeps original behavior)

# Trigger condition (choose one)
trigger_type: "time" # Options: "position", "time", "zone", "edge"

# Position trigger configuration
time_trigger:
duration: 3.0 # Warmup duration in seconds
simulator:
module: "terasim.simulator"
class: "Simulator"
parameters:
num_tries: 10
gui_flag: false
realtime_flag: false # Only applicable for AV mode
sumo_output_file_types:
- "fcd_all"
- "collision"
- "tripinfo"

# Path resolution mode:
# - "config_relative": Relative paths are resolved from the config file's directory
# - "cwd_relative": Relative paths are resolved from the current working directory
path_resolution: "config_relative"

# New section for file paths
input:
sumo_net_file: "../maps/Mcity/mcity.net.xml"
sumo_config_file: "../maps/Mcity/mcity.sumocfg"

output:
dir: "outputs" # Output directory (use "output" for non-AV simulations)
name: "Mcity" # Experiment name
nth: "0_0" # Experiment number
aggregated_dir: "aggregated" # Directory for aggregated logs

logging:
levels:
- "TRACE" # Log level for the main log file
- "INFO" # Log level for the aggregated log file
25 changes: 24 additions & 1 deletion packages/terasim-nde-nade/terasim_nde_nade/envs/nade.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,25 @@ def update_distance(self):
distance_info_dict[veh_id] = traci.vehicle.getDistance(veh_id)
return distance_info_dict


def save_conflict_info(self, conflict_veh_info):
sim_time = utils.get_time()

conflict_info_file = self.simulator.output_path / "conflict_info.jsonl"

vehicle_conflict_info = conflict_veh_info.get(AgentType.VEHICLE, {})
if not vehicle_conflict_info:
return

record = {
"timestamp": sim_time,
"conflict_veh_info": vehicle_conflict_info
}
# write JSON Lines file in append mode
with open(conflict_info_file, "a") as f:
f.write(json.dumps(record) + "\n")
return

@profile
def NADE_decision(self, env_command_information, env_observation):
"""NADE decision here.
Expand All @@ -369,13 +388,17 @@ def NADE_decision(self, env_command_information, env_observation):
)

# Step 2. Get maneuver challenge and mark the conflict vehicles and vrus.
env_maneuver_challenge, env_command_information = get_environment_maneuver_challenge(
env_maneuver_challenge, env_command_information, conflict_veh_info = get_environment_maneuver_challenge(
env_future_trajectory,
env_observation,
env_command_information,
centered_agent_set=self.excluded_agent_set,
)

# Step 2.5. Save the conflict vehicle information
if conflict_veh_info:
self.save_conflict_info(conflict_veh_info)

# Step 3. Add collision avoidance and acceptance command for the victim vehicles.
env_future_trajectory, env_command_information = add_avoid_accept_collision_command(
env_future_trajectory,
Expand Down
19 changes: 19 additions & 0 deletions packages/terasim-nde-nade/terasim_nde_nade/envs/nade_with_av.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from addict import Dict
import copy
import json
from loguru import logger
import numpy as np
import random
Expand Down Expand Up @@ -995,3 +996,21 @@ def collect_drl_obs(self, env_command_information):
)
total_obs_for_DRL = np.clip(total_obs_for_DRL, -5, 5)
return np.array(total_obs_for_DRL).astype(float)

def save_conflict_info(self, conflict_veh_info):
sim_time = utils.get_time()

conflict_info_file = self.simulator.output_path / "conflict_info.jsonl"

vehicle_conflict_info = conflict_veh_info.get(AgentType.VEHICLE, {})
if not vehicle_conflict_info:
return

record = {
"timestamp": sim_time,
"conflict_veh_info": vehicle_conflict_info
}
# write JSON Lines file in append mode
with open(conflict_info_file, "a") as f:
f.write(json.dumps(record) + "\n")
return
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def get_environment_maneuver_challenge(env_future_trajectory, env_observation, e
Returns:
dict: Environment maneuver challenge information.
dict: Updated environment command information.
dict: Conflict vehicle information.
"""
normal_future_trajectory_veh = Dict(
{
Expand Down Expand Up @@ -276,5 +277,4 @@ def get_environment_maneuver_challenge(env_future_trajectory, env_observation, e
AgentType.VEHICLE: maneuver_challenge_veh,
AgentType.VULNERABLE_ROAD_USER: maneuver_challenge_vru,
}
return env_maneuver_challenge, env_command_information

return env_maneuver_challenge, env_command_information, conflict_vehicle_info
99 changes: 99 additions & 0 deletions scripts/gen_case_NADE.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import argparse
import random
import hydra
from datetime import datetime
from loguru import logger
from omegaconf import DictConfig, OmegaConf
from pathlib import Path
from tqdm import tqdm
from terasim.logger.infoextractor import InfoExtractor
from terasim.simulator import Simulator

from terasim_nde_nade.envs import NADE, NADEWithAV
from terasim_nde_nade.vehicle import NDEVehicleFactory
from terasim_nde_nade.vru import NDEVulnerableRoadUserFactory

# Import resolve_config_paths function
from terasim_service.utils.base import resolve_config_paths

# Add packages directory to sys path if needed
# sys.path.append(str(Path(__file__).resolve().parent.parent))



def main(config_path: str) -> None:
config = OmegaConf.load(config_path)

# Convert OmegaConf to dict for path resolution
config_dict = OmegaConf.to_container(config, resolve=True)

# Resolve all paths in config
config_dict = resolve_config_paths(config_dict, config_path)

# Convert back to OmegaConf for attribute access
config = OmegaConf.create(config_dict)

# Time (format: 2025-09-15_16-05-30)
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

base_dir = Path(config.output.dir) / config.output.name / "raw_data" / timestamp
base_dir.mkdir(parents=True, exist_ok=True)

env = NADE(
vehicle_factory=NDEVehicleFactory(cfg=config.environment.parameters),
vru_factory=NDEVulnerableRoadUserFactory(cfg=config.environment.parameters),
info_extractor=InfoExtractor,
log_flag=True,
log_dir=base_dir,
warmup_time_lb=config.environment.parameters.warmup_time_lb,
warmup_time_ub=config.environment.parameters.warmup_time_ub,
run_time=1200,
configuration=config.environment.parameters,
)

# Paths already resolved in config
sumo_net_file = config.input.sumo_net_file
sumo_config_file = config.input.sumo_config_file

sim = Simulator(
sumo_net_file_path=sumo_net_file,
sumo_config_file_path=sumo_config_file,
num_tries=10,
# gui_flag=config.simulator.parameters.gui_flag,
gui_flag=True,
realtime_flag=config.simulator.parameters.realtime_flag,
output_path=base_dir,
sumo_output_file_types=["fcd_all"],
traffic_scale=config.simulator.parameters.traffic_scale if hasattr(config.simulator.parameters, "traffic_scale") else 1,
additional_sumo_args=[
"--device.bluelight.explicit","true",
],
)
sim.bind_env(env)

terasim_logger = logger.bind(name="terasim_nde_nade")
terasim_logger.info(f"terasim_nde_nade: Experiment started")

sim.run()


if __name__ == "__main__":
# Get all yaml files in examples/scenarios directory
config_dir = Path(__file__).parent / "examples" / "scenarios"
# yaml_files = sorted(config_dir.glob("*.yaml"), key=lambda x: int(''.join(filter(str.isdigit, x.stem)) or '0'))
# yaml_files = ["examples/scenarios/cutin.yaml"]
yaml_files = [Path("examples/scenarios/Mcity_corner_case_generation.yaml")]
# Randomly shuffle yaml files
random.shuffle(yaml_files)

# Run experiments for each yaml file
for yaml_file in tqdm(yaml_files):
print(yaml_file)
logger.info(f"Running experiment with config: {yaml_file}")
main(str(yaml_file))
# try:
# main(str(yaml_file))
# except Exception as e:
# logger.error(f"Error running {yaml_file}: {e}")
# # yaml_file.unlink() # Delete the yaml file
# continue
Loading
Loading