Skip to content

Commit e96285f

Browse files
Merge pull request #41 from Intelligent-Testing-Lab/28-add-support-for-spawning-ego-vehicle-through-scenario-runner
completed refactor
2 parents 1d5cab1 + dd0b585 commit e96285f

7 files changed

Lines changed: 192 additions & 189 deletions

File tree

File renamed without changes.

LICENSE Sheffield

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 The University of Sheffield
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

aw_scenario_runner.py

Lines changed: 64 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
import carla
1313

1414
from srunner.scenariomanager.scenario_manager import ScenarioManager
15-
from srunner.scenario_decoder.json_to_xml_files import XMLToFiles
16-
from srunner.tools.results_manager import ResultsManager
15+
from srunner.tools.results_manager import ScenarioDefinitionManager
1716
from srunner.scenarios.route_scenario import RouteScenario
1817
from srunner.scenariomanager.carla_data_provider import CarlaDataProvider
1918
from srunner.tools.route_parser import RouteParser
@@ -23,55 +22,60 @@
2322
RouteScenarioConfiguration,
2423
)
2524

25+
from srunner.objects.ego_vehicle import EgoVehicle
26+
2627
from srunner.tools.log import LogUtil
2728

29+
logger = logging.getLogger("scenario-runner")
30+
2831

2932
class AWScenarioRunner(object):
33+
# flags
34+
DEV_MODE = False
35+
DEBUG = False
36+
37+
# global class instances
3038
ego_vehicles = []
3139

32-
# world and scenario handlers
3340
carla_world = None
3441
carla_client = None
3542

3643
scenario_manager = None
37-
scenario_decoder = None
38-
results_manager = None
39-
40-
wait_for_update = False
41-
finished = False
44+
definition_manager = None
4245

4346
aw_agent = None
4447

4548
def __init__(self, config: dict) -> None:
4649
"""
4750
Setup Scenario Manager and the Carla client
4851
"""
52+
4953
self._carla_config = config["carla"]
5054
self._tm_config = config["traffic_manager"]
5155
self._scenario_config = config["scenario_runner"]
5256

5357
self.carla_client = carla.Client(
5458
self._carla_config["host"], int(self._carla_config["port"])
5559
)
60+
5661
self.carla_client.set_timeout(self._carla_config["timeout"])
5762

58-
# update the client
63+
# Flags
64+
self.DEV_MODE = self._scenario_config["dev_mode"]
65+
self.DEBUG = self._scenario_config["dev_mode"]
66+
5967
CarlaDataProvider.set_client(self.carla_client)
6068

61-
# load autoware agent
62-
# only load if in docker environment
63-
# debug
64-
if self._scenario_config["in_docker"]:
65-
autoware_agent_path = "srunner/autoagents/autoware_agent"
69+
if not self.DEV_MODE: # only load agents and algorithms in non-dev mode
70+
autoware_agent_path = self._scenario_config["agent"]
6671
module_name = os.path.basename(autoware_agent_path).split(".")[0]
6772
sys.path.insert(0, os.path.dirname(autoware_agent_path))
6873
self.module_aw_agent = importlib.import_module(module_name)
6974

70-
# load the algorithm of choice
71-
# algorithm = self._scenario_config["algorithm"] # relative path to entry point
72-
# alg_module = os.path.basename(algorithm).split(".")[0]
73-
# sys.path.insert(0, os.path.dirname(algorithm))
74-
# self.module_algorithm = importlib.import_module(alg_module)
75+
algorithm = self._scenario_config["algorithm"]["path"]
76+
alg_module = os.path.basename(algorithm).split(".")[0]
77+
sys.path.insert(0, os.path.dirname(algorithm))
78+
self.module_algorithm = importlib.import_module(alg_module)
7579

7680
# main class to execute scenarios
7781
self.scenario_manager = ScenarioManager(
@@ -80,9 +84,9 @@ def __init__(self, config: dict) -> None:
8084
self._carla_config["timeout"],
8185
)
8286

83-
self.results_manager = ResultsManager()
87+
self.results_manager = ScenarioDefinitionManager()
8488

85-
# Create signal handler for SIGINT
89+
# capture SIGINT for cleanp
8690
self._shutdown_requested = False
8791
if sys.platform != "win32":
8892
signal.signal(signal.SIGHUP, self._signal_handler)
@@ -91,33 +95,14 @@ def __init__(self, config: dict) -> None:
9195

9296
self._start_wall_time = datetime.datetime.now()
9397

94-
# parse the JSON scenario file
95-
if self.scenario_decoder is None:
96-
self.scenario_decoder = XMLToFiles()
97-
9898
scenario_name = os.path.split(
9999
os.path.splitext(self._scenario_config["json"])[0]
100100
)[1]
101-
self._parse_json(self._scenario_config["json"], scenario_name, "0")
102-
103-
def _parse_json(self, json: str, scenario: str, iteration: str) -> None:
104-
"""Parses a given JSON Scenario definition. Outputs two XML files used by scenario runner
105-
106-
Args:
107-
json (str): filepath to JSON scenario definition
108-
scenario (str): Name of the scenario
109-
iteration (str): ID of the scenario. Can be anything, but must be unique
110-
"""
111-
# create run directory if doesn't exist
112-
if not self.results_manager.results_path:
113-
self.results_manager.create_run_folder()
114101

