basic_scenario.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #!/usr/bin/env python
  2. # Copyright (c) 2018-2020 Intel Corporation
  3. #
  4. # This work is licensed under the terms of the MIT license.
  5. # For a copy, see <https://opensource.org/licenses/MIT>.
  6. """
  7. This module provide BasicScenario, the basic class of all the scenarios.
  8. """
  9. from __future__ import print_function
  10. import operator
  11. import py_trees
  12. import carla
  13. import srunner.scenariomanager.scenarioatomics.atomic_trigger_conditions as conditions
  14. from srunner.scenariomanager.carla_data_provider import CarlaDataProvider
  15. from srunner.scenariomanager.timer import TimeOut
  16. from srunner.scenariomanager.weather_sim import WeatherBehavior
  17. from srunner.scenariomanager.scenarioatomics.atomic_behaviors import UpdateAllActorControls
  18. class BasicScenario(object):
  19. """
  20. Base class for user-defined scenario
  21. """
  22. def __init__(self, name, ego_vehicles, config, world,
  23. debug_mode=False, terminate_on_failure=False, criteria_enable=False):
  24. """
  25. Setup all relevant parameters and create scenario
  26. and instantiate scenario manager
  27. """
  28. self.other_actors = []
  29. if not self.timeout: # pylint: disable=access-member-before-definition
  30. self.timeout = 60 # If no timeout was provided, set it to 60 seconds
  31. self.criteria_list = [] # List of evaluation criteria
  32. self.scenario = None
  33. self.ego_vehicles = ego_vehicles
  34. self.name = name
  35. self.config = config
  36. self.terminate_on_failure = terminate_on_failure
  37. self._initialize_environment(world)
  38. # Initializing adversarial actors
  39. self._initialize_actors(config)
  40. if CarlaDataProvider.is_sync_mode():
  41. world.tick()
  42. else:
  43. world.wait_for_tick()
  44. # Setup scenario
  45. if debug_mode:
  46. py_trees.logging.level = py_trees.logging.Level.DEBUG
  47. behavior = self._create_behavior()
  48. criteria = None
  49. if criteria_enable:
  50. criteria = self._create_test_criteria()
  51. # Add a trigger condition for the behavior to ensure the behavior is only activated, when it is relevant
  52. behavior_seq = py_trees.composites.Sequence()
  53. trigger_behavior = self._setup_scenario_trigger(config)
  54. if trigger_behavior:
  55. behavior_seq.add_child(trigger_behavior)
  56. if behavior is not None:
  57. behavior_seq.add_child(behavior)
  58. behavior_seq.name = behavior.name
  59. end_behavior = self._setup_scenario_end(config)
  60. if end_behavior:
  61. behavior_seq.add_child(end_behavior)
  62. self.scenario = Scenario(behavior_seq, criteria, self.name, self.timeout, self.terminate_on_failure)
  63. def _initialize_environment(self, world):
  64. """
  65. Default initialization of weather and road friction.
  66. Override this method in child class to provide custom initialization.
  67. """
  68. # Set the appropriate weather conditions
  69. world.set_weather(self.config.weather)
  70. # Set the appropriate road friction
  71. if self.config.friction is not None:
  72. friction_bp = world.get_blueprint_library().find('static.trigger.friction')
  73. extent = carla.Location(1000000.0, 1000000.0, 1000000.0)
  74. friction_bp.set_attribute('friction', str(self.config.friction))
  75. friction_bp.set_attribute('extent_x', str(extent.x))
  76. friction_bp.set_attribute('extent_y', str(extent.y))
  77. friction_bp.set_attribute('extent_z', str(extent.z))
  78. # Spawn Trigger Friction
  79. transform = carla.Transform()
  80. transform.location = carla.Location(-10000.0, -10000.0, 0.0)
  81. world.spawn_actor(friction_bp, transform)
  82. def _initialize_actors(self, config):
  83. """
  84. Default initialization of other actors.
  85. Override this method in child class to provide custom initialization.
  86. """
  87. if config.other_actors:
  88. new_actors = CarlaDataProvider.request_new_actors(config.other_actors)
  89. if not new_actors:
  90. raise Exception("Error: Unable to add actors")
  91. for new_actor in new_actors:
  92. self.other_actors.append(new_actor)
  93. def _setup_scenario_trigger(self, config):
  94. """
  95. This function creates a trigger maneuver, that has to be finished before the real scenario starts.
  96. This implementation focuses on the first available ego vehicle.
  97. The function can be overloaded by a user implementation inside the user-defined scenario class.
  98. """
  99. start_location = None
  100. if config.trigger_points and config.trigger_points[0]:
  101. start_location = config.trigger_points[0].location # start location of the scenario
  102. ego_vehicle_route = CarlaDataProvider.get_ego_vehicle_route()
  103. if start_location:
  104. if ego_vehicle_route:
  105. if config.route_var_name is None: # pylint: disable=no-else-return
  106. return conditions.InTriggerDistanceToLocationAlongRoute(self.ego_vehicles[0],
  107. ego_vehicle_route,
  108. start_location,
  109. 5)
  110. else:
  111. check_name = "WaitForBlackboardVariable: {}".format(config.route_var_name)
  112. return conditions.WaitForBlackboardVariable(name=check_name,
  113. variable_name=config.route_var_name,
  114. variable_value=True,
  115. var_init_value=False)
  116. return conditions.InTimeToArrivalToLocation(self.ego_vehicles[0],
  117. 2.0,
  118. start_location)
  119. return None
  120. def _setup_scenario_end(self, config):
  121. """
  122. This function adds and additional behavior to the scenario, which is triggered
  123. after it has ended.
  124. The function can be overloaded by a user implementation inside the user-defined scenario class.
  125. """
  126. ego_vehicle_route = CarlaDataProvider.get_ego_vehicle_route()
  127. if ego_vehicle_route:
  128. if config.route_var_name is not None:
  129. set_name = "Reset Blackboard Variable: {} ".format(config.route_var_name)
  130. return py_trees.blackboard.SetBlackboardVariable(name=set_name,
  131. variable_name=config.route_var_name,
  132. variable_value=False)
  133. return None
  134. def _create_behavior(self):
  135. """
  136. Pure virtual function to setup user-defined scenario behavior
  137. """
  138. raise NotImplementedError(
  139. "This function is re-implemented by all scenarios"
  140. "If this error becomes visible the class hierarchy is somehow broken")
  141. def _create_test_criteria(self):
  142. """
  143. Pure virtual function to setup user-defined evaluation criteria for the
  144. scenario
  145. """
  146. raise NotImplementedError(
  147. "This function is re-implemented by all scenarios"
  148. "If this error becomes visible the class hierarchy is somehow broken")
  149. def change_control(self, control): # pylint: disable=no-self-use
  150. """
  151. This is a function that changes the control based on the scenario determination
  152. :param control: a carla vehicle control
  153. :return: a control to be changed by the scenario.
  154. Note: This method should be overriden by the user-defined scenario behavior
  155. """
  156. return control
  157. def remove_all_actors(self):
  158. """
  159. Remove all actors
  160. """
  161. for i, _ in enumerate(self.other_actors):
  162. if self.other_actors[i] is not None:
  163. if CarlaDataProvider.actor_id_exists(self.other_actors[i].id):
  164. CarlaDataProvider.remove_actor_by_id(self.other_actors[i].id)
  165. self.other_actors[i] = None
  166. self.other_actors = []
  167. class Scenario(object):
  168. """
  169. Basic scenario class. This class holds the behavior_tree describing the
  170. scenario and the test criteria.
  171. The user must not modify this class.
  172. Important parameters:
  173. - behavior: User defined scenario with py_tree
  174. - criteria_list: List of user defined test criteria with py_tree
  175. - timeout (default = 60s): Timeout of the scenario in seconds
  176. - terminate_on_failure: Terminate scenario on first failure
  177. """
  178. def __init__(self, behavior, criteria, name, timeout=60, terminate_on_failure=False):
  179. self.behavior = behavior
  180. self.test_criteria = criteria
  181. self.timeout = timeout
  182. self.name = name
  183. if self.test_criteria is not None and not isinstance(self.test_criteria, py_trees.composites.Parallel):
  184. # list of nodes
  185. for criterion in self.test_criteria:
  186. criterion.terminate_on_failure = terminate_on_failure
  187. # Create py_tree for test criteria
  188. self.criteria_tree = py_trees.composites.Parallel(
  189. name="Test Criteria",
  190. policy=py_trees.common.ParallelPolicy.SUCCESS_ON_ONE
  191. )
  192. self.criteria_tree.add_children(self.test_criteria)
  193. self.criteria_tree.setup(timeout=1)
  194. else:
  195. self.criteria_tree = criteria
  196. # Create node for timeout
  197. self.timeout_node = TimeOut(self.timeout, name="TimeOut")
  198. # Create overall py_tree
  199. self.scenario_tree = py_trees.composites.Parallel(name, policy=py_trees.common.ParallelPolicy.SUCCESS_ON_ONE)
  200. if behavior is not None:
  201. self.scenario_tree.add_child(self.behavior)
  202. self.scenario_tree.add_child(self.timeout_node)
  203. self.scenario_tree.add_child(WeatherBehavior())
  204. self.scenario_tree.add_child(UpdateAllActorControls())
  205. if criteria is not None:
  206. self.scenario_tree.add_child(self.criteria_tree)
  207. self.scenario_tree.setup(timeout=1)
  208. def _extract_nodes_from_tree(self, tree): # pylint: disable=no-self-use
  209. """
  210. Returns the list of all nodes from the given tree
  211. """
  212. node_list = [tree]
  213. more_nodes_exist = True
  214. while more_nodes_exist:
  215. more_nodes_exist = False
  216. for node in node_list:
  217. if node.children:
  218. node_list.remove(node)
  219. more_nodes_exist = True
  220. for child in node.children:
  221. node_list.append(child)
  222. if len(node_list) == 1 and isinstance(node_list[0], py_trees.composites.Parallel):
  223. return []
  224. return node_list
  225. def get_criteria(self):
  226. """
  227. Return the list of test criteria (all leave nodes)
  228. """
  229. criteria_list = self._extract_nodes_from_tree(self.criteria_tree)
  230. return criteria_list
  231. def terminate(self):
  232. """
  233. This function sets the status of all leaves in the scenario tree to INVALID
  234. """
  235. # Get list of all nodes in the tree
  236. node_list = self._extract_nodes_from_tree(self.scenario_tree)
  237. # Set status to INVALID
  238. for node in node_list:
  239. node.terminate(py_trees.common.Status.INVALID)
  240. # Cleanup all instantiated controllers
  241. actor_dict = {}
  242. try:
  243. check_actors = operator.attrgetter("ActorsWithController")
  244. actor_dict = check_actors(py_trees.blackboard.Blackboard())
  245. except AttributeError:
  246. pass
  247. for actor_id in actor_dict:
  248. actor_dict[actor_id].reset()
  249. py_trees.blackboard.Blackboard().set("ActorsWithController", {}, overwrite=True)