Add "frame number" to measurements and sensor data
This commit is contained in:
Néstor Subirón 2018-04-17 15:37:04 +02:00 committed by GitHub
commit c5ccf00134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 283 additions and 183 deletions

View File

@ -35,6 +35,11 @@ to a more human readable palette of colors. It can be found at
["Util/ImageConverter"][imgconvlink]. Alternatively, they can also be converted
using the functions at `carla.image_converter` Python module.
Note that all the sensor data comes with a _frame number_ stamp, this _frame
number_ matches the one received in the measurements. This is especially useful
for running the simulator in asynchronous mode and synchronize sensor data on
the client side.
[clientexamplelink]: https://github.com/carla-simulator/carla/blob/master/PythonClient/client_example.py
[settingslink]: https://github.com/carla-simulator/carla/blob/master/Docs/Example.CarlaSettings.ini
[imgconvlink]: https://github.com/carla-simulator/carla/tree/master/Util/ImageConverter

View File

@ -11,13 +11,13 @@ to the client. This document describes the details of these measurements.
Time-stamps
-----------
Since CARLA can be run at fixed-frame rate, we keep track of two different
time-stamps.
Every frame is described by three different counters/time-stamps
Key | Type | Units | Description
-------------------------- | --------- | ------------ | ------------
frame_number | uint64 | | Frame counter (it is **not** restarted on each episode).
platform_timestamp | uint32 | milliseconds | Time-stamp of the current frame, as given by the OS.
game_timestamp | uint32 | milliseconds | In-game time-stamp, elapsed since the beginning of the current level.
game_timestamp | uint32 | milliseconds | In-game time-stamp, elapsed since the beginning of the current episode.
In real-time mode, the elapsed time between two time steps should be similar
both platform and game time-stamps. When run in fixed-time step, the game

View File

