diff --git a/Makefile b/Makefile index 1e5c63c90..bebeff0a0 100644 --- a/Makefile +++ b/Makefile @@ -71,13 +71,13 @@ run_test_release: @-LD_LIBRARY_PATH=$(INSTALL_FOLDER)/shared $(INSTALL_FOLDER)/bin/test_carlaserver --gtest_shuffle $(GTEST_ARGS) launch_test_clients: - @echo "Launch echo_client.py" - @python $(PYTHON_CLIENT_FOLDER)/echo_client.py -p 4000 & echo $$! > echo_client.pid - @echo "Launch carla_client.py" - @python $(PYTHON_CLIENT_FOLDER)/carla_client.py -p 2000 & echo $$! > carla_client.pid + @echo "Launch echo client" + @python3 $(PYTHON_CLIENT_FOLDER)/client_test.py --echo -p 4000 --log echo_client.log & echo $$! > echo_client.pid + @echo "Launch carla client" + @python3 $(PYTHON_CLIENT_FOLDER)/client_test.py -p 2000 --log carla_client.log & echo $$! > carla_client.pid kill_test_clients: - @echo "Kill echo_client.py" + @echo "Kill echo client" @kill `cat echo_client.pid` && rm echo_client.pid - @echo "Kill carla_client.py" + @echo "Kill carla client" @kill `cat carla_client.pid` && rm carla_client.pid diff --git a/Util/Protoc.bat b/Util/Protoc.bat index 89e6f20fa..b2c88f582 100644 --- a/Util/Protoc.bat +++ b/Util/Protoc.bat @@ -2,7 +2,7 @@ set PROTOBUF_SRC_DIR=Proto set PROTOBUF_CPP_OUT_DIR=CarlaServer/source/carla/server -set PROTOBUF_PY_OUT_DIR=PythonClient/lib +set PROTOBUF_PY_OUT_DIR=PythonClient/carla set PROTO_BASENAME=carla_server if "%1" == "--clean" ( diff --git a/Util/Protoc.sh b/Util/Protoc.sh index b24561ce4..6b4ffb446 100755 --- a/Util/Protoc.sh +++ b/Util/Protoc.sh @@ -7,7 +7,7 @@ pushd "$SCRIPT_DIR" >/dev/null PROTOBUF_SRC_DIR=Proto PROTOBUF_CPP_OUT_DIR=CarlaServer/source/carla/server -PROTOBUF_PY_OUT_DIR=PythonClient/lib +PROTOBUF_PY_OUT_DIR=PythonClient/carla PROTO_BASENAME=carla_server if [ "$1" == "--clean" ]; then diff --git a/Util/PythonClient/.gitignore b/Util/PythonClient/.gitignore index b4f3dc823..2335ac148 100644 --- a/Util/PythonClient/.gitignore +++ b/Util/PythonClient/.gitignore @@ -1,2 +1,3 @@ *.py[cod] *_pb2.py +__pycache__ diff --git a/Util/PythonClient/lib/__init__.py b/Util/PythonClient/carla/__init__.py similarity index 100% rename from Util/PythonClient/lib/__init__.py rename to Util/PythonClient/carla/__init__.py diff --git a/Util/PythonClient/carla/client.py b/Util/PythonClient/carla/client.py new file mode 100644 index 000000000..1dd92741d --- /dev/null +++ b/Util/PythonClient/carla/client.py @@ -0,0 +1,93 @@ +# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +"""CARLA Client.""" + +from . import tcp + +from . import carla_server_pb2 as carla_protocol + + +class CarlaClient(object): + def __init__(self, host, world_port, timeout=15): + self._world_client = tcp.TCPClient(host, world_port, timeout) + self._stream_client = tcp.TCPClient(host, world_port + 1, timeout) + self._control_client = tcp.TCPClient(host, world_port + 2, timeout) + + def connect(self): + self._world_client.connect() + + def disconnect(self): + self._control_client.disconnect() + self._stream_client.disconnect() + self._world_client.disconnect() + + def request_new_episode(self, carla_settings): + """Request a new episode. carla_settings object must be convertible to + a str holding a CarlaSettings.ini. + + Returns a protobuf object holding the scene description. + """ + # Disconnect agent clients. + self._stream_client.disconnect() + self._control_client.disconnect() + # Send new episode request. + pb_message = carla_protocol.RequestNewEpisode() + pb_message.ini_file = str(carla_settings) + self._world_client.write(pb_message.SerializeToString()) + # Read scene description. + data = self._world_client.read() + if not data: + raise RuntimeError('failed to read data from server') + pb_message = carla_protocol.SceneDescription() + pb_message.ParseFromString(data) + if len(pb_message.player_start_spots) < 1: + raise RuntimeError("received 0 player start spots") + return pb_message + + def start_episode(self, player_start_index): + """Start the new episode at the player start given by the + player_start_index. The list of player starts is retrieved by + request_new_episode(). + + This function waits until the server answers with an EpisodeReady. + """ + pb_message = carla_protocol.EpisodeStart() + pb_message.player_start_spot_index = player_start_index + self._world_client.write(pb_message.SerializeToString()) + # Wait for EpisodeReady. + data = self._world_client.read() + if not data: + raise RuntimeError('failed to read data from server') + pb_message = carla_protocol.EpisodeReady() + pb_message.ParseFromString(data) + if not pb_message.ready: + raise RuntimeError('cannot start episode: server failed to start episode') + # We can start the agent clients now. + self._stream_client.connect() + self._control_client.connect() + + def read_measurements(self): + """Read measuremnts of current frame. The episode must be started. + Return the protobuf object with the measurements followed by the raw + data with the images. + """ + # Read measurements. + data = self._stream_client.read() + if not data: + raise RuntimeError('failed to read data from server') + pb_message = carla_protocol.Measurements() + pb_message.ParseFromString(data) + # Read images. + images_raw_data = self._stream_client.read() + return pb_message, images_raw_data + + def send_control(self, **kwargs): + """Send vehicle control for the current frame.""" + pb_message = carla_protocol.Control() + pb_message.steer = kwargs.get('steer', 0.0) + pb_message.throttle = kwargs.get('throttle', 0.0) + pb_message.brake = kwargs.get('brake', 0.0) + pb_message.hand_brake = kwargs.get('hand_brake', False) + pb_message.reverse = kwargs.get('reverse', False) + pb_message.autopilot = kwargs.get('autopilot', False) + self._control_client.write(pb_message.SerializeToString()) diff --git a/Util/PythonClient/lib/carla_settings.py b/Util/PythonClient/carla/settings.py similarity index 82% rename from Util/PythonClient/lib/carla_settings.py rename to Util/PythonClient/carla/settings.py index 75424f35d..57e498075 100644 --- a/Util/PythonClient/lib/carla_settings.py +++ b/Util/PythonClient/carla/settings.py @@ -1,11 +1,9 @@ # CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - """CARLA Settings""" - -import ConfigParser -import StringIO +import configparser +import io import random @@ -13,6 +11,7 @@ MAX_NUMBER_OF_WEATHER_IDS = 14 class Camera(object): + """Camera description. To be added to CarlaSettings.""" def __init__(self, name, **kwargs): self.CameraName = name self.PostProcessing = 'SceneFinal' @@ -28,7 +27,7 @@ class Camera(object): self.set(**kwargs) def set(self, **kwargs): - for key, value in kwargs.iteritems(): + for key, value in kwargs.items(): if not hasattr(self, key): raise ValueError('CarlaSettings.Camera: no key named %r' % key) setattr(self, key, value) @@ -49,6 +48,7 @@ class Camera(object): class CarlaSettings(object): + """CARLA settings object. Convertible to str as CarlaSettings.ini.""" def __init__(self, **kwargs): # [CARLA/Server] self.SynchronousMode = False @@ -57,14 +57,15 @@ class CarlaSettings(object): self.PlayerVehicle = None self.NumberOfVehicles = 20 self.NumberOfPedestrians = 30 - self.WeatherId = random.randint(-1, MAX_NUMBER_OF_WEATHER_IDS) + self.WeatherId = -1 self.SeedVehicles = None self.SeedPedestrians = None + self.randomize_weather() self.set(**kwargs) self._cameras = [] def set(self, **kwargs): - for key, value in kwargs.iteritems(): + for key, value in kwargs.items(): if not hasattr(self, key): raise ValueError('CarlaSettings: no key named %r' % key) setattr(self, key, value) @@ -74,9 +75,18 @@ class CarlaSettings(object): return 0 return self.NumberOfVehicles + self.NumberOfPedestrians + def get_images_byte_size(self): + size = 0 + for camera in self._cameras: + size += 3 + int(camera.ImageSizeX) * int(camera.ImageSizeY) + return 4 * size + def randomize_seeds(self): - self.SeedVehicles = random.getrandbits(64) - self.SeedPedestrians = random.getrandbits(64) + self.SeedVehicles = random.getrandbits(32) + self.SeedPedestrians = random.getrandbits(32) + + def randomize_weather(self): + self.WeatherId = random.randint(-1, MAX_NUMBER_OF_WEATHER_IDS) def add_camera(self, camera): if not isinstance(camera, Camera): @@ -84,7 +94,7 @@ class CarlaSettings(object): self._cameras.append(camera) def __str__(self): - ini = ConfigParser.RawConfigParser() + ini = configparser.ConfigParser() ini.optionxform=str S_SERVER = 'CARLA/Server' S_LEVEL = 'CARLA/LevelSettings' @@ -95,7 +105,7 @@ class CarlaSettings(object): if hasattr(obj, key) and getattr(obj, key) is not None: if not ini.has_section(section): ini.add_section(section) - ini.set(section, key, getattr(obj, key)) + ini.set(section, key, str(getattr(obj, key))) add_section(S_SERVER, self, [ 'SynchronousMode', @@ -123,20 +133,6 @@ class CarlaSettings(object): 'CameraRotationRoll', 'CameraRotationYaw']) - text = StringIO.StringIO() + text = io.StringIO() ini.write(text) return text.getvalue().replace(' = ', '=') - - -if __name__ == '__main__': - - settings = CarlaSettings(NumberOfVehicles=200, SendNonPlayerAgentsInfo=True) - settings.randomize_seeds() - camera1 = Camera('Camera1') - camera1.set_image_size(1920, 1080) - camera1.set_position(20, 0, 100) - settings.add_camera(camera1) - camera2 = Camera('Camera2') - camera2.set(CameraFOV=120, PostProcessing='Depth') - settings.add_camera(camera2) - print(settings) diff --git a/Util/PythonClient/lib/tcp_client.py b/Util/PythonClient/carla/tcp.py similarity index 72% rename from Util/PythonClient/lib/tcp_client.py rename to Util/PythonClient/carla/tcp.py index ef71280da..d9be858ee 100644 --- a/Util/PythonClient/lib/tcp_client.py +++ b/Util/PythonClient/carla/tcp.py @@ -1,13 +1,11 @@ # CARLA, Copyright (C) 2017 Computer Vision Center (CVC) +"""Basic TCP client.""" + import logging import socket import struct -from contextlib import contextmanager - -from util import to_hex_str - class TCPClient(object): def __init__(self, host, port, timeout): @@ -23,29 +21,27 @@ class TCPClient(object): def disconnect(self): if self._socket is not None: + self._log('disconnecting...') self._socket.close() def write(self, message): header = struct.pack(' 0: data = self._socket.recv(length) - if data == '': - raise RuntimeError('connection closed') + if not data: + raise RuntimeError('%s:%s connection closed' % (self._host, self._port)) buf += data length -= len(data) return buf diff --git a/Util/PythonClient/lib/util.py b/Util/PythonClient/carla/util.py similarity index 85% rename from Util/PythonClient/lib/util.py rename to Util/PythonClient/carla/util.py index 71c3fced8..71932cf03 100644 --- a/Util/PythonClient/lib/util.py +++ b/Util/PythonClient/carla/util.py @@ -10,8 +10,8 @@ def to_hex_str(header): @contextmanager -def make_client(client_type, *args, **kwargs): - """Context manager to create a client.""" +def make_connection(client_type, *args, **kwargs): + """Context manager to create and connect a networking object.""" client = None try: client = client_type(*args, **kwargs) diff --git a/Util/PythonClient/carla_client.py b/Util/PythonClient/carla_client.py deleted file mode 100755 index b41f9b21b..000000000 --- a/Util/PythonClient/carla_client.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python2 - -# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - - -"""Basic CARLA client for testing.""" - - -import argparse -import logging -import time - - -from lib.carla_util import TestCarlaClientBase -from lib.util import make_client - - -def main(): - argparser = argparse.ArgumentParser(description=__doc__) - argparser.add_argument( - '-v', '--verbose', - action='store_true', - help='print debug information to console instead of log file') - argparser.add_argument( - '-d', '--debug', - action='store_true', - help='print debug extra information to log') - argparser.add_argument( - '--host', - metavar='H', - default='127.0.0.1', - help='IP of the host server') - argparser.add_argument( - '-p', '--port', - metavar='P', - default=2000, - type=int, - help='TCP port to listen to') - - args = argparser.parse_args() - - logging_config = { - 'format': 'carla_client:%(levelname)s: %(message)s', - 'level': logging.DEBUG if args.debug else logging.INFO - } - if not args.verbose: - logging_config['filename'] = 'carla_client.log' - logging_config['filemode'] = 'w+' - logging.basicConfig(**logging_config) - - while True: - try: - with make_client(TestCarlaClientBase, args) as client: - while True: - client.start_episode() - client.loop_on_agent_client(iterations=10) - - except Exception as exception: - logging.error('exception: %s', exception) - time.sleep(1) - - -if __name__ == '__main__': - - main() diff --git a/Util/PythonClient/client_test.py b/Util/PythonClient/client_test.py new file mode 100755 index 000000000..291ea2b45 --- /dev/null +++ b/Util/PythonClient/client_test.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +"""Basic CARLA client.""" + +import argparse +import logging +import random +import time + +import carla + +from carla.client import CarlaClient +from carla.settings import CarlaSettings, Camera +from carla.tcp import TCPClient +from carla.util import make_connection + + +def run_carla_server(host, port): + with make_connection(CarlaClient, host, port, timeout=15) as client: + logging.info('CarlaClient connected') + frames_per_episode = 300 + while True: + settings = CarlaSettings() + settings.set(SendNonPlayerAgentsInfo=True) + settings.randomize_seeds() + settings.add_camera(Camera('DefaultCamera')) + + logging.debug('sending CarlaSettings:\n%s', settings) + logging.info('new episode requested') + + scene = client.request_new_episode(settings) + + number_of_player_starts = len(scene.player_start_spots) + player_start = random.randint(0, max(0, number_of_player_starts - 1)) + logging.info( + 'start episode at %d/%d player start (%d frames)', + player_start, + number_of_player_starts, + frames_per_episode) + + client.start_episode(player_start) + + autopilot = (random.random() < 0.5) + reverse = (random.random() < 0.2) + + for _ in range(0, frames_per_episode): + logging.debug('reading measurements...') + measurements, images = client.read_measurements() + + logging.debug('received data of %d agents', len(measurements.non_player_agents)) + logging.debug('received %d bytes of images', len(images)) + + logging.debug('sending control...') + client.send_control( + steer=random.uniform(-1.0, 1.0), + throttle=0.3, + reverse=reverse, + autopilot=autopilot) + + +def main(): + argparser = argparse.ArgumentParser(description=__doc__) + argparser.add_argument( + '-v', '--verbose', + action='store_true', + dest='debug', + help='print debug information') + argparser.add_argument( + '--log', + metavar='LOG_FILE', + default=None, + help='print output to file') + argparser.add_argument( + '--host', + metavar='H', + default='127.0.0.1', + help='IP of the host server (default: 127.0.0.1)') + argparser.add_argument( + '-p', '--port', + metavar='P', + default=2000, + type=int, + help='TCP port to listen to (default: 2000)') + argparser.add_argument( + '--echo', + action='store_true', + help='start a client that just echoes what the server sends') + + args = argparser.parse_args() + + name = 'echo_client: ' if args.echo else 'carla_client: ' + logging_config = { + 'format': name + '%(levelname)s: %(message)s', + 'level': logging.DEBUG if args.debug else logging.INFO + } + if args.log: + logging_config['filename'] = args.log + logging_config['filemode'] = 'w+' + logging.basicConfig(**logging_config) + + logging.info('listening to server %s:%s', args.host, args.port) + + while True: + try: + + if args.echo: + + with make_connection(TCPClient, args.host, args.port, timeout=15) as client: + while True: + logging.info('reading...') + data = client.read() + if not data: + raise RuntimeError('failed to read data from server') + logging.info('writing...') + client.write(data) + + else: + + run_carla_server(args.host, args.port) + + except Exception as exception: + logging.error('exception: %s', exception) + time.sleep(1) + + +if __name__ == '__main__': + + try: + main() + except KeyboardInterrupt: + print('\nCancelled by user. Bye!') diff --git a/Util/PythonClient/echo_client.py b/Util/PythonClient/echo_client.py deleted file mode 100755 index 82126b215..000000000 --- a/Util/PythonClient/echo_client.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python2 - -# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - - -"""Implements TCP client that connects, reads, and sends back what it receives.""" - - -import argparse -import logging -import time - -from lib.tcp_client import TCPClient -from lib.util import make_client - - -def launch_echo_client(host, port): - while True: - try: - logging.debug('connecting...') - with make_client(TCPClient, host, port, timeout=20) as client: - while True: - logging.debug('reading...') - data = client.read() - if data == '': - logging.debug('no data received!') - break - # logging.info('received: %s', data) - logging.debug('writing...') - client.write(data) - except Exception as e: - logging.error('exception: %s', e) - time.sleep(1) - - -def main(): - argparser = argparse.ArgumentParser(description=__doc__) - argparser.add_argument( - '-v', '--verbose', - action='store_true', - help='print debug information to console instead of log file') - argparser.add_argument( - '--host', - metavar='H', - default='127.0.0.1', - help='IP of the host server') - argparser.add_argument( - '-p', '--port', - metavar='P', - default=4000, - type=int, - help='TCP port to listen to') - - args = argparser.parse_args() - - logging_config = { - 'format': 'echo_client:%(levelname)s: %(message)s', - 'level': logging.DEBUG - } - if not args.verbose: - logging_config['filename'] = 'echo_client.log' - logging_config['filemode'] = 'w+' - logging.basicConfig(**logging_config) - - launch_echo_client(args.host, args.port) - - -if __name__ == '__main__': - - main() diff --git a/Util/PythonClient/lib/carla_client.py b/Util/PythonClient/lib/carla_client.py deleted file mode 100644 index 621e3125c..000000000 --- a/Util/PythonClient/lib/carla_client.py +++ /dev/null @@ -1,81 +0,0 @@ -# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - - -"""CARLA Client.""" - - -from tcp_client import TCPClient - -import carla_server_pb2 as carla_protocol - - -class CarlaClient(object): - def __init__(self, host, world_port, timeout): - self._world_client = TCPClient(host, world_port, timeout) - self._stream_client = TCPClient(host, world_port + 1, timeout) - self._control_client = TCPClient(host, world_port + 2, timeout) - - def connect_world_client(self): - self._world_client.connect() - - def disconnect_all(self): - self.disconnect_agent_client() - self._world_client.disconnect() - - def connect_agent_client(self): - self._stream_client.connect() - self._control_client.connect() - - def disconnect_agent_client(self): - self._stream_client.disconnect() - self._control_client.disconnect() - - def write_request_new_episode(self, ini_file): - pb_message = carla_protocol.RequestNewEpisode() - pb_message.ini_file = ini_file - self._world_client.write(pb_message.SerializeToString()) - - def read_scene_description(self): - data = self._world_client.read() - if not data: - return None - pb_message = carla_protocol.SceneDescription() - pb_message.ParseFromString(data) - return pb_message - - def write_episode_start(self, player_start_location_index): - pb_message = carla_protocol.EpisodeStart() - pb_message.player_start_spot_index = player_start_location_index - self._world_client.write(pb_message.SerializeToString()) - - def read_episode_ready(self): - data = self._world_client.read() - if not data: - return None - pb_message = carla_protocol.EpisodeReady() - pb_message.ParseFromString(data) - return pb_message - - def read_measurements(self): - data = self._stream_client.read() - if not data: - return None - pb_message = carla_protocol.Measurements() - pb_message.ParseFromString(data) - return pb_message - - def read_images(self): - data = self._stream_client.read() - if not data: - return None - return data - - def write_control(self, **kwargs): - pb_message = carla_protocol.Control() - pb_message.steer = kwargs.get('steer', 0.0) - pb_message.throttle = kwargs.get('throttle', 0.0) - pb_message.brake = kwargs.get('brake', 0.0) - pb_message.hand_brake = kwargs.get('hand_brake', False) - pb_message.reverse = kwargs.get('reverse', False) - pb_message.autopilot = kwargs.get('autopilot', False) - self._control_client.write(pb_message.SerializeToString()) diff --git a/Util/PythonClient/lib/carla_util.py b/Util/PythonClient/lib/carla_util.py deleted file mode 100644 index d2176b8a1..000000000 --- a/Util/PythonClient/lib/carla_util.py +++ /dev/null @@ -1,162 +0,0 @@ -# CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - - -"""Test suite for CARLA Client.""" - - -import logging -import random - - -from carla_client import CarlaClient - - -MAX_NUMBER_OF_WEATHER_IDS = 14 - - -def _get_settings_dict(**kwargs): - return { - 'SynchronousMode': kwargs.get('SynchronousMode', False), - 'SendNonPlayerAgentsInfo': kwargs.get('SendNonPlayerAgentsInfo', False), - 'NumberOfVehicles': max(0, int(kwargs.get('NumberOfVehicles', 20))), - 'NumberOfPedestrians': max(0, int(kwargs.get('NumberOfPedestrians', 30))), - 'WeatherId': int(kwargs.get('WeatherId', random.randint(-1, MAX_NUMBER_OF_WEATHER_IDS))), - } - - -def get_carla_settings(**kwargs): - """Create a CarlaSettings.ini based in the arguments provided""" - return """ -[CARLA/Server] -ServerTimeOut=10000 -SynchronousMode={SynchronousMode} -SendNonPlayerAgentsInfo={SendNonPlayerAgentsInfo} - -[CARLA/LevelSettings] -PlayerVehicle= -NumberOfVehicles={NumberOfVehicles} -NumberOfPedestrians={NumberOfPedestrians} -WeatherId={WeatherId} -SeedVehicles=123456789 -SeedPedestrians=123456789 - -[CARLA/SceneCapture] -Cameras=MyCamera - -[CARLA/SceneCapture/MyCamera] -PostProcessing=SceneFinal -ImageSizeX=800 -ImageSizeY=600 -CameraFOV=90 -CameraPositionX=15 -CameraPositionY=0 -CameraPositionZ=123 -CameraRotationPitch=8 -CameraRotationRoll=0 -CameraRotationYaw=0 -""".format(**_get_settings_dict(**kwargs)) - - -def get_number_of_agents(**kwargs): - settings = _get_settings_dict(**kwargs) - if not settings['SendNonPlayerAgentsInfo']: - return 0 - else: - return settings['NumberOfVehicles'] + settings['NumberOfPedestrians'] - - -class TestCarlaClientBase(object): - def __init__(self, args): - """Create a TestCarlaClientBase and initialize the CarlaClient""" - self.args = args - self.number_of_agents = 0 - logging.info('connecting carla client to %s:%d', self.args.host, self.args.port) - self.client = CarlaClient(self.args.host, self.args.port, timeout=15) - - def connect(self): - self.client.connect_world_client() - - def disconnect(self): - if self.client is not None: - self.client.disconnect_all() - - def request_new_episode(self, **kwargs): - """Create and send a CarlaSettigns.ini""" - self.client.disconnect_agent_client() - logging.info('requesting new episode') - carla_settings = get_carla_settings(**kwargs) - self.number_of_agents = get_number_of_agents(**kwargs) - logging.debug(carla_settings) - self.client.write_request_new_episode(carla_settings) - - def start_episode(self, **kwargs): - """Start a default episode and launch the agent client""" - self.request_new_episode(**kwargs) - logging.debug('waiting for the scene description') - data = self.client.read_scene_description() - if data is None: - raise RuntimeError("received empty scene description") - number_of_start_spots = len(data.player_start_spots) - if number_of_start_spots == 0: - raise RuntimeError("received 0 player start spots") - logging.debug('received %d player start locations', number_of_start_spots) - if self.args.debug: - for spot in data.player_start_spots: - logging.debug(spot) - - logging.debug('sending a random episode start') - self.client.write_episode_start(random.randint(0, max(0, number_of_start_spots - 1))) - - logging.debug('waiting for episode to be ready') - data = self.client.read_episode_ready() - if not data.ready: - raise RuntimeError('received episode not ready') - - logging.info('connecting secondary clients') - self.client.connect_agent_client() - - def loop_on_agent_client(self, iterations=100, autopilot=None, control=None): - """ - Loop on receive measurements and send control. If autopilot or control - are None they will be chosen at random. - """ - if control is not None: - autopilot = False - if autopilot is None: - autopilot = random.choice([True, False]) - - reverse = (random.random() < 0.2) - - logging.info('running episode with %d iterations', iterations) - - for x in xrange(0, iterations): - logging.debug('waiting for measurements') - data = self.client.read_measurements() - if not data: - raise RuntimeError('received empty measurements') - if not data.IsInitialized(): - raise RuntimeError('received non-initialized measurements') - else: - logging.debug('received valid measurements') - number_of_agents = len(data.non_player_agents) - logging.debug('received data of %d agents', number_of_agents) - if number_of_agents > self.number_of_agents or number_of_agents + 10 < self.number_of_agents: - raise RuntimeError('received data for %d agents, but %d was requested' % (number_of_agents, self.number_of_agents)) - if self.args.debug: - for agent in data.non_player_agents: - logging.debug(agent) - logging.debug('waiting for images') - data = self.client.read_images() - logging.debug('received %d bytes of images', len(data) if data is not None else 0) - - logging.debug('sending control') - if autopilot: - self.client.write_control(autopilot=True) - else: - if control is None: - self.client.write_control( - steer=random.uniform(-1.0, 1.0), - throttle=0.3, - reverse=reverse) - else: - self.client.write_control(**control) diff --git a/Util/PythonClient/test/Basic.py b/Util/PythonClient/test/Basic.py index bd461e252..a1bf10fde 100644 --- a/Util/PythonClient/test/Basic.py +++ b/Util/PythonClient/test/Basic.py @@ -1,40 +1,99 @@ import test +import logging +import random -from lib.carla_util import TestCarlaClientBase -from lib.util import make_client +import carla + +from carla.client import CarlaClient +from carla.settings import CarlaSettings, Camera +from carla.util import make_connection -class UseCase(test.CarlaServerTest): +class _BasicTestBase(test.CarlaServerTest): + def run_carla_client(self, carla_settings, number_of_episodes, number_of_frames): + with make_connection(CarlaClient, self.args.host, self.args.port, timeout=15) as client: + logging.info('CarlaClient connected, running %d episodes', number_of_episodes) + for _ in range(0, number_of_episodes): + carla_settings.randomize_seeds() + carla_settings.randomize_weather() + logging.debug('sending CarlaSettings:\n%s', carla_settings) + logging.info('new episode requested') + scene = client.request_new_episode(carla_settings) + number_of_player_starts = len(scene.player_start_spots) + player_start = random.randint(0, max(0, number_of_player_starts - 1)) + logging.info( + 'start episode at %d/%d player start (%d frames)', + player_start, + number_of_player_starts, + number_of_frames) + client.start_episode(player_start) + autopilot = (random.random() < 0.5) + reverse = (random.random() < 0.2) + for _ in range(0, number_of_frames): + logging.debug('reading measurements...') + measurements, images = client.read_measurements() + number_of_agents = len(measurements.non_player_agents) + expected_number_of_agents = carla_settings.get_number_of_agents() + logging.debug('received data of %d/%d agents', number_of_agents, expected_number_of_agents) + logging.debug('received %d bytes of images', len(images)) + if number_of_agents > expected_number_of_agents or number_of_agents + 10 < expected_number_of_agents: + raise RuntimeError('received data for %d agents, but %d was requested' % (number_of_agents, expected_number_of_agents)) + if len(images) != carla_settings.get_images_byte_size(): + raise RuntimeError('received %d bytes of images, expected %d' % (len(images), carla_settings.get_images_byte_size())) + logging.debug('sending control...') + client.send_control( + steer=random.uniform(-1.0, 1.0), + throttle=0.3, + reverse=reverse, + autopilot=autopilot) + + +class UseCase(_BasicTestBase): def run(self): - with make_client(TestCarlaClientBase, self.args) as client: - for x in xrange(0, 5): - client.start_episode() - client.loop_on_agent_client(iterations=200) + settings = CarlaSettings() + settings.add_camera(Camera('DefaultCamera')) + self.run_carla_client(settings, 5, 200) -class SynchronousMode(test.CarlaServerTest): +class NoCamera(_BasicTestBase): def run(self): - with make_client(TestCarlaClientBase, self.args) as client: - for x in xrange(0, 3): - client.start_episode(SynchronousMode=True) - client.loop_on_agent_client(iterations=200) + settings = CarlaSettings() + self.run_carla_client(settings, 3, 200) -class GetAgentsInfo(test.CarlaServerTest): +class TwoCameras(_BasicTestBase): def run(self): - with make_client(TestCarlaClientBase, self.args) as client: - for x in xrange(0, 3): - client.start_episode( - SynchronousMode=True, - SendNonPlayerAgentsInfo=True, - NumberOfVehicles=60, - NumberOfPedestrians=90) - client.loop_on_agent_client(iterations=100) + settings = CarlaSettings() + settings.add_camera(Camera('DefaultCamera')) + camera2 = Camera('Camera2') + camera2.set(PostProcessing='Depth', CameraFOV=120) + camera2.set_image_size(1924, 1028) + settings.add_camera(camera2) + self.run_carla_client(settings, 3, 100) -class LongEpisode(test.CarlaServerTest): +class SynchronousMode(_BasicTestBase): def run(self): - with make_client(TestCarlaClientBase, self.args) as client: - client.start_episode() - client.loop_on_agent_client(iterations=2000) + settings = CarlaSettings(SynchronousMode=True) + settings.add_camera(Camera('DefaultCamera')) + self.run_carla_client(settings, 3, 200) + + +class GetAgentsInfo(_BasicTestBase): + def run(self): + settings = CarlaSettings() + settings.set( + SynchronousMode=True, + SendNonPlayerAgentsInfo=True, + NumberOfVehicles=60, + NumberOfPedestrians=90) + settings.add_camera(Camera('DefaultCamera')) + self.run_carla_client(settings, 3, 100) + + +class LongEpisode(_BasicTestBase): + def run(self): + settings = CarlaSettings() + settings.add_camera(Camera('DefaultCamera')) + self.run_carla_client(settings, 1, 2000) diff --git a/Util/PythonClient/test_suite.py b/Util/PythonClient/test_suite.py index b54e084be..1b1076a10 100755 --- a/Util/PythonClient/test_suite.py +++ b/Util/PythonClient/test_suite.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # CARLA, Copyright (C) 2017 Computer Vision Center (CVC) - """Test suite for testing CARLAUE4.""" - import argparse import glob import imp @@ -16,15 +14,14 @@ import random import time -from lib.carla_util import TestCarlaClientBase -from lib.util import StopWatch -from lib.util import make_client +import carla +from carla.util import StopWatch from test import CarlaServerTest # Modified by command-line args. -VERBOSE = False +LOGGING_TO_FILE = False # Output. GREEN = '\x1b[0;32m%s\x1b[0m' @@ -43,7 +40,7 @@ FAILED = RED % '[ FAILED ]' def log_test(prep, message, *args): message = prep + ' ' + message % args print(message) - if not VERBOSE: + if LOGGING_TO_FILE: logging.info(message) @@ -72,14 +69,15 @@ def iterate_tests(): folder = os.path.join(os.path.dirname(__file__), 'test') modules = glob.glob(os.path.join(folder, "*.py")) - for module_name in set(strip_ext(m) for m in modules if not m.endswith('__init__.py')): + for module_name in set(strip_ext(m) for m in modules if not m.startswith('_')): logging.debug('parsing module %r', module_name) try: module_info = imp.find_module(module_name, [folder]) # This do a reload if already imported. module = imp.load_module(module_name, *module_info) for name, declaration in inspect.getmembers(module, is_valid): - yield TestProxy(name, declaration, module_name) + if not name.startswith('_'): + yield TestProxy(name, declaration, module_name) except Exception as exception: logging.error('failed to load module %r: %s', module_name, exception) finally: @@ -135,41 +133,48 @@ def main(): argparser.add_argument( '-v', '--verbose', action='store_true', - help='print debug information to console instead of log file') + dest='debug', + help='print debug information') argparser.add_argument( - '-d', '--debug', - action='store_true', - help='print debug extra information to log') + '--log', + metavar='LOG_FILE', + default=None, + help='print output to file') argparser.add_argument( '--host', metavar='H', default='127.0.0.1', - help='IP of the host server (default 127.0.0.1)') + help='IP of the host server (default: 127.0.0.1)') argparser.add_argument( '-p', '--port', metavar='P', default=2000, type=int, - help='TCP port to listen to (default 2000)') + help='TCP port to listen to (default: 2000)') args = argparser.parse_args() - global VERBOSE - VERBOSE = args.verbose + global LOGGING_TO_FILE + LOGGING_TO_FILE = args.log is not None logging_config = { 'format': '%(levelname)s: %(message)s', 'level': logging.DEBUG if args.debug else logging.INFO } - if not args.verbose: - logging_config['filename'] = 'test_suite.log' + if args.log: + logging_config['filename'] = args.log logging_config['filemode'] = 'w+' logging.basicConfig(**logging_config) + logging.info('listening to server %s:%s', args.host, args.port) + print('Running the CARLAUE4 test suite (looks like GTest but is not).') do_the_tests(args) if __name__ == '__main__': - main() + try: + main() + except KeyboardInterrupt: + print('\nCancelled by user. Bye!')