diff --git a/.gitignore b/.gitignore index 8dc2d97e9..fa06fd814 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,9 @@ Util/Build *.workspace *CodeCompletionFolders.txt *CodeLitePreProcessor.txt -*_pb2.py .codelite .tags* .vs __pycache__ +_images core diff --git a/CARLA.sublime-project b/CARLA.sublime-project index e5ffd445d..b5f335ef3 100644 --- a/CARLA.sublime-project +++ b/CARLA.sublime-project @@ -42,7 +42,8 @@ "settings": { "tab_size": 2, - "translate_tabs_to_spaces": true + "translate_tabs_to_spaces": true, + "trim_trailing_white_space_on_save": true }, "build_systems": [ diff --git a/Docs/how_to_run.md b/Docs/how_to_run.md index ea3493488..a374cad2d 100644 --- a/Docs/how_to_run.md +++ b/Docs/how_to_run.md @@ -34,29 +34,30 @@ To run CARLA as server, see ["Running the server"](#running-the-server) below. Running the Python client ------------------------- -Requires Python 3 with some extra modules installed +The "carla" Python module provides a basic API for communicating with the CARLA +server. In the "PythonClient" folder we provide a couple of examples on how to +use this API. We recommend Python 3, but they are also compatible with Python 2. + +The basic functionality requires only the protobuf module to be installed $ sudo apt-get install python3 python3-pip - $ sudo pip3 install protobuf numpy Pillow + $ sudo pip3 install protobuf -A sample Python script is provided at `PythonClient/client_example.py`. The -script is well commented explaining how to use the client API. +However, other operations as handling images require some extra modules, and the +"manual_control.py" example requires pygame -The script can be run and provides basic functionality for controlling the -vehicle and saving images to disk. Run the help command to see options available + $ sudo pip3 install numpy Pillow pygame - $ ./carla_example.py --help +The script "PythonClient/client_example.py" provides basic functionality for +controlling the vehicle and saving images to disk. Run the help command to see +options available -A second Python script is provided at `PythonClient/carla_manual_control.py`. -The script is pygame dependent and serves as an interactive example where the -user controls the car with a keyboard. + $ ./client_example.py --help - $ sudo apt-get install python3-tk - $ sudo pip3 install pygame matplolib +The script "PythonClient/manual_control.py" launches a PyGame window with +several views and allows to control the vehicle using the WASD keys. -Run the help command to see options available - - $ ./carla_manual_control.py --help + $ ./manual_control.py --help Running the server ------------------ diff --git a/Docs/measurements.md b/Docs/measurements.md index 21ad9e0e6..94c031401 100644 --- a/Docs/measurements.md +++ b/Docs/measurements.md @@ -33,7 +33,7 @@ collision_pedestrians | float | Collision intensity with pedestrians. collision_other | float | General collision intensity (everything else but pedestrians and vehicles). intersection_otherlane | float | Percentage of the car invading other lanes. intersection_offroad | float | Percentage of the car off-road. -ai_control | Control | Vehicle's AI control that would apply this frame. +autopilot_control | Control | Vehicle's autopilot control that would apply this frame. ###### Transform @@ -67,10 +67,10 @@ rectangle) against the map image of the city. These images are generated in the editor and serialized for runtime use. You can find them too in the release package under the folder "RoadMaps". -###### AI control +###### Autopilot control -The `ai_control` measurement contains the control values that the in-game AI -would apply if it were controlling the vehicle. +The `autopilot_control` measurement contains the control values that the in-game +autopilot system would apply as if it were controlling the vehicle. This is the same structure used to send the vehicle control to the server. diff --git a/Makefile b/Makefile index b187fd454..41e50596c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ INSTALL_FOLDER=$(CURDIR)/Unreal/CarlaUE4/Plugins/Carla/CarlaServer -PYTHON_CLIENT_FOLDER=$(CURDIR)/Util/TestingClient/test +PYTHON_CLIENT_FOLDER=$(CURDIR)/PythonClient/test BASE_BUILD_FOLDER=$(CURDIR)/Util/Build/carlaserver-build MY_CMAKE_FOLDER=$(CURDIR)/Util/cmake MY_CMAKE_FLAGS=-B"$(BUILD_FOLDER)" -DCMAKE_INSTALL_PREFIX="$(INSTALL_FOLDER)" diff --git a/PythonClient/CarlaSettings.ini b/PythonClient/CarlaSettings.ini deleted file mode 100644 index 4653a01ea..000000000 --- a/PythonClient/CarlaSettings.ini +++ /dev/null @@ -1,36 +0,0 @@ -[CARLA/Server] -UseNetworking=true -SynchronousMode = false -SendNonPlayerAgentsInfo= true - -[CARLA/LevelSettings] -NumberOfVehicles=20 -NumberOfPedestrians=30 -WeatherId=3 - -[CARLA/SceneCapture] -Cameras=RGB,DepthMap,Labels -ImageSizeX=800 -ImageSizeY=600 -CameraFOV=100 -CameraPositionX=200 -CameraPositionY=0 -CameraPositionZ=140 -CameraRotationPitch=-15.0 -CameraRotationRoll=0 -CameraRotationYaw=0 - -[CARLA/SceneCapture/RGB] -PostProcessing=SceneFinal -CameraPositionY=0 -CameraRotationYaw=0 - -[CARLA/SceneCapture/DepthMap] -PostProcessing=Depth -CameraPositionY=0 -CameraRotationYaw=0 - -[CARLA/SceneCapture/Labels] -PostProcessing=SemanticSegmentation -CameraPositionY=0 -CameraRotationYaw=0 diff --git a/PythonClient/carla/__init__.py b/PythonClient/carla/__init__.py index 5c2828245..e69de29bb 100644 --- a/PythonClient/carla/__init__.py +++ b/PythonClient/carla/__init__.py @@ -1,3 +0,0 @@ -from .carla import CARLA -from .protoc import SceneDescription,EpisodeStart,EpisodeReady,Control,Measurements,RequestNewEpisode - diff --git a/PythonClient/carla/carla.py b/PythonClient/carla/carla.py deleted file mode 100644 index 6c130cbf3..000000000 --- a/PythonClient/carla/carla.py +++ /dev/null @@ -1,323 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -from __future__ import print_function - -from .datastream import DataStream -import socket -import time -import os, signal -import sys -import logging -import struct - -import re -from .protoc import * - -from . import socket_util - - - -def get_image_resolution(data): - return int(re.search('[\n\r].*ImageSizeX\s*=([^\n\r]*)', data).group(1)),int(re.search('[\n\r].*ImageSizeY\s*=([^\n\r]*)', data).group(1)) - - -class CARLA(object): - - """ - Normal instanciation of the class, creating also the thread class responsible for receiving data - """ - def __init__(self,host,port): - self._host = host - self._port = port - logging.debug('selected host %s' % host) - logging.debug('selected port %s' % port) - - - self._port_control = self._port +2 - self._port_stream = self._port +1 - - # Default start. Keep it as class param for eventual restart - self._image_x =0 - self._image_y = 0 - - - self._socket_world = socket_util.pers_connect(self._host ,self._port) - print ('Successfully Connected to Carla Server') - logging.debug("Connected to Unreal Server World Socket") - self._socket_stream = 0 - self._socket_control = 0 - self._latest_start = 0 - self._agent_is_running = False - - self._episode_requested = False - - self._data_stream = None - logging.debug("Started Unreal Client") - - - - """ - Starting the Player Agent. The image stream port - and the control port - - Args: - None - Returns: - None - - """ - - - def startAgent(self): - - self._data_stream = DataStream(self._image_x,self._image_y) - - logging.debug("Going to Connect Stream and start thread") - # Perform persistent connections, try up to 10 times - try: - self._socket_stream = socket_util.pers_connect(self._host ,self._port_stream) - except Exception: - logging.exception("Attempts to connect Stream all failed, restart...") - self.restart() - - self._data_stream.start(self._socket_stream) - logging.debug("Streaming Thread Started") - - try: - self._socket_control = socket_util.pers_connect(self._host ,self._port_control) - except Exception: - logging.exception("Attempts to connect Agent all failed, restart ...") - self.restart() - - logging.debug("Control Socket Connected") - self._agent_is_running = True - - def stopAgent(self): - - - logging.debug("Going to Stop thread and Disconect Stream") - self._data_stream.stop() - - logging.debug("Streaming Thread Stoped") - - socket_util.disconnect(self._socket_control) - logging.debug("Control Socket DisConnected") - self._agent_is_running = False - - - - - # This function requests a new episode and send the string containing this episode configuration file - - - def receiveSceneConfiguration(self): - - try: - logging.debug("Reading for the scene configuration") - data = socket_util.get_message(self._socket_world) - - scene = SceneDescription() - scene.ParseFromString(data) - logging.debug("Received Scene Configuration") - - - return scene.player_start_spots - - - except Exception as e: - logging.exception("Server not responing when receiving configuration") - return self.restart() - - - def requestNewEpisode(self,ini_path=None): - - - - if ini_path == None: - ini_file = self._config_path - else: - ini_file = ini_path - self._config_path = ini_path # We just save the last config file in case the client dies - - requestEpisode = RequestNewEpisode() - with open (ini_file, "r") as myfile: - data=myfile.read() - try: - self._image_x,self._image_y = get_image_resolution(data) - except Exception as e: - logging.exception("No image resolution found on config file") - - - logging.debug("Resolution %d , %d",self._image_x,self._image_y) - - logging.debug("Set the Init File") - logging.debug("sent %s" % (data)) - requestEpisode.ini_file = data.encode('utf-8') - try: - socket_util.send_message(self._socket_world,requestEpisode) - except Exception as e: - logging.exception("Server not responding when requesting new episode") - self.restart() - else: - logging.debug("Successfully sent the new episode Request") - - - - if self._agent_is_running: - self.stopAgent() - self._episode_requested = True - - return self.receiveSceneConfiguration() - - def loadConfigurationFile(self,ini_path): - - return self.requestNewEpisode(ini_path) - - - - - def newEpisode(self,start_index): - - # Save the latest new episode positon, just in case of crashes - self._latest_start = start_index - - if not self._episode_requested: - positions = self.requestNewEpisode(self._config_path) - - scene_init = EpisodeStart() - scene_init.player_start_spot_index = start_index - try: - socket_util.send_message(self._socket_world,scene_init) - except Exception: - logging.exception("Server not responding when trying to send episode start confirmation") - self.restart() - else: - logging.debug("Successfully sent the new episode Message") - - episode_ready = EpisodeReady() - episode_ready.ready = False - - try: - - data = socket_util.get_message(self._socket_world) - logging.debug("Got the episode ready message") - episode_ready.ParseFromString(data) - except Exception: - logging.exception("Server not responding when trying to receive episode reading") - self.restart() - else: - logging.debug("Episode is Ready") - - self.startAgent() - self._episode_requested = False - - - - """ Measurements - returns - @game time - @wall time - @player measurements - @non_player_agents : vector with all agents present on the game - @image_data - - """ - - - def getMeasurements(self): - - while True: - try: - meas_dict = self._data_stream.get_the_latest_data() - - - logging.debug("Got A new Measurement") - return meas_dict - except AttributeError: - logging.exception("Unitialized DataStream. Tip: Connect and start an episode before requesting measurements") - return None - except Exception: - logging.exception("Got an empty Measurement") - self.restart() - - - - - - """ Command contains: - Steering: -1 to 1 - Acc : -1 to 1 - """ - - def sendCommand(self,control): - - logging.debug("Send Control Comand : throttle -> %f , steer %f, brake %f, hand_brake %d, gear %d" % (control.throttle,control.steer,control.brake,control.hand_brake,control.reverse)) - try: - socket_util.send_message(self._socket_control,control) - except Exception: - - logging.exception("Problems on sending the commands... restarting") - self.restart() # the mensage is not resend because it likely lost its relevance. - - - - def restart(self): - logging.debug("Trying to close clients") - self.closeConections() - connected = False - if self._data_stream != None: - self._data_stream._running = False - self._agent_is_running = False - while not connected: - try: - logging.debug("Trying to connect to the world thread") - self._socket_world = socket_util.connect(self._host ,self._port) - connected = True - except Exception: - logging.exception("Couldn't connected ... retry in 10 seconds...") - time.sleep(10) - - self._data_stream = DataStream(self._image_x,self._image_y) - - - positions = self.requestNewEpisode() - self.newEpisode(self._latest_start) - logging.debug("restarted the world connection") - return positions - - def stop(self): - self.closeConections() - connected = False - - self._data_stream._running = False - self._data_stream = DataStream(self._image_x,self._image_y) - - def closeConections(self): - - try: - self._socket_world.shutdown(socket.SHUT_RDWR) - self._socket_world.close() - - logging.debug("Close world") - except Exception as ex: - logging.exception("Exception on closing Connections") - - try: - self._socket_stream.shutdown(socket.SHUT_RDWR) - self._socket_stream.close() - logging.debug("Close Stream") - except Exception as ex: - logging.exception("Exception on closing Connections") - - try: - self._socket_control.shutdown(socket.SHUT_RDWR) - self._socket_control.close() - logging.debug("Close Control") - except Exception as ex: - logging.exception("Exception on closing Connections") \ No newline at end of file diff --git a/PythonClient/carla/carla_server_pb2.py b/PythonClient/carla/carla_server_pb2.py new file mode 100644 index 000000000..0ac64474b --- /dev/null +++ b/PythonClient/carla/carla_server_pb2.py @@ -0,0 +1,836 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: carla_server.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='carla_server.proto', + package='carla_server', + syntax='proto3', + serialized_pb=_b('\n\x12\x63\x61rla_server.proto\x12\x0c\x63\x61rla_server\"+\n\x08Vector3D\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"b\n\tTransform\x12(\n\x08location\x18\x01 \x01(\x0b\x32\x16.carla_server.Vector3D\x12+\n\x0borientation\x18\x02 \x01(\x0b\x32\x16.carla_server.Vector3D\"x\n\x07Vehicle\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12*\n\nbox_extent\x18\x02 \x01(\x0b\x32\x16.carla_server.Vector3D\x12\x15\n\rforward_speed\x18\x03 \x01(\x02\"{\n\nPedestrian\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12*\n\nbox_extent\x18\x02 \x01(\x0b\x32\x16.carla_server.Vector3D\x12\x15\n\rforward_speed\x18\x03 \x01(\x02\"\x94\x01\n\x0cTrafficLight\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12/\n\x05state\x18\x02 \x01(\x0e\x32 .carla_server.TrafficLight.State\"\'\n\x05State\x12\t\n\x05GREEN\x10\x00\x12\n\n\x06YELLOW\x10\x01\x12\x07\n\x03RED\x10\x02\"Q\n\x0eSpeedLimitSign\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12\x13\n\x0bspeed_limit\x18\x02 \x01(\x02\"\xe5\x01\n\x05\x41gent\x12\n\n\x02id\x18\x01 \x01(\x07\x12(\n\x07vehicle\x18\x02 \x01(\x0b\x32\x15.carla_server.VehicleH\x00\x12.\n\npedestrian\x18\x03 \x01(\x0b\x32\x18.carla_server.PedestrianH\x00\x12\x33\n\rtraffic_light\x18\x04 \x01(\x0b\x32\x1a.carla_server.TrafficLightH\x00\x12\x38\n\x10speed_limit_sign\x18\x05 \x01(\x0b\x32\x1c.carla_server.SpeedLimitSignH\x00\x42\x07\n\x05\x61gent\"%\n\x11RequestNewEpisode\x12\x10\n\x08ini_file\x18\x01 \x01(\t\"G\n\x10SceneDescription\x12\x33\n\x12player_start_spots\x18\x01 \x03(\x0b\x32\x17.carla_server.Transform\"/\n\x0c\x45pisodeStart\x12\x1f\n\x17player_start_spot_index\x18\x01 \x01(\r\"\x1d\n\x0c\x45pisodeReady\x12\r\n\x05ready\x18\x01 \x01(\x08\"^\n\x07\x43ontrol\x12\r\n\x05steer\x18\x01 \x01(\x02\x12\x10\n\x08throttle\x18\x02 \x01(\x02\x12\r\n\x05\x62rake\x18\x03 \x01(\x02\x12\x12\n\nhand_brake\x18\x04 \x01(\x08\x12\x0f\n\x07reverse\x18\x05 \x01(\x08\"\x8a\x04\n\x0cMeasurements\x12\x1a\n\x12platform_timestamp\x18\x01 \x01(\r\x12\x16\n\x0egame_timestamp\x18\x02 \x01(\r\x12J\n\x13player_measurements\x18\x03 \x01(\x0b\x32-.carla_server.Measurements.PlayerMeasurements\x12.\n\x11non_player_agents\x18\x04 \x03(\x0b\x32\x13.carla_server.Agent\x1a\xc9\x02\n\x12PlayerMeasurements\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12,\n\x0c\x61\x63\x63\x65leration\x18\x03 \x01(\x0b\x32\x16.carla_server.Vector3D\x12\x15\n\rforward_speed\x18\x04 \x01(\x02\x12\x1a\n\x12\x63ollision_vehicles\x18\x05 \x01(\x02\x12\x1d\n\x15\x63ollision_pedestrians\x18\x06 \x01(\x02\x12\x17\n\x0f\x63ollision_other\x18\x07 \x01(\x02\x12\x1e\n\x16intersection_otherlane\x18\x08 \x01(\x02\x12\x1c\n\x14intersection_offroad\x18\t \x01(\x02\x12\x30\n\x11\x61utopilot_control\x18\n \x01(\x0b\x32\x15.carla_server.ControlB\x03\xf8\x01\x01\x62\x06proto3') +) + + + +_TRAFFICLIGHT_STATE = _descriptor.EnumDescriptor( + name='State', + full_name='carla_server.TrafficLight.State', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='GREEN', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='YELLOW', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RED', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=538, + serialized_end=577, +) +_sym_db.RegisterEnumDescriptor(_TRAFFICLIGHT_STATE) + + +_VECTOR3D = _descriptor.Descriptor( + name='Vector3D', + full_name='carla_server.Vector3D', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='x', full_name='carla_server.Vector3D.x', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='y', full_name='carla_server.Vector3D.y', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='z', full_name='carla_server.Vector3D.z', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=36, + serialized_end=79, +) + + +_TRANSFORM = _descriptor.Descriptor( + name='Transform', + full_name='carla_server.Transform', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='location', full_name='carla_server.Transform.location', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='orientation', full_name='carla_server.Transform.orientation', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=81, + serialized_end=179, +) + + +_VEHICLE = _descriptor.Descriptor( + name='Vehicle', + full_name='carla_server.Vehicle', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='transform', full_name='carla_server.Vehicle.transform', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='box_extent', full_name='carla_server.Vehicle.box_extent', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='forward_speed', full_name='carla_server.Vehicle.forward_speed', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=181, + serialized_end=301, +) + + +_PEDESTRIAN = _descriptor.Descriptor( + name='Pedestrian', + full_name='carla_server.Pedestrian', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='transform', full_name='carla_server.Pedestrian.transform', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='box_extent', full_name='carla_server.Pedestrian.box_extent', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='forward_speed', full_name='carla_server.Pedestrian.forward_speed', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=303, + serialized_end=426, +) + + +_TRAFFICLIGHT = _descriptor.Descriptor( + name='TrafficLight', + full_name='carla_server.TrafficLight', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='transform', full_name='carla_server.TrafficLight.transform', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='state', full_name='carla_server.TrafficLight.state', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _TRAFFICLIGHT_STATE, + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=429, + serialized_end=577, +) + + +_SPEEDLIMITSIGN = _descriptor.Descriptor( + name='SpeedLimitSign', + full_name='carla_server.SpeedLimitSign', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='transform', full_name='carla_server.SpeedLimitSign.transform', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='speed_limit', full_name='carla_server.SpeedLimitSign.speed_limit', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=579, + serialized_end=660, +) + + +_AGENT = _descriptor.Descriptor( + name='Agent', + full_name='carla_server.Agent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='carla_server.Agent.id', index=0, + number=1, type=7, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='vehicle', full_name='carla_server.Agent.vehicle', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='pedestrian', full_name='carla_server.Agent.pedestrian', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='traffic_light', full_name='carla_server.Agent.traffic_light', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='speed_limit_sign', full_name='carla_server.Agent.speed_limit_sign', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='agent', full_name='carla_server.Agent.agent', + index=0, containing_type=None, fields=[]), + ], + serialized_start=663, + serialized_end=892, +) + + +_REQUESTNEWEPISODE = _descriptor.Descriptor( + name='RequestNewEpisode', + full_name='carla_server.RequestNewEpisode', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='ini_file', full_name='carla_server.RequestNewEpisode.ini_file', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=894, + serialized_end=931, +) + + +_SCENEDESCRIPTION = _descriptor.Descriptor( + name='SceneDescription', + full_name='carla_server.SceneDescription', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='player_start_spots', full_name='carla_server.SceneDescription.player_start_spots', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=933, + serialized_end=1004, +) + + +_EPISODESTART = _descriptor.Descriptor( + name='EpisodeStart', + full_name='carla_server.EpisodeStart', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='player_start_spot_index', full_name='carla_server.EpisodeStart.player_start_spot_index', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1006, + serialized_end=1053, +) + + +_EPISODEREADY = _descriptor.Descriptor( + name='EpisodeReady', + full_name='carla_server.EpisodeReady', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='ready', full_name='carla_server.EpisodeReady.ready', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1055, + serialized_end=1084, +) + + +_CONTROL = _descriptor.Descriptor( + name='Control', + full_name='carla_server.Control', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='steer', full_name='carla_server.Control.steer', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='throttle', full_name='carla_server.Control.throttle', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='brake', full_name='carla_server.Control.brake', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='hand_brake', full_name='carla_server.Control.hand_brake', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='reverse', full_name='carla_server.Control.reverse', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1086, + serialized_end=1180, +) + + +_MEASUREMENTS_PLAYERMEASUREMENTS = _descriptor.Descriptor( + name='PlayerMeasurements', + full_name='carla_server.Measurements.PlayerMeasurements', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='transform', full_name='carla_server.Measurements.PlayerMeasurements.transform', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='acceleration', full_name='carla_server.Measurements.PlayerMeasurements.acceleration', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='forward_speed', full_name='carla_server.Measurements.PlayerMeasurements.forward_speed', index=2, + number=4, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='collision_vehicles', full_name='carla_server.Measurements.PlayerMeasurements.collision_vehicles', index=3, + number=5, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='collision_pedestrians', full_name='carla_server.Measurements.PlayerMeasurements.collision_pedestrians', index=4, + number=6, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='collision_other', full_name='carla_server.Measurements.PlayerMeasurements.collision_other', index=5, + number=7, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='intersection_otherlane', full_name='carla_server.Measurements.PlayerMeasurements.intersection_otherlane', index=6, + number=8, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='intersection_offroad', full_name='carla_server.Measurements.PlayerMeasurements.intersection_offroad', index=7, + number=9, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='autopilot_control', full_name='carla_server.Measurements.PlayerMeasurements.autopilot_control', index=8, + number=10, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1376, + serialized_end=1705, +) + +_MEASUREMENTS = _descriptor.Descriptor( + name='Measurements', + full_name='carla_server.Measurements', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='platform_timestamp', full_name='carla_server.Measurements.platform_timestamp', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='game_timestamp', full_name='carla_server.Measurements.game_timestamp', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='player_measurements', full_name='carla_server.Measurements.player_measurements', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='non_player_agents', full_name='carla_server.Measurements.non_player_agents', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[_MEASUREMENTS_PLAYERMEASUREMENTS, ], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1183, + serialized_end=1705, +) + +_TRANSFORM.fields_by_name['location'].message_type = _VECTOR3D +_TRANSFORM.fields_by_name['orientation'].message_type = _VECTOR3D +_VEHICLE.fields_by_name['transform'].message_type = _TRANSFORM +_VEHICLE.fields_by_name['box_extent'].message_type = _VECTOR3D +_PEDESTRIAN.fields_by_name['transform'].message_type = _TRANSFORM +_PEDESTRIAN.fields_by_name['box_extent'].message_type = _VECTOR3D +_TRAFFICLIGHT.fields_by_name['transform'].message_type = _TRANSFORM +_TRAFFICLIGHT.fields_by_name['state'].enum_type = _TRAFFICLIGHT_STATE +_TRAFFICLIGHT_STATE.containing_type = _TRAFFICLIGHT +_SPEEDLIMITSIGN.fields_by_name['transform'].message_type = _TRANSFORM +_AGENT.fields_by_name['vehicle'].message_type = _VEHICLE +_AGENT.fields_by_name['pedestrian'].message_type = _PEDESTRIAN +_AGENT.fields_by_name['traffic_light'].message_type = _TRAFFICLIGHT +_AGENT.fields_by_name['speed_limit_sign'].message_type = _SPEEDLIMITSIGN +_AGENT.oneofs_by_name['agent'].fields.append( + _AGENT.fields_by_name['vehicle']) +_AGENT.fields_by_name['vehicle'].containing_oneof = _AGENT.oneofs_by_name['agent'] +_AGENT.oneofs_by_name['agent'].fields.append( + _AGENT.fields_by_name['pedestrian']) +_AGENT.fields_by_name['pedestrian'].containing_oneof = _AGENT.oneofs_by_name['agent'] +_AGENT.oneofs_by_name['agent'].fields.append( + _AGENT.fields_by_name['traffic_light']) +_AGENT.fields_by_name['traffic_light'].containing_oneof = _AGENT.oneofs_by_name['agent'] +_AGENT.oneofs_by_name['agent'].fields.append( + _AGENT.fields_by_name['speed_limit_sign']) +_AGENT.fields_by_name['speed_limit_sign'].containing_oneof = _AGENT.oneofs_by_name['agent'] +_SCENEDESCRIPTION.fields_by_name['player_start_spots'].message_type = _TRANSFORM +_MEASUREMENTS_PLAYERMEASUREMENTS.fields_by_name['transform'].message_type = _TRANSFORM +_MEASUREMENTS_PLAYERMEASUREMENTS.fields_by_name['acceleration'].message_type = _VECTOR3D +_MEASUREMENTS_PLAYERMEASUREMENTS.fields_by_name['autopilot_control'].message_type = _CONTROL +_MEASUREMENTS_PLAYERMEASUREMENTS.containing_type = _MEASUREMENTS +_MEASUREMENTS.fields_by_name['player_measurements'].message_type = _MEASUREMENTS_PLAYERMEASUREMENTS +_MEASUREMENTS.fields_by_name['non_player_agents'].message_type = _AGENT +DESCRIPTOR.message_types_by_name['Vector3D'] = _VECTOR3D +DESCRIPTOR.message_types_by_name['Transform'] = _TRANSFORM +DESCRIPTOR.message_types_by_name['Vehicle'] = _VEHICLE +DESCRIPTOR.message_types_by_name['Pedestrian'] = _PEDESTRIAN +DESCRIPTOR.message_types_by_name['TrafficLight'] = _TRAFFICLIGHT +DESCRIPTOR.message_types_by_name['SpeedLimitSign'] = _SPEEDLIMITSIGN +DESCRIPTOR.message_types_by_name['Agent'] = _AGENT +DESCRIPTOR.message_types_by_name['RequestNewEpisode'] = _REQUESTNEWEPISODE +DESCRIPTOR.message_types_by_name['SceneDescription'] = _SCENEDESCRIPTION +DESCRIPTOR.message_types_by_name['EpisodeStart'] = _EPISODESTART +DESCRIPTOR.message_types_by_name['EpisodeReady'] = _EPISODEREADY +DESCRIPTOR.message_types_by_name['Control'] = _CONTROL +DESCRIPTOR.message_types_by_name['Measurements'] = _MEASUREMENTS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Vector3D = _reflection.GeneratedProtocolMessageType('Vector3D', (_message.Message,), dict( + DESCRIPTOR = _VECTOR3D, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Vector3D) + )) +_sym_db.RegisterMessage(Vector3D) + +Transform = _reflection.GeneratedProtocolMessageType('Transform', (_message.Message,), dict( + DESCRIPTOR = _TRANSFORM, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Transform) + )) +_sym_db.RegisterMessage(Transform) + +Vehicle = _reflection.GeneratedProtocolMessageType('Vehicle', (_message.Message,), dict( + DESCRIPTOR = _VEHICLE, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Vehicle) + )) +_sym_db.RegisterMessage(Vehicle) + +Pedestrian = _reflection.GeneratedProtocolMessageType('Pedestrian', (_message.Message,), dict( + DESCRIPTOR = _PEDESTRIAN, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Pedestrian) + )) +_sym_db.RegisterMessage(Pedestrian) + +TrafficLight = _reflection.GeneratedProtocolMessageType('TrafficLight', (_message.Message,), dict( + DESCRIPTOR = _TRAFFICLIGHT, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.TrafficLight) + )) +_sym_db.RegisterMessage(TrafficLight) + +SpeedLimitSign = _reflection.GeneratedProtocolMessageType('SpeedLimitSign', (_message.Message,), dict( + DESCRIPTOR = _SPEEDLIMITSIGN, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.SpeedLimitSign) + )) +_sym_db.RegisterMessage(SpeedLimitSign) + +Agent = _reflection.GeneratedProtocolMessageType('Agent', (_message.Message,), dict( + DESCRIPTOR = _AGENT, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Agent) + )) +_sym_db.RegisterMessage(Agent) + +RequestNewEpisode = _reflection.GeneratedProtocolMessageType('RequestNewEpisode', (_message.Message,), dict( + DESCRIPTOR = _REQUESTNEWEPISODE, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.RequestNewEpisode) + )) +_sym_db.RegisterMessage(RequestNewEpisode) + +SceneDescription = _reflection.GeneratedProtocolMessageType('SceneDescription', (_message.Message,), dict( + DESCRIPTOR = _SCENEDESCRIPTION, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.SceneDescription) + )) +_sym_db.RegisterMessage(SceneDescription) + +EpisodeStart = _reflection.GeneratedProtocolMessageType('EpisodeStart', (_message.Message,), dict( + DESCRIPTOR = _EPISODESTART, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.EpisodeStart) + )) +_sym_db.RegisterMessage(EpisodeStart) + +EpisodeReady = _reflection.GeneratedProtocolMessageType('EpisodeReady', (_message.Message,), dict( + DESCRIPTOR = _EPISODEREADY, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.EpisodeReady) + )) +_sym_db.RegisterMessage(EpisodeReady) + +Control = _reflection.GeneratedProtocolMessageType('Control', (_message.Message,), dict( + DESCRIPTOR = _CONTROL, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Control) + )) +_sym_db.RegisterMessage(Control) + +Measurements = _reflection.GeneratedProtocolMessageType('Measurements', (_message.Message,), dict( + + PlayerMeasurements = _reflection.GeneratedProtocolMessageType('PlayerMeasurements', (_message.Message,), dict( + DESCRIPTOR = _MEASUREMENTS_PLAYERMEASUREMENTS, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Measurements.PlayerMeasurements) + )) + , + DESCRIPTOR = _MEASUREMENTS, + __module__ = 'carla_server_pb2' + # @@protoc_insertion_point(class_scope:carla_server.Measurements) + )) +_sym_db.RegisterMessage(Measurements) +_sym_db.RegisterMessage(Measurements.PlayerMeasurements) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\370\001\001')) +# @@protoc_insertion_point(module_scope) diff --git a/PythonClient/carla/client.py b/PythonClient/carla/client.py new file mode 100644 index 000000000..748200928 --- /dev/null +++ b/PythonClient/carla/client.py @@ -0,0 +1,190 @@ +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""CARLA Client.""" + +import struct + +from contextlib import contextmanager + +from . import sensor +from . import settings +from . import tcp +from . import util + +try: + from . import carla_server_pb2 as carla_protocol +except ImportError: + raise RuntimeError('cannot import "carla_server_pb2.py", run the protobuf compiler to generate this file') + + +VehicleControl = carla_protocol.Control + + +@contextmanager +def make_carla_client(host, world_port, timeout=15): + """Context manager for creating and connecting a CarlaClient.""" + with util.make_connection(CarlaClient, host, world_port, timeout) as client: + yield client + + +class CarlaClient(object): + """The CARLA client. Manages communications with the CARLA server.""" + + 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) + self._current_settings = None + self._is_episode_requested = False + self._sensor_names = [] + + def connect(self, connection_attempts=10): + """ + Try to establish a connection to a CARLA server at the given host:port. + """ + self._world_client.connect(connection_attempts) + + def disconnect(self): + """Disconnect from server.""" + self._control_client.disconnect() + self._stream_client.disconnect() + self._world_client.disconnect() + + def connected(self): + """Return whether there is an active connection.""" + return self._world_client.connected() + + def load_settings(self, carla_settings): + """ + Load new settings and request a new episode based on these settings. + carla_settings object must be convertible to a str holding the contents + of a CarlaSettings.ini file. + + Return a protobuf object holding the scene description. + """ + self._current_settings = carla_settings + return self._request_new_episode(carla_settings) + + + 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 + "load_settings". + + The new episode is started based on the last settings loaded by + "load_settings". + + This function waits until the server answers with an EpisodeReady. + """ + if self._current_settings is None: + raise RuntimeError('no settings loaded, cannot start episode') + + # if no new settings are loaded, request new episode with previous + if not self._is_episode_requested: + self._request_new_episode(self._current_settings) + + try: + 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() + # Set again the status for no episode requested + finally: + self._is_episode_requested = False + + def read_data(self): + """ + Read the data sent from the server this frame. The episode must be + started. Return a pair containing the protobuf object containing the + measurements followed by the raw data of the sensors. + """ + # 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 sensor data. + raw_sensor_data = self._stream_client.read() + return pb_message, self._parse_raw_sensor_data(raw_sensor_data) + + def send_control(self, *args, **kwargs): + """ + Send the VehicleControl to be applied this frame. + + If synchronous mode was requested, the server will pause the simulation + until this message is received. + """ + if isinstance(args[0] if args else None, carla_protocol.Control): + pb_message = args[0] + else: + 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) + self._control_client.write(pb_message.SerializeToString()) + + def _request_new_episode(self, carla_settings): + """ + Internal function to request a new episode. Prepare the client for a new + episode by disconnecting agent clients. + """ + # 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") + self._sensor_names = settings._get_sensor_names(carla_settings) + self._is_episode_requested = True + return pb_message + + def _parse_raw_sensor_data(self, raw_data): + """Return a dict of {'sensor_name': sensor_data, ...}.""" + return dict((name, data) for name, data in zip( + self._sensor_names, + self._iterate_sensor_data(raw_data))) + + @staticmethod + def _iterate_sensor_data(raw_data): + # At this point the only sensors available are images, the raw_data + # consists of images only. + image_types = ['None', 'SceneFinal', 'Depth', 'SemanticSegmentation'] + gettype = lambda id: image_types[id] if len(image_types) > id else 'Unknown' + getval = lambda index: struct.unpack('. - -"""Basic CARLA client example.""" - - -import sys -is_py2 = sys.version[0] == '2' -if is_py2: - import Queue as Queue -else: - import queue as Queue - -from threading import Thread -import time -import random -from PIL import Image -from .socket_util import * -import io -import sys -import numpy as np -import logging - -from .protoc import * - - - - - -def threaded(fn): - def wrapper(*args, **kwargs): - thread = Thread(target=fn, args=args, kwargs=kwargs) - thread.setDaemon(True) - thread.start() - - return thread - return wrapper - - - -class DataStream(object): - - def __init__(self,image_x=640,image_y=480): - self._data_buffer = Queue.Queue(1) - self._image_x = image_x - self._image_y = image_y - - self._socket = 0 - self._running = True - - - - def _read_image(self,imagedata,pointer): - - - width = struct.unpack('. + +""" +Handy conversions for CARLA images. + +The functions here are provided for real-time display, if you want to save the +converted images, save the images from Python without conversion and convert +them afterwards with the C++ implementation at "Util/ImageConverter" as it +provides considerably better performance. +""" + + +try: + import numpy +except ImportError: + raise RuntimeError('cannot import numpy, make sure numpy package is installed') + + +from . import sensor + + +def to_bgra_array(image): + """Convert a CARLA raw image to a BGRA numpy array.""" + if not isinstance(image, sensor.Image): + raise ValueError("Argument must be a carla.sensor.Image") + array = numpy.frombuffer(image.raw_data, dtype=numpy.dtype("uint8")) + array = numpy.reshape(array, (image.height, image.width, 4)) + return array + + +def to_rgb_array(image): + """Convert a CARLA raw image to a RGB numpy array.""" + array = to_bgra_array(image) + # Convert BGRA to RGB. + array = array[:, :, :3] + array = array[:, :, ::-1] + return array + + +def labels_to_array(image): + """ + Convert an image containing CARLA semantic segmentation labels to a 2D array + containing the label of each pixel. + """ + return to_bgra_array(image)[:, :, 2] + + +def labels_to_cityscapes_palette(image): + """ + Convert an image containing CARLA semantic segmentation labels to + Cityscapes palette. + """ + classes = { + 0: [0, 0, 0], # None + 1: [70, 70, 70], # Buildings + 2: [190, 153, 153], # Fences + 3: [72, 0, 90], # Other + 4: [220, 20, 60], # Pedestrians + 5: [153, 153, 153], # Poles + 6: [157, 234, 50], # RoadLines + 7: [128, 64, 128], # Roads + 8: [244, 35, 232], # Sidewalks + 9: [107, 142, 35], # Vegetation + 10: [0, 0, 255], # Vehicles + 11: [102, 102, 156], # Walls + 12: [220, 220, 0] # TrafficSigns + } + array = labels_to_array(image) + result = numpy.zeros((array.shape[0], array.shape[1], 3)) + for key, value in classes.items(): + result[numpy.where(array == key)] = value + return result + + +def depth_to_array(image): + """ + Convert an image containing CARLA encoded depth-map to a 2D array containing + the depth value of each pixel normalized between [0.0, 1.0]. + """ + array = to_bgra_array(image) + array = array.astype(numpy.float32) + # Apply (R + G * 256 + B * 256 * 256) / (256 * 256 * 256 - 1). + grayscale = numpy.dot(array[:, :, :3], [256.0 * 256.0, 256.0, 1.0]) + grayscale /= (256.0 * 256.0 * 256.0 - 1.0) + return grayscale + + +def depth_to_logarithmic_grayscale(image): + """ + Convert an image containing CARLA encoded depth-map to a logarithmic + grayscale image array. + """ + grayscale = depth_to_array(image) + # Convert to logarithmic depth. + logdepth = numpy.ones(grayscale.shape) + (numpy.log(grayscale) / 5.70378) + logdepth = numpy.clip(logdepth, 0.0, 1.0) + logdepth *= 255.0 + # Expand to three colors. + return numpy.repeat(logdepth[:, :, numpy.newaxis], 3, axis=2) diff --git a/PythonClient/carla/planner/Town01.png b/PythonClient/carla/planner/Town01.png new file mode 100644 index 000000000..817035353 Binary files /dev/null and b/PythonClient/carla/planner/Town01.png differ diff --git a/PythonClient/carla/planner/Town01.txt b/PythonClient/carla/planner/Town01.txt new file mode 100644 index 000000000..7e527b550 --- /dev/null +++ b/PythonClient/carla/planner/Town01.txt @@ -0,0 +1,49 @@ +0.0,0.0,-3811.000000 +0.000000,0.000000,0.0 +1.000000,1.000000,1.000000 +-1643.022,-1643.022,0.000 +49, 41 +0,0 0,40 40 +0,40 0,0 40 +48,40 41,40 7 +41,40 48,40 7 +48,0 48,40 40 +48,40 48,0 40 +0,0 11,0 11 +11,0 0,0 11 +41,0 48,0 7 +48,0 41,0 7 +41,40 11,40 30 +11,40 41,40 30 +41,0 41,7 7 +41,7 41,0 7 +11,40 0,40 11 +0,40 11,40 11 +11,0 19,0 8 +19,0 11,0 8 +11,40 11,24 16 +11,24 11,40 16 +41,24 41,40 16 +41,40 41,24 16 +11,24 11,16 8 +11,16 11,24 8 +41,24 11,24 30 +11,24 41,24 30 +41,16 41,24 8 +41,24 41,16 8 +11,16 11,7 9 +11,7 11,16 9 +41,16 11,16 30 +11,16 41,16 30 +41,7 41,16 9 +41,16 41,7 9 +11,7 11,0 7 +11,0 11,7 7 +41,7 19,7 22 +19,7 41,7 22 +19,0 41,0 22 +41,0 19,0 22 +19,7 11,7 8 +11,7 19,7 8 +19,0 19,7 7 +19,7 19,0 7 diff --git a/PythonClient/carla/planner/Town02.png b/PythonClient/carla/planner/Town02.png new file mode 100644 index 000000000..2f2b18da7 Binary files /dev/null and b/PythonClient/carla/planner/Town02.png differ diff --git a/PythonClient/carla/planner/Town02.txt b/PythonClient/carla/planner/Town02.txt new file mode 100644 index 000000000..21ab5fe3c --- /dev/null +++ b/PythonClient/carla/planner/Town02.txt @@ -0,0 +1,37 @@ +544.000000,-10748.000000,-22.000000 +0.000000,0.000000,0.000000 +1.000000,1.000000,1.000000 +-1643.022,-1643.022,0.000 +25, 25 +0,10 0,24 14 +0,24 0,10 14 +24,24 6,24 18 +6,24 24,24 18 +24,0 24,10 10 +24,10 24,0 10 +0,0 24,0 24 +24,0 0,0 24 +0,10 0,0 10 +0,0 0,10 10 +24,10 24,16 6 +24,16 24,10 6 +0,10 6,10 6 +6,10 0,10 6 +6,24 0,24 6 +0,24 6,24 6 +6,10 17,10 11 +17,10 6,10 11 +6,24 6,16 8 +6,16 6,24 8 +24,16 24,24 8 +24,24 24,16 8 +6,16 6,10 6 +6,10 6,16 6 +24,16 17,16 7 +17,16 24,16 7 +17,16 6,16 11 +6,16 17,16 11 +17,10 24,10 7 +24,10 17,10 7 +17,16 17,10 6 +17,10 17,16 6 diff --git a/Util/TestingClient/__init__.py b/PythonClient/carla/planner/__init__.py similarity index 100% rename from Util/TestingClient/__init__.py rename to PythonClient/carla/planner/__init__.py diff --git a/PythonClient/carla/planner/map.py b/PythonClient/carla/planner/map.py new file mode 100644 index 000000000..1d172bad8 --- /dev/null +++ b/PythonClient/carla/planner/map.py @@ -0,0 +1,140 @@ +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""Class used for operating the city map.""" + +import math +import os + +try: + import numpy as np +except ImportError: + raise RuntimeError('cannot import numpy, make sure numpy package is installed') + +try: + from PIL import Image +except ImportError: + raise RuntimeError('cannot import PIL, make sure pillow package is installed') + + +def string_to_node(string): + vec = string.split(',') + return (int(vec[0]), int(vec[1])) + + +def string_to_floats(string): + vec = string.split(',') + return (float(vec[0]), float(vec[1]), float(vec[2])) + + +class CarlaMap(object): + def __init__(self, city): + dir_path = os.path.dirname(__file__) + city_file = os.path.join(dir_path, city + '.txt') + city_map_file = os.path.join(dir_path, city + '.png') + + with open(city_file, 'r') as file: + + linewordloffset = file.readline() + # The offset of the world from the zero coordinates ( The + # coordinate we consider zero) + self.worldoffset = string_to_floats(linewordloffset) + + lineworldangles = file.readline() + self.angles = string_to_floats(lineworldangles) + + self.worldrotation = np.array([ + [math.cos(math.radians(self.angles[2])), -math.sin(math.radians(self.angles[2])), 0.0], + [math.sin(math.radians(self.angles[2])), math.cos(math.radians(self.angles[2])), 0.0], + [0.0, 0.0, 1.0]]) + + # Ignore for now, these are offsets for map coordinates and scale + # (not used). + _ = file.readline() + linemapoffset = file.readline() + + # The offset of the map zero coordinate. + self.mapoffset = string_to_floats(linemapoffset) + + # the graph resolution. + linegraphres = file.readline() + self.resolution = string_to_node(linegraphres) + + # The number of game units per pixel. + self.pixel_density = 16.43 + self.map_image = Image.open(city_map_file) + self.map_image.load() + self.map_image = np.asarray(self.map_image, dtype="int32") + + def draw_position_on_map(self, position, color, size=20): + position = self.get_position_on_map([position.x, position.y, position.z]) + for i in range(0, size): + self.map_image[int(position[1]), int(position[0])] = color + self.map_image[int(position[1]) + i, int(position[0])] = color + self.map_image[int(position[1]), int(position[0]) + i] = color + self.map_image[int(position[1]) - i, int(position[0])] = color + self.map_image[int(position[1]), int(position[0]) - i] = color + self.map_image[int(position[1]) + i, int(position[0]) + i] = color + self.map_image[int(position[1]) - i, int(position[0]) - i] = color + self.map_image[int(position[1]) + i, int(position[0]) - i] = color + self.map_image[int(position[1]) - i, int(position[0]) + i] = color + + def get_map(self, size=None): + if size is not None: + img = Image.fromarray(self.map_image.astype(np.uint8)) + img = img.resize((size[1], size[0]), Image.ANTIALIAS) + img.load() + return np.fliplr(np.asarray(img, dtype="int32")) + return np.fliplr(self.map_image) + + def get_position_on_map(self, world): + """Get the position on the map for a certain world position.""" + relative_location = [] + pixel = [] + + rotation = np.array([world[0], world[1], world[2]]) + rotation = rotation.dot(self.worldrotation) + + relative_location.append(rotation[0] + self.worldoffset[0] - self.mapoffset[0]) + relative_location.append(rotation[1] + self.worldoffset[1] - self.mapoffset[1]) + relative_location.append(rotation[2] + self.worldoffset[2] - self.mapoffset[2]) + + pixel.append(math.floor(relative_location[0] / float(self.pixel_density))) + pixel.append(math.floor(relative_location[1] / float(self.pixel_density))) + + return pixel + + def get_position_on_world(self, pixel): + """Get world position of a certain map position.""" + relative_location = [] + world_vertex = [] + relative_location.append(pixel[0] * self.pixel_density) + relative_location.append(pixel[1] * self.pixel_density) + + world_vertex.append(relative_location[0] + self.mapoffset[0] - self.worldoffset[0]) + world_vertex.append(relative_location[1] + self.mapoffset[1] - self.worldoffset[1]) + world_vertex.append(22) # Z does not matter for now. + + return world_vertex + + def get_lane_orientation(self, world): + """Get the lane orientation of a certain world position.""" + relative_location = [] + pixel = [] + rotation = np.array([world[0], world[1], world[2]]) + rotation = rotation.dot(self.worldrotation) + + relative_location.append(rotation[0] + self.worldoffset[0] - self.mapoffset[0]) + relative_location.append(rotation[1] + self.worldoffset[1] - self.mapoffset[1]) + relative_location.append(rotation[2] + self.worldoffset[2] - self.mapoffset[2]) + + pixel.append(math.floor(relative_location[0] / float(self.pixel_density))) + pixel.append(math.floor(relative_location[1] / float(self.pixel_density))) + + ori = self.map_image[int(pixel[1]), int(pixel[0]), 2] + ori = ((float(ori) / 255.0)) * 2 * math.pi + + return (-math.cos(ori), -math.sin(ori)) diff --git a/PythonClient/carla/protoc/__init__.py b/PythonClient/carla/protoc/__init__.py deleted file mode 100644 index 9650c1101..000000000 --- a/PythonClient/carla/protoc/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .carla_server_pb2 import SceneDescription,EpisodeStart,EpisodeReady,Control,Measurements,RequestNewEpisode - diff --git a/PythonClient/carla/sensor.py b/PythonClient/carla/sensor.py new file mode 100644 index 000000000..e8cccb9e9 --- /dev/null +++ b/PythonClient/carla/sensor.py @@ -0,0 +1,122 @@ +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""CARLA sensors.""" + + +import os + + +# ============================================================================== +# -- Sensor -------------------------------------------------------------------- +# ============================================================================== + + +class Sensor(object): + """ + Base class for sensor descriptions. Used to add sensors to CarlaSettings. + """ + pass + + +class Camera(Sensor): + """ + Camera description. This class can be added to a CarlaSettings object to add + a camera to the player vehicle. + """ + + def __init__(self, name, **kwargs): + self.CameraName = name + self.PostProcessing = 'SceneFinal' + self.ImageSizeX = 800 + self.ImageSizeY = 600 + self.CameraFOV = 90 + self.CameraPositionX = 140 + self.CameraPositionY = 0 + self.CameraPositionZ = 140 + self.CameraRotationPitch = 0 + self.CameraRotationRoll = 0 + self.CameraRotationYaw = 0 + self.set(**kwargs) + + def set(self, **kwargs): + for key, value in kwargs.items(): + if not hasattr(self, key): + raise ValueError('CarlaSettings.Camera: no key named %r' % key) + setattr(self, key, value) + + def set_image_size(self, pixels_x, pixels_y): + self.ImageSizeX = pixels_x + self.ImageSizeY = pixels_y + + def set_position(self, x, y, z): + self.CameraPositionX = x + self.CameraPositionY = y + self.CameraPositionZ = z + + def set_rotation(self, pitch, roll, yaw): + self.CameraRotationPitch = pitch + self.CameraRotationRoll = roll + self.CameraRotationYaw = yaw + + +# ============================================================================== +# -- SensorData ---------------------------------------------------------------- +# ============================================================================== + + +class SensorData(object): + """Base class for sensor data returned from the server.""" + pass + + +class Image(SensorData): + """Data generated by a Camera.""" + + def __init__(self, width, height, image_type, raw_data): + assert len(raw_data) == 4 * width * height + self.width = width + self.height = height + self.type = image_type + self.raw_data = raw_data + self._converted_data = None + + @property + def data(self): + """ + Lazy initialization for data property, stores converted data in its + default format. + """ + if self._converted_data is None: + from . import image_converter + + if self.type == 'Depth': + self._converted_data = image_converter.depth_to_array(self) + elif self.type == 'SemanticSegmentation': + self._converted_data = image_converter.labels_to_array(self) + else: + self._converted_data = image_converter.to_rgb_array(self) + return self._converted_data + + def save_to_disk(self, filename): + """Save this image to disk (requires PIL installed).""" + try: + from PIL import Image as PImage + except ImportError: + raise RuntimeError('cannot import PIL, make sure pillow package is installed') + + image = PImage.frombytes( + mode='RGBA', + size=(self.width, self.height), + data=self.raw_data, + decoder_name='raw') + b, g, r, a = image.split() + image = PImage.merge("RGB", (r, g, b)) + + folder = os.path.dirname(filename) + if not os.path.isdir(folder): + os.makedirs(folder) + image.save(filename) diff --git a/Util/TestingClient/carla/settings.py b/PythonClient/carla/settings.py similarity index 61% rename from Util/TestingClient/carla/settings.py rename to PythonClient/carla/settings.py index dd6892939..0fcc98767 100644 --- a/Util/TestingClient/carla/settings.py +++ b/PythonClient/carla/settings.py @@ -6,53 +6,32 @@ """CARLA Settings""" -import configparser import io import random +import sys + + +if sys.version_info >= (3, 0): + + from configparser import ConfigParser + +else: + + from ConfigParser import RawConfigParser as ConfigParser + + +from . import sensor as carla_sensor 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' - self.ImageSizeX = 800 - self.ImageSizeY = 600 - self.CameraFOV = 90 - self.CameraPositionX = 15 - self.CameraPositionY = 0 - self.CameraPositionZ = 123 - self.CameraRotationPitch = 8 - self.CameraRotationRoll = 0 - self.CameraRotationYaw = 0 - self.set(**kwargs) - - def set(self, **kwargs): - for key, value in kwargs.items(): - if not hasattr(self, key): - raise ValueError('CarlaSettings.Camera: no key named %r' % key) - setattr(self, key, value) - - def set_image_size(self, pixels_x, pixels_y): - self.ImageSizeX = pixels_x - self.ImageSizeY = pixels_y - - def set_position(self, x, y, z): - self.CameraPositionX = x - self.CameraPositionY = y - self.CameraPositionZ = z - - def set_rotation(self, pitch, roll, yaw): - self.CameraRotationPitch = pitch - self.CameraRotationRoll = roll - self.CameraRotationYaw = yaw - - class CarlaSettings(object): - """CARLA settings object. Convertible to str as CarlaSettings.ini.""" + """ + The CarlaSettings object controls the settings of an episode. The __str__ + method retrieves an str with a CarlaSettings.ini file contents. + """ + def __init__(self, **kwargs): # [CARLA/Server] self.SynchronousMode = True @@ -74,25 +53,28 @@ class CarlaSettings(object): raise ValueError('CarlaSettings: no key named %r' % key) setattr(self, key, value) - def get_number_of_agents(self): - if not self.SendNonPlayerAgentsInfo: - return 0 - return self.NumberOfVehicles + self.NumberOfPedestrians - def randomize_seeds(self): + """ + Randomize the seeds of the new episode's pseudo-random number + generators. + """ self.SeedVehicles = random.getrandbits(16) self.SeedPedestrians = random.getrandbits(16) def randomize_weather(self): + """Randomized the WeatherId.""" self.WeatherId = random.randint(0, MAX_NUMBER_OF_WEATHER_IDS) - def add_camera(self, camera): - if not isinstance(camera, Camera): - raise ValueError('Can only add instances of carla_settings.Camera') - self._cameras.append(camera) + def add_sensor(self, sensor): + """Add a sensor to the player vehicle (see sensor.py).""" + if isinstance(sensor, carla_sensor.Camera): + self._cameras.append(sensor) + else: + raise ValueError('Sensor not supported') def __str__(self): - ini = configparser.ConfigParser() + """Converts this object to an INI formatted string.""" + ini = ConfigParser() ini.optionxform=str S_SERVER = 'CARLA/Server' S_LEVEL = 'CARLA/LevelSettings' @@ -131,6 +113,32 @@ class CarlaSettings(object): 'CameraRotationRoll', 'CameraRotationYaw']) - text = io.StringIO() + if sys.version_info >= (3, 0): + text = io.StringIO() + else: + text = io.BytesIO() + ini.write(text) return text.getvalue().replace(' = ', '=') + + +def _get_sensor_names(settings): + """ + Return a list with the names of the sensors defined in the settings object. + The settings object can be a CarlaSettings or an INI formatted string. + """ + if isinstance(settings, CarlaSettings): + return [camera.CameraName for camera in settings._cameras] + ini = ConfigParser() + if sys.version_info >= (3, 0): + ini.readfp(io.StringIO(settings)) + else: + ini.readfp(io.BytesIO(settings)) + + section_name = 'CARLA/SceneCapture' + option_name = 'Cameras' + + if ini.has_section(section_name) and ini.has_option(section_name, option_name): + cameras = ini.get(section_name, option_name) + return cameras.split(',') + return [] diff --git a/PythonClient/carla/socket_util.py b/PythonClient/carla/socket_util.py deleted file mode 100644 index c8e561a2a..000000000 --- a/PythonClient/carla/socket_util.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -import binascii -import socket -import struct -import logging -import time -import select - -def int2bytes(i): - hex_string = '%x' % i - n = len(hex_string) - return binascii.unhexlify(hex_string.zfill(n + (n & 1))) - -def bytes2int(str): - return int(str.encode('hex'), 16) - - -def connect(host,port): - - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(100) - except socket.error: - logginge.exception("Error Creating Socket World") - - sock.connect((host,port)) - logging.debug("connected to %s port %d" %(host,port)) - - return sock - -def pers_connect(host,port): - for attempt in range(10): - try: - sock = connect(host,port) - return sock - except Exception as e: - logging.exception("Failure on connection") - time.sleep(1) - continue - else: - break - raise socket.error - - -def disconnect(sock): - - sock.shutdown(socket.SHUT_RDWR) - sock.close() - - -def send_message(sock, message): - """ Send a serialized message (protobuf Message interface) - to a socket, prepended by its length packed in 4 - bytes (big endian). - """ - - s = message.SerializeToString() - packed_len =struct.pack(' 0: - - sock.setblocking(0) - ready = select.select([sock], [], [], 3) - if ready[0]: - try: - data = sock.recv(n) - if data == b'': - raise RuntimeError('unexpected connection close') - buf += data - n -= len(data) - except socket.error: - raise socket.error - - sock.setblocking(1) - - - return buf - - diff --git a/PythonClient/carla/tcp.py b/PythonClient/carla/tcp.py new file mode 100644 index 000000000..b9ca34b6a --- /dev/null +++ b/PythonClient/carla/tcp.py @@ -0,0 +1,98 @@ +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""Basic TCP client.""" + +import logging +import socket +import struct +import time + +class TCPConnectionError(Exception): + pass + + +class TCPClient(object): + """ + Basic networking client for TCP connections. Errors occurred during + networking operations are raised as TCPConnectionError. + + Received messages are expected to be prepended by a int32 defining the + message size. Messages are sent following this convention. + """ + + def __init__(self, host, port, timeout): + self._host = host + self._port = port + self._timeout = timeout + self._socket = None + self._logprefix = '(%s:%s) ' % (self._host, self._port) + + def connect(self, connection_attempts=10): + """Try to establish a connection to the given host:port.""" + connection_attempts = max(1, connection_attempts) + error = None + for attempt in range(1, connection_attempts + 1): + try: + self._socket = socket.create_connection(address=(self._host, self._port), timeout=self._timeout) + self._socket.settimeout(self._timeout) + logging.debug(self._logprefix + 'connected') + return + except Exception as exception: + error = exception + logging.debug(self._logprefix + 'connection attempt %d: %s', attempt, error) + time.sleep(1) + continue + self._reraise_exception_as_tcp_error('failed to connect', error) + + def disconnect(self): + """Disconnect any active connection.""" + if self._socket is not None: + logging.debug(self._logprefix + 'disconnecting') + self._socket.close() + self._socket = None + + def connected(self): + """Return whether there is an active connection.""" + return self._socket is not None + + def write(self, message): + """Send message to the server.""" + if self._socket is None: + raise TCPConnectionError(self._logprefix + 'not connected') + header = struct.pack(' 0: + try: + data = self._socket.recv(length) + except Exception as exception: + self._reraise_exception_as_tcp_error('failed to read data', exception) + if not data: + raise TCPConnectionError(self._logprefix + 'connection closed') + buf += data + length -= len(data) + return buf + + def _reraise_exception_as_tcp_error(self, message, exception): + raise TCPConnectionError('%s%s: %s' % (self._logprefix, message, exception)) diff --git a/Util/TestingClient/carla/util.py b/PythonClient/carla/util.py similarity index 54% rename from Util/TestingClient/carla/util.py rename to PythonClient/carla/util.py index 606d41401..02fb4db8e 100644 --- a/Util/TestingClient/carla/util.py +++ b/PythonClient/carla/util.py @@ -5,17 +5,14 @@ # For a copy, see . import datetime +import sys from contextlib import contextmanager -def to_hex_str(header): - return ':'.join('{:02x}'.format(ord(c)) for c in header) - - @contextmanager def make_connection(client_type, *args, **kwargs): - """Context manager to create and connect a networking object.""" + """Context manager to create and connect a networking client object.""" client = None try: client = client_type(*args, **kwargs) @@ -36,3 +33,29 @@ class StopWatch(object): def milliseconds(self): return 1000.0 * (self.end - self.start).total_seconds() + + +def to_hex_str(header): + return ':'.join('{:02x}'.format(ord(c)) for c in header) + + +if sys.version_info >= (3, 3): + + import shutil + + def print_over_same_line(text): + terminal_width = shutil.get_terminal_size((80, 20)).columns + empty_space = max(0, terminal_width - len(text)) + sys.stdout.write('\r' + text + empty_space * ' ') + sys.stdout.flush() + +else: + + # Workaround for older Python versions. + def print_over_same_line(text): + line_length = max(print_over_same_line._last_line_length, len(text)) + empty_space = max(0, line_length - len(text)) + sys.stdout.write('\r' + text + empty_space * ' ') + sys.stdout.flush() + print_over_same_line._last_line_length = line_length + print_over_same_line._last_line_length = 0 diff --git a/PythonClient/carla_example.py b/PythonClient/carla_example.py deleted file mode 100755 index d3e8ce05e..000000000 --- a/PythonClient/carla_example.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -"""Basic CARLA client example.""" - - - -from __future__ import print_function -# General Imports -import numpy as np -from PIL import Image -import random -import time -import sys -import argparse -import logging -import os - -# Carla imports, Everything can be imported directly from "carla" package - -from carla import CARLA -from carla import Control -from carla import Measurements - -# Constant that set how offten the episodes are reseted - -RESET_FREQUENCY = 100 - -""" -Print function, prints all the measurements saving -the images into a folder. WARNING just prints the first BGRA image -Args: - param1: The measurements dictionary to be printed - param2: The iterations - -Returns: - None -Raises: - None -""" - -def print_pack(measurements,i,write_images): - - if write_images: - image_result = Image.fromarray( measurements['BGRA'][0]) - - b, g, r,a = image_result.split() - image_result = Image.merge("RGBA", (r, g, b,a)) - - if not os.path.exists('images'): - os.mkdir('images') - image_result.save('images/image' + str(i) + '.png') - - - print ('Pack ',i) - print (' Wall Time: ',measurements['WallTime']) - print (' Game Time: ',measurements['GameTime']) - print (' Player Measurements ') - - print (' Position: (%f,%f,%f)' % (measurements['PlayerMeasurements'].\ - transform.location.x,measurements['PlayerMeasurements'].transform.location.y,\ - measurements['PlayerMeasurements'].transform.location.z )) - print (' Orientation: (%f,%f,%f)' % (measurements['PlayerMeasurements'].\ - transform.orientation.x,measurements['PlayerMeasurements'].transform.orientation.y,\ - measurements['PlayerMeasurements'].transform.orientation.z )) - - print (' Acceleration: (%f,%f,%f)' % (measurements['PlayerMeasurements'].\ - acceleration.x,measurements['PlayerMeasurements'].acceleration.y,measurements['PlayerMeasurements'].acceleration.z )) - print (' Speed: ',measurements['PlayerMeasurements'].forward_speed) - print (' Collision Vehicles (Acum. Impact): ',measurements['PlayerMeasurements'].collision_vehicles) - print (' Collision Pedestrians (Acum. Impact): ',measurements['PlayerMeasurements'].collision_pedestrians) - print (' Collision Other (Acum. Impact): ',measurements['PlayerMeasurements'].collision_other) - print (' Intersection Opposite Lane (% Volume): ',measurements['PlayerMeasurements'].intersection_otherlane) - print (' Intersection Opposite Sidewalk (% Volume): ',measurements['PlayerMeasurements'].intersection_offroad) - - - print (' ',len(measurements['Agents']),' Agents (Positions not printed)') - print (' ',end='') - for agent in measurements['Agents']: - - if agent.HasField('vehicle'): - print ('Car',end='') - - elif agent.HasField('pedestrian'): - print ('Pedestrian',end='') - - elif agent.HasField('traffic_light'): - print ('Traffic Light',end='') - - - elif agent.HasField('speed_limit_sign'): - print ('Speed Limit Sign',end='') - print(',',end='') - print('') - - - -def use_example(ini_file,port = 2000, host ='127.0.0.1',print_measurements =False,images_to_disk=False): - - # We assume the CARLA server is already waiting for a client to connect at - # host:port. To create a connection we can use the CARLA - # constructor, it creates a CARLA client object and starts the - # connection. It will throw an exception if something goes wrong. - - carla =CARLA(host,port) - - """ As a first step, Carla must have a configuration file loaded. This will load a map in the server - with the properties specified by the ini file. It returns all the posible starting positions on the map - in a vector. - """ - positions = carla.loadConfigurationFile(ini_file) - - """ - Ask Server for a new episode starting on position of index zero in the positions vector - """ - carla.newEpisode(0) - - capture = time.time() - # General iteratior - i = 1 - # Iterator that will go over the positions on the map after each episode - iterator_start_positions = 1 - - while True: - try: - """ - User get the measurements dictionary from the server. - Measurements contains: - * WallTime: Current time on Wall from server machine. - * GameTime: Current time on Game. Restarts at every episode - * PlayerMeasurements : All information and events that happens to player - * Agents : All non-player agents present on the map information such as cars positions, traffic light states - * BRGA : BGRA optical images - * Depth : Depth Images - * Labels : Images containing the semantic segmentation. NOTE: the semantic segmentation must be - previously activated on the server. See documentation for that. - - """ - - - measurements = carla.getMeasurements() - - # Print all the measurements... Will write images to disk - if print_measurements: - print_pack(measurements,i,images_to_disk) - - """ - Sends a control command to the server - This control structue contains the following fields: - * throttle : goes from 0 to -1 - * steer : goes from -1 to 1 - * brake : goes from 0 to 1 - * hand_brake : Activate or desactivate the Hand Brake. - * reverse: Activate or desactive the reverse gear. - - """ - - control = Control() - control.throttle = 0.9 - control.steer = (random.random() * 2) - 1 - - carla.sendCommand(control) - - - - i+=1 - - - if i % RESET_FREQUENCY ==0: - - print ('Fps for this episode : ',(1.0/((time.time() -capture)/100.0))) - - - """ - Starts another new episode, the episode will have the same configuration as the previous - one. In order to change configuration, the loadConfigurationFile could be called at any - time. - """ - if iterator_start_positions < len(positions): - carla.newEpisode(iterator_start_positions) - iterator_start_positions+=1 - else : - carla.newEpisode(0) - iterator_start_positions = 1 - - print("Now Starting on Position: ",iterator_start_positions-1) - capture = time.time() - - - except Exception as e: - - logging.exception('Exception raised to the top') - time.sleep(1) - - - - -if __name__ == "__main__" : - parser = argparse.ArgumentParser(description='Run the carla client example that connects to a server') - parser.add_argument('host', metavar='HOST', type=str, help='host to connect to') - parser.add_argument('port', metavar='PORT', type=int, help='port to connect to') - - parser.add_argument("-c", "--config", help="the path for the server ini file that the client sends",type=str,default="CarlaSettings.ini") - - - parser.add_argument("-l", "--log", help="activate the log file",action="store_true") - parser.add_argument("-lv", "--log_verbose", help="activate log and put the log file to screen",action="store_true") - - parser.add_argument("-pm", "--print", help=" prints the game measurements",action="store_true") - parser.add_argument( - '-i', '--images-to-disk', - action='store_true', - help='save images to disk') - - - args = parser.parse_args() - if args.log or args.log_verbose: - LOG_FILENAME = 'log_manual_control.log' - logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) - - if args.log_verbose: # set of functions to put the logging to screen - - - root = logging.getLogger() - root.setLevel(logging.DEBUG) - ch = logging.StreamHandler(sys.stdout) - ch.setLevel(logging.DEBUG) - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - root.addHandler(ch) - else: - sys.tracebacklimit=0 # Avoid huge exception messages out of debug mode - - - - use_example(args.config,port=args.port, host=args.host,print_measurements=args.print,images_to_disk= args.images_to_disk) diff --git a/PythonClient/carla_manual_control.py b/PythonClient/carla_manual_control.py deleted file mode 100755 index 11e50e0db..000000000 --- a/PythonClient/carla_manual_control.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -# Keyboard controlling for carla. Please refer to carla_use_example for a -# simpler and more documented example. - -""" -Welcome to CARLA manual control. - -Use ARROWS or WASD keys for control. - - W : throttle - S : brake - AD : steer - Q : toggle reverse - Space : hand-brake - - R : restart level - -STARTING in a moment... -""" - -from __future__ import print_function - -import argparse -import logging -import sys -import time - -import numpy as np - -import matplotlib.pyplot as plt - -import pygame -from pygame.locals import * - -from carla import CARLA -from carla import Control - - - -def join_classes(labels_image): - classes_join = { - 0: [0, 0, 0], # None - 1: [70, 70, 70], # Buildings - 2: [190, 153, 153], # Fences - 3: [72, 0, 90], # Other - 4: [220, 20, 60], # Pedestrians - 5: [153, 153, 153], # Poles - 6: [157, 234, 50], # RoadLines - 7: [128, 64, 128], # Roads - 8: [244, 35, 232], # Sidewalks - 9: [107, 142, 35], # Vegetation - 10: [0, 0, 255], # Vehicles - 11: [102, 102, 156], # Walls - 12: [220, 220, 0] # TrafficSigns - } - compressed_labels_image = np.zeros((labels_image.shape[0], labels_image.shape[1], 3)) - for (key, value) in classes_join.items(): - compressed_labels_image[np.where(labels_image == key)] = value - return compressed_labels_image - - -def grayscale_colormap(img, colormap): - """Make colormaps from grayscale.""" - cmap = plt.get_cmap(colormap) - rgba_img = cmap(img) - rgb_img = np.delete(rgba_img, 3, 2) - return rgb_img - - -def convert_depth(depth): - """Convert depth to human readable format.""" - depth = depth.astype(np.float32) - gray_depth = ((depth[:, :, 0] + # Red - depth[:, :, 1] * 256.0 + # Green - depth[:, :, 2] * 256.0 * 256.0)) # Blue - gray_depth /= (256.0 * 256.0 * 256.0 - 1) - color_depth = grayscale_colormap(gray_depth, 'jet') * 255 - return color_depth - -if sys.version_info >= (3, 3): - - import shutil - - def get_terminal_width(): - return shutil.get_terminal_size((80, 20)).columns - -else: - - def get_terminal_width(): - return 120 - - -class App(object): - def __init__( - self, - port=2000, - host='127.0.0.1', - config='./CarlaSettings.ini', - resolution=(800, 600), - verbose=True): - self._running = True - self._display_surf = None - self.port = port - self.host = host - self.config = config - self.verbose = verbose - self.resolution = resolution - self.size = self.weight, self.height = resolution - self.reverse_gear = False - - def on_init(self): - pygame.init() - print(__doc__) - time.sleep(3) - self._display_surf = pygame.display.set_mode( - self.size, pygame.HWSURFACE | pygame.DOUBLEBUF) - logging.debug('Started the PyGame Library') - self._running = True - self.step = 0 - self.prev_step = 0 - self.prev_time = time.time() - - self.carla = CARLA(self.host, self.port) - - positions = self.carla.loadConfigurationFile(self.config) - self.num_pos = len(positions) - print("Staring Episode on Position ", self.num_pos) - self.carla.newEpisode(np.random.randint(self.num_pos)) - self.prev_restart_time = time.time() - - def on_event(self, event): - if event.type == pygame.QUIT: - self._running = False - - def on_loop(self): - self.step += 1 - keys = pygame.key.get_pressed() - restart = False - - control = Control() - - pressed_keys = [] - if keys[K_LEFT] or keys[K_a]: - control.steer = -1.0 - pressed_keys.append('left') - if keys[K_RIGHT] or keys[K_d]: - control.steer = 1.0 - pressed_keys.append('right') - if keys[K_UP] or keys[K_w]: - control.throttle = 1.0 - pressed_keys.append('reverse' if self.reverse_gear else 'forward') - if keys[K_DOWN] or keys[K_s]: - control.brake = 1.0 - pressed_keys.append('brake') - if keys[K_SPACE]: - control.hand_brake = True - pressed_keys.append('hand-brake') - if keys[K_q]: - self.reverse_gear = not self.reverse_gear - pressed_keys.append('toggle reverse') - if keys[K_r]: - pressed_keys.append('reset') - if time.time() - self.prev_restart_time > 2.: - self.prev_restart_time = time.time() - restart = True - - if time.time() - self.prev_restart_time < 2.: - control.throttle = 0.0 - control.steer = 0.0 - - control.reverse = self.reverse_gear - self.carla.sendCommand(control) - - measurements = self.carla.getMeasurements() - pack = measurements['PlayerMeasurements'] - self.img_vec = measurements['BGRA'] - self.depth_vec = measurements['Depth'] - self.labels_vec = measurements['Labels'] - - if time.time() - self.prev_time > 1.: - message = 'Step {step} ({fps:.1f} FPS): ' - message += '{speed:.2f} km/h, ' - message += '{other_lane:.0f}% other lane, {offroad:.0f}% off-road' - message += ': pressed [%s]' % ', '.join(pressed_keys) - - message = message.format( - step=self.step, - fps=float(self.step - self.prev_step) / (time.time() - self.prev_time), - speed=pack.forward_speed, - other_lane=100 * pack.intersection_otherlane, - offroad=100 * pack.intersection_offroad) - - empty_space = max(0, get_terminal_width() - len(message)) - sys.stdout.write('\r' + message + empty_space * ' ') - sys.stdout.flush() - - self.prev_step = self.step - self.prev_time = time.time() - - if restart: - print('\n *** RESTART *** \n') - player_pos = np.random.randint(self.num_pos) - print(' Player pos %d \n' % (player_pos)) - self.carla.newEpisode(player_pos) - - - def on_render(self): - """ - The render method plots the First RGB, the First Depth and First - Semantic Segmentation Camera. - """ - - auxImgResolution = (320, 180) - auxImgYPos = self.height - auxImgResolution[1] - 25 - nImages = 2 - f = ((self.weight - nImages * auxImgResolution[0]) / (nImages + 1) + auxImgResolution[0]) - x_pos = (self.weight - nImages * auxImgResolution[0]) / (nImages + 1) - - if self.img_vec: - self.img_vec[0] = self.img_vec[0][:, :, :3] - self.img_vec[0] = self.img_vec[0][:, :, ::-1] - surface = pygame.surfarray.make_surface( - np.transpose(self.img_vec[0], (1, 0, 2))) - self._display_surf.blit(surface, (0, 0)) - - if self.depth_vec: - self.depth_vec[0] = self.depth_vec[0][:, :, :3] - self.depth_vec[0] = self.depth_vec[0][:, :, ::-1] - self.depth_vec[0] = convert_depth(self.depth_vec[0]) - surface = pygame.surfarray.make_surface( - np.transpose(self.depth_vec[0], (1, 0, 2))) - # Resize image - auxImgXPos = (self.weight / 4) - (auxImgResolution[0] / 2) - surface = pygame.transform.scale(surface, auxImgResolution) - self._display_surf.blit(surface, (x_pos, auxImgYPos)) - x_pos += f - - if self.labels_vec: - self.labels_vec[0] = join_classes(self.labels_vec[0][:, :, 2]) - surface = pygame.surfarray.make_surface( - np.transpose(self.labels_vec[0], (1, 0, 2))) - # Resize image - auxImgXPos = ((self.weight / 4) * 3) - (auxImgResolution[0] / 2) - surface = pygame.transform.scale(surface, auxImgResolution) - self._display_surf.blit(surface, (x_pos, auxImgYPos)) - x_pos += f - - pygame.display.flip() - - def on_cleanup(self): - self.carla.closeConections() - pygame.quit() - - def on_execute(self): - if self.on_init() == False: - self._running = False - - while(self._running): - try: - - for event in pygame.event.get(): - self.on_event(event) - self.on_loop() - self.on_render() - - except Exception as e: - logging.exception(e) - self._running = False - break - - self.on_cleanup() - - -def main(): - parser = argparse.ArgumentParser( - description='Run the carla client manual that connects to CARLA server') - parser.add_argument( - 'host', - metavar='HOST', - type=str, - help='host to connect to') - parser.add_argument( - 'port', - metavar='PORT', - type=int, - help='port to connect to') - - parser.add_argument( - "-c", - "--config", - help="the path for the server .ini config file that the client sends", - type=str, - default="./CarlaSettings.ini") - - parser.add_argument( - "-l", - "--log", - help="activate the log file", - action="store_true") - parser.add_argument( - "-lv", - "--log_verbose", - help="put the log file to screen", - action="store_true") - args = parser.parse_args() - - if args.log or args.log_verbose: - LOG_FILENAME = 'log_manual_control.log' - logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) - if args.log_verbose: # set of functions to put the logging to screen - - root = logging.getLogger() - root.setLevel(logging.DEBUG) - ch = logging.StreamHandler(sys.stdout) - ch.setLevel(logging.DEBUG) - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - root.addHandler(ch) - - theApp = App(port=args.port, host=args.host, config=args.config) - theApp.on_execute() - - -if __name__ == '__main__': - - try: - main() - except KeyboardInterrupt: - print('\nCancelled by user. Bye!') diff --git a/PythonClient/client_example.py b/PythonClient/client_example.py new file mode 100755 index 000000000..a5453ac31 --- /dev/null +++ b/PythonClient/client_example.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""Basic CARLA client example.""" + +from __future__ import print_function + +import argparse +import logging +import random +import sys +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=False, + 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.') + return + + except TCPConnectionError as error: + logging.error(error) + time.sleep(1) + except Exception as exception: + logging.exception(exception) + sys.exit(1) + + +if __name__ == '__main__': + + try: + main() + except KeyboardInterrupt: + print('\nCancelled by user. Bye!') diff --git a/PythonClient/manual_control.py b/PythonClient/manual_control.py new file mode 100755 index 000000000..7aed9b754 --- /dev/null +++ b/PythonClient/manual_control.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +# Keyboard controlling for carla. Please refer to client_example for a simpler +# and more documented example. + +""" +Welcome to CARLA manual control. + +Use ARROWS or WASD keys for control. + + W : throttle + S : brake + AD : steer + Q : toggle reverse + Space : hand-brake + + R : restart level + +STARTING in a moment... +""" + +from __future__ import print_function + +import argparse +import logging +import random +import sys +import time + +try: + import pygame + from pygame.locals import * +except ImportError: + raise RuntimeError('cannot import pygame, make sure pygame package is installed') + +try: + import numpy as np +except ImportError: + raise RuntimeError('cannot import numpy, make sure numpy package is installed') + +from carla import image_converter +from carla import sensor +from carla.client import make_carla_client, VehicleControl +from carla.planner.map import CarlaMap +from carla.settings import CarlaSettings +from carla.tcp import TCPConnectionError +from carla.util import print_over_same_line + + +WINDOW_WIDTH = 800 +WINDOW_HEIGHT = 600 +MINI_WINDOW_WIDTH = 320 +MINI_WINDOW_HEIGHT = 180 + + +def make_carla_settings(): + """Make a CarlaSettings object with the settings we need.""" + settings = CarlaSettings() + settings.set( + SynchronousMode=False, + NumberOfVehicles=15, + NumberOfPedestrians=30, + WeatherId=random.choice([1, 3, 7, 8, 14])) + settings.randomize_seeds() + camera0 = sensor.Camera('CameraRGB') + camera0.set_image_size(WINDOW_WIDTH, WINDOW_HEIGHT) + camera0.set_position(200, 0, 140) + camera0.set_rotation(0.0, 0.0, 0.0) + settings.add_sensor(camera0) + camera1 = sensor.Camera('CameraDepth', PostProcessing='Depth') + camera1.set_image_size(MINI_WINDOW_WIDTH, MINI_WINDOW_HEIGHT) + camera1.set_position(200, 0, 140) + camera1.set_rotation(0.0, 0.0, 0.0) + settings.add_sensor(camera1) + camera2 = sensor.Camera('CameraSemSeg', PostProcessing='SemanticSegmentation') + camera2.set_image_size(MINI_WINDOW_WIDTH, MINI_WINDOW_HEIGHT) + camera2.set_position(200, 0, 140) + camera2.set_rotation(0.0, 0.0, 0.0) + settings.add_sensor(camera2) + return settings + + +class Timer(object): + def __init__(self): + self.step = 0 + self._lap_step = 0 + self._lap_time = time.time() + + def tick(self): + self.step += 1 + + def lap(self): + self._lap_step = self.step + self._lap_time = time.time() + + def ticks_per_second(self): + return float(self.step - self._lap_step) / self.elapsed_seconds_since_lap() + + def elapsed_seconds_since_lap(self): + return time.time() - self._lap_time + + +class CarlaGame(object): + def __init__(self, carla_client, city_name=None): + self.client = carla_client + self._timer = None + self._display = None + self._main_image = None + self._mini_view_image1 = None + self._mini_view_image2 = None + self._map_view = None + self._is_on_reverse = False + self._city_name = city_name + self._map = CarlaMap(city_name) if city_name is not None else None + + def execute(self): + """Launch the PyGame.""" + pygame.init() + self._initialize_game() + try: + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return + self._on_loop() + self._on_render() + finally: + pygame.quit() + + def _initialize_game(self): + if self._city_name is not None: + self._display = pygame.display.set_mode( + (WINDOW_WIDTH * 2, WINDOW_HEIGHT), + pygame.HWSURFACE | pygame.DOUBLEBUF) + else: + self._display = pygame.display.set_mode( + (WINDOW_WIDTH, WINDOW_HEIGHT), + pygame.HWSURFACE | pygame.DOUBLEBUF) + + logging.debug('pygame started') + self._on_new_episode() + + def _on_new_episode(self): + scene = self.client.load_settings(make_carla_settings()) + number_of_player_starts = len(scene.player_start_spots) + player_start = np.random.randint(number_of_player_starts) + print('Starting new episode...') + self.client.start_episode(player_start) + self._timer = Timer() + self._is_on_reverse = False + + def _on_loop(self): + self._timer.tick() + + measurements, sensor_data = self.client.read_data() + + self._main_image = sensor_data['CameraRGB'] + self._mini_view_image1 = sensor_data['CameraDepth'] + self._mini_view_image2 = sensor_data['CameraSemSeg'] + + # Print measurements every second. + if self._timer.elapsed_seconds_since_lap() > 1.0: + if self._city_name is not None: + # Function to get car position on map. + map_position = self._map.get_position_on_map([ + measurements.player_measurements.transform.location.x, + measurements.player_measurements.transform.location.y, + measurements.player_measurements.transform.location.z]) + # Function to get orientation of the road car is in. + lane_orientation = self._map.get_lane_orientation([ + measurements.player_measurements.transform.location.x, + measurements.player_measurements.transform.location.y, + measurements.player_measurements.transform.location.z]) + + self._print_player_measurements_map( + measurements.player_measurements, + map_position, + lane_orientation) + else: + self._print_player_measurements(measurements.player_measurements) + + # Plot position on the map as well. + if self._city_name is not None: + self._map.draw_position_on_map( + measurements.player_measurements.transform.location, + [255, 0, 0, 255]) + self._map_view = self._map.get_map([WINDOW_WIDTH, WINDOW_HEIGHT]) + + self._timer.lap() + + control = self._get_keyboard_control(pygame.key.get_pressed()) + + if control is None: + self._on_new_episode() + else: + self.client.send_control(control) + + def _get_keyboard_control(self, keys): + """ + Return a VehicleControl message based on the pressed keys. Return None + if a new episode was requested. + """ + if keys[K_r]: + return None + control = VehicleControl() + if keys[K_LEFT] or keys[K_a]: + control.steer = -1.0 + if keys[K_RIGHT] or keys[K_d]: + control.steer = 1.0 + if keys[K_UP] or keys[K_w]: + control.throttle = 1.0 + if keys[K_DOWN] or keys[K_s]: + control.brake = 1.0 + if keys[K_SPACE]: + control.hand_brake = True + if keys[K_q]: + self._is_on_reverse = not self._is_on_reverse + control.reverse = self._is_on_reverse + return control + + def _print_player_measurements_map( + self, + player_measurements, + map_position, + lane_orientation): + message = 'Step {step} ({fps:.1f} FPS): ' + message += 'Map Position ({map_x:.1f},{map_y:.1f}) Lane Orientation ({ori_x:.1f},{ori_y:.1f}) ' + message += '{speed:.2f} km/h, ' + message += '{other_lane:.0f}% other lane, {offroad:.0f}% off-road' + message = message.format( + map_x=map_position[0], + map_y=map_position[1], + ori_x=lane_orientation[0], + ori_y=lane_orientation[1], + step=self._timer.step, + fps=self._timer.ticks_per_second(), + speed=player_measurements.forward_speed, + other_lane=100 * player_measurements.intersection_otherlane, + offroad=100 * player_measurements.intersection_offroad) + print_over_same_line(message) + + def _print_player_measurements(self, player_measurements): + message = 'Step {step} ({fps:.1f} FPS): ' + message += '{speed:.2f} km/h, ' + message += '{other_lane:.0f}% other lane, {offroad:.0f}% off-road' + message = message.format( + step=self._timer.step, + fps=self._timer.ticks_per_second(), + speed=player_measurements.forward_speed, + other_lane=100 * player_measurements.intersection_otherlane, + offroad=100 * player_measurements.intersection_offroad) + print_over_same_line(message) + + def _on_render(self): + gap_x = (WINDOW_WIDTH - 2 * MINI_WINDOW_WIDTH) / 3 + mini_image_y = WINDOW_HEIGHT - MINI_WINDOW_HEIGHT - gap_x + + if self._main_image is not None: + array = image_converter.to_rgb_array(self._main_image) + surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) + self._display.blit(surface, (0, 0)) + + if self._mini_view_image1 is not None: + array = image_converter.depth_to_logarithmic_grayscale(self._mini_view_image1) + surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) + self._display.blit(surface, (gap_x, mini_image_y)) + + if self._mini_view_image2 is not None: + array = image_converter.labels_to_cityscapes_palette( + self._mini_view_image2) + surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) + self._display.blit( + surface, (2 * gap_x + MINI_WINDOW_WIDTH, mini_image_y)) + + if self._map_view is not None: + array = self._map_view + array = array[:, :, :3] + surface = pygame.surfarray.make_surface(array) + self._display.blit(surface, (WINDOW_WIDTH, 0)) + + pygame.display.flip() + + +def main(): + argparser = argparse.ArgumentParser( + description='CARLA Manual Control Client') + 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( + '-m', '--map-name', + metavar='M', + default=None, + help='plot the map of the current city (needs to match active map in server, options: Town01 or Town02)') + 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) + + print(__doc__) + + while True: + try: + + with make_carla_client(args.host, args.port) as client: + game = CarlaGame(client, args.map_name) + game.execute() + break + + except TCPConnectionError as error: + logging.error(error) + time.sleep(1) + except Exception as exception: + logging.exception(exception) + sys.exit(1) + + +if __name__ == '__main__': + + try: + main() + except KeyboardInterrupt: + print('\nCancelled by user. Bye!') diff --git a/Util/TestingClient/carla/__init__.py b/PythonClient/test/__init__.py similarity index 100% rename from Util/TestingClient/carla/__init__.py rename to PythonClient/test/__init__.py diff --git a/Util/TestingClient/carla/console.py b/PythonClient/test/console.py similarity index 93% rename from Util/TestingClient/carla/console.py rename to PythonClient/test/console.py index 60eae2ed0..48b7805ff 100644 --- a/Util/TestingClient/carla/console.py +++ b/PythonClient/test/console.py @@ -15,8 +15,9 @@ import threading import time -from .client import CarlaClient -from .settings import CarlaSettings, Camera +from carla.client import CarlaClient +from carla.sensor import Camera, Image +from carla.settings import CarlaSettings class _Control(object): @@ -55,7 +56,7 @@ def get_default_carla_settings(args): NumberOfVehicles=20, NumberOfPedestrians=40, WeatherId=1) - settings.add_camera(Camera('Camera1')) + settings.add_sensor(Camera('Camera1')) return str(settings) @@ -88,7 +89,6 @@ class CarlaClientConsole(cmd.Cmd): self.done = False self.thread.start() - def cleanup(self): self.do_disconnect() self.done = True @@ -129,7 +129,7 @@ class CarlaClientConsole(cmd.Cmd): self.control = _Control() if not self.client.connected(): self.client.connect() - self.client.request_new_episode(self.settings) + self.client.load_settings(self.settings) self.client.start_episode(0) logging.info('new episode started') except Exception as exception: @@ -165,11 +165,12 @@ class CarlaClientConsole(cmd.Cmd): filename = '_images/console/camera_{:0>3d}/image_{:0>8d}.png' while not self.done: try: - measurements, images = self.client.read_measurements() + measurements, sensor_data = self.client.read_data() if self.print_measurements: print(measurements) if self.args.images_to_disk: + images = [x for x in sensor_data.values() if isinstance(x, Image)] for n, image in enumerate(images): path = filename.format(n, measurements.game_timestamp) image.save_to_disk(path) diff --git a/Util/TestingClient/test/test_client.py b/PythonClient/test/test_client.py similarity index 90% rename from Util/TestingClient/test/test_client.py rename to PythonClient/test/test_client.py index d41ce7e80..96d0d0340 100755 --- a/Util/TestingClient/test/test_client.py +++ b/PythonClient/test/test_client.py @@ -20,11 +20,13 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import carla from carla.client import CarlaClient -from carla.console import CarlaClientConsole -from carla.settings import CarlaSettings, Camera +from carla.sensor import Camera, Image +from carla.settings import CarlaSettings from carla.tcp import TCPClient from carla.util import make_connection +import console + def run_carla_client(args): with make_connection(CarlaClient, args.host, args.port, timeout=15) as client: @@ -39,12 +41,12 @@ def run_carla_client(args): settings.randomize_seeds() camera = Camera('DefaultCamera') camera.set_image_size(300, 200) # Do not change this, hard-coded in test. - settings.add_camera(camera) + settings.add_sensor(camera) logging.debug('sending CarlaSettings:\n%s', settings) logging.info('new episode requested') - scene = client.request_new_episode(settings) + scene = client.load_settings(settings) number_of_player_starts = len(scene.player_start_spots) player_start = random.randint(0, max(0, number_of_player_starts - 1)) @@ -56,12 +58,13 @@ def run_carla_client(args): client.start_episode(player_start) - use_ai_control = (random.random() < 0.5) + use_autopilot_control = (random.random() < 0.5) reverse = (random.random() < 0.2) for frame in range(0, frames_per_episode): logging.debug('reading measurements...') - measurements, images = client.read_measurements() + measurements, sensor_data = client.read_data() + images = [x for x in sensor_data.values() if isinstance(x, Image)] logging.debug('received data of %d agents', len(measurements.non_player_agents)) assert len(images) == 1 @@ -71,8 +74,8 @@ def run_carla_client(args): images[0].save_to_disk(filename.format(episode, frame)) logging.debug('sending control...') - control = measurements.player_measurements.ai_control - if not use_ai_control: + control = measurements.player_measurements.autopilot_control + if not use_autopilot_control: control.steer = random.uniform(-1.0, 1.0) control.throttle = 0.3 control.hand_brake = False @@ -140,7 +143,7 @@ def main(): logging.info('listening to server %s:%s', args.host, args.port) if args.console: - cmd = CarlaClientConsole(args) + cmd = console.CarlaClientConsole(args) try: cmd.cmdloop() finally: diff --git a/Util/TestingClient/test/test_suite.py b/PythonClient/test/test_suite.py similarity index 92% rename from Util/TestingClient/test/test_suite.py rename to PythonClient/test/test_suite.py index 08f1e7658..b1efbd4d9 100755 --- a/Util/TestingClient/test/test_suite.py +++ b/PythonClient/test/test_suite.py @@ -23,6 +23,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '.')) import carla +from carla.tcp import TCPConnectionError from carla.util import StopWatch from unit_tests import CarlaServerTest @@ -99,14 +100,20 @@ def run_test(test, args): logging.error('exception instantiating %r: %s', test.name, exception) return False log_test(RUN, test.name) - try: - timer = StopWatch() - result = test.run() - timer.stop() - except Exception as exception: - timer.stop() - logging.error('exception: %s', exception) - result = False + while True: + try: + timer = StopWatch() + result = test.run() + timer.stop() + break + except TCPConnectionError as error: + logging.error(error) + time.sleep(1) + except Exception as exception: + timer.stop() + logging.exception('exception: %s', exception) + result = False + break log_test(OK if result else FAILED, '%s (%d ms)', test.name, timer.milliseconds()) return result @@ -118,9 +125,6 @@ def do_the_tests(args): failed = [] log_test(SEP0, 'Running %d tests.', len(tests)) for test in tests: - if succeeded or failed: - logging.info('waiting for the server to be ready again') - time.sleep(7) if run_test(test, args): succeeded.append(test) else: diff --git a/Util/TestingClient/test/unit_tests/Basic.py b/PythonClient/test/unit_tests/Basic.py similarity index 80% rename from Util/TestingClient/test/unit_tests/Basic.py rename to PythonClient/test/unit_tests/Basic.py index 2b05d8632..fc9e2323a 100644 --- a/Util/TestingClient/test/unit_tests/Basic.py +++ b/PythonClient/test/unit_tests/Basic.py @@ -4,20 +4,21 @@ # This work is licensed under the terms of the MIT license. # For a copy, see . -import unit_tests - import logging import random +import unit_tests + import carla from carla.client import CarlaClient -from carla.settings import CarlaSettings, Camera +from carla.sensor import Camera, Image +from carla.settings import CarlaSettings from carla.util import make_connection class _BasicTestBase(unit_tests.CarlaServerTest): - def run_carla_client(self, carla_settings, number_of_episodes, number_of_frames, use_ai_control=None): + def run_carla_client(self, carla_settings, number_of_episodes, number_of_frames, use_autopilot_control=None): 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): @@ -25,7 +26,7 @@ class _BasicTestBase(unit_tests.CarlaServerTest): carla_settings.randomize_weather() logging.debug('sending CarlaSettings:\n%s', carla_settings) logging.info('new episode requested') - scene = client.request_new_episode(carla_settings) + scene = client.load_settings(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( @@ -34,20 +35,21 @@ class _BasicTestBase(unit_tests.CarlaServerTest): number_of_player_starts, number_of_frames) client.start_episode(player_start) - if use_ai_control is None: - use_ai_control = (random.random() < 0.5) + if use_autopilot_control is None: + use_autopilot_control = (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() + measurements, sensor_data = client.read_data() + images = [x for x in sensor_data.values() if isinstance(x, Image)] number_of_agents = len(measurements.non_player_agents) logging.debug('received data of %d agents', number_of_agents) logging.debug('received %d images', len(images)) if len(images) != len(carla_settings._cameras): raise RuntimeError('received %d images, expected %d' % (len(images), len(carla_settings._cameras))) logging.debug('sending control...') - control = measurements.player_measurements.ai_control - if not use_ai_control: + control = measurements.player_measurements.autopilot_control + if not use_autopilot_control: control.steer = random.uniform(-1.0, 1.0) control.throttle = 0.3 control.hand_brake = False @@ -63,7 +65,7 @@ class _BasicTestBase(unit_tests.CarlaServerTest): class UseCase(_BasicTestBase): def run(self): settings = CarlaSettings() - settings.add_camera(Camera('DefaultCamera')) + settings.add_sensor(Camera('DefaultCamera')) self.run_carla_client(settings, 5, 200) @@ -76,18 +78,18 @@ class NoCamera(_BasicTestBase): class TwoCameras(_BasicTestBase): def run(self): settings = CarlaSettings() - settings.add_camera(Camera('DefaultCamera')) + settings.add_sensor(Camera('DefaultCamera')) camera2 = Camera('Camera2') camera2.set(PostProcessing='Depth', CameraFOV=120) camera2.set_image_size(1924, 1028) - settings.add_camera(camera2) + settings.add_sensor(camera2) self.run_carla_client(settings, 3, 100) class SynchronousMode(_BasicTestBase): def run(self): settings = CarlaSettings(SynchronousMode=True) - settings.add_camera(Camera('DefaultCamera')) + settings.add_sensor(Camera('DefaultCamera')) self.run_carla_client(settings, 3, 200) @@ -99,12 +101,12 @@ class GetAgentsInfo(_BasicTestBase): SendNonPlayerAgentsInfo=True, NumberOfVehicles=60, NumberOfPedestrians=90) - settings.add_camera(Camera('DefaultCamera')) + settings.add_sensor(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, use_ai_control=True) + settings.add_sensor(Camera('DefaultCamera')) + self.run_carla_client(settings, 1, 2000, use_autopilot_control=True) diff --git a/Util/TestingClient/test/unit_tests/__init__.py b/PythonClient/test/unit_tests/__init__.py similarity index 100% rename from Util/TestingClient/test/unit_tests/__init__.py rename to PythonClient/test/unit_tests/__init__.py diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaServer.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaServer.cpp index 4f273633d..4627c010d 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaServer.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaServer.cpp @@ -311,11 +311,11 @@ CarlaServer::ErrorCode CarlaServer::SendMeasurements( Set(player.collision_other, PlayerState.GetCollisionIntensityOther()); Set(player.intersection_otherlane, PlayerState.GetOtherLaneIntersectionFactor()); Set(player.intersection_offroad, PlayerState.GetOffRoadIntersectionFactor()); - Set(player.ai_control.steer, PlayerState.GetSteer()); - Set(player.ai_control.throttle, PlayerState.GetThrottle()); - Set(player.ai_control.brake, PlayerState.GetBrake()); - Set(player.ai_control.hand_brake, PlayerState.GetHandBrake()); - Set(player.ai_control.reverse, PlayerState.GetCurrentGear() < 0); + Set(player.autopilot_control.steer, PlayerState.GetSteer()); + Set(player.autopilot_control.throttle, PlayerState.GetThrottle()); + Set(player.autopilot_control.brake, PlayerState.GetBrake()); + Set(player.autopilot_control.hand_brake, PlayerState.GetHandBrake()); + Set(player.autopilot_control.reverse, PlayerState.GetCurrentGear() < 0); TArray Agents; if (bSendNonPlayerAgentsInfo) { diff --git a/Util/CarlaServer/include/carla/carla_server.h b/Util/CarlaServer/include/carla/carla_server.h index 307a7b8ac..824f28957 100644 --- a/Util/CarlaServer/include/carla/carla_server.h +++ b/Util/CarlaServer/include/carla/carla_server.h @@ -139,7 +139,7 @@ extern "C" { /** Percentage of the car off-road. */ float intersection_offroad; /** Vehicle's AI control that would apply this frame. */ - struct carla_control ai_control; + struct carla_control autopilot_control; }; /* ======================================================================== */ diff --git a/Util/CarlaServer/source/carla/server/CarlaEncoder.cpp b/Util/CarlaServer/source/carla/server/CarlaEncoder.cpp index 33954ff5a..682e96c89 100644 --- a/Util/CarlaServer/source/carla/server/CarlaEncoder.cpp +++ b/Util/CarlaServer/source/carla/server/CarlaEncoder.cpp @@ -126,7 +126,7 @@ namespace server { player->set_collision_other(values.player_measurements.collision_other); player->set_intersection_otherlane(values.player_measurements.intersection_otherlane); player->set_intersection_offroad(values.player_measurements.intersection_offroad); - Set(player->mutable_ai_control(), values.player_measurements.ai_control); + Set(player->mutable_autopilot_control(), values.player_measurements.autopilot_control); // Non-player agents. message->clear_non_player_agents(); // we need to clear as we cache the message. for (auto &agent : agents(values)) { diff --git a/Util/Proto/carla_server.proto b/Util/Proto/carla_server.proto index 45a60fe4e..5d36112ed 100644 --- a/Util/Proto/carla_server.proto +++ b/Util/Proto/carla_server.proto @@ -111,7 +111,7 @@ message Measurements { float intersection_otherlane = 8; float intersection_offroad = 9; - Control ai_control = 10; + Control autopilot_control = 10; } uint32 platform_timestamp = 1; diff --git a/Util/Protoc.bat b/Util/Protoc.bat index 96d7a574d..551811f29 100644 --- a/Util/Protoc.bat +++ b/Util/Protoc.bat @@ -2,16 +2,14 @@ set PROTOBUF_SRC_DIR=Proto set PROTOBUF_CPP_OUT_DIR=CarlaServer/source/carla/server -set PROTOBUF_PY_OUT_DIR0=../PythonClient/carla/protoc -set PROTOBUF_PY_OUT_DIR1=TestingClient/carla +set PROTOBUF_PY_OUT_DIR=../PythonClient/carla set PROTO_BASENAME=carla_server if "%1" == "--clean" ( rem Delete existing ones. rm -f %PROTOBUF_CPP_OUT_DIR%/carla_server.pb.h rm -f %PROTOBUF_CPP_OUT_DIR%/carla_server.pb.cc - rm -f %PROTOBUF_PY_OUT_DIR0%/carla_server_pb2.py - rm -f %PROTOBUF_PY_OUT_DIR1%/carla_server_pb2.py + rm -f %PROTOBUF_PY_OUT_DIR%/carla_server_pb2.py goto end ) @@ -24,8 +22,7 @@ if exist %PROTOC% ( %PROTOC% ^ -I=%PROTOBUF_SRC_DIR% ^ --cpp_out=%PROTOBUF_CPP_OUT_DIR% ^ - --python_out=%PROTOBUF_PY_OUT_DIR0% ^ - --python_out=%PROTOBUF_PY_OUT_DIR1% ^ + --python_out=%PROTOBUF_PY_OUT_DIR% ^ %PROTOBUF_SRC_DIR%/%PROTO_BASENAME%.proto echo done. diff --git a/Util/Protoc.sh b/Util/Protoc.sh index 4e717d423..8dd7a5ad2 100755 --- a/Util/Protoc.sh +++ b/Util/Protoc.sh @@ -7,15 +7,13 @@ pushd "$SCRIPT_DIR" >/dev/null PROTOBUF_SRC_DIR=Proto PROTOBUF_CPP_OUT_DIR=CarlaServer/source/carla/server -PROTOBUF_PY_OUT_DIR0=../PythonClient/carla/protoc -PROTOBUF_PY_OUT_DIR1=TestingClient/carla +PROTOBUF_PY_OUT_DIR=../PythonClient/carla PROTO_BASENAME=carla_server if [ "$1" == "--clean" ]; then # Delete existing ones. rm -f ${PROTOBUF_CPP_OUT_DIR}/*.pb.h ${PROTOBUF_CPP_OUT_DIR}/*.pb.cc - rm -f ${PROTOBUF_PY_OUT_DIR0}/*_pb2.py - rm -f ${PROTOBUF_PY_OUT_DIR1}/*_pb2.py + rm -f ${PROTOBUF_PY_OUT_DIR}/*_pb2.py exit 0 fi @@ -33,8 +31,7 @@ echo "Compiling ${PROTO_BASENAME}.proto..." ${PROTOC} \ -I=${PROTOBUF_SRC_DIR} \ --cpp_out=${PROTOBUF_CPP_OUT_DIR} \ - --python_out=${PROTOBUF_PY_OUT_DIR0} \ - --python_out=${PROTOBUF_PY_OUT_DIR1} \ + --python_out=${PROTOBUF_PY_OUT_DIR} \ ${PROTOBUF_SRC_DIR}/${PROTO_BASENAME}.proto popd >/dev/null diff --git a/Util/TestingClient/README.md b/Util/TestingClient/README.md deleted file mode 100644 index 1593ae292..000000000 --- a/Util/TestingClient/README.md +++ /dev/null @@ -1,12 +0,0 @@ -CARLA Client for Testing -======================== - -Requires Python 3 and the protobuf module installed, saving images to disk -requires the PIL module too. - - $ sudo apt-get install python3 python3-pip - $ sudo pip3 install protobuf - -A sample Python script explaining how to use the client API is provided - - $ ./client_example.py --help diff --git a/Util/TestingClient/carla/client.py b/Util/TestingClient/carla/client.py deleted file mode 100644 index 21ad4a9d7..000000000 --- a/Util/TestingClient/carla/client.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -"""CARLA Client.""" - -import os -import struct - -from contextlib import contextmanager - -from . import tcp -from . import util - -try: - from . import carla_server_pb2 as carla_protocol -except ImportError: - raise RuntimeError('cannot import "carla_server_pb2.py", run the protobuf compiler to generate this file') - - -@contextmanager -def make_carla_client(host, world_port, timeout=15): - with util.make_connection(CarlaClient, host, world_port, timeout) as client: - yield client - - -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 connected(self): - return self._world_client.connected() - - 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, CarlaImage.parse_raw_data(images_raw_data) - - def send_control(self, *args, **kwargs): - """Send vehicle control for the current frame.""" - if isinstance(args[0] if args else None, carla_protocol.Control): - pb_message = args[0] - else: - 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) - self._control_client.write(pb_message.SerializeToString()) - - -class CarlaImage(object): - @staticmethod - def parse_raw_data(raw_data): - getval = lambda index: struct.unpack('. - -"""Basic TCP client.""" - -import logging -import socket -import struct - - -class TCPClient(object): - def __init__(self, host, port, timeout): - self._host = host - self._port = port - self._timeout = timeout - self._socket = None - - def connect(self): - self._socket = socket.create_connection(address=(self._host, self._port), timeout=self._timeout) - self._socket.settimeout(self._timeout) - self._log('connected') - - def disconnect(self): - if self._socket is not None: - self._log('disconnecting...') - self._socket.close() - self._socket = None - - def connected(self): - return self._socket is not None - - def write(self, message): - if self._socket is None: - raise RuntimeError('%s:%s not connected' % (self._host, self._port)) - header = struct.pack(' 0: - data = self._socket.recv(length) - if not data: - raise RuntimeError('%s:%s connection closed' % (self._host, self._port)) - buf += data - length -= len(data) - return buf - - def _log(self, message, *args): - logging.debug('tcpclient %s:%d - ' + message, self._host, self._port, *args) diff --git a/Util/TestingClient/client_example.py b/Util/TestingClient/client_example.py deleted file mode 100755 index 74e73779e..000000000 --- a/Util/TestingClient/client_example.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de -# Barcelona (UAB), and the INTEL Visual Computing Lab. -# -# This work is licensed under the terms of the MIT license. -# For a copy, see . - -"""Basic CARLA client example.""" - - -import argparse -import logging -import random -import shutil -import sys -import time - - -from carla.client import make_carla_client -from carla.console import CarlaClientConsole -from carla.settings import CarlaSettings, Camera - - -def run_carla_client(host, port, autopilot_on, save_images_to_disk, image_filename_format): - # 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. - - # Create a CarlaSettings object. This object is a handy wrapper - # around the CarlaSettings.ini file. Here we set the configuration - # we want for the new episode. - settings = CarlaSettings() - settings.set( - SynchronousMode=True, - NumberOfVehicles=30, - NumberOfPedestrians=50, - 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_camera(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_camera(camera1) - - print('Requesting new episode...') - - # Now we request a new episode with these settings. The server - # replies with a scene description containing the available start - # spots for the player. Here instead of a CarlaSettings object we - # could also provide a CarlaSettings.ini file as string. - scene = client.request_new_episode(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 - # `player_start`. This function blocks until the server is ready to - # start the episode. - client.start_episode(player_start) - - # Iterate every frame in the episode. - for frame in range(0, frames_per_episode): - - # Read the measurements and images produced by the server this - # frame. - measurements, images = client.read_measurements() - - # Print some of the measurements we received. - print_player_measurements(measurements.player_measurements) - - # Save the images to disk if requested. - if save_images_to_disk: - for n, image in enumerate(images): - image.save_to_disk(image_filename_format.format(episode, n, frame)) - - # 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.3, - brake=False, - hand_brake=False, - reverse=False) - - else: - - # Together with the measurements, the server has sent the - # control that the in-game AI would do this frame. We can - # enable autopilot by sending back this control to the - # server. Here we will also add some noise to the steer. - - control = measurements.player_measurements.ai_control - control.steer += random.uniform(-0.1, 0.1) - client.send_control(control) - - print('Done.') - return True - - -def print_player_measurements(player_measurements): - message = 'Vehicle at ({pos_x:.1f}, {pos_y:.1f}, {pos_z:.1f}) ' - message += '{speed:.2f} km/h, ' - message += '{other_lane:.0f}% other lane, {offroad:.0f}% off-road' - message = message.format( - pos_x=player_measurements.transform.location.x / 100, # cm -> m - pos_y=player_measurements.transform.location.y / 100, - pos_z=player_measurements.transform.location.z / 100, - speed=player_measurements.forward_speed, - other_lane=100 * player_measurements.intersection_otherlane, - offroad=100 * player_measurements.intersection_offroad) - empty_space = shutil.get_terminal_size((80, 20)).columns - len(message) - sys.stdout.write('\r' + message + empty_space * ' ') - sys.stdout.flush() - - -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='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( - '-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', '--console', - action='store_true', - help='start the client console') - - args = argparser.parse_args() - - log_level = logging.DEBUG if args.debug else logging.INFO - logging.basicConfig(format='carla_client: %(levelname)s: %(message)s', level=log_level) - - logging.info('listening to server %s:%s', args.host, args.port) - - if args.console: - args.synchronous = True - cmd = CarlaClientConsole(args) - try: - cmd.cmdloop() - finally: - cmd.cleanup() - return - - while True: - try: - - end = 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}/camera_{:0>3d}/image_{:0>5d}.png') - - if end: - return - - except Exception as exception: - logging.error('exception: %s', exception) - time.sleep(1) - - -if __name__ == '__main__': - - try: - main() - except KeyboardInterrupt: - print('\nCancelled by user. Bye!')