@ -19,7 +19,7 @@ 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\"6\n\nRotation3D\x12\r\n\x05pitch\x18\x01 \x01(\x02\x12\x0b\n\x03yaw\x18\x02 \x01(\x02\x12\x0c\n\x04roll\x18\x03 \x01(\x02\"\x92\x01\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.Vector3DB\x02\x18\x01\x12*\n\x08rotation\x18\x03 \x01(\x0b\x32\x18.carla_server.Rotation3D\"\x80\x01\n\x06Sensor\x12\n\n\x02id\x18\x01 \x01(\x07\x12\'\n\x04type\x18\x02 \x01(\x0e\x32\x19.carla_server.Sensor.Type\x12\x0c\n\x04name\x18\x03 \x01(\t\"3\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06\x43\x41MERA\x10\x01\x12\x12\n\x0eLIDAR_RAY_CAST\x10\x02\"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\"n\n\x10SceneDescription\x12\x33\n\x12player_start_spots\x18\x01 \x03(\x0b\x32\x17.carla_server.Transform\x12%\n\x07sensors\x18\x02 \x03(\x0b\x32\x14.carla_server.Sensor\"/\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\"\xb6\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\xf5\x02\n\x12PlayerMeasurements\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12*\n\nbox_extent\x18\x0b \x01(\x0b\x32\x16.carla_server.Vector3D\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')
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\"6\n\nRotation3D\x12\r\n\x05pitch\x18\x01 \x01(\x02\x12\x0b\n\x03yaw\x18\x02 \x01(\x02\x12\x0c\n\x04roll\x18\x03 \x01(\x02\"\x92\x01\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.Vector3DB\x02\x18\x01\x12*\n\x08rotation\x18\x03 \x01(\x0b\x32\x18.carla_server.Rotation3D\"\x80\x01\n\x06Sensor\x12\n\n\x02id\x18\x01 \x01(\x07\x12\'\n\x04type\x18\x02 \x01(\x0e\x32\x19.carla_server.Sensor.Type\x12\x0c\n\x04name\x18\x03 \x01(\t\"3\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06\x43\x41MERA\x10\x01\x12\x12\n\x0eLIDAR_RAY_CAST\x10\x02\"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\"n\n\x10SceneDescription\x12\x33\n\x12player_start_spots\x18\x01 \x03(\x0b\x32\x17.carla_server.Transform\x12%\n\x07sensors\x18\x02 \x03(\x0b\x32\x14.carla_server.Sensor\"/\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\"\xcc\x04\n\x0cMeasurements\x12\x14\n\x0c\x66rame_number\x18\x05 \x01(\x04\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\xf5\x02\n\x12PlayerMeasurements\x12*\n\ttransform\x18\x01 \x01(\x0b\x32\x17.carla_server.Transform\x12*\n\nbox_extent\x18\x0b \x01(\x0b\x32\x16.carla_server.Vector3D\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')
)
@ -766,8 +766,8 @@ _MEASUREMENTS_PLAYERMEASUREMENTS = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1651,
serialized_end=2024,
serialized_start=1673,
serialized_end=2046,
)
_MEASUREMENTS = _descriptor.Descriptor(
@ -778,28 +778,35 @@ _MEASUREMENTS = _descriptor.Descriptor(
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='platform_timestamp', full_name='carla_server.Measurements.platform_timestamp', index=0,
name='frame_number', full_name='carla_server.Measurements.frame_number', index=0,
number=5, type=4, cpp_type=4, 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='platform_timestamp', full_name='carla_server.Measurements.platform_timestamp', index=1,
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,
name='game_timestamp', full_name='carla_server.Measurements.game_timestamp', index=2,
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,
name='player_measurements', full_name='carla_server.Measurements.player_measurements', index=3,
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,
name='non_player_agents', full_name='carla_server.Measurements.non_player_agents', index=4,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
@ -818,7 +825,7 @@ _MEASUREMENTS = _descriptor.Descriptor(
oneofs=[
],
serialized_start=1458,
serialized_end=2024,
serialized_end=2046,
)
_TRANSFORM.fields_by_name['location'].message_type = _VECTOR3D

View File

@ -183,31 +183,36 @@ class CarlaClient(object):
def _make_sensor_parsers(sensors):
image_types = ['None', 'SceneFinal', 'Depth', 'SemanticSegmentation']
getimgtype = lambda id: image_types[id] if len(image_types) > id else 'Unknown'
getint = lambda data, index: struct.unpack('<L', data[index*4:index*4+4])[0]
getint32 = lambda data, index: struct.unpack('<L', data[index*4:index*4+4])[0]
getint64 = lambda data, index: struct.unpack('<Q', data[index*4:index*4+8])[0]
getfloat = lambda data, index: struct.unpack('<f', data[index*4:index*4+4])[0]
def parse_image(data):
width = getint(data, 0)
height = getint(data, 1)
image_type = getimgtype(getint(data, 2))
fov = getfloat(data, 3)
return sensor.Image(width, height, image_type, fov, data[16:])
frame_number = getint64(data, 0)
width = getint32(data, 2)
height = getint32(data, 3)
image_type = getimgtype(getint32(data, 4))
fov = getfloat(data, 5)
return sensor.Image(frame_number, width, height, image_type, fov, data[24:])
def parse_lidar(data):
horizontal_angle = getfloat(data, 0)
channels = getint(data, 1)
frame_number = getint64(data, 0)
horizontal_angle = getfloat(data, 2)
channels = getint32(data, 3)
header_size = 16
point_count_by_channel = numpy.frombuffer(
data[8:8+channels*4],
data[header_size:header_size+channels*4],
dtype=numpy.dtype('uint32'))
points = numpy.frombuffer(
data[8+channels*4:],
data[header_size+channels*4:],
dtype=numpy.dtype('f4'))
points = numpy.reshape(points, (int(points.shape[0]/3), 3))
return sensor.LidarMeasurement(
frame_number,
horizontal_angle,
channels,
point_count_by_channel,
sensor.PointCloud(points))
sensor.PointCloud(frame_number, points))
class SensorDefinition(object):
def __init__(self, s):

View File

@ -154,6 +154,9 @@ def depth_to_local_point_cloud(image, color=None, max_depth=0.9):
# [[X1,Y1,Z1,R1,G1,B1],[X2,Y2,Z2,R2,G2,B2], ... [Xn,Yn,Zn,Rn,Gn,Bn]]
if color is not None:
# numpy.concatenate((numpy.transpose(p3d), color), axis=1)
return sensor.PointCloud(numpy.transpose(p3d), color_array=color)
return sensor.PointCloud(
image.frame_number,
numpy.transpose(p3d),
color_array=color)
# [[X1,Y1,Z1],[X2,Y2,Z2], ... [Xn,Yn,Zn]]
return sensor.PointCloud(numpy.transpose(p3d))
return sensor.PointCloud(image.frame_number, numpy.transpose(p3d))

View File

@ -134,15 +134,18 @@ class Lidar(Sensor):
# -- SensorData ----------------------------------------------------------------
# ==============================================================================
class SensorData(object):
"""Base class for sensor data returned from the server."""
pass
def __init__(self, frame_number):
self.frame_number = frame_number
class Image(SensorData):
"""Data generated by a Camera."""
def __init__(self, width, height, image_type, fov, raw_data):
def __init__(self, frame_number, width, height, image_type, fov, raw_data):
super(Image, self).__init__(frame_number=frame_number)
assert len(raw_data) == 4 * width * height
self.width = width
self.height = height
@ -195,7 +198,8 @@ class Image(SensorData):
class PointCloud(SensorData):
"""A list of points."""
def __init__(self, array, color_array=None):
def __init__(self, frame_number, array, color_array=None):
super(PointCloud, self).__init__(frame_number=frame_number)
self._array = array
self._color_array = color_array
self._has_colors = color_array is not None
@ -306,7 +310,8 @@ class PointCloud(SensorData):
class LidarMeasurement(SensorData):
"""Data generated by a Lidar."""
def __init__(self, horizontal_angle, channels, point_count_by_channel, point_cloud):
def __init__(self, frame_number, horizontal_angle, channels, point_count_by_channel, point_cloud):
super(LidarMeasurement, self).__init__(frame_number=frame_number)
assert numpy.sum(point_count_by_channel) == len(point_cloud.array)
self.horizontal_angle = horizontal_angle
self.channels = channels

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "CarlaPlayerState.h"
#include "CoreGlobals.h"
void ACarlaPlayerState::Reset()
{
Super::Reset();
@ -25,6 +27,7 @@ void ACarlaPlayerState::CopyProperties(APlayerState *PlayerState)
ACarlaPlayerState *Other = Cast<ACarlaPlayerState>(PlayerState);
if (Other != nullptr)
{
FrameNumber = Other->FrameNumber;
FramesPerSecond = Other->FramesPerSecond;
PlatformTimeStamp = Other->PlatformTimeStamp;
GameTimeStamp = Other->GameTimeStamp;
@ -74,6 +77,7 @@ static int32 RoundToMilliseconds(float Seconds)
void ACarlaPlayerState::UpdateTimeStamp(float DeltaSeconds)
{
FrameNumber = GFrameCounter;
FramesPerSecond = 1.0f / DeltaSeconds;
PlatformTimeStamp = RoundToMilliseconds(FPlatformTime::Seconds());
GameTimeStamp += RoundToMilliseconds(DeltaSeconds);

View File

@ -40,6 +40,11 @@ public:
// ===========================================================================
/// @{
uint64 GetFrameNumber() const
{
return FrameNumber;
}
UFUNCTION(BlueprintCallable)
float GetFramesPerSecond() const
{
@ -214,6 +219,9 @@ private:
// If you add another variable here, don't forget to copy it inside
// CopyProperties if necessary.
UPROPERTY(VisibleAnywhere)
uint64 FrameNumber;
UPROPERTY(VisibleAnywhere)
float FramesPerSecond;

View File

@ -94,6 +94,7 @@ void ALidar::ReadPoints(const float DeltaTime)
}
const float HorizontalAngle = std::fmod(CurrentHorizontalAngle + AngleDistanceOfTick, 360.0f);
LidarMeasurement.SetFrameNumber(GFrameCounter);
LidarMeasurement.SetHorizontalAngle(HorizontalAngle);
}

View File

@ -15,6 +15,7 @@
/// The header consists of an array of uint32's in the following layout
///
/// {
/// Frame number (uint64)
/// Horizontal angle (float),
/// Channel count,
/// Point count of channel 0,
@ -37,8 +38,8 @@ public:
explicit FLidarMeasurement(uint32 SensorId = 0u, uint32 ChannelCount = 0u)
: SensorId(SensorId)
{
Header.AddDefaulted(2u + ChannelCount);
Header[1] = ChannelCount;
Header.AddDefaulted(4u + ChannelCount);
Header[3] = ChannelCount;
}
FLidarMeasurement &operator=(FLidarMeasurement &&Other)
@ -50,31 +51,36 @@ public:
return *this;
}
void SetFrameNumber(uint64 FrameNumber)
{
std::memcpy(Header.GetData(), reinterpret_cast<const void *>(&FrameNumber), 2u);
}
float GetHorizontalAngle() const
{
return reinterpret_cast<const float &>(Header[0]);
return reinterpret_cast<const float &>(Header[2]);
}
void SetHorizontalAngle(float HorizontalAngle)
{
Header[0] = reinterpret_cast<const uint32 &>(HorizontalAngle);
Header[2] = reinterpret_cast<const uint32 &>(HorizontalAngle);
}
uint32 GetChannelCount() const
{
return Header[1];
return Header[3];
}
void Reset(uint32 TotalPointCount)
{
std::memset(Header.GetData() + 2u, 0, sizeof(uint32) * GetChannelCount());
std::memset(Header.GetData() + 4u, 0, sizeof(uint32) * GetChannelCount());
Points.Reset(3u * TotalPointCount);
}
void WritePoint(uint32 Channel, const FVector &Point)
{
check(Header[1] > Channel);
Header[2u + Channel] += 1u;
check(Header[3] > Channel);
Header[4u + Channel] += 1u;
constexpr float TO_METERS = 1e-2f;
Points.Emplace(TO_METERS * Point.X);
Points.Emplace(TO_METERS * Point.Y);

View File

@ -8,40 +8,63 @@
#include "SceneCaptureCamera.h"
#include "Sensor/SensorDataView.h"
#include "Game/CarlaGameInstance.h"
#include "Settings/CarlaSettings.h"
#include "Components/DrawFrustumComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Components/StaticMeshComponent.h"
#include "ConstructorHelpers.h"
#include "CoreGlobals.h"
#include "Engine/CollisionProfile.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Materials/Material.h"
#include "Game/CarlaGameInstance.h"
#include "Kismet/KismetSystemLibrary.h"
#include <memory>
#include "ConstructorHelpers.h"
#include "Materials/Material.h"
#include <memory>
// =============================================================================
// -- Local static variables ---------------------------------------------------
// =============================================================================
static constexpr auto DEPTH_MAT_PATH =
#if PLATFORM_LINUX
TEXT("Material'/Carla/PostProcessingMaterials/DepthEffectMaterial_GLSL.DepthEffectMaterial_GLSL'");
TEXT("Material'/Carla/PostProcessingMaterials/DepthEffectMaterial_GLSL.DepthEffectMaterial_GLSL'");
#elif PLATFORM_WINDOWS
TEXT("Material'/Carla/PostProcessingMaterials/DepthEffectMaterial.DepthEffectMaterial'");
TEXT("Material'/Carla/PostProcessingMaterials/DepthEffectMaterial.DepthEffectMaterial'");
#else
# error No depth material defined for this platform
#endif
static constexpr auto SEMANTIC_SEGMENTATION_MAT_PATH =
TEXT("Material'/Carla/PostProcessingMaterials/GTMaterial.GTMaterial'");
TEXT("Material'/Carla/PostProcessingMaterials/GTMaterial.GTMaterial'");
// =============================================================================
// -- Local static methods and types -------------------------------------------
// =============================================================================
struct FImageHeaderData
{
uint64 FrameNumber;
uint32 Width;
uint32 Height;
uint32 Type;
float FOV;
};
static void RemoveShowFlags(FEngineShowFlags &ShowFlags);
// =============================================================================
// -- ASceneCaptureCamera ------------------------------------------------------
// =============================================================================
uint32 ASceneCaptureCamera::NumSceneCapture = 0;
ASceneCaptureCamera::ASceneCaptureCamera(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer),
SizeX(720u),
SizeY(512u),
PostProcessEffect(EPostProcessEffect::SceneFinal)
ASceneCaptureCamera::ASceneCaptureCamera(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer),
SizeX(720u),
SizeY(512u),
PostProcessEffect(EPostProcessEffect::SceneFinal)
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.TickGroup = TG_PrePhysics;
@ -59,24 +82,28 @@ ASceneCaptureCamera::ASceneCaptureCamera(const FObjectInitializer& ObjectInitial
DrawFrustum->bIsEditorOnly = true;
DrawFrustum->SetupAttachment(MeshComp);
CaptureRenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(FName(*FString::Printf(TEXT("CaptureRenderTarget%d"),NumSceneCapture)));
#if WITH_EDITORONLY_DATA
CaptureRenderTarget->CompressionNoAlpha = true;
CaptureRenderTarget->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
CaptureRenderTarget->bUseLegacyGamma = false;
#endif
CaptureRenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(
FName(*FString::Printf(TEXT("CaptureRenderTarget%d"), NumSceneCapture)));
#if WITH_EDITORONLY_DATA
CaptureRenderTarget->CompressionNoAlpha = true;
CaptureRenderTarget->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
CaptureRenderTarget->bUseLegacyGamma = false;
#endif
CaptureRenderTarget->CompressionSettings = TextureCompressionSettings::TC_Default;
CaptureRenderTarget->SRGB = false;
CaptureRenderTarget->bAutoGenerateMips = false;
CaptureRenderTarget->AddressX = TextureAddress::TA_Clamp;
CaptureRenderTarget->AddressY = TextureAddress::TA_Clamp;
CaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("SceneCaptureComponent2D"));
CaptureComponent2D->SetupAttachment(MeshComp);
CaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(
TEXT("SceneCaptureComponent2D"));
CaptureComponent2D->SetupAttachment(MeshComp);
// Load post-processing materials.
static ConstructorHelpers::FObjectFinder<UMaterial> DEPTH(DEPTH_MAT_PATH);
static ConstructorHelpers::FObjectFinder<UMaterial> DEPTH(
DEPTH_MAT_PATH);
PostProcessDepth = DEPTH.Object;
static ConstructorHelpers::FObjectFinder<UMaterial> SEMANTIC_SEGMENTATION(SEMANTIC_SEGMENTATION_MAT_PATH);
static ConstructorHelpers::FObjectFinder<UMaterial> SEMANTIC_SEGMENTATION(
SEMANTIC_SEGMENTATION_MAT_PATH);
PostProcessSemanticSegmentation = SEMANTIC_SEGMENTATION.Object;
NumSceneCapture++;
}
@ -87,18 +114,23 @@ void ASceneCaptureCamera::PostActorCreated()
// no need load the editor mesh when there is no editor
#if WITH_EDITOR
if(MeshComp)
if (MeshComp)
{
if (!IsRunningCommandlet())
{
if( !MeshComp->GetStaticMesh())
if (!MeshComp->GetStaticMesh())
{
UStaticMesh* CamMesh = LoadObject<UStaticMesh>(NULL, TEXT("/Engine/EditorMeshes/MatineeCam_SM.MatineeCam_SM"), NULL, LOAD_None, NULL);
UStaticMesh *CamMesh = LoadObject<UStaticMesh>(
NULL,
TEXT("/Engine/EditorMeshes/MatineeCam_SM.MatineeCam_SM"),
NULL,
LOAD_None,
NULL);
MeshComp->SetStaticMesh(CamMesh);
}
}
}
#endif // WITH_EDITOR
#endif // WITH_EDITOR
// Sync component with CameraActor frustum settings.
UpdateDrawFrustum();
@ -111,51 +143,53 @@ void ASceneCaptureCamera::BeginPlay()
// Setup render target.
const bool bInForceLinearGamma = bRemovePostProcessing;
CaptureRenderTarget->InitCustomFormat(SizeX, SizeY, PF_B8G8R8A8, bInForceLinearGamma);
if(!IsValid(CaptureComponent2D)||CaptureComponent2D->IsPendingKill())
if (!IsValid(CaptureComponent2D) || CaptureComponent2D->IsPendingKill())
{
CaptureComponent2D = NewObject<USceneCaptureComponent2D>(this,TEXT("SceneCaptureComponent2D"));
CaptureComponent2D = NewObject<USceneCaptureComponent2D>(this, TEXT("SceneCaptureComponent2D"));
CaptureComponent2D->SetupAttachment(MeshComp);
}
CaptureComponent2D->Deactivate();
CaptureComponent2D->TextureTarget = CaptureRenderTarget;
// Setup camera post-processing depending on the quality level:
const UCarlaGameInstance* GameInstance = Cast<UCarlaGameInstance>(GetWorld()->GetGameInstance());
check(GameInstance!=nullptr);
const UCarlaSettings& CarlaSettings = GameInstance->GetCarlaSettings();
switch(PostProcessEffect)
// Setup camera post-processing depending on the quality level:
const UCarlaGameInstance *GameInstance = Cast<UCarlaGameInstance>(GetWorld()->GetGameInstance());
check(GameInstance != nullptr);
const UCarlaSettings &CarlaSettings = GameInstance->GetCarlaSettings();
switch (PostProcessEffect)
{
case EPostProcessEffect::None: break;
case EPostProcessEffect::SceneFinal:
{
//we set LDR for high quality because it will include post-fx
//and HDR for low quality to avoid high contrast
switch(CarlaSettings.GetQualitySettingsLevel())
{
case EQualitySettingsLevel::Low:
CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneColorHDRNoAlpha;
break;
default:
//LDR is faster than HDR (smaller bitmap array)
CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
break;
}
case EPostProcessEffect::None:
break;
case EPostProcessEffect::SceneFinal:
{
// We set LDR for high quality because it will include post-fx and HDR for
// low quality to avoid high contrast.
switch (CarlaSettings.GetQualitySettingsLevel())
{
case EQualitySettingsLevel::Low:
CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneColorHDRNoAlpha;
break;
default:
// LDR is faster than HDR (smaller bitmap array).
CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
break;
}
break;
}
default:
CaptureComponent2D->CaptureSource = SCS_FinalColorLDR;
break;
}
default:
CaptureComponent2D->CaptureSource = SCS_FinalColorLDR;
break;
}
if (bRemovePostProcessing)
if (bRemovePostProcessing)
{
RemoveShowFlags(CaptureComponent2D->ShowFlags);
}
if (PostProcessEffect == EPostProcessEffect::Depth)
if (PostProcessEffect == EPostProcessEffect::Depth)
{
CaptureComponent2D->PostProcessSettings.AddBlendable(PostProcessDepth, 1.0f);
} else if (PostProcessEffect == EPostProcessEffect::SemanticSegmentation)
}
else if (PostProcessEffect == EPostProcessEffect::SemanticSegmentation)
{
CaptureComponent2D->PostProcessSettings.AddBlendable(PostProcessSemanticSegmentation, 1.0f);
}
@ -163,37 +197,50 @@ void ASceneCaptureCamera::BeginPlay()
CaptureComponent2D->UpdateContent();
CaptureComponent2D->Activate();
//Make sure that there is enough time in the render queue
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), FString("g.TimeoutForBlockOnRenderFence 300000"));
// Make sure that there is enough time in the render queue.
UKismetSystemLibrary::ExecuteConsoleCommand(
GetWorld(),
FString("g.TimeoutForBlockOnRenderFence 300000"));
Super::BeginPlay();
}
void ASceneCaptureCamera::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if(NumSceneCapture!=0) NumSceneCapture = 0;
if (NumSceneCapture != 0)
{
NumSceneCapture = 0;
}
}
void ASceneCaptureCamera::Tick(const float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if(IsVulkanPlatform(GMaxRHIShaderPlatform))
const auto FrameNumber = GFrameCounter;
if (IsVulkanPlatform(GMaxRHIShaderPlatform))
{
auto fn = [=](FRHICommandListImmediate& RHICmdList){WritePixelsNonBlocking(DeltaSeconds,RHICmdList);};
auto fn = [=](FRHICommandListImmediate &RHICmdList) {
WritePixelsNonBlocking(FrameNumber, RHICmdList);
};
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FWritePixelsNonBlocking,
decltype(fn),write_function_vulkan,fn,
{
write_function_vulkan(RHICmdList);
});
} else
{
auto fn = [=](){WritePixels(DeltaSeconds);};
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FWritePixels,
decltype(fn),write_function,fn,
FWritePixelsNonBlocking,
decltype(fn), write_function_vulkan, fn,
{
write_function();
write_function_vulkan(RHICmdList);
});
}
else
{
auto fn = [=]() {
WritePixels(FrameNumber);
};
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FWritePixels,
decltype(fn), write_function, fn,
{
write_function();
});
}
}
@ -214,7 +261,8 @@ void ASceneCaptureCamera::SetPostProcessEffect(EPostProcessEffect otherPostProce
{
PostProcessEffect = otherPostProcessEffect;
auto &PostProcessSettings = CaptureComponent2D->PostProcessSettings;
if (PostProcessEffect != EPostProcessEffect::SceneFinal) {
if (PostProcessEffect != EPostProcessEffect::SceneFinal)
{
PostProcessSettings.bOverride_AutoExposureMethod = false;
PostProcessSettings.bOverride_AutoExposureMinBrightness = false;
PostProcessSettings.bOverride_AutoExposureMaxBrightness = false;
@ -238,7 +286,8 @@ void ASceneCaptureCamera::Set(const UCameraDescription &CameraDescription)
{
Super::Set(CameraDescription);
if (CameraDescription.bOverrideCameraPostProcessParameters) {
if (CameraDescription.bOverrideCameraPostProcessParameters)
{
auto &Override = CameraDescription.CameraPostProcessParameters;
auto &PostProcessSettings = CaptureComponent2D->PostProcessSettings;
PostProcessSettings.bOverride_AutoExposureMethod = true;
@ -257,13 +306,15 @@ void ASceneCaptureCamera::Set(const UCameraDescription &CameraDescription)
bool ASceneCaptureCamera::ReadPixels(TArray<FColor> &BitMap) const
{
if(!CaptureRenderTarget)
if (!CaptureRenderTarget)
{
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target"));
return false;
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target"));
return false;
}
FTextureRenderTargetResource* RTResource = CaptureRenderTarget->GameThread_GetRenderTargetResource();
if (RTResource == nullptr) {
FTextureRenderTargetResource *RTResource =
CaptureRenderTarget->GameThread_GetRenderTargetResource();
if (RTResource == nullptr)
{
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target"));
return false;
}
@ -272,76 +323,62 @@ bool ASceneCaptureCamera::ReadPixels(TArray<FColor> &BitMap) const
return RTResource->ReadPixels(BitMap, ReadPixelFlags);
}
void ASceneCaptureCamera::WritePixelsNonBlocking(float DeltaTime, FRHICommandListImmediate& rhi_cmd_list) const
{
void ASceneCaptureCamera::WritePixelsNonBlocking(
const uint64 FrameNumber,
FRHICommandListImmediate &rhi_cmd_list) const
{
check(IsInRenderingThread());
if(!CaptureRenderTarget)
if (!CaptureRenderTarget)
{
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target"));
return ;
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target"));
return;
}
FTextureRenderTarget2DResource* RenderResource = (FTextureRenderTarget2DResource*)CaptureRenderTarget->Resource;
FTextureRenderTarget2DResource *RenderResource =
(FTextureRenderTarget2DResource *) CaptureRenderTarget->Resource;
FTextureRHIParamRef texture = RenderResource->GetRenderTargetTexture();
if(!texture)
if (!texture)
{
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render target texture"));
return;
}
struct {
uint32 Width;
uint32 Height;
uint32 Type;
float FOV;
} ImageHeader = {
FImageHeaderData ImageHeader = {
FrameNumber,
SizeX,
SizeY,
PostProcessEffect::ToUInt(PostProcessEffect),
CaptureComponent2D->FOVAngle
};
struct FReadSurfaceContext
{
FRenderTarget* SrcRenderTarget;
TArray<FColor>* OutData;
FIntRect Rect;
FReadSurfaceDataFlags Flags;
};
TArray<FColor> Pixels;
TArray<FColor> Pixels;
rhi_cmd_list.ReadSurfaceData(
texture,
FIntRect(0, 0, RenderResource->GetSizeXY().X, RenderResource->GetSizeXY().Y),
Pixels,
FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX)
);
texture,
FIntRect(0, 0, RenderResource->GetSizeXY().X, RenderResource->GetSizeXY().Y),
Pixels,
FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX));
FSensorDataView DataView(
GetId(),
FReadOnlyBufferView{reinterpret_cast<const void *>(&ImageHeader), sizeof(ImageHeader)},
FReadOnlyBufferView{Pixels}
);
WriteSensorData(DataView);
GetId(),
FReadOnlyBufferView{reinterpret_cast<const void *>(&ImageHeader), sizeof(ImageHeader)},
FReadOnlyBufferView{Pixels});
WriteSensorData(DataView);
}
void ASceneCaptureCamera::WritePixels(float DeltaTime) const
void ASceneCaptureCamera::WritePixels(const uint64 FrameNumber) const
{
FRHITexture2D *texture = CaptureRenderTarget->GetRenderTargetResource()->GetRenderTargetTexture();
if(!texture)
if (!texture)
{
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render texture"));
return ;
UE_LOG(LogCarla, Error, TEXT("SceneCaptureCamera: Missing render texture"));
return;
}
const uint32 num_bytes_per_pixel = 4; // PF_R8G8B8A8
const uint32 width = texture->GetSizeX();
const uint32 height = texture->GetSizeY();
const uint32 dest_stride = width * height * num_bytes_per_pixel;
uint32 src_stride;
uint8 *src = reinterpret_cast<uint8*>(RHILockTexture2D(texture, 0, RLM_ReadOnly, src_stride, false));
struct {
uint32 Width;
uint32 Height;
uint32 Type;
float FOV;
} ImageHeader = {
uint8 *src = reinterpret_cast<uint8 *>(
RHILockTexture2D(texture, 0, RLM_ReadOnly, src_stride, false));
FImageHeaderData ImageHeader = {
FrameNumber,
width,
height,
PostProcessEffect::ToUInt(PostProcessEffect),
@ -349,49 +386,53 @@ void ASceneCaptureCamera::WritePixels(float DeltaTime) const
};
std::unique_ptr<uint8[]> dest = nullptr;
//Direct 3D uses additional rows in the buffer,so we need check the result stride from the lock:
if(IsD3DPlatform(GMaxRHIShaderPlatform,false) && (dest_stride!=src_stride))
// Direct 3D uses additional rows in the buffer,so we need check the result
// stride from the lock:
if (IsD3DPlatform(GMaxRHIShaderPlatform, false) && (dest_stride != src_stride))
{
const uint32 copy_row_stride = width * num_bytes_per_pixel;
dest = std::make_unique<uint8[]>(dest_stride);
// Copy per row
uint8* dest_row = dest.get();
uint8* src_row = src;
uint8 *dest_row = dest.get();
uint8 *src_row = src;
for (uint32 Row = 0; Row < height; ++Row)
{
FMemory::Memcpy(dest_row, src_row, copy_row_stride);
dest_row += copy_row_stride;
src_row += src_stride;
FMemory::Memcpy(dest_row, src_row, copy_row_stride);
dest_row += copy_row_stride;
src_row += src_stride;
}
src = dest.get();
}
}
const FSensorDataView DataView(
GetId(),
FReadOnlyBufferView{reinterpret_cast<const void *>(&ImageHeader), sizeof(ImageHeader)},
FReadOnlyBufferView{src,dest_stride}
);
GetId(),
FReadOnlyBufferView{reinterpret_cast<const void *>(&ImageHeader), sizeof(ImageHeader)},
FReadOnlyBufferView{src, dest_stride});
WriteSensorData(DataView);
RHIUnlockTexture2D(texture, 0, false);
}
void ASceneCaptureCamera::UpdateDrawFrustum()
{
if(DrawFrustum && CaptureComponent2D)
if (DrawFrustum && CaptureComponent2D)
{
DrawFrustum->FrustumStartDist = GNearClippingPlane;
// 1000 is the default frustum distance, ideally this would be infinite but that might cause rendering issues
DrawFrustum->FrustumEndDist = (CaptureComponent2D->MaxViewDistanceOverride > DrawFrustum->FrustumStartDist)
// 1000 is the default frustum distance, ideally this would be infinite but
// that might cause rendering issues.
DrawFrustum->FrustumEndDist =
(CaptureComponent2D->MaxViewDistanceOverride > DrawFrustum->FrustumStartDist)
? CaptureComponent2D->MaxViewDistanceOverride : 1000.0f;
DrawFrustum->FrustumAngle = CaptureComponent2D->FOVAngle;
//DrawFrustum->FrustumAspectRatio = CaptureComponent2D->AspectRatio;
}
}
// =============================================================================
// -- Local static functions implementations -----------------------------------
// =============================================================================
// Remove the show flags that might interfere with post-processing effects like
// depth and semantic segmentation.
static void RemoveShowFlags(FEngineShowFlags &ShowFlags)

View File

@ -73,10 +73,16 @@ protected:
static uint32 NumSceneCapture;
private:
///Read the camera buffer and write it to the client with no lock of the resources (for Vulkan API)
void WritePixelsNonBlocking(float DeltaTime,FRHICommandListImmediate& rhi_cmd_list) const;
///Read the camera buffer and write it to the client with opengl or direct3d
void WritePixels(float DeltaTime) const;
/// Read the camera buffer and write it to the client with no lock of the
/// resources (for Vulkan API).
void WritePixelsNonBlocking(
uint64 FrameNumber,
FRHICommandListImmediate& rhi_cmd_list) const;
/// Read the camera buffer and write it to the client with opengl or direct3d.
void WritePixels(uint64 FrameNumber) const;
/// Used to synchronize the DrawFrustumComponent with the
/// SceneCaptureComponent2D settings.
void UpdateDrawFrustum();

View File

@ -106,6 +106,7 @@ void FCarlaEncoder::Encode(
const ACarlaPlayerState &PlayerState,
carla_measurements &Data)
{
Data.frame_number = PlayerState.GetFrameNumber();
Data.platform_timestamp = PlayerState.GetPlatformTimeStamp();
Data.game_timestamp = PlayerState.GetGameTimeStamp();
auto &Player = Data.player_measurements;

View File

@ -184,6 +184,8 @@ extern "C" {
/* ======================================================================== */
struct carla_measurements {
/** Frame counter. */
uint32_t frame_number;
/** Time-stamp of the current frame, in milliseconds as given by the OS. */
uint32_t platform_timestamp;
/** In-game time-stamp, milliseconds elapsed since the beginning of the current level. */

View File

@ -149,6 +149,7 @@ namespace server {
std::string CarlaEncoder::Encode(const carla_measurements &values) {
static thread_local auto *message = _protobuf.CreateMessage<cs::Measurements>();
DEBUG_ASSERT(message != nullptr);
message->set_frame_number(values.frame_number);
message->set_platform_timestamp(values.platform_timestamp);
message->set_game_timestamp(values.game_timestamp);
// Player measurements.

View File

@ -17,11 +17,12 @@ namespace test {
std::lock_guard<std::mutex> lock(_mutex);
const struct {
uint64_t FrameNumber;
uint32_t Width;
uint32_t Height;
uint32_t Type;
float FOV;
} ImageHeader = {300u, 200u, 1u, 90.0f};
} ImageHeader = {++_frame_number, 300u, 200u, 1u, 90.0f};
_data.header_size = sizeof(ImageHeader);
auto header = std::make_unique<unsigned char[]>(_data.header_size);

View File

@ -34,6 +34,8 @@ namespace test {
Sensor(uint32_t id);
uint64_t _frame_number = 0u;
mutable std::mutex _mutex;
const std::string _name;

View File

@ -141,6 +141,8 @@ message Measurements {
Control autopilot_control = 10;
}
uint64 frame_number = 5;
uint32 platform_timestamp = 1;
uint32 game_timestamp = 2;