7
7
This module provides a human agent to control the ego vehicle via keyboard
8
8
"""
9
9
10
- import time
11
- from threading import Thread
12
- import cv2
13
- import numpy as np
10
+ from __future__ import print_function
11
+
12
+ import json
14
13
15
14
try :
16
15
import pygame
23
22
from pygame .locals import K_d
24
23
from pygame .locals import K_s
25
24
from pygame .locals import K_w
25
+ from pygame .locals import K_q
26
26
except ImportError :
27
27
raise RuntimeError ('cannot import pygame, make sure pygame package is installed' )
28
28
@@ -37,13 +37,9 @@ class HumanInterface(object):
37
37
Class to control a vehicle manually for debugging purposes
38
38
"""
39
39
40
- def __init__ (self , parent ):
41
- self .quit = False
42
- self ._parent = parent
40
+ def __init__ (self ):
43
41
self ._width = 800
44
42
self ._height = 600
45
- self ._throttle_delta = 0.05
46
- self ._steering_delta = 0.01
47
43
self ._surface = None
48
44
49
45
pygame .init ()
@@ -52,39 +48,23 @@ def __init__(self, parent):
52
48
self ._display = pygame .display .set_mode ((self ._width , self ._height ), pygame .HWSURFACE | pygame .DOUBLEBUF )
53
49
pygame .display .set_caption ("Human Agent" )
54
50
55
- def run (self ):
51
+ def run_interface (self , input_data ):
56
52
"""
57
53
Run the GUI
58
54
"""
59
- while not self ._parent .agent_engaged and not self .quit :
60
- time .sleep (0.5 )
61
-
62
- controller = KeyboardControl ()
63
- while not self .quit :
64
- self ._clock .tick_busy_loop (20 )
65
- controller .parse_events (self ._parent .current_control , self ._clock )
66
- # Process events
67
- pygame .event .pump ()
68
-
69
- # process sensor data
70
- input_data = self ._parent .sensor_interface .get_data ()
71
- image_center = input_data ['Center' ][1 ][:, :, - 2 ::- 1 ]
72
- image_left = input_data ['Left' ][1 ][:, :, - 2 ::- 1 ]
73
- image_right = input_data ['Right' ][1 ][:, :, - 2 ::- 1 ]
74
- image_rear = input_data ['Rear' ][1 ][:, :, - 2 ::- 1 ]
75
-
76
- top_row = np .hstack ((image_left , image_center , image_right ))
77
- bottom_row = np .hstack ((0 * image_rear , image_rear , 0 * image_rear ))
78
- comp_image = np .vstack ((top_row , bottom_row ))
79
- # resize image
80
- image_rescaled = cv2 .resize (comp_image , dsize = (self ._width , self ._height ), interpolation = cv2 .INTER_CUBIC )
81
-
82
- # display image
83
- self ._surface = pygame .surfarray .make_surface (image_rescaled .swapaxes (0 , 1 ))
84
- if self ._surface is not None :
85
- self ._display .blit (self ._surface , (0 , 0 ))
86
- pygame .display .flip ()
55
+ # process sensor data
56
+ image_center = input_data ['Center' ][1 ][:, :, - 2 ::- 1 ]
57
+
58
+ # display image
59
+ self ._surface = pygame .surfarray .make_surface (image_center .swapaxes (0 , 1 ))
60
+ if self ._surface is not None :
61
+ self ._display .blit (self ._surface , (0 , 0 ))
62
+ pygame .display .flip ()
87
63
64
+ def quit_interface (self ):
65
+ """
66
+ Stops the pygame window
67
+ """
88
68
pygame .quit ()
89
69
90
70
@@ -96,21 +76,17 @@ class HumanAgent(AutonomousAgent):
96
76
97
77
current_control = None
98
78
agent_engaged = False
79
+ prev_timestamp = 0
99
80
100
81
def setup (self , path_to_conf_file ):
101
82
"""
102
83
Setup the agent parameters
103
84
"""
104
85
105
86
self .agent_engaged = False
106
- self .current_control = carla .VehicleControl ()
107
- self .current_control .steer = 0.0
108
- self .current_control .throttle = 1.0
109
- self .current_control .brake = 0.0
110
- self .current_control .hand_brake = False
111
- self ._hic = HumanInterface (self )
112
- self ._thread = Thread (target = self ._hic .run )
113
- self ._thread .start ()
87
+ self .prev_timestamp = 0
88
+ self ._hic = HumanInterface ()
89
+ self ._controller = KeyboardControl (path_to_conf_file )
114
90
115
91
def sensors (self ):
116
92
"""
@@ -132,17 +108,7 @@ def sensors(self):
132
108
133
109
"""
134
110
sensors = [{'type' : 'sensor.camera.rgb' , 'x' : 0.7 , 'y' : 0.0 , 'z' : 1.60 , 'roll' : 0.0 , 'pitch' : 0.0 , 'yaw' : 0.0 ,
135
- 'width' : 300 , 'height' : 200 , 'fov' : 100 , 'id' : 'Center' },
136
-
137
- {'type' : 'sensor.camera.rgb' , 'x' : 0.7 , 'y' : - 0.4 , 'z' : 1.60 , 'roll' : 0.0 , 'pitch' : 0.0 ,
138
- 'yaw' : - 45.0 , 'width' : 300 , 'height' : 200 , 'fov' : 100 , 'id' : 'Left' },
139
-
140
- {'type' : 'sensor.camera.rgb' , 'x' : 0.7 , 'y' : 0.4 , 'z' : 1.60 , 'roll' : 0.0 , 'pitch' : 0.0 , 'yaw' : 45.0 ,
141
- 'width' : 300 , 'height' : 200 , 'fov' : 100 , 'id' : 'Right' },
142
-
143
- {'type' : 'sensor.camera.rgb' , 'x' : - 1.8 , 'y' : 0 , 'z' : 1.60 , 'roll' : 0.0 , 'pitch' : 0.0 ,
144
- 'yaw' : 180.0 , 'width' : 300 , 'height' : 200 , 'fov' : 130 , 'id' : 'Rear' },
145
-
111
+ 'width' : 800 , 'height' : 600 , 'fov' : 100 , 'id' : 'Center' },
146
112
{'type' : 'sensor.other.gnss' , 'x' : 0.7 , 'y' : - 0.4 , 'z' : 1.60 , 'id' : 'GPS' }
147
113
]
148
114
@@ -153,15 +119,18 @@ def run_step(self, input_data, timestamp):
153
119
Execute one step of navigation.
154
120
"""
155
121
self .agent_engaged = True
156
- time .sleep (0.1 )
157
- return self .current_control
122
+ self ._hic .run_interface (input_data )
123
+
124
+ control = self ._controller .parse_events (timestamp - self .prev_timestamp )
125
+ self .prev_timestamp = timestamp
126
+
127
+ return control
158
128
159
129
def destroy (self ):
160
130
"""
161
131
Cleanup
162
132
"""
163
- self ._hic .quit = True
164
- self ._thread .join ()
133
+ self ._hic .quit_interface = True
165
134
166
135
167
136
class KeyboardControl (object ):
@@ -170,33 +139,91 @@ class KeyboardControl(object):
170
139
Keyboard control for the human agent
171
140
"""
172
141
173
- def __init__ (self ):
142
+ def __init__ (self , path_to_conf_file ):
174
143
"""
175
144
Init
176
145
"""
177
146
self ._control = carla .VehicleControl ()
178
147
self ._steer_cache = 0.0
148
+ self ._clock = pygame .time .Clock ()
149
+
150
+ # Get the mode
151
+ if path_to_conf_file :
152
+
153
+ with (open (path_to_conf_file , "r" )) as f :
154
+ lines = f .read ().split ("\n " )
155
+ self ._mode = lines [0 ].split (" " )[1 ]
156
+ self ._endpoint = lines [1 ].split (" " )[1 ]
157
+
158
+ # Get the needed vars
159
+ if self ._mode == "log" :
160
+ self ._log_data = {'records' : []}
161
+
162
+ elif self ._mode == "playback" :
163
+ self ._index = 0
164
+ self ._control_list = []
165
+
166
+ with open (self ._endpoint ) as fd :
167
+ try :
168
+ self ._records = json .load (fd )
169
+ self ._json_to_control ()
170
+ except ValueError :
171
+ # Moving to Python 3.5+ this can be replaced with json.JSONDecodeError
172
+ pass
173
+ else :
174
+ self ._mode = "normal"
175
+ self ._endpoint = None
179
176
180
- def parse_events (self , control , clock ):
177
+ def _json_to_control (self ):
178
+ """
179
+ Parses the json file into a list of carla.VehicleControl
180
+ """
181
+
182
+ # transform strs into VehicleControl commands
183
+ for entry in self ._records ['records' ]:
184
+ control = carla .VehicleControl (throttle = entry ['control' ]['throttle' ],
185
+ steer = entry ['control' ]['steer' ],
186
+ brake = entry ['control' ]['brake' ],
187
+ hand_brake = entry ['control' ]['hand_brake' ],
188
+ reverse = entry ['control' ]['reverse' ],
189
+ manual_gear_shift = entry ['control' ]['manual_gear_shift' ],
190
+ gear = entry ['control' ]['gear' ])
191
+ self ._control_list .append (control )
192
+
193
+ def parse_events (self , timestamp ):
181
194
"""
182
195
Parse the keyboard events and set the vehicle controls accordingly
183
196
"""
184
- for event in pygame .event .get ():
185
- if event .type == pygame .QUIT :
186
- return
197
+ # Move the vehicle
198
+ if self ._mode == "playback" :
199
+ self ._parse_json_control ()
200
+ else :
201
+ self ._parse_vehicle_keys (pygame .key .get_pressed (), timestamp * 1000 )
202
+
203
+ # Record the control
204
+ if self ._mode == "log" :
205
+ self ._record_control ()
187
206
188
- self ._parse_vehicle_keys (pygame .key .get_pressed (), clock .get_time ())
189
- control .steer = self ._control .steer
190
- control .throttle = self ._control .throttle
191
- control .brake = self ._control .brake
192
- control .hand_brake = self ._control .hand_brake
207
+ return self ._control
193
208
194
209
def _parse_vehicle_keys (self , keys , milliseconds ):
195
210
"""
196
211
Calculate new vehicle controls based on input keys
197
212
"""
198
- self ._control .throttle = 0.6 if keys [K_UP ] or keys [K_w ] else 0.0
199
- steer_increment = 15.0 * 5e-4 * milliseconds
213
+ for event in pygame .event .get ():
214
+ if event .type == pygame .QUIT :
215
+ return
216
+ elif event .type == pygame .KEYUP :
217
+ if event .key == K_q :
218
+ self ._control .gear = 1 if self ._control .reverse else - 1
219
+ self ._control .reverse = self ._control .gear < 0
220
+
221
+ if keys [K_UP ] or keys [K_w ]:
222
+ self ._control .throttle = 0.6
223
+ else :
224
+ self ._control .throttle = 0.0
225
+
226
+ steer_increment = 3e-4 * milliseconds
200
227
if keys [K_LEFT ] or keys [K_a ]:
201
228
self ._steer_cache -= steer_increment
202
229
elif keys [K_RIGHT ] or keys [K_d ]:
@@ -208,3 +235,42 @@ def _parse_vehicle_keys(self, keys, milliseconds):
208
235
self ._control .steer = round (self ._steer_cache , 1 )
209
236
self ._control .brake = 1.0 if keys [K_DOWN ] or keys [K_s ] else 0.0
210
237
self ._control .hand_brake = keys [K_SPACE ]
238
+
239
+ def _parse_json_control (self ):
240
+ """
241
+ Gets the control corresponding to the current frame
242
+ """
243
+
244
+ if self ._index < len (self ._control_list ):
245
+ self ._control = self ._control_list [self ._index ]
246
+ self ._index += 1
247
+ else :
248
+ print ("JSON file has no more entries" )
249
+
250
+ def _record_control (self ):
251
+ """
252
+ Saves the list of control into a json file
253
+ """
254
+
255
+ new_record = {
256
+ 'control' : {
257
+ 'throttle' : self ._control .throttle ,
258
+ 'steer' : self ._control .steer ,
259
+ 'brake' : self ._control .brake ,
260
+ 'hand_brake' : self ._control .hand_brake ,
261
+ 'reverse' : self ._control .reverse ,
262
+ 'manual_gear_shift' : self ._control .manual_gear_shift ,
263
+ 'gear' : self ._control .gear
264
+ }
265
+ }
266
+
267
+ self ._log_data ['records' ].append (new_record )
268
+
269
+ def __del__ (self ):
270
+ """
271
+ Delete method
272
+ """
273
+ # Get ready to log user commands
274
+ if self ._mode == "log" and self ._log_data :
275
+ with open (self ._endpoint , 'w' ) as fd :
276
+ json .dump (self ._log_data , fd , indent = 4 , sort_keys = True )
0 commit comments