CARLA
Loading...
Searching...
No Matches
HSSLidar.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//
7// Lidar implemented by Csonthó Mihály based on RayCastLidar code.
8
10#include "Carla.h"
12
14#include "carla/geom/Math.h"
15#include "carla/ros2/ROS2.h"
16#include "carla/geom/Location.h"
18
19#include <util/ue-header-guard-begin.h>
20#include "DrawDebugHelpers.h"
21#include "Engine/CollisionProfile.h"
22#include "Kismet/KismetMathLibrary.h"
23#include "PhysicsEngine/PhysicsObjectExternalInterface.h"
24#include "Async/ParallelFor.h"
25#include <util/ue-header-guard-end.h>
26
27#include <cmath>
28#include <numeric>
29
34
35AHSSLidar::AHSSLidar(const FObjectInitializer& ObjectInitializer)
36 : Super(ObjectInitializer)
37{
38 RandomEngine = CreateDefaultSubobject<URandomEngine>(TEXT("RandomEngine"));
40}
41
42void AHSSLidar::Set(const FActorDescription &ActorDescription)
43{
44 ASensor::Set(ActorDescription);
45 FLidarDescription LidarDescription;
46 UActorBlueprintFunctionLibrary::SetLidar(ActorDescription, LidarDescription);
47 Set(LidarDescription);
48}
49
50void AHSSLidar::Set(const FLidarDescription &LidarDescription)
51{
52 Description = LidarDescription;
56
57 // Apply the user-supplied noise_seed; the constructor's SetSeed ran before
58 // Description was populated from the blueprint attributes, so the engine
59 // would otherwise be stuck on the FLidarDescription default seed.
61
63 // Guard against dropoff_intensity_limit == 0: alpha = x/0 would propagate
64 // inf/NaN through PostprocessDetection and silently disable the
65 // intensity-based dropoff branch. With alpha = 0 the postprocess simply
66 // falls back to the constant beta probability for low-intensity points.
67 DropOffAlpha = (Description.DropOffIntensityLimit > std::numeric_limits<float>::epsilon())
69 : 0.0f;
70 DropOffGenActive = Description.DropOffGenRate > std::numeric_limits<float>::epsilon();
71}
72
73void AHSSLidar::SendData(const float DeltaTime)
74{
75 TRACE_CPUPROFILER_EVENT_SCOPE(AHSSLidar::SendData);
76 auto DataStream = GetDataStream(*this);
77 auto SensorTransform = DataStream.GetSensorTransform();
78
79 {
80 TRACE_CPUPROFILER_EVENT_SCOPE_STR("Send Stream");
81 DataStream.SerializeAndSend(*this, LidarData, DataStream.PopBufferFromPool());
82 }
83 // ROS2
84 #if defined(WITH_ROS2)
86 if (ROS2->IsEnabled())
87 {
88 TRACE_CPUPROFILER_EVENT_SCOPE_STR("ROS2 Send");
90 AActor* ParentActor = GetAttachParentActor();
91 if (ParentActor)
92 {
93 FTransform LocalTransformRelativeToParent = GetActorTransform().GetRelativeTransform(ParentActor->GetActorTransform());
94 ROS2->ProcessDataFromLidar(DataStream.GetSensorType(), StreamId, LocalTransformRelativeToParent, LidarData, this);
95 }
96 else
97 {
98 ROS2->ProcessDataFromLidar(DataStream.GetSensorType(), StreamId, SensorTransform, LidarData, this);
99 }
100 }
101 #endif
102}
103
105 const FHitResult& HitInfo, const FTransform& InverseSensorTransform) const
106{
107 FDetection Detection;
108 const FVector HitPoint = HitInfo.ImpactPoint;
109 Detection.point = InverseSensorTransform.TransformPosition(HitPoint);
110
111 const float Distance = Detection.point.Length();
112
113 const float AttenAtm = Description.AtmospAttenRate;
114 const float AbsAtm = exp(-AttenAtm * Distance);
115
116 const float IntRec = AbsAtm;
117
118 Detection.intensity = IntRec;
119
120 return Detection;
121}
122
123void AHSSLidar::PreprocessRays(uint32_t Channels, uint32_t MaxPointsPerChannel)
124{
125 Super::PreprocessRays(Channels, MaxPointsPerChannel);
126
127 if (!DropOffGenActive)
128 {
129 return;
130 }
131
132 for (auto ch = 0u; ch < Channels; ch++) {
133 for (auto p = 0u; p < MaxPointsPerChannel; p++) {
136 }
137 }
138}
139
141{
142 if (Description.NoiseStdDev > std::numeric_limits<float>::epsilon()) {
143 const auto ForwardVector = Detection.point.MakeUnitVector();
144 const auto Noise = ForwardVector * RandomEngine->GetNormalDistribution(0.0f, Description.NoiseStdDev);
145 Detection.point += Noise;
146 }
147
148 const float Intensity = Detection.intensity;
149 if(Intensity > Description.DropOffIntensityLimit)
150 return true;
151 else
152 return RandomEngine->GetUniformFloat() < DropOffAlpha * Intensity + DropOffBeta;
153}
154
155void AHSSLidar::ResetDetections(uint32_t Channels, uint32_t MaxPointsPerChannel)
156{
157 ResetChannelDetections(Detections, Channels, MaxPointsPerChannel);
158}
159
160void AHSSLidar::WriteDetectionAsync(uint32_t Channel, const FHitResult& HitInfo, const FTransform& InverseSensorTransform, const FVector& /*SensorLocation*/)
161{
162 DEBUG_ASSERT(GetChannelCount() > Channel);
163 Detections[Channel].emplace_back(ComputeDetection(HitInfo, InverseSensorTransform));
164}
165
166void AHSSLidar::ComputeAndSaveDetections(const FTransform& SensorTransform)
167{
168 TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
169
171 [this](FDetection& Detection) { return PostprocessDetection(Detection); });
172
173#if WITH_EDITOR
175 {
177 }
178#endif
179}
180
181// SimulateLidar(float DeltaTime)
182//
183// Fixed-sweep scan centred on the sensor's forward axis. Casts
184// HorizontalFov / HorizontalResolution rays per vertical channel each tick;
185// no horizontal-angle accumulation across ticks (the sensor does not rotate).
186static float SnapToStep(float value, float step)
187{
188 return std::round(value / step) * step;
189}
190
191void AHSSLidar::SimulateLidar(const float DeltaTime, bool bLockPhysics)
192{
193 TRACE_CPUPROFILER_EVENT_SCOPE(AHSSLidar::SimulateLidar);
194 const uint32 ChannelCount = Description.Channels;
195
196 // horizontal_resolution and horizontal_fov are user-supplied attributes;
197 // clamp them to safe positive values before dividing. A zero or negative
198 // resolution would divide to inf/NaN and then cast to a huge uint32,
199 // triggering an OOM-sized allocation in ResetDetections/PreprocessRays.
200 constexpr float MinHorizontalResolution = 0.01f;
201 const float HorizontalResolution = FMath::Max(
202 SnapToStep(Description.HorizontalResolution, MinHorizontalResolution),
203 MinHorizontalResolution);
204 const float HorizontalFov = FMath::Max(Description.HorizontalFov, 0.0f);
205 const uint32 PointsToScanWithOneLaser = static_cast<uint32>(
206 FMath::RoundHalfFromZero(HorizontalFov / HorizontalResolution));
207
208 if (PointsToScanWithOneLaser == 0)
209 {
210 UE_LOG(
211 LogCarla,
212 Warning,
213 TEXT("%s: no points requested this frame, increase horizontal_fov or decrease horizontal_resolution."),
214 *GetName());
215 return;
216 }
217
218 check(ChannelCount == LaserAngles.Num());
219
220 ResetDetections(ChannelCount, PointsToScanWithOneLaser);
221 PreprocessRays(ChannelCount, PointsToScanWithOneLaser);
222
223 const FTransform ActorTransform = GetTransform();
224 const FTransform InverseSensorTransform = ActorTransform.Inverse();
225 const FVector LidarBodyLocation = ActorTransform.GetLocation();
226 const FRotator LidarBodyRotation = ActorTransform.Rotator();
227
228 auto RunChannelRaycasts = [&]()
229 {
230 TRACE_CPUPROFILER_EVENT_SCOPE(ParallelFor);
231 ParallelFor(ChannelCount, [&](int32 idxChannel) {
232 TRACE_CPUPROFILER_EVENT_SCOPE(ParallelForTask);
233
234 FCollisionQueryParams TraceParams = FCollisionQueryParams(FName(TEXT("Laser_Trace")), true, this);
235 TraceParams.bTraceComplex = true;
236 TraceParams.bReturnPhysicalMaterial = false;
237
238 const float VertAngle = LaserAngles[idxChannel];
239 const std::vector<bool>& ChannelPreprocessCondition = RayPreprocessCondition[idxChannel];
240
241 for (auto idxPtsOneLaser = 0u; idxPtsOneLaser < PointsToScanWithOneLaser; idxPtsOneLaser++) {
242 if (!ChannelPreprocessCondition[idxPtsOneLaser])
243 {
244 continue;
245 }
246
247 FHitResult HitResult;
248 const float HorizAngle =
249 -HorizontalFov / 2.0f + static_cast<float>(idxPtsOneLaser) * HorizontalResolution;
250
251 if (ShootLaser(VertAngle, HorizAngle, HitResult, TraceParams, LidarBodyLocation, LidarBodyRotation)) {
252 WriteDetectionAsync(idxChannel, HitResult, InverseSensorTransform, LidarBodyLocation);
253 }
254 };
255 });
256 };
257
258 if (bLockPhysics)
259 {
260 auto LockedPhysObject = FPhysicsObjectExternalInterface::LockRead(GetWorld()->GetPhysicsScene());
261 RunChannelRaycasts();
262 LockedPhysObject.Release();
263 }
264 else
265 {
266 RunChannelRaycasts();
267 }
268
269 ComputeAndSaveDetections(ActorTransform);
270}
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
static float SnapToStep(float value, float step)
Definition HSSLidar.cpp:186
virtual void Set(const FActorDescription &Description) override
Definition HSSLidar.cpp:42
void ComputeAndSaveDetections(const FTransform &SensorTransform) override
Copies the per-channel detections into the LidarData structure.
Definition HSSLidar.cpp:166
carla::sensor::data::LidarData FLidarData
Definition HSSLidar.h:31
float DropOffAlpha
Slope for the intensity dropoff of lidar points: alpha*Intensity + beta where alpha = dropoff_zero_in...
Definition HSSLidar.h:67
void ResetDetections(uint32_t Channels, uint32_t MaxPointsPerChannel) override
Clear the per-channel detection buffers.
Definition HSSLidar.cpp:155
void WriteDetectionAsync(uint32_t Channel, const FHitResult &HitInfo, const FTransform &InverseSensorTransform, const FVector &SensorLocation) override
Computes the detection of a hit inside the raycast ParallelFor.
Definition HSSLidar.cpp:160
AHSSLidar(const FObjectInitializer &ObjectInitializer)
Definition HSSLidar.cpp:35
virtual void SimulateLidar(const float DeltaTime, bool bLockPhysics=true) override
Definition HSSLidar.cpp:191
FDetection ComputeDetection(const FHitResult &HitInfo, const FTransform &InverseSensorTransform) const
Definition HSSLidar.cpp:104
FLidarData LidarData
Definition HSSLidar.h:57
float DropOffBeta
Definition HSSLidar.h:68
std::vector< std::vector< FDetection > > Detections
Definition HSSLidar.h:59
static FActorDefinition GetSensorDefinition()
Definition HSSLidar.cpp:30
bool PostprocessDetection(FDetection &Detection) const
Definition HSSLidar.cpp:140
bool DropOffGenActive
Enable/Disable general dropoff of lidar points.
Definition HSSLidar.h:62
virtual void SendData(const float DeltaTime) override
Definition HSSLidar.cpp:73
void PreprocessRays(uint32_t Channels, uint32_t MaxPointsPerChannel) override
Method that allow to preprocess if the rays will be traced.
Definition HSSLidar.cpp:123
TArray< float > PointCloudLidarData
WITH_EDITOR-only mirror of the point cloud, used by the debug save path.
Definition HSSLidar.h:71
std::vector< uint32_t > PointsPerChannel
static void CompactAndWriteDetections(std::vector< std::vector< DetectionT > > &Detections, std::vector< uint32_t > &OutPointsPerChannel, uint32_t Channels, LidarDataT &LidarData, PostprocessT &&Postprocess)
Post-processes the detections channel by channel, drops the rejected ones and writes the rest to Lida...
void CreateLasers()
Creates a Laser for each channel.
static void ResetChannelDetections(std::vector< std::vector< DetectionT > > &Detections, uint32_t Channels, uint32_t MaxPointsPerChannel)
Clears every channel buffer and reserves room for a full scan.
static void CopyDetectionsToPointCloud(const std::vector< std::vector< DetectionT > > &Detections, const std::vector< uint32_t > &InPointsPerChannel, TArray< float > &PointCloud)
Copies the kept detections into a flat x, y, z, intensity array (editor debug point cloud).
std::vector< std::vector< bool > > RayPreprocessCondition
FLidarDescription Description
bool ShootLaser(const float VerticalAngle, float HorizontalAngle, FHitResult &HitResult, FCollisionQueryParams &TraceParams, const FVector &LidarBodyLocation, const FRotator &LidarBodyRotation) const
Shoot a laser ray-trace, return whether the laser hit something.
virtual void Set(const FActorDescription &Description)
Definition Sensor.cpp:45
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.
void SetSeed(int32 InSeed)
Definition Sensor.cpp:104
static void SetLidar(const FActorDescription &Description, FLidarDescription &Lidar)
static FActorDefinition MakeLidarDefinition(const FString &Id)
float GetUniformFloat()
float GetNormalDistribution(float Mean, float StandardDeviation)
static std::shared_ptr< ROS2 > GetInstance()
Definition ROS2.h:61
Helper class to store and serialize the data generated by a Lidar.
Definition LidarData.h:54
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.
float DropOffAtZeroIntensity
General drop off rate.
float HorizontalFov
Horizontal field of view.
int RandomSeed
Random seed for the noise/dropoff used by this sensor.
float DropOffGenRate
General drop off rate.
uint32 Channels
Number of lasers.
float DropOffIntensityLimit
General drop off rate.
float AtmospAttenRate
Attenuation Rate in the atmosphere in m^-1.
float HorizontalResolution
Horizontal resolution of the lidar, in degrees.
Vector3D MakeUnitVector(const float epsilon=2.0f *std::numeric_limits< float >::epsilon()) const
Return a unit-length copy of this vector.