115-
self.results_manager.create_scenario_folder(
116-
scenario, iteration, self.results_manager.results_path
102+
self.results_manager.parse_json(
103+
self._scenario_config["json"], scenario_name, "0"
117104
)
118105

119-
self.scenario_decoder.parse_scenario(json, self.results_manager.last_scenario)
120-
121106
def _signal_handler(self, signum, frame) -> None:
122107
"""
123108
Handle shutdown signal, do cleanup
@@ -132,148 +117,87 @@ def _signal_handler(self, signum, frame) -> None:
132117
def run_scenario(
133118
self, route_config: RouteScenarioConfiguration, env_config: EnvironmentConfig
134119
) -> bool:
135-
# find the ego vehicle by name
136-
# only supports one ego
137-
138-
# setup world based on env config
139-
# make sure to reload
140120
self.carla_world = self.carla_client.get_world()
141121
self.carla_client.load_world(env_config.town)
142122

143-
# update carla provider
144123
CarlaDataProvider.set_world(self.carla_world)
145124

146-
# replace with spawning ego
147-
# ego_missing = True
148-
# while ego_missing:
149-
# self.ego_vehicles = []
150-
# for ego in route_config.ego_vehicles:
151-
# carla_vehicles = (
152-
# self.carla_client.get_world().get_actors().filter("vehicle.*")
153-
# )
154-
#
155-
# for carla_vehicle in carla_vehicles:
156-
# if carla_vehicle.attributes["role_name"] == ego:
157-
# self.ego_vehicles.append(carla_vehicle)
158-
# ego_missing = False
159-
# break
160-
# print("Can't find ego, waiting...")
161-
# time.sleep(1)
162-
# ego_missing = False
163-
164-
print("Spawning ego...")
165-
self._spawn_ego(env_config)
166-
167-
print("Spawned ego...")
125+
logger.info("Spawning ego...")
126+
ego = EgoVehicle(env_config)
127+
self.ego_vehicles.append(ego.spawn())
128+
logger.info("Spawned ego...")
168129

169130
self.carla_world.wait_for_tick()
170131

171-
if self._scenario_config["in_docker"]:
172-
print("Loading Autoware agent")
132+
logger.info("Setting up sensort configuration...")
133+
ego.setup_sensors()
134+
135+
if not self.DEV_MODE:
136+
logger.info("Loading Autoware agent")
173137
agent_class_name = self.module_aw_agent.__name__.title().replace("_", "")
174138
try:
175-
print(getattr(self.module_aw_agent, agent_class_name))
176-
# call the agent method to notify the bridge of the ego spawn
177-
# this will loop until the bridge is ready
139+
logger.info(getattr(self.module_aw_agent, agent_class_name))
178140
self.aw_agent = getattr(self.module_aw_agent, agent_class_name)(
179141
env_config
180142
)
181143
route_config.agent = self.aw_agent
182144
except Exception as e: # Forces the simulation to run synchronously # pylint: disable=broad-except
183-
traceback.print_exc()
184-
print("Could not setup required agent due to {}".format(e))
145+
logger.error("Could not setup required agent due to {}".format(e))
185146
# self._cleanup()
186147
return False
187148

188-
# only set synchronous mode once bridge is ready
149+
ego.prepare_ego()
150+
151+
logger.info("Updating world settings:")
152+
189153
# tick asynchronously until then
190154
settings = CarlaDataProvider.get_world().get_settings()
191155
settings.synchronous_mode = True
192156
settings.fixed_delta_seconds = self._carla_config["fixed_delta_seconds"]
193157
CarlaDataProvider.get_world().apply_settings(settings)
194158

195-
# ADD TRAFFIC MANAGER SEED TO CONFIG
196-
tm_port = int(self._tm_config["port"]) # type: ignore
197-
CarlaDataProvider.set_traffic_manager_port(tm_port)
198-
tm = self.carla_client.get_trafficmanager(tm_port)
199-
tm.set_random_device_seed(1) # ADD TO CONFIG
159+
logger.info(f"{settings.__str__()}")
200160

201-
tm.set_synchronous_mode(self._tm_config["sync"])
161+
if self._tm_config["active"]:
162+
logger.info("Loading Traffic Manager...")
163+
tm_port = int(self._tm_config["port"]) # type: ignore
164+
CarlaDataProvider.set_traffic_manager_port(tm_port)
165+
tm = self.carla_client.get_trafficmanager(tm_port)
202166

203-
print("Preparing ego...")
167+
tm.set_random_device_seed(int(self._tm_config["seed"])) # ADD TO CONFIG
168+
tm.set_synchronous_mode(self._tm_config["sync"])
204169

205-
# update ego position to one specified in route
206-
self.ego_vehicles[0].set_transform(env_config.ego_spawn)
207-
self.ego_vehicles[0].set_target_velocity(carla.Vector3D())
208-
self.ego_vehicles[0].set_target_angular_velocity(carla.Vector3D())
209-
CarlaDataProvider.register_actor(self.ego_vehicles[0], env_config.ego_spawn)
170+
logger.info("Loading route...")
210171

211-
print("Loading route...")
212172
try:
213173
scenario = RouteScenario(
214174
world=self.carla_world,
215175
config=route_config,
216176
debug_mode=self._carla_config["debug"],
217177
)
218178
except Exception:
219-
print("Could not load Route Scenario")
179+
logger.info("Could not load Route Scenario")
220180
traceback.print_exc()
221181
return False
222182

223-
print("Starting scenario...")
183+
logger.info("Starting scenario...")
224184
try:
225185
self.scenario_manager.load_scenario(scenario, self.aw_agent)
226186
self.scenario_manager.run_scenario()
227187
result = True
228188
except Exception:
229189
traceback.print_exc()
230-
print("It doesn't work")
190+
logger.info("It doesn't work")
231191
result = False
232192
return result
233193

234-
def _load_scenario_config(self) -> EnvironmentConfig:
235-
return EnvironmentParser.parse_scenario_env(
194+
def run(self) -> bool:
195+
env_config = EnvironmentParser.parse_scenario_env(
236196
os.path.join(self.results_manager.last_scenario, "scenario.xml")
237197
)
238-
239-
def _spawn_ego(self, env_config: EnvironmentConfig) -> None:
240-
ego = CarlaDataProvider.request_new_actor(
241-
model=env_config.ego_model,
242-
spawn_point=env_config.ego_spawn,
243-
rolename=env_config.ego_name,
244-
)
245-
self.ego_vehicles.append(ego)
246-
247-
CarlaDataProvider.get_world().wait_for_tick() # wait for tick
248-
249-
bp_library = self.carla_world.get_blueprint_library()
250-
# setup sensors
251-
for sensor in env_config.sensor_config:
252-
sensor._spawn(bp_library, ego)
253-
print(f"Spawned sensor type {sensor.type}")
254-
255-
def _load_route_scenario(
256-
self, env_config: EnvironmentConfig
257-
) -> RouteScenarioConfiguration:
258198
route_config = RouteParser.parse_routes_file(
259199
self.results_manager.last_scenario, env_config
260-
) # type: ignore
261-
262-
return route_config[0]
263-
264-
def run(self) -> bool:
265-
# load the original JSON file
266-
# load the route config
267-
# load the scenario config
268-
# run scenario
269-
# get the metrics
270-
# call the algorithm callback
271-
# save using ResultsManager static class
272-
273-
# repeat for iterations
274-
275-
env_config = self._load_scenario_config()
276-
route_config = self._load_route_scenario(env_config)
200+
)[self._scenario_config["route_id"]]
277201

278202
scenario_result = self.run_scenario(route_config, env_config)
279203
return scenario_result
@@ -296,7 +220,7 @@ def _cleanup(self) -> None:
296220
try:
297221
# Reset to asynchronous mode
298222
self.carla_client.get_trafficmanager(
299-
int(self._args.traffic_port)
223+
int(self._tm_config["port"])
300224
).set_synchronous_mode(False)
301225
except RuntimeError:
302226
sys.exit(-1)
@@ -308,7 +232,9 @@ def _cleanup(self) -> None:
308232
for i, _ in enumerate(self.ego_vehicles):
309233
if self.ego_vehicles[i]:
310234
if self.ego_vehicles[i] is not None and self.ego_vehicles[i].is_alive:
311-
print("Destroying ego vehicle {}".format(self.ego_vehicles[i].id))
235+
logger.info(
236+
"Destroying ego vehicle {}".format(self.ego_vehicles[i].id)
237+
)
312238
self.ego_vehicles[i].destroy()
313239
self.ego_vehicles[i] = None
314240
self.ego_vehicles = []
@@ -319,14 +245,15 @@ def _cleanup(self) -> None:
319245

320246

321247
def main():
248+
# single argument of configuration file
249+
322250
# configure logger
323251
config = None
324252
with open("config.yaml", "r") as stream:
325253
config = yaml.safe_load(stream)
326254

327255
log_config = config["log"]
328256

329-
logger = logging.getLogger("scenario-runner")
330257
logger.setLevel(logging.INFO)
331258

332259
log_path = LogUtil.create_log_file(log_config["path"])
@@ -338,7 +265,7 @@ def main():
338265
try:
339266
scenario_runner = AWScenarioRunner(config)
340267
results = scenario_runner.run()
341-
print(results)
268+
logger.info(results)
342269
except Exception: # NOT GOOD PRACTICE PROBABLY CHANGE
343270
traceback.print_exc()
344271
finally:

0 commit comments

Comments
 (0)