|
#!/usr/bin/env python3 |
|
|
|
# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de |
|
# Barcelona (UAB). |
|
# |
|
# This work is licensed under the terms of the MIT license. |
|
# For a copy, see <https://opensource.org/licenses/MIT>. |
|
|
|
"""Basic CARLA client example.""" |
|
|
|
from __future__ import print_function |
|
|
|
import argparse |
|
import logging |
|
import random |
|
import time |
|
|
|
from carla.client import make_carla_client |
|
from carla.sensor import Camera |
|
from carla.settings import CarlaSettings |
|
from carla.tcp import TCPConnectionError |
|
from carla.util import print_over_same_line |
|
|
|
|
|
def run_carla_client(host, port, autopilot_on, save_images_to_disk, image_filename_format, settings_filepath): |
|
# Here we will run 3 episodes with 300 frames each. |
|
number_of_episodes = 3 |
|
frames_per_episode = 300 |
|
|
|
# We assume the CARLA server is already waiting for a client to connect at |
|
# host:port. To create a connection we can use the `make_carla_client` |
|
# context manager, it creates a CARLA client object and starts the |
|
# connection. It will throw an exception if something goes wrong. The |
|
# context manager makes sure the connection is always cleaned up on exit. |
|
with make_carla_client(host, port) as client: |
|
print('CarlaClient connected') |
|
|
|
for episode in range(0, number_of_episodes): |
|
# Start a new episode. |
|
|
|
if settings_filepath is 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() |
|
settings.set( |
|
SynchronousMode=True, |
|
SendNonPlayerAgentsInfo=True, |
|
NumberOfVehicles=20, |
|
NumberOfPedestrians=40, |
|
WeatherId=random.choice([1, 3, 7, 8, 14])) |
|
settings.randomize_seeds() |
|
|
|
# Now we want to add a couple of cameras to the player vehicle. |
|
# We will collect the images produced by these cameras every |
|
# frame. |
|
|
|
# The default camera captures RGB images of the scene. |
|
camera0 = Camera('CameraRGB') |
|
# Set image resolution in pixels. |
|
camera0.set_image_size(800, 600) |
|
# Set its position relative to the car in centimeters. |
|
camera0.set_position(30, 0, 130) |
|
settings.add_sensor(camera0) |
|
|
|
# Let's add another camera producing ground-truth depth. |
|
camera1 = Camera('CameraDepth', PostProcessing='Depth') |
|
camera1.set_image_size(800, 600) |
|
camera1.set_position(30, 0, 130) |
|
settings.add_sensor(camera1) |
|
|
|
else: |
|
|
|
# Alternatively, we can load these settings from a file. |
|
with open(settings_filepath, 'r') as fp: |
|
settings = fp.read() |
|
|
|
# Now we load these settings into the server. The server replies |
|
# with a scene description containing the available start spots for |
|
# the player. Here we can provide a CarlaSettings object or a |
|
# CarlaSettings.ini file as string. |
|
scene = client.load_settings(settings) |
|
|
|
# Choose one player start at random. |
|
number_of_player_starts = len(scene.player_start_spots) |
|
player_start = random.randint(0, max(0, number_of_player_starts - 1)) |
|
|
|
# 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...') |
|
client.start_episode(player_start) |
|
|
|
# Iterate every frame in the episode. |
|
for frame in range(0, frames_per_episode): |
|
|
|
# Read the data produced by the server this frame. |
|
measurements, sensor_data = client.read_data() |
|
|
|
# Print some of the measurements. |
|
print_measurements(measurements) |
|
|
|
# Save the images to disk if requested. |
|
if save_images_to_disk: |
|
for name, image in sensor_data.items(): |
|
image.save_to_disk(image_filename_format.format(episode, name, frame)) |
|
|
|
# We can access the encoded data of a given image as numpy |
|
# array using its "data" property. For instance, to get the |
|
# depth value (normalized) at pixel X, Y |
|
# |
|
# depth_array = sensor_data['CameraDepth'].data |
|
# value_at_pixel = depth_array[Y, X] |
|
# |
|
|
|
# Now we have to send the instructions to control the vehicle. |
|
# If we are in synchronous mode the server will pause the |
|
# simulation until we send this control. |
|
|
|
if not autopilot_on: |
|
|
|
client.send_control( |
|
steer=random.uniform(-1.0, 1.0), |
|
throttle=0.5, |
|
brake=0.0, |
|
hand_brake=False, |
|
reverse=False) |
|
|
|
else: |
|
|
|
# Together with the measurements, the server has sent the |
|
# control that the in-game autopilot would do this frame. We |
|
# can enable autopilot by sending back this control to the |
|
# server. We can modify it if wanted, here for instance we |
|
# will add some noise to the steer. |
|
|
|
control = measurements.player_measurements.autopilot_control |
|
control.steer += random.uniform(-0.1, 0.1) |
|
client.send_control(control) |
|
|
|
|
|
def print_measurements(measurements): |
|
number_of_agents = len(measurements.non_player_agents) |
|
player_measurements = measurements.player_measurements |
|
message = 'Vehicle at ({pos_x:.1f}, {pos_y:.1f}), ' |
|
message += '{speed:.2f} km/h, ' |
|
message += 'Collision: {{vehicles={col_cars:.0f}, pedestrians={col_ped:.0f}, other={col_other:.0f}}}, ' |
|
message += '{other_lane:.0f}% other lane, {offroad:.0f}% off-road, ' |
|
message += '({agents_num:d} non-player agents in the scene)' |
|
message = message.format( |
|
pos_x=player_measurements.transform.location.x / 100, # cm -> m |
|
pos_y=player_measurements.transform.location.y / 100, |
|
speed=player_measurements.forward_speed, |
|
col_cars=player_measurements.collision_vehicles, |
|
col_ped=player_measurements.collision_pedestrians, |
|
col_other=player_measurements.collision_other, |
|
other_lane=100 * player_measurements.intersection_otherlane, |
|
offroad=100 * player_measurements.intersection_offroad, |
|
agents_num=number_of_agents) |
|
print_over_same_line(message) |
|
|
|
|
|
def main(): |
|
argparser = argparse.ArgumentParser(description=__doc__) |
|
argparser.add_argument( |
|
'-v', '--verbose', |
|
action='store_true', |
|
dest='debug', |
|
help='print debug information') |
|
argparser.add_argument( |
|
'--host', |
|
metavar='H', |
|
default='localhost', |
|
help='IP of the host server (default: localhost)') |
|
argparser.add_argument( |
|
'-p', '--port', |
|
metavar='P', |
|
default=2000, |
|
type=int, |
|
help='TCP port to listen to (default: 2000)') |
|
argparser.add_argument( |
|
'-a', '--autopilot', |
|
action='store_true', |
|
help='enable autopilot') |
|
argparser.add_argument( |
|
'-i', '--images-to-disk', |
|
action='store_true', |
|
help='save images to disk') |
|
argparser.add_argument( |
|
'-c', '--carla-settings', |
|
metavar='PATH', |
|
default=None, |
|
help='Path to a "CarlaSettings.ini" file') |
|
|
|
args = argparser.parse_args() |
|
|
|
log_level = logging.DEBUG if args.debug else logging.INFO |
|
logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level) |
|
|
|
logging.info('listening to server %s:%s', args.host, args.port) |
|
|
|
while True: |
|
try: |
|
|
|
run_carla_client( |
|
host=args.host, |
|
port=args.port, |
|
autopilot_on=args.autopilot, |
|
save_images_to_disk=args.images_to_disk, |
|
image_filename_format='_images/episode_{:0>3d}/{:s}/image_{:0>5d}.png', |
|
settings_filepath=args.carla_settings) |
|
|
|
print('Done.') |
|
|
|
except TCPConnectionError as error: |
|
logging.error(error) |
|
time.sleep(1) |
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
try: |
|
main() |
|
except KeyboardInterrupt: |
|
print('\nCancelled by user. Bye!') |