#32 Comment the client API
This commit is contained in:
parent
9f95e088c4
commit
1b621ded46
|
@ -21,44 +21,50 @@ 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
|
||||
# Controls the state, if an episode is already started.
|
||||
self._is_episode_requested = False
|
||||
self._sensor_names = []
|
||||
|
||||
def connect(self):
|
||||
self._world_client.connect()
|
||||
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 to
|
||||
the server. carla_settings object must be convertible to a str holding a
|
||||
CarlaSettings.ini.
|
||||
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.
|
||||
|
||||
Returns a protobuf object holding the scene description.
|
||||
Return a protobuf object holding the scene description.
|
||||
"""
|
||||
self._current_settings = carla_settings
|
||||
return self._request_new_episode(carla_settings)
|
||||
|
@ -68,8 +74,10 @@ class CarlaClient(object):
|
|||
"""
|
||||
Start the new episode at the player start given by the
|
||||
player_start_index. The list of player starts is retrieved by
|
||||
"load_settings". Requests a new episode based on the last settings
|
||||
loaded by "load_settings".
|
||||
"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.
|
||||
"""
|
||||
|
@ -101,9 +109,9 @@ class CarlaClient(object):
|
|||
|
||||
def read_data(self):
|
||||
"""
|
||||
Read data sent from the server this frame. The episode must be started.
|
||||
Return the protobuf object with the measurements followed by the raw
|
||||
data of the sensors.
|
||||
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()
|
||||
|
@ -116,7 +124,12 @@ class CarlaClient(object):
|
|||
return pb_message, self._parse_raw_sensor_data(raw_sensor_data)
|
||||
|
||||
def send_control(self, *args, **kwargs):
|
||||
"""Send vehicle control for the current frame."""
|
||||
"""
|
||||
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:
|
||||
|
@ -130,9 +143,8 @@ class CarlaClient(object):
|
|||
|
||||
def _request_new_episode(self, carla_settings):
|
||||
"""
|
||||
Request a new episode. Internal function to request information about a
|
||||
new episode episode that is going to start. It also prepare the client
|
||||
for reset by disconnecting stream and control clients.
|
||||
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()
|
||||
|
@ -154,6 +166,7 @@ class CarlaClient(object):
|
|||
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)))
|
||||
|
|
|
@ -15,11 +15,8 @@ provides considerably better performance.
|
|||
|
||||
|
||||
try:
|
||||
|
||||
import numpy
|
||||
|
||||
except ImportError:
|
||||
|
||||
raise RuntimeError('cannot import numpy, make sure numpy package is installed')
|
||||
|
||||
|
||||
|
@ -92,10 +89,10 @@ def depth_to_array(image):
|
|||
return grayscale
|
||||
|
||||
|
||||
def depth_to_grayscale(image):
|
||||
def depth_to_logarithmic_grayscale(image):
|
||||
"""
|
||||
Convert an image containing CARLA encoded depth-map to logarithmic
|
||||
grayscale.
|
||||
Convert an image containing CARLA encoded depth-map to a logarithmic
|
||||
grayscale image array.
|
||||
"""
|
||||
grayscale = depth_to_array(image)
|
||||
# Convert to logarithmic depth.
|
||||
|
|
|
@ -17,14 +17,17 @@ import os
|
|||
|
||||
class Sensor(object):
|
||||
"""
|
||||
Base class for sensor descriptions. Used to add sensors to the
|
||||
CarlaSettings.
|
||||
Base class for sensor descriptions. Used to add sensors to CarlaSettings.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Camera(Sensor):
|
||||
"""Camera description. To be added to CarlaSettings."""
|
||||
"""
|
||||
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'
|
||||
|
@ -66,14 +69,13 @@ class Camera(Sensor):
|
|||
|
||||
|
||||
class SensorData(object):
|
||||
"""
|
||||
Base class for sensor data returned from the server.
|
||||
"""
|
||||
"""Base class for sensor data returned from the server."""
|
||||
pass
|
||||
|
||||
|
||||
class Image(SensorData):
|
||||
"""Data generated by a camera."""
|
||||
"""Data generated by a Camera."""
|
||||
|
||||
def __init__(self, width, height, image_type, raw_data):
|
||||
assert len(raw_data) == 4 * width * height
|
||||
self.width = width
|
||||
|
|
|
@ -27,7 +27,11 @@ MAX_NUMBER_OF_WEATHER_IDS = 14
|
|||
|
||||
|
||||
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
|
||||
|
@ -49,25 +53,27 @@ 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_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):
|
||||
"""Converts this object to an INI formatted string."""
|
||||
ini = ConfigParser()
|
||||
ini.optionxform=str
|
||||
S_SERVER = 'CARLA/Server'
|
||||
|
@ -117,6 +123,10 @@ class CarlaSettings(object):
|
|||
|
||||
|
||||
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()
|
||||
|
|
|
@ -16,6 +16,14 @@ class TCPConnectionError(Exception):
|
|||
|
||||
|
||||
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
|
||||
|
@ -23,8 +31,10 @@ class TCPClient(object):
|
|||
self._socket = None
|
||||
self._logprefix = '(%s:%s) ' % (self._host, self._port)
|
||||
|
||||
def connect(self):
|
||||
for attempt in range(10):
|
||||
def connect(self, connection_attempts=10):
|
||||
"""Try to establish a connection to the given host:port."""
|
||||
connection_attempts = max(1, connection_attempts)
|
||||
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)
|
||||
|
@ -37,15 +47,18 @@ class TCPClient(object):
|
|||
self._reraise_exception_as_tcp_error('failed to connect', exception)
|
||||
|
||||
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('<L', len(message))
|
||||
|
@ -55,6 +68,7 @@ class TCPClient(object):
|
|||
self._reraise_exception_as_tcp_error('failed to write data', exception)
|
||||
|
||||
def read(self):
|
||||
"""Read a message from the server."""
|
||||
header = self._read_n(4)
|
||||
if not header:
|
||||
raise TCPConnectionError(self._logprefix + 'connection closed')
|
||||
|
@ -63,6 +77,7 @@ class TCPClient(object):
|
|||
return data
|
||||
|
||||
def _read_n(self, length):
|
||||
"""Read n bytes from the socket."""
|
||||
if self._socket is None:
|
||||
raise TCPConnectionError(self._logprefix + 'not connected')
|
||||
buf = bytes()
|
||||
|
|
|
@ -12,7 +12,7 @@ from contextlib import contextmanager
|
|||
|
||||
@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)
|
||||
|
|
|
@ -52,6 +52,7 @@ 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
|
||||
|
@ -59,6 +60,7 @@ MINI_WINDOW_HEIGHT = 180
|
|||
|
||||
|
||||
def make_carla_settings():
|
||||
"""Make a CarlaSettings object with the settings we need."""
|
||||
settings = CarlaSettings()
|
||||
settings.set(
|
||||
SynchronousMode=False,
|
||||
|
@ -118,6 +120,7 @@ class CarlaGame(object):
|
|||
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:
|
||||
|
@ -264,7 +267,7 @@ class CarlaGame(object):
|
|||
self._display.blit(surface, (0, 0))
|
||||
|
||||
if self._mini_view_image1 is not None:
|
||||
array = image_converter.depth_to_grayscale(self._mini_view_image1)
|
||||
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))
|
||||
|
||||
|
@ -304,10 +307,10 @@ def main():
|
|||
type=int,
|
||||
help='TCP port to listen to (default: 2000)')
|
||||
argparser.add_argument(
|
||||
'-m', '--city_name',
|
||||
'-m', '--map-name',
|
||||
metavar='M',
|
||||
default=None,
|
||||
help='Plot the carla town map of city sent as argument (default: None), needs to match city from server, Options: Town01 or Town02')
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue