CARLA
Loading...
Searching...
No Matches
InertialMeasurementUnit.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"
15
17#include "carla/geom/Math.h"
18#include "carla/ros2/ROS2.h"
20
21#include <limits>
22
23// Based on OpenDRIVE's lon and lat
25 FVector(0.0f, -1.0f, 0.0f);
26
28 const FObjectInitializer &ObjectInitializer)
29 : Super(ObjectInitializer)
30{
31 PrimaryActorTick.bCanEverTick = true;
32 PrimaryActorTick.TickGroup = TG_PostPhysics;
33 RandomEngine = CreateDefaultSubobject<URandomEngine>(TEXT("RandomEngine"));
34 PrevLocation = { FVector::ZeroVector, FVector::ZeroVector };
35 // Initialized to something hight to minimize the artifacts
36 // when the initial values are unknown
37 PrevDeltaTime = std::numeric_limits<float>::max();
38}
39
44
46{
47 Super::Set(ActorDescription);
48 UActorBlueprintFunctionLibrary::SetIMU(ActorDescription, this);
49}
50
52{
53 Super::SetOwner(OwningActor);
54}
55
56// Returns the angular velocity of Actor, expressed in the frame of Actor
58 AActor &Actor)
59{
60 const auto RootComponent = Cast<UPrimitiveComponent>(Actor.GetRootComponent());
61
62 FVector AngularVelocity;
63
64 if (RootComponent != nullptr) {
65 const FQuat ActorGlobalRotation = RootComponent->GetComponentTransform().GetRotation();
66 // The primary's on a Multi-GPU secondary, whose mirror has no motion of its own.
67 const FVector GlobalAngularVelocity = CarlaGetActorAngularVelocityInRadians(Actor);
68 AngularVelocity = ActorGlobalRotation.UnrotateVector(GlobalAngularVelocity);
69 } else {
70 AngularVelocity = FVector::ZeroVector;
71 }
72
73 return AngularVelocity;
74}
75
77 const FVector &Accelerometer)
78{
79 // Normal (or Gaussian or Gauss) distribution will be used as noise function.
80 // A mean of 0.0 is used as a first parameter, the standard deviation is
81 // determined by the client
82 constexpr float Mean = 0.0f;
84 {
85 (float)(Accelerometer.X + RandomEngine->GetNormalDistribution(Mean, StdDevAccel.X)),
86 (float)(Accelerometer.Y + RandomEngine->GetNormalDistribution(Mean, StdDevAccel.Y)),
87 (float)(Accelerometer.Z + RandomEngine->GetNormalDistribution(Mean, StdDevAccel.Z))
88 };
89}
90
92 const FVector &Gyroscope)
93{
94 // Normal (or Gaussian or Gauss) distribution and a bias will be used as
95 // noise function.
96 // A mean of 0.0 is used as a first parameter.The standard deviation and the
97 // bias are determined by the client
98 constexpr float Mean = 0.0f;
100 {
101 (float)(Gyroscope.X + BiasGyro.X + RandomEngine->GetNormalDistribution(Mean, StdDevGyro.X)),
102 (float)(Gyroscope.Y + BiasGyro.Y + RandomEngine->GetNormalDistribution(Mean, StdDevGyro.Y)),
103 (float)(Gyroscope.Z + BiasGyro.Z + RandomEngine->GetNormalDistribution(Mean, StdDevGyro.Z))
104 };
105}
106
108 const float DeltaTime)
109{
110 // Used to convert from UE4's cm to meters
111 constexpr float TO_METERS = 1e-2;
112 // Gravity is configured per-simulation on the game mode; fall back to
113 // Earth's ~9.81 m/s^2 if the CARLA game mode is unavailable.
114 ACarlaGameModeBase* GameMode = UCarlaStatics::GetGameMode(GetWorld());
115 const float GRAVITY = (GameMode != nullptr) ? GameMode->IMUSensorGravity : 9.81f;
116
117 // 2nd derivative of the polynomic (quadratic) interpolation
118 // using the point in current time and two previous steps:
119 // d2[i] = -2.0*(y1/(h1*h2)-y2/((h2+h1)*h2)-y0/(h1*(h2+h1)))
120 const FVector CurrentLocation = GetActorLocation();
121
122 const FVector Y2 = PrevLocation[0];
123 const FVector Y1 = PrevLocation[1];
124 const FVector Y0 = CurrentLocation;
125 const float H1 = DeltaTime;
126 const float H2 = PrevDeltaTime;
127
128 const float H1AndH2 = H2 + H1;
129 const FVector A = Y1 / ( H1 * H2 );
130 const FVector B = Y2 / ( H2 * (H1AndH2) );
131 const FVector C = Y0 / ( H1 * (H1AndH2) );
132 FVector FVectorAccelerometer = TO_METERS * -2.0f * ( A - B - C );
133
134 // Update the previous locations
136 PrevLocation[1] = CurrentLocation;
137 PrevDeltaTime = DeltaTime;
138
139 // Add gravitational acceleration
140 FVectorAccelerometer.Z += GRAVITY;
141
142 FQuat ImuRotation =
143 GetRootComponent()->GetComponentTransform().GetRotation();
144 FVectorAccelerometer = ImuRotation.UnrotateVector(FVectorAccelerometer);
145
146 // Cast from FVector to our Vector3D to correctly send the data in m/s^2
147 // and apply the desired noise function, in this case a normal distribution
148 const carla::geom::Vector3D Accelerometer =
149 ComputeAccelerometerNoise(FVectorAccelerometer);
150
151 return Accelerometer;
152}
153
155{
156 check(GetOwner() != nullptr);
157 const FVector AngularVelocity =
159
160 // A Multi-GPU mirror is attached rigidly: the primary's rotation relative
161 // to the parent, which a spring arm keeps on its own.
162 const FQuat SensorLocalRotation = MirroredLocalRotation.IsSet() ?
163 MirroredLocalRotation.GetValue() :
164 RootComponent->GetRelativeTransform().GetRotation();
165
166 const FVector FVectorGyroscope =
167 SensorLocalRotation.RotateVector(AngularVelocity);
168
169 // Cast from FVector to our Vector3D to correctly send the data in rad/s
170 // and apply the desired noise function, in this case a normal distribution
171 const carla::geom::Vector3D Gyroscope =
172 ComputeGyroscopeNoise(FVectorGyroscope);
173
174 return Gyroscope;
175}
176
178{
179 // Magnetometer: orientation with respect to the North in rad
180 const FVector ForwVect = GetActorForwardVector().GetSafeNormal2D();
181 const float DotProd = FVector::DotProduct(CarlaNorthVector, ForwVect);
182
183 // We check if the dot product is higher than 1.0 due to numerical error
184 if (DotProd >= 1.00f)
185 return 0.0f;
186
187 const float Compass = std::acos(DotProd);
188 // Keep the angle between [0, 2pi)
189 if (FVector::CrossProduct(CarlaNorthVector, ForwVect).Z < 0.0f)
190 return carla::geom::Math::Pi2<float>() - Compass;
191
192 return Compass;
193}
194
195void AInertialMeasurementUnit::PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaTime)
196{
197 TRACE_CPUPROFILER_EVENT_SCOPE(AInertialMeasurementUnit::PostPhysTick);
198 // The parent vehicle can be destroyed before this sensor (client-side
199 // destruction order is not guaranteed); the sensor then receives one more
200 // PostPhysTick with a null owner and the gyroscope owner dereference would
201 // assert, taking down the whole server. Skip the measurement instead.
202 if (GetOwner() == nullptr)
203 {
204 return;
205 }
209
210 auto DataStream = GetDataStream(*this);
211
212 // ROS2
213 #if defined(WITH_ROS2)
214 auto ROS2 = carla::ros2::ROS2::GetInstance();
215 if (ROS2->IsEnabled())
216 {
217 TRACE_CPUPROFILER_EVENT_SCOPE_STR("ROS2 Send");
219 AActor* ParentActor = GetAttachParentActor();
220 if (ParentActor)
221 {
222 FTransform LocalTransformRelativeToParent = GetActorTransform().GetRelativeTransform(ParentActor->GetActorTransform());
223 ROS2->ProcessDataFromIMU(DataStream.GetSensorType(), StreamId, LocalTransformRelativeToParent, AccelerometerValue, GyroscopeValue, CompassValue, this);
224 }
225 else
226 {
227 ROS2->ProcessDataFromIMU(DataStream.GetSensorType(), StreamId, DataStream.GetSensorTransform(), AccelerometerValue, GyroscopeValue, CompassValue, this);
228 }
229 }
230 #endif
231
232 {
233 TRACE_CPUPROFILER_EVENT_SCOPE(AInertialMeasurementUnit::SerializeAndSend);
234 DataStream.SerializeAndSend(*this, AccelerometerValue, GyroscopeValue, CompassValue);
235 }
236}
237
242
244{
245 StdDevGyro = Vec;
246}
247
249{
250 BiasGyro = Vec;
251}
252
257
259{
260 return StdDevGyro;
261}
262
264{
265 return BiasGyro;
266}
267
272
277
279{
280 return CompassValue;
281}
282
284{
285 Super::BeginPlay();
286}
287
288/// World-frame location, the same on every server (a large map rebases each
289/// server's local origin on its own).
290static std::array<double, 3> FIMU_ToWorld(const AActor &Sensor, const FVector &Local)
291{
292 const ALargeMapManager *LargeMap = UCarlaStatics::GetLargeMapManager(Sensor.GetWorld());
293 const FVector World = (LargeMap != nullptr) ? LargeMap->LocalToGlobalLocation(Local) : Local;
294 return {World.X, World.Y, World.Z};
295}
296
297static FVector FIMU_ToLocal(const AActor &Sensor, const std::array<double, 3> &World)
298{
299 const FVector Location(World[0], World[1], World[2]);
300 const ALargeMapManager *LargeMap = UCarlaStatics::GetLargeMapManager(Sensor.GetWorld());
301 return (LargeMap != nullptr) ? LargeMap->GlobalToLocalLocation(Location) : Location;
302}
303
305{
306 if (RandomEngine != nullptr)
307 {
308 State.random_engine = RandomEngine->GetState();
309 }
311 History.older_location = FIMU_ToWorld(*this, PrevLocation[0]);
312 History.last_location = FIMU_ToWorld(*this, PrevLocation[1]);
314 State.imu_history = History;
315 const FQuat LocalRotation = RootComponent->GetRelativeTransform().GetRotation();
316 State.imu_local_rotation = std::array<double, 4>{LocalRotation.X, LocalRotation.Y, LocalRotation.Z, LocalRotation.W};
317}
318
320{
321 if (State.random_engine && (RandomEngine != nullptr))
322 {
323 RandomEngine->SetState(*State.random_engine);
324 }
325 if (State.imu_history)
326 {
327 PrevLocation[0] = FIMU_ToLocal(*this, State.imu_history->older_location);
328 PrevLocation[1] = FIMU_ToLocal(*this, State.imu_history->last_location);
329 PrevDeltaTime = State.imu_history->last_delta_seconds;
330 }
331 if (State.imu_local_rotation)
332 {
333 const auto &Rotation = *State.imu_local_rotation;
334 MirroredLocalRotation = FQuat(Rotation[0], Rotation[1], Rotation[2], Rotation[3]);
335 }
336}
FVector CarlaGetActorAngularVelocityInRadians(const AActor &Actor)
Angular velocity of Actor in the world frame, in rad/s: on a Multi-GPU secondary the primary's (a mir...
static std::array< double, 3 > FIMU_ToWorld(const AActor &Sensor, const FVector &Local)
World-frame location, the same on every server (a large map rebases each server's local origin on its...
static FVector FIMU_ToLocal(const AActor &Sensor, const std::array< double, 3 > &World)
static FVector FIMU_GetActorAngularVelocityInRadians(AActor &Actor)
Base class for the CARLA Game Mode.
float IMUSensorGravity
Gravitational acceleration used by IMU sensors.
float ComputeCompass()
Magnetometer: orientation with respect to the North in rad.
const carla::geom::Vector3D & GetGyroscopeValue() const
carla::geom::Vector3D GyroscopeValue
Gyroscope value calculated after each PostPhysTick.
void GetMirrorState(carla::multigpu::SensorState &State) const override
Multi-GPU: the runtime state this sensor starts its next tick from (noise generator,...
void SetMirrorState(const carla::multigpu::SensorState &State) override
carla::geom::Vector3D AccelerometerValue
Accelerometer value calculated after each PostPhysTick.
float CompassValue
Magnetometer value calculated after each PostPhysTick.
const FVector & GetAccelerationStandardDeviation() const
AInertialMeasurementUnit(const FObjectInitializer &ObjectInitializer)
const carla::geom::Vector3D ComputeGyroscopeNoise(const FVector &Gyroscope)
FVector StdDevAccel
Standard deviation for acceleration settings.
const carla::geom::Vector3D ComputeAccelerometerNoise(const FVector &Accelerometer)
void SetGyroscopeBias(const FVector &Vec)
void SetOwner(AActor *Owner) override
const carla::geom::Vector3D & GetAccelerometerValue() const
static const FVector CarlaNorthVector
Based on OpenDRIVE's lon and lat, North is in (0.0f, -1.0f, 0.0f)
FVector BiasGyro
Bias for gyroscope settings.
carla::geom::Vector3D ComputeGyroscope()
Gyroscope: measures angular velocity in rad/sec.
virtual void PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaTime) override
FVector StdDevGyro
Standard deviation for gyroscope settings.
static FActorDefinition GetSensorDefinition()
TOptional< FQuat > MirroredLocalRotation
Multi-GPU secondary: the primary IMU's rotation relative to its parent.
void Set(const FActorDescription &ActorDescription) override
const FVector & GetGyroscopeBias() const
float PrevDeltaTime
Used to compute the acceleration.
void SetAccelerationStandardDeviation(const FVector &Vec)
carla::geom::Vector3D ComputeAccelerometer(const float DeltaTime)
Accelerometer: measures linear acceleration in m/s^2.
void SetGyroscopeStandardDeviation(const FVector &Vec)
std::array< FVector, 2 > PrevLocation
Used to compute the acceleration.
const FVector & GetGyroscopeStandardDeviation() const
FVector GlobalToLocalLocation(const FVector &InLocation) const
FVector LocalToGlobalLocation(const FVector &InLocation) const
auto GetToken() const
Return the token that allows subscribing to this sensor's stream.
FAsyncDataStream GetDataStream(SensorType &&Self)
Return the FDataStream associated with this sensor.
URandomEngine * RandomEngine
Random Engine used to provide noise for sensor output.
static void SetIMU(const FActorDescription &Description, AInertialMeasurementUnit *IMU)
static ALargeMapManager * GetLargeMapManager(const UObject *WorldContextObject)
static ACarlaGameModeBase * GetGameMode(const UObject *WorldContextObject)
uint32 GetState() const
The engine's state, to continue its exact sequence elsewhere (a Multi-GPU secondary's mirror of a sen...
float GetNormalDistribution(float Mean, float StandardDeviation)
void SetState(uint32 State)
Continue from State, as GetState returned it; an impossible value is ignored.
static std::shared_ptr< ROS2 > GetInstance()
Definition ROS2.h:61
Serializes a stream endpoint.
A definition of a Carla Actor with all the variation and attributes.
A description of a Carla Actor with all its variation.
An IMU's history: its two previous locations (world, cm) and the delta between them; its acceleromete...
Definition sensorSync.h:77
std::array< double, 3 > last_location
Definition sensorSync.h:79
std::array< double, 3 > older_location
Definition sensorSync.h:78
One record of frame data packet 27: the runtime state a sensor of the primary starts this frame's tic...
Definition sensorSync.h:87