human_agent.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #!/usr/bin/env python
  2. # This work is licensed under the terms of the MIT license.
  3. # For a copy, see <https://opensource.org/licenses/MIT>.
  4. """
  5. This module provides a human agent to control the ego vehicle via keyboard
  6. """
  7. from __future__ import print_function
  8. import json
  9. try:
  10. import pygame
  11. from pygame.locals import K_DOWN
  12. from pygame.locals import K_LEFT
  13. from pygame.locals import K_RIGHT
  14. from pygame.locals import K_SPACE
  15. from pygame.locals import K_UP
  16. from pygame.locals import K_a
  17. from pygame.locals import K_d
  18. from pygame.locals import K_s
  19. from pygame.locals import K_w
  20. from pygame.locals import K_q
  21. except ImportError:
  22. raise RuntimeError('cannot import pygame, make sure pygame package is installed')
  23. import carla
  24. from srunner.autoagents.autonomous_agent import AutonomousAgent
  25. class HumanInterface(object):
  26. """
  27. Class to control a vehicle manually for debugging purposes
  28. """
  29. def __init__(self):
  30. self._width = 800
  31. self._height = 600
  32. self._surface = None
  33. pygame.init()
  34. pygame.font.init()
  35. self._clock = pygame.time.Clock()
  36. self._display = pygame.display.set_mode((self._width, self._height), pygame.HWSURFACE | pygame.DOUBLEBUF)
  37. pygame.display.set_caption("Human Agent")
  38. def run_interface(self, input_data):
  39. """
  40. Run the GUI
  41. """
  42. # process sensor data
  43. image_center = input_data['Center'][1][:, :, -2::-1]
  44. # display image
  45. self._surface = pygame.surfarray.make_surface(image_center.swapaxes(0, 1))
  46. if self._surface is not None:
  47. self._display.blit(self._surface, (0, 0))
  48. pygame.display.flip()
  49. def quit_interface(self):
  50. """
  51. Stops the pygame window
  52. """
  53. pygame.quit()
  54. class HumanAgent(AutonomousAgent):
  55. """
  56. Human agent to control the ego vehicle via keyboard
  57. """
  58. current_control = None
  59. agent_engaged = False
  60. prev_timestamp = 0
  61. def setup(self, path_to_conf_file):
  62. """
  63. Setup the agent parameters
  64. """
  65. self.agent_engaged = False
  66. self.prev_timestamp = 0
  67. self._hic = HumanInterface()
  68. self._controller = KeyboardControl(path_to_conf_file)
  69. def sensors(self):
  70. """
  71. Define the sensor suite required by the agent
  72. :return: a list containing the required sensors in the following format:
  73. [
  74. ['sensor.camera.rgb', {'x':x_rel, 'y': y_rel, 'z': z_rel,
  75. 'yaw': yaw, 'pitch': pitch, 'roll': roll,
  76. 'width': width, 'height': height, 'fov': fov}, 'Sensor01'],
  77. ['sensor.camera.rgb', {'x':x_rel, 'y': y_rel, 'z': z_rel,
  78. 'yaw': yaw, 'pitch': pitch, 'roll': roll,
  79. 'width': width, 'height': height, 'fov': fov}, 'Sensor02'],
  80. ['sensor.lidar.ray_cast', {'x':x_rel, 'y': y_rel, 'z': z_rel,
  81. 'yaw': yaw, 'pitch': pitch, 'roll': roll}, 'Sensor03']
  82. ]
  83. """
  84. sensors = [{'type': 'sensor.camera.rgb', 'x': 0.7, 'y': 0.0, 'z': 1.60, 'roll': 0.0, 'pitch': 0.0, 'yaw': 0.0,
  85. 'width': 800, 'height': 600, 'fov': 100, 'id': 'Center'},
  86. {'type': 'sensor.other.gnss', 'x': 0.7, 'y': -0.4, 'z': 1.60, 'id': 'GPS'}
  87. ]
  88. return sensors
  89. def run_step(self, input_data, timestamp):
  90. """
  91. Execute one step of navigation.
  92. """
  93. self.agent_engaged = True
  94. self._hic.run_interface(input_data)
  95. control = self._controller.parse_events(timestamp - self.prev_timestamp)
  96. self.prev_timestamp = timestamp
  97. return control
  98. def destroy(self):
  99. """
  100. Cleanup
  101. """
  102. self._hic.quit_interface = True
  103. class KeyboardControl(object):
  104. """
  105. Keyboard control for the human agent
  106. """
  107. def __init__(self, path_to_conf_file):
  108. """
  109. Init
  110. """
  111. self._control = carla.VehicleControl()
  112. self._steer_cache = 0.0
  113. self._clock = pygame.time.Clock()
  114. # Get the mode
  115. if path_to_conf_file:
  116. with (open(path_to_conf_file, "r")) as f:
  117. lines = f.read().split("\n")
  118. self._mode = lines[0].split(" ")[1]
  119. self._endpoint = lines[1].split(" ")[1]
  120. # Get the needed vars
  121. if self._mode == "log":
  122. self._log_data = {'records': []}
  123. elif self._mode == "playback":
  124. self._index = 0
  125. self._control_list = []
  126. with open(self._endpoint) as fd:
  127. try:
  128. self._records = json.load(fd)
  129. self._json_to_control()
  130. except ValueError:
  131. # Moving to Python 3.5+ this can be replaced with json.JSONDecodeError
  132. pass
  133. else:
  134. self._mode = "normal"
  135. self._endpoint = None
  136. def _json_to_control(self):
  137. """
  138. Parses the json file into a list of carla.VehicleControl
  139. """
  140. # transform strs into VehicleControl commands
  141. for entry in self._records['records']:
  142. control = carla.VehicleControl(throttle=entry['control']['throttle'],
  143. steer=entry['control']['steer'],
  144. brake=entry['control']['brake'],
  145. hand_brake=entry['control']['hand_brake'],
  146. reverse=entry['control']['reverse'],
  147. manual_gear_shift=entry['control']['manual_gear_shift'],
  148. gear=entry['control']['gear'])
  149. self._control_list.append(control)
  150. def parse_events(self, timestamp):
  151. """
  152. Parse the keyboard events and set the vehicle controls accordingly
  153. """
  154. # Move the vehicle
  155. if self._mode == "playback":
  156. self._parse_json_control()
  157. else:
  158. self._parse_vehicle_keys(pygame.key.get_pressed(), timestamp * 1000)
  159. # Record the control
  160. if self._mode == "log":
  161. self._record_control()
  162. return self._control
  163. def _parse_vehicle_keys(self, keys, milliseconds):
  164. """
  165. Calculate new vehicle controls based on input keys
  166. """
  167. for event in pygame.event.get():
  168. if event.type == pygame.QUIT:
  169. return
  170. elif event.type == pygame.KEYUP:
  171. if event.key == K_q:
  172. self._control.gear = 1 if self._control.reverse else -1
  173. self._control.reverse = self._control.gear < 0
  174. if keys[K_UP] or keys[K_w]:
  175. self._control.throttle = 0.6
  176. else:
  177. self._control.throttle = 0.0
  178. steer_increment = 3e-4 * milliseconds
  179. if keys[K_LEFT] or keys[K_a]:
  180. self._steer_cache -= steer_increment
  181. elif keys[K_RIGHT] or keys[K_d]:
  182. self._steer_cache += steer_increment
  183. else:
  184. self._steer_cache = 0.0
  185. self._steer_cache = min(0.95, max(-0.95, self._steer_cache))
  186. self._control.steer = round(self._steer_cache, 1)
  187. self._control.brake = 1.0 if keys[K_DOWN] or keys[K_s] else 0.0
  188. self._control.hand_brake = keys[K_SPACE]
  189. def _parse_json_control(self):
  190. """
  191. Gets the control corresponding to the current frame
  192. """
  193. if self._index < len(self._control_list):
  194. self._control = self._control_list[self._index]
  195. self._index += 1
  196. else:
  197. print("JSON file has no more entries")
  198. def _record_control(self):
  199. """
  200. Saves the list of control into a json file
  201. """
  202. new_record = {
  203. 'control': {
  204. 'throttle': self._control.throttle,
  205. 'steer': self._control.steer,
  206. 'brake': self._control.brake,
  207. 'hand_brake': self._control.hand_brake,
  208. 'reverse': self._control.reverse,
  209. 'manual_gear_shift': self._control.manual_gear_shift,
  210. 'gear': self._control.gear
  211. }
  212. }
  213. self._log_data['records'].append(new_record)
  214. def __del__(self):
  215. """
  216. Delete method
  217. """
  218. # Get ready to log user commands
  219. if self._mode == "log" and self._log_data:
  220. with open(self._endpoint, 'w') as fd:
  221. json.dump(self._log_data, fd, indent=4, sort_keys=True)