CARLA
Loading...
Searching...
No Matches
WorldObserver.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8#include "Carla.h"
20
22#include <carla/rpc/String.h>
26
27#include <util/ue-header-guard-begin.h>
28#include "CoreGlobals.h"
29#include <util/ue-header-guard-end.h>
30
31static auto FWorldObserver_GetActorState(const FCarlaActor &View, const FActorRegistry &Registry)
32{
33 using AType = FCarlaActor::ActorType;
34
36
37 if (AType::Vehicle == View.GetActorType())
38 {
39 auto Vehicle = Cast<ACarlaWheeledVehicle>(View.GetActor());
40 if (Vehicle != nullptr)
41 {
42 state.vehicle_data.control = carla::rpc::VehicleControl{Vehicle->GetVehicleControl()};
43 auto Controller = Cast<AWheeledVehicleAIController>(Vehicle->GetController());
44 if (Controller != nullptr)
45 {
47 state.vehicle_data.traffic_light_state = static_cast<TLS>(Controller->GetTrafficLightState());
48 state.vehicle_data.speed_limit = Controller->GetSpeedLimit();
49 auto TrafficLight = Controller->GetTrafficLight();
50 if (TrafficLight != nullptr)
51 {
52 state.vehicle_data.has_traffic_light = true;
53 auto* TrafficLightView = Registry.FindCarlaActor(TrafficLight);
54 if(TrafficLightView)
55 {
56 state.vehicle_data.traffic_light_id = TrafficLightView->GetActorId();
57 }
58 else
59 {
60 state.vehicle_data.has_traffic_light = false;
61 }
62 }
63 else
64 {
65 state.vehicle_data.has_traffic_light = false;
66 }
67 }
68 // Get the failure state by checking the rollover one as it is the only one currently implemented.
69 // This will have to be expanded once more states are added
70 state.vehicle_data.failure_state = Vehicle->GetFailureState();
71 }
72 }
73
74 else if (AType::Walker == View.GetActorType())
75 {
76 auto Walker = Cast<APawn>(View.GetActor());
77 auto Controller = Walker != nullptr ? Cast<AWalkerController>(Walker->GetController()) : nullptr;
78 if (Controller != nullptr)
79 {
80 state.walker_control = carla::rpc::WalkerControl{Controller->GetWalkerControl()};
81 }
82 }
83 else if (AType::TrafficLight == View.GetActorType())
84 {
85 auto TrafficLight = Cast<ATrafficLightBase>(View.GetActor());
86 if (TrafficLight != nullptr)
87 {
88 auto* TrafficLightComponent =
89 TrafficLight->GetTrafficLightComponent();
90
92
93 if(TrafficLightComponent == nullptr)
94 {
95 // Old way: traffic lights are actors
96 state.traffic_light_data.sign_id[0] = '\0';
97 state.traffic_light_data.state = static_cast<TLS>(TrafficLight->GetTrafficLightState());
98 state.traffic_light_data.green_time = TrafficLight->GetGreenTime();
99 state.traffic_light_data.yellow_time = TrafficLight->GetYellowTime();
100 state.traffic_light_data.red_time = TrafficLight->GetRedTime();
101 state.traffic_light_data.elapsed_time = TrafficLight->GetElapsedTime();
102 state.traffic_light_data.time_is_frozen = TrafficLight->GetTimeIsFrozen();
103 state.traffic_light_data.pole_index = TrafficLight->GetPoleIndex();
104 }
105 else
106 {
107 const UTrafficLightController* Controller = TrafficLightComponent->GetController();
108 const ATrafficLightGroup* Group = TrafficLightComponent->GetGroup();
109
110 // This serializer runs every tick for every traffic light; on maps
111 // whose signals fail junction grouping (e.g. HD-map OpenDRIVE with
112 // placeholder junction connections) a per-tick per-light error floods
113 // the log hard enough to stall the whole server. Log each condition
114 // once per session -- the affected lights simply stream zeroed state.
115 if (!Controller)
116 {
117 static bool bLoggedMissingController = false;
118 if (!bLoggedMissingController)
119 {
120 bLoggedMissingController = true;
121 UE_LOG(LogCarla, Warning, TEXT("TrafficLightComponent doesn't have any Controller assigned (logged once; affected lights stream zeroed state)"));
122 }
123 }
124 else if (!Group)
125 {
126 static bool bLoggedMissingGroup = false;
127 if (!bLoggedMissingGroup)
128 {
129 bLoggedMissingGroup = true;
130 UE_LOG(LogCarla, Warning, TEXT("TrafficLightComponent doesn't have any Group assigned (logged once; affected lights stream zeroed state)"));
131 }
132 }
133 else
134 {
135 const FString fstring_sign_id = TrafficLightComponent->GetSignId();
136 const std::string sign_id = carla::rpc::FromFString(fstring_sign_id);
137 constexpr size_t max_size = sizeof(state.traffic_light_data.sign_id);
138 size_t sign_id_length = sign_id.length();
139 if(max_size < sign_id_length)
140 {
141 UE_LOG(LogCarla, Warning, TEXT("The max size of a signal id is 32. %s (%d)"), *fstring_sign_id, sign_id.length());
142 sign_id_length = max_size;
143 }
144 std::memset(state.traffic_light_data.sign_id, '\0', max_size);
145 std::memcpy(state.traffic_light_data.sign_id, sign_id.c_str(), sign_id_length);
146 state.traffic_light_data.state = static_cast<TLS>(TrafficLightComponent->GetLightState());
147 state.traffic_light_data.green_time = Controller->GetGreenTime();
148 state.traffic_light_data.yellow_time = Controller->GetYellowTime();
149 state.traffic_light_data.red_time = Controller->GetRedTime();
150 state.traffic_light_data.elapsed_time = Controller->GetElapsedTime();
151 state.traffic_light_data.time_is_frozen = Group->IsFrozen();
152 state.traffic_light_data.pole_index = TrafficLight->GetPoleIndex();
153 }
154 }
155 }
156 }
157 else if (AType::TrafficSign == View.GetActorType())
158 {
159 auto TrafficSign = Cast<ATrafficSignBase>(View.GetActor());
160 if (TrafficSign != nullptr)
161 {
162 USignComponent* TrafficSignComponent =
163 Cast<USignComponent>(TrafficSign->FindComponentByClass<USignComponent>());
164
165 if(TrafficSignComponent)
166 {
167 const FString fstring_sign_id = TrafficSignComponent->GetSignId();
168 const std::string sign_id = carla::rpc::FromFString(fstring_sign_id);
169 constexpr size_t max_size = sizeof(state.traffic_sign_data.sign_id);
170 size_t sign_id_length = sign_id.length();
171 if(max_size < sign_id_length)
172 {
173 UE_LOG(LogCarla, Warning, TEXT("The max size of a signal id is 32. %s (%d)"), *fstring_sign_id, sign_id.length());
174 sign_id_length = max_size;
175 }
176 std::memset(state.traffic_light_data.sign_id, '\0', max_size);
177 std::memcpy(state.traffic_sign_data.sign_id, sign_id.c_str(), sign_id_length);
178 }
179 }
180 }
181 return state;
182}
183
184static auto FWorldObserver_GetDormantActorState(const FCarlaActor &View, const FActorRegistry &Registry)
185{
186 using AType = FCarlaActor::ActorType;
187
189
190 if (AType::Vehicle == View.GetActorType())
191 {
192 const FVehicleData* ActorData = View.GetActorData<FVehicleData>();
193 state.vehicle_data.control = carla::rpc::VehicleControl{ActorData->Control};
195 state.vehicle_data.traffic_light_state = TLS::Green;
196 state.vehicle_data.speed_limit = ActorData->SpeedLimit;
197 state.vehicle_data.has_traffic_light = false;
198 }
199 else if (AType::Walker == View.GetActorType())
200 {
201 const FWalkerData* ActorData = View.GetActorData<FWalkerData>();
202 // auto Walker = Cast<APawn>(View.GetActor());
203 // auto Controller = Walker != nullptr ? Cast<AWalkerController>(Walker->GetController()) : nullptr;
204 // if (Controller != nullptr)
205 // {
206 // state.walker_control = carla::rpc::WalkerControl{Controller->GetWalkerControl()};
207 // }
208 state.walker_control = ActorData->WalkerControl;
209 }
210 else if (AType::TrafficLight == View.GetActorType())
211 {
212 const FTrafficLightData* ActorData = View.GetActorData<FTrafficLightData>();
213 const UTrafficLightController* Controller = ActorData->Controller;
214 if(Controller)
215 {
217 const ATrafficLightGroup* Group = Controller->GetGroup();
218 if(!Group)
219 {
220 // Same per-tick flood hazard as above: log once per session.
221 static bool bLoggedMissingGroup = false;
222 if (!bLoggedMissingGroup)
223 {
224 bLoggedMissingGroup = true;
225 UE_LOG(LogCarla, Warning, TEXT("TrafficLight doesn't have any Group assigned (logged once; affected lights stream zeroed state)"));
226 }
227 }
228 else
229 {
230 const FString fstring_sign_id = ActorData->SignId;
231 const std::string sign_id = carla::rpc::FromFString(fstring_sign_id);
232 constexpr size_t max_size = sizeof(state.traffic_light_data.sign_id);
233 size_t sign_id_length = sign_id.length();
234 if(max_size < sign_id_length)
235 {
236 UE_LOG(LogCarla, Warning, TEXT("The max size of a signal id is 32. %s (%d)"), *fstring_sign_id, sign_id.length());
237 sign_id_length = max_size;
238 }
239 std::memset(state.traffic_light_data.sign_id, '\0', max_size);
240 std::memcpy(state.traffic_light_data.sign_id, sign_id.c_str(), sign_id_length);
241 state.traffic_light_data.state = static_cast<TLS>(Controller->GetCurrentState().State);
242 state.traffic_light_data.green_time = Controller->GetGreenTime();
243 state.traffic_light_data.yellow_time = Controller->GetYellowTime();
244 state.traffic_light_data.red_time = Controller->GetRedTime();
245 state.traffic_light_data.elapsed_time = Controller->GetElapsedTime();
246 state.traffic_light_data.time_is_frozen = Group->IsFrozen();
247 state.traffic_light_data.pole_index = ActorData->PoleIndex;
248 }
249 }
250 }
251 else if (AType::TrafficSign == View.GetActorType())
252 {
253 const FTrafficSignData* ActorData = View.GetActorData<FTrafficSignData>();
254 const FString fstring_sign_id = ActorData->SignId;
255 const std::string sign_id = carla::rpc::FromFString(fstring_sign_id);
256 constexpr size_t max_size = sizeof(state.traffic_sign_data.sign_id);
257 size_t sign_id_length = sign_id.length();
258 if(max_size < sign_id_length)
259 {
260 UE_LOG(LogCarla, Warning, TEXT("The max size of a signal id is 32. %s (%d)"), *fstring_sign_id, sign_id.length());
261 sign_id_length = max_size;
262 }
263 std::memset(state.traffic_light_data.sign_id, '\0', max_size);
264 std::memcpy(state.traffic_sign_data.sign_id, sign_id.c_str(), sign_id_length);
265 }
266 return state;
267}
268
270{
271 const auto RootComponent = Cast<UPrimitiveComponent>(Actor.GetRootComponent());
272 // A Multi-GPU secondary's mirror has the primary's, not motion of its own.
273 const FVector AngularVelocity =
274 View.HasMirroredKinematics() ?
276 RootComponent != nullptr ?
277 RootComponent->GetPhysicsAngularVelocityInDegrees() :
278 FVector{0.0f, 0.0f, 0.0f};
279 return
280 {
281 (float)AngularVelocity.X,
282 (float)AngularVelocity.Y,
283 (float)AngularVelocity.Z
284 };
285}
286
288 const FCarlaActor &View,
289 const FVector &Velocity,
290 const float DeltaSeconds)
291{
292 FVector &PreviousVelocity = View.GetActorInfo()->Velocity;
293 const FVector Acceleration = (Velocity - PreviousVelocity) / DeltaSeconds;
294 PreviousVelocity = Velocity;
295 return
296 {
297 (float)Acceleration.X,
298 (float)Acceleration.Y,
299 (float)Acceleration.Z
300 };
301}
302
304 carla::Buffer &&buffer,
305 const UCarlaEpisode &Episode,
306 float DeltaSeconds,
307 bool MapChange,
308 bool PendingLightUpdates)
309{
310 TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
313 using ActorDynamicState = carla::sensor::data::ActorDynamicState;
314
315
316 const FActorRegistry &Registry = Episode.GetActorRegistry();
317
318 auto total_size = sizeof(Serializer::Header) + sizeof(ActorDynamicState) * Registry.Num();
319 auto current_size = 0;
320 // Set up buffer for writing.
321 buffer.reset(total_size);
322 auto write_data = [&current_size, &buffer](const auto &data)
323 {
324 auto begin = buffer.begin() + current_size;
325 std::memcpy(begin, &data, sizeof(data));
326 current_size += sizeof(data);
327 };
328
329 constexpr float TO_METERS = 1e-2;
330
331 // Write header.
332 Serializer::Header header;
333 header.episode_id = Episode.GetId();
334 header.platform_timestamp = FPlatformTime::Seconds();
335 header.delta_seconds = DeltaSeconds;
336 FIntVector MapOrigin = Episode.GetCurrentMapOrigin();
337 FIntVector MapOriginInMeters = MapOrigin / 100;
338 header.map_origin = carla::geom::Vector3DInt{ MapOriginInMeters.X, MapOriginInMeters.Y, MapOriginInMeters.Z };
339
340 uint8_t simulation_state = (SimulationState::MapChange * MapChange);
341 simulation_state |= (SimulationState::PendingLightUpdate * PendingLightUpdates);
342
343 header.simulation_state = static_cast<SimulationState>(simulation_state);
344
345 write_data(header);
346
347 // Write every actor.
348 for (auto& It : Registry)
349 {
350 const FCarlaActor* View = It.Value.Get();
351 const FActorInfo* ActorInfo = View->GetActorInfo();
352
353 FTransform ActorTransform;
354 FVector Velocity(0.0f);
355 carla::geom::Vector3D AngularVelocity(0.0f, 0.0f, 0.0f);
356 carla::geom::Vector3D Acceleration(0.0f, 0.0f, 0.0f);
358
359 check(View);
360
361 if(View->IsDormant())
362 {
363 const FActorData* ActorData = View->GetActorData();
364 Velocity = TO_METERS * ActorData->Velocity;
365 AngularVelocity = carla::geom::Vector3D
366 {
367 (float)ActorData->AngularVelocity.X,
368 (float)ActorData->AngularVelocity.Y,
369 (float)ActorData->AngularVelocity.Z
370 };
371 Acceleration = FWorldObserver_GetAcceleration(*View, Velocity, DeltaSeconds);
373 }
374 else
375 {
376 const AActor* Actor = View->GetActor();
377 if (Actor == nullptr)
378 {
379 // World Partition streamed the actor out without notifying the
380 // registry (the entry is still Active but the weak pointer is now
381 // null). Skip it this frame; converting these to dormant on
382 // stream-out is tracked as follow-up work.
383 continue;
384 }
385 // Go through FCarlaActor::GetActorVelocity() rather than
386 // Actor->GetVelocity() directly: FWalkerActor overrides it to read
387 // UCharacterMovementComponent::Velocity, since AActor::GetVelocity()
388 // (via APawn::GetVelocity()) prefers the root component's
389 // physics-body velocity whenever it's simulating physics -- true for
390 // walker Blueprints set up for ragdoll death even while walking
391 // normally via AddMovementInput -- which reported near-zero here
392 // despite the walker actually moving. All other actor types are
393 // unaffected: the base FCarlaActor::GetActorVelocity() still just
394 // returns GetActor()->GetVelocity(), identical to before.
395 Velocity = TO_METERS * View->GetActorVelocity();
396 AngularVelocity = FWorldObserver_GetAngularVelocity(*View, *Actor);
397 Acceleration = FWorldObserver_GetAcceleration(*View, Velocity, DeltaSeconds);
398 State = FWorldObserver_GetActorState(*View, Registry);
399 }
400 ActorTransform = View->GetActorGlobalTransform();
401
402 ActorDynamicState info = {
403 View->GetActorId(),
404 View->GetActorState(),
405 carla::geom::Transform(ActorTransform),
406 carla::geom::Vector3D(Velocity.X, Velocity.Y, Velocity.Z),
407 AngularVelocity,
408 Acceleration,
409 State
410 };
411 write_data(info);
412 }
413
414 // Shrink buffer
415 buffer.resize(current_size);
416
417 check(buffer.size() == current_size);
418
419 return std::move(buffer);
420}
421
423 const UCarlaEpisode &Episode,
424 float DeltaSecond,
425 bool MapChange,
426 bool PendingLightUpdates)
427{
428 TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
429
430 if (!Stream.IsStreamReady())
431 return;
432
433 auto AsyncStream = Stream.MakeAsyncDataStream(*this, Episode.GetElapsedGameTime());
434
436 AsyncStream.PopBufferFromPool(),
437 Episode,
438 DeltaSecond,
439 MapChange,
440 PendingLightUpdates);
441
442 AsyncStream.SerializeAndSend(*this, std::move(buffer));
443}
static auto FWorldObserver_GetDormantActorState(const FCarlaActor &View, const FActorRegistry &Registry)
static auto FWorldObserver_GetActorState(const FCarlaActor &View, const FActorRegistry &Registry)
static carla::Buffer FWorldObserver_Serialize(carla::Buffer &&buffer, const UCarlaEpisode &Episode, float DeltaSeconds, bool MapChange, bool PendingLightUpdates)
static carla::geom::Vector3D FWorldObserver_GetAcceleration(const FCarlaActor &View, const FVector &Velocity, const float DeltaSeconds)
static carla::geom::Vector3D FWorldObserver_GetAngularVelocity(const FCarlaActor &View, const AActor &Actor)
Class which implements the state changing of traffic lights.
FVector Velocity
Definition ActorData.h:57
FVector AngularVelocity
Definition ActorData.h:59
A registry of all the Carla actors.
int32 Num() const
FCarlaActor * FindCarlaActor(IdType Id)
A view over an actor and its properties.
Definition CarlaActor.h:25
ActorType GetActorType() const
Definition CarlaActor.h:86
bool IsDormant() const
Definition CarlaActor.h:71
AActor * GetActor()
Definition CarlaActor.h:91
bool HasMirroredKinematics() const
Definition CarlaActor.h:174
carla::rpc::ActorState GetActorState() const
Definition CarlaActor.h:106
FVector GetActorAngularVelocity() const
FTransform GetActorGlobalTransform() const
virtual FVector GetActorVelocity() const
FActorData * GetActorData()
Definition CarlaActor.h:205
const FActorInfo * GetActorInfo() const
Definition CarlaActor.h:101
IdType GetActorId() const
Definition CarlaActor.h:81
bool IsStreamReady()
Definition DataStream.h:47
FAsyncDataStreamTmpl< T > MakeAsyncDataStream(const SensorT &Sensor, double Timestamp)
Create a FAsyncDataStream object.
Definition DataStream.h:41
UTrafficLightController * Controller
Definition ActorData.h:132
FVehicleControl Control
Definition ActorData.h:80
float SpeedLimit
Definition ActorData.h:90
carla::rpc::WalkerControl WalkerControl
Definition ActorData.h:103
void BroadcastTick(const UCarlaEpisode &Episode, float DeltaSeconds, bool MapChange, bool PendingLightUpdate)
Send a message to every connected client with the info about the given Episode.
FDataMultiStream Stream
A simulation episode.
FIntVector GetCurrentMapOrigin() const
auto GetId() const
Return the unique id of this episode.
double GetElapsedGameTime() const
Game seconds since the start of this episode.
const FActorRegistry & GetActorRegistry() const
Maps a controller from OpenDrive.
A piece of raw data.
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
Serializes the current state of the whole episode.
A view over an actor and its properties.
Definition ActorInfo.h:18
FVector Velocity
Definition ActorInfo.h:30
Dynamic state of an actor at a certain frame.