From 0ae660ce4e9d96d3fc87eeac753e12c9b414147e Mon Sep 17 00:00:00 2001 From: Eric Liang Date: Thu, 21 Dec 2017 15:19:55 -0800 Subject: [PATCH] [carla] In carla example, save all images and measurements to local disk (#1350) * revamp saving * smaller jpgs * hide verbose * Tue Dec 19 22:25:01 PST 2017 * make sure temp dirs sort lexiographically * save total reward too * zero pad i * 160x160 dqn * ever higher res dqn --- examples/carla/env.py | 201 ++++++++++++++++++++++++++---------- examples/carla/train_dqn.py | 16 ++- examples/carla/train_ppo.py | 1 + python/ray/tune/trial.py | 6 +- 4 files changed, 165 insertions(+), 59 deletions(-) diff --git a/examples/carla/env.py b/examples/carla/env.py index a1099d549..ee8f9fd4c 100644 --- a/examples/carla/env.py +++ b/examples/carla/env.py @@ -2,7 +2,10 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +from datetime import datetime +import cv2 import os +import json import random import signal import subprocess @@ -23,26 +26,37 @@ import gym from gym.spaces import Box, Discrete -RETRIES_ON_ERROR = 5 -IMAGE_OUT_PATH = os.environ.get("CARLA_OUT") +# Set this where you want to save image outputs (or empty string to disable) +CARLA_OUT_PATH = os.environ.get("CARLA_OUT", os.path.expanduser("~/carla_out")) +if CARLA_OUT_PATH and not os.path.exists(CARLA_OUT_PATH): + os.makedirs(CARLA_OUT_PATH) + +# Set this to the path of your Carla binary SERVER_BINARY = os.environ.get( "CARLA_SERVER", "/home/ubuntu/carla-0.7/CarlaUE4.sh") +# Number of retries if the server doesn't respond +RETRIES_ON_ERROR = 5 +# Default environment configuration ENV_CONFIG = { + "verbose": True, + "render_x_res": 400, + "render_y_res": 300, "x_res": 80, "y_res": 80, + "map": "/Game/Maps/Town02", "random_starting_location": False, - "use_depth_camera": True, + "use_depth_camera": False, "discrete_actions": False, - "max_steps": 150, + "max_steps": 50, "num_vehicles": 20, "num_pedestrians": 40, "weather": [1], # [1, 3, 7, 8, 14] # Defaults to driving down the road /Game/Maps/Town02, start pos 0 "target_x": -7.5, - "target_y": 300, + "target_y": 120, } @@ -57,10 +71,10 @@ class CarlaEnv(gym.Env): self.action_space = Box(-1.0, 1.0, shape=(3,)) if config["use_depth_camera"]: self.observation_space = Box( - -1.0, 1.0, shape=(config["x_res"], config["y_res"], 1)) + -1.0, 1.0, shape=(config["y_res"], config["x_res"], 1)) else: self.observation_space = Box( - 0.0, 255.0, shape=(config["x_res"], config["y_res"], 3)) + 0.0, 255.0, shape=(config["y_res"], config["x_res"], 3)) self._spec = lambda: None self._spec.id = "Carla-v0" @@ -68,14 +82,19 @@ class CarlaEnv(gym.Env): self.server_process = None self.client = None self.num_steps = 0 + self.total_reward = 0 self.prev_measurement = None + self.episode_id = None + self.measurements_file = None + self.weather = None + self.player_start = None def init_server(self): print("Initializing new Carla server...") # Create a new server process and start the client. self.server_port = random.randint(10000, 60000) self.server_process = subprocess.Popen( - [SERVER_BINARY, "/Game/Maps/Town02", + [SERVER_BINARY, self.config["map"], "-windowed", "-ResX=400", "-ResY=300", "-carla-server", "-carla-world-port={}".format(self.server_port)], @@ -107,6 +126,9 @@ class CarlaEnv(gym.Env): try: if not self.server_process: self.init_server() + # reset twice since the first time a server is initialized, + # the starting location is different + self._reset() return self._reset() except Exception as e: print("Error during reset: {}".format(traceback.format_exc())) @@ -117,48 +139,52 @@ class CarlaEnv(gym.Env): def _reset(self): self.num_steps = 0 self.prev_measurement = None + self.episode_id = datetime.today().strftime("%Y-%m-%d_%H-%M-%S_%f") + self.measurements_file = None # Create a CarlaSettings object. This object is a wrapper around # the CarlaSettings.ini file. Here we set the configuration we # want for the new episode. settings = CarlaSettings() + self.weather = random.choice(self.config["weather"]) settings.set( SynchronousMode=True, SendNonPlayerAgentsInfo=True, NumberOfVehicles=self.config["num_vehicles"], NumberOfPedestrians=self.config["num_pedestrians"], - WeatherId=random.choice(self.config["weather"])) + WeatherId=self.weather) settings.randomize_seeds() - if self.config["use_depth_camera"]: - camera = Camera("CameraDepth", PostProcessing="Depth") - camera.set_image_size(self.config["x_res"], self.config["y_res"]) - camera.set_position(30, 0, 130) - settings.add_sensor(camera) - else: - camera = Camera("CameraRGB") - camera.set_image_size(self.config["x_res"], self.config["y_res"]) - camera.set_position(30, 0, 130) - settings.add_sensor(camera) + camera1 = Camera("CameraDepth", PostProcessing="Depth") + camera1.set_image_size( + self.config["render_x_res"], self.config["render_y_res"]) + camera1.set_position(30, 0, 130) + settings.add_sensor(camera1) + + camera2 = Camera("CameraRGB") + camera2.set_image_size( + self.config["render_x_res"], self.config["render_y_res"]) + camera2.set_position(30, 0, 130) + settings.add_sensor(camera2) scene = self.client.load_settings(settings) # Choose one player start at random. number_of_player_starts = len(scene.player_start_spots) if self.config["random_starting_location"]: - player_start = random.randint( + self.player_start = random.randint( 0, max(0, number_of_player_starts - 1)) else: - player_start = 0 + self.player_start = 0 # Notify the server that we want to start the episode at the # player_start index. This function blocks until the server is ready # to start the episode. print("Starting new episode...") - self.client.start_episode(player_start) + self.client.start_episode(self.player_start) - image, measurements = self._read_observation() - self.prev_measurement = measurements + image, py_measurements = self._read_observation() + self.prev_measurement = py_measurements return self.preprocess_image(image) def step(self, action): @@ -206,38 +232,79 @@ class CarlaEnv(gym.Env): brake = max(0.0, min(1.0, action[2])) reverse = action[1] < 0.0 - print( - "steer", steer, "throttle", throttle, "brake", brake, - "reverse", reverse) + hand_brake = False + + if self.config["verbose"]: + print( + "steer", steer, "throttle", throttle, "brake", brake, + "reverse", reverse) self.client.send_control( - steer=steer, throttle=throttle, brake=brake, hand_brake=False, + steer=steer, throttle=throttle, brake=brake, hand_brake=hand_brake, reverse=reverse) - image, measurements = self._read_observation() + + # Process observations + image, py_measurements = self._read_observation() reward, done = compute_reward( - self.config, self.prev_measurement, measurements) - self.prev_measurement = measurements + self.config, self.prev_measurement, py_measurements) if self.num_steps > self.config["max_steps"]: done = True + self.total_reward += reward + py_measurements["reward"] = reward + py_measurements["total_reward"] = self.total_reward + py_measurements["done"] = done + py_measurements["action"] = action + py_measurements["control"] = { + "steer": steer, + "throttle": throttle, + "brake": brake, + "reverse": reverse, + "hand_brake": hand_brake, + } + self.prev_measurement = py_measurements + + # Write out measurements to file + if CARLA_OUT_PATH: + if not self.measurements_file: + self.measurements_file = open( + os.path.join( + CARLA_OUT_PATH, + "measurements_{}.json".format(self.episode_id)), + "w") + self.measurements_file.write(json.dumps(py_measurements)) + self.measurements_file.write("\n") + if done: + self.measurements_file.close() + self.measurements_file = None + self.num_steps += 1 - info = {} image = self.preprocess_image(image) - return image, reward, done, info + return image, reward, done, py_measurements def preprocess_image(self, image): if self.config["use_depth_camera"]: data = (image.data - 0.5) * 2 - return data.reshape(self.config["x_res"], self.config["y_res"], 1) + data = data.reshape( + self.config["render_y_res"], self.config["render_x_res"], 1) + data = cv2.resize( + data, (self.config["x_res"], self.config["y_res"]), + interpolation=cv2.INTER_AREA) else: - return image.data.reshape( - self.config["x_res"], self.config["y_res"], 3) + data = image.data.reshape( + self.config["render_y_res"], self.config["render_x_res"], 3) + data = cv2.resize( + data, (self.config["x_res"], self.config["y_res"]), + interpolation=cv2.INTER_AREA) + data = (data.astype(np.float32) - 128) / 128 + return data def _read_observation(self): # Read the data produced by the server this frame. measurements, sensor_data = self.client.read_data() # Print some of the measurements. - print_measurements(measurements) + if self.config["verbose"]: + print_measurements(measurements) observation = None if self.config["use_depth_camera"]: @@ -248,13 +315,41 @@ class CarlaEnv(gym.Env): if name == camera_name: observation = image - if IMAGE_OUT_PATH: + cur = measurements.player_measurements + py_measurements = { + "episode_id": self.episode_id, + "step": self.num_steps, + "x": cur.transform.location.x, + "y": cur.transform.location.y, + "forward_speed": cur.forward_speed, + "collision_vehicles": cur.collision_vehicles, + "collision_pedestrians": cur.collision_pedestrians, + "collision_other": cur.collision_other, + "intersection_offroad": cur.intersection_offroad, + "intersection_otherlane": cur.intersection_otherlane, + "weather": self.weather, + "map": self.config["map"], + "target_x": self.config["target_x"], + "target_y": self.config["target_y"], + "x_res": self.config["x_res"], + "y_res": self.config["y_res"], + "num_vehicles": self.config["num_vehicles"], + "num_pedestrians": self.config["num_pedestrians"], + "max_steps": self.config["max_steps"], + } + + if CARLA_OUT_PATH: for name, image in sensor_data.items(): - scipy.misc.imsave("{}/{}-{}.jpg".format( - IMAGE_OUT_PATH, name, self.num_steps), image.data) + out_dir = os.path.join(CARLA_OUT_PATH, name) + if not os.path.exists(out_dir): + os.makedirs(out_dir) + out_file = os.path.join( + out_dir, + "{}_{:>04}.jpg".format(self.episode_id, self.num_steps)) + scipy.misc.imsave(out_file, image.data) assert observation is not None, sensor_data - return observation, measurements + return observation, py_measurements def distance(x1, y1, x2, y2): @@ -262,13 +357,10 @@ def distance(x1, y1, x2, y2): def compute_reward(config, prev, current): - prev = prev.player_measurements - current = current.player_measurements - - prev_x = prev.transform.location.x / 100 # cm -> m - prev_y = prev.transform.location.y / 100 - cur_x = current.transform.location.x / 100 # cm -> m - cur_y = current.transform.location.y / 100 + prev_x = prev["x"] / 100 # cm -> m + prev_y = prev["y"] / 100 + cur_x = current["x"] / 100 # cm -> m + cur_y = current["y"] / 100 reward = 0.0 done = False @@ -279,20 +371,21 @@ def compute_reward(config, prev, current): distance(cur_x, cur_y, config["target_x"], config["target_y"])) # Change in speed (km/h) - reward += 0.05 * (current.forward_speed - prev.forward_speed) + reward += 0.05 * (current["forward_speed"] - prev["forward_speed"]) # New collision damage reward -= .00002 * ( - current.collision_vehicles + current.collision_pedestrians + - current.collision_other - prev.collision_vehicles - - prev.collision_pedestrians - prev.collision_other) + current["collision_vehicles"] + current["collision_pedestrians"] + + current["collision_other"] - prev["collision_vehicles"] - + prev["collision_pedestrians"] - prev["collision_other"]) # New sidewalk intersection - reward -= 2 * (current.intersection_offroad - prev.intersection_offroad) + reward -= 2 * ( + current["intersection_offroad"] - prev["intersection_offroad"]) # New opposite lane intersection reward -= 2 * ( - current.intersection_otherlane - prev.intersection_otherlane) + current["intersection_otherlane"] - prev["intersection_otherlane"]) if distance(cur_x, cur_y, config["target_x"], config["target_y"]) < 10: done = True diff --git a/examples/carla/train_dqn.py b/examples/carla/train_dqn.py index a7db684d9..a0135202d 100644 --- a/examples/carla/train_dqn.py +++ b/examples/carla/train_dqn.py @@ -9,11 +9,13 @@ from env import CarlaEnv, ENV_CONFIG env_name = "carla_env" env_config = ENV_CONFIG.copy() env_config.update({ - "x_res": 210, - "y_res": 160, + "verbose": False, + "x_res": 240, + "y_res": 240, "use_depth_camera": False, "discrete_actions": True, - "max_steps": 50, + "max_steps": 200, + "weather": [1, 3, 7, 8, 14], }) register_env(env_name, lambda: CarlaEnv(env_config)) @@ -23,6 +25,14 @@ run_experiments({ "env": "carla_env", "resources": {"cpu": 4, "gpu": 1}, "config": { + "model": { + "conv_filters": [ + [16, [8, 8], 4], + [32, [5, 5], 3], + [32, [5, 5], 2], + [512, [10, 10], 1], + ], + }, "timesteps_per_iteration": 100, "learning_starts": 1000, "schedule_max_timesteps": 100000, diff --git a/examples/carla/train_ppo.py b/examples/carla/train_ppo.py index 949430cd4..ab13df4e7 100644 --- a/examples/carla/train_ppo.py +++ b/examples/carla/train_ppo.py @@ -9,6 +9,7 @@ from env import CarlaEnv, ENV_CONFIG env_name = "carla_env" env_config = ENV_CONFIG.copy() env_config.update({ + "verbose": False, "x_res": 80, "y_res": 80, "use_depth_camera": True, diff --git a/python/ray/tune/trial.py b/python/ray/tune/trial.py index 0d6aa9fb3..7958fda13 100644 --- a/python/ray/tune/trial.py +++ b/python/ray/tune/trial.py @@ -296,8 +296,10 @@ class Trial(object): if not os.path.exists(self.local_dir): os.makedirs(self.local_dir) self.logdir = tempfile.mkdtemp( - prefix=str(self), dir=self.local_dir, - suffix=datetime.today().strftime("_%Y-%m-%d_%H-%M-%S")) + prefix="{}_{}".format( + self, + datetime.today().strftime("%Y-%m-%d_%H-%M-%S")), + dir=self.local_dir) self.result_logger = UnifiedLogger( self.config, self.logdir, self.upload_dir) remote_logdir = self.logdir