Agrarsense
Radar.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "Radar.h"
7
9
10#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
11#include "NiagaraFunctionLibrary.h"
12#include "CollisionQueryParams.h"
13#include "Async/ParallelFor.h"
14#include "NiagaraComponent.h"
15#include "DrawDebugHelpers.h"
16
17ARadar::ARadar(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
18{
19 PrimaryActorTick.bCanEverTick = true;
20}
21
22void ARadar::Init(FRadarParameters parameters, bool SimulateSensor)
23{
24 SetSimulateSensor(SimulateSensor);
25 SetRadarParameters(parameters);
26
27 World = GetWorld();
28
30
31 // Load pointcloud Niagara particle system
32 UNiagaraSystem* NS = LoadObject<UNiagaraSystem>(nullptr, TEXT("/Game/Agrarsense/Particles/PointCloud/NS_PointCloudFast"), nullptr, LOAD_None, nullptr);
33 if (NS)
34 {
35 NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(World, NS, GetActorLocation());
37 {
38 // Hide this NiagaraComponent for all Camera sensors.
39 HideComponentForAllCameras(Cast<UPrimitiveComponent>(NiagaraComponent));
40
41 NiagaraComponent->SetAffectDynamicIndirectLighting(false);
42 NiagaraComponent->SetAffectDistanceFieldLighting(false);
43 NiagaraComponent->SetCastContactShadow(false);
44 NiagaraComponent->SetCastShadow(false);
45 }
46 }
47#if WITH_EDITOR
48 else
49 {
50 UE_LOG(LogTemp, Warning, TEXT("Radar.cpp: Couldn't find Niagara particle system."));
51 }
52#endif
53}
54
56{
57 Super::BeginPlay();
58
59 if (!World)
60 {
61 World = GetWorld();
62 }
63
64 PrevLocation = GetActorLocation();
65
66 // Create RadarMessage
67 RadarMessage = MakeShared<ROSMessages::sensor_msgs::PointCloud2>();
68 RadarMessage->header.frame_id = "world";
69 RadarMessage->height = 1;
70 RadarMessage->is_dense = true;
71 RadarMessage->fields.SetNum(7);
72
73 RadarMessage->fields[0].name = "x";
74 RadarMessage->fields[0].offset = 0;
75 RadarMessage->fields[0].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
76 RadarMessage->fields[0].count = 1;
77
78 RadarMessage->fields[1].name = "y";
79 RadarMessage->fields[1].offset = 4;
80 RadarMessage->fields[1].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
81 RadarMessage->fields[1].count = 1;
82
83 RadarMessage->fields[2].name = "z";
84 RadarMessage->fields[2].offset = 8;
85 RadarMessage->fields[2].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
86 RadarMessage->fields[2].count = 1;
87
88 RadarMessage->fields[3].name = "Range";
89 RadarMessage->fields[3].offset = 12;
90 RadarMessage->fields[3].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
91 RadarMessage->fields[3].count = 1;
92
93 RadarMessage->fields[4].name = "Velocity";
94 RadarMessage->fields[4].offset = 16;
95 RadarMessage->fields[4].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
96 RadarMessage->fields[4].count = 1;
97
98 RadarMessage->fields[5].name = "AzimuthAngle";
99 RadarMessage->fields[5].offset = 20;
100 RadarMessage->fields[5].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
101 RadarMessage->fields[5].count = 1;
102
103 RadarMessage->fields[6].name = "ElevationAngle";
104 RadarMessage->fields[6].offset = 28;
105 RadarMessage->fields[6].datatype = ROSMessages::sensor_msgs::PointCloud2::PointField::FLOAT32;
106 RadarMessage->fields[6].count = 1;
107
108 RadarMessage->point_step = 56;
109}
110
111void ARadar::EndPlay(const EEndPlayReason::Type EndPlayReason)
112{
113 Super::EndPlay(EndPlayReason);
114
115 HitLocations.Empty();
116
117 detections.clear();
118 Rays.clear();
119
120 if (RadarMessage.IsValid())
121 {
122 RadarMessage.Reset();
123 }
124
126 {
127 NiagaraComponent->UnregisterComponent();
128 NiagaraComponent->DestroyComponent();
129 }
130
131 World = nullptr;
132}
133
134void ARadar::Tick(float DeltaTime)
135{
136 TRACE_CPUPROFILER_EVENT_SCOPE(ARadar::Tick);
137
138 if (!CanSimulateSensor())
139 {
140 return;
141 }
142
143 SimulateRadar(DeltaTime);
144
146 {
149 }
150}
151
153{
154 if (!CanSimulateSensor())
155 {
156 // We are not currently simulating sensor, we can safely change parameters here
157 SetRadarParameters(Parameters);
158 }
159 else
160 {
161 // Change parameters at the end of the frame
162 TempRadarParameters = Parameters;
164 }
165}
166
168{
169 VisualizeParticles = Visualize;
171 {
172 NiagaraComponent->SetRenderingEnabled(VisualizeParticles);
173 }
174}
175
177{
178 RadarParameters = Parameters;
179
182
183 detections.clear();
184 detections.shrink_to_fit();
186}
187
188void ARadar::CalculateCurrentVelocity(const float DeltaTime)
189{
190 const FVector RadarLocation = GetActorLocation();
191 CurrentVelocity = (RadarLocation - PrevLocation) / DeltaTime;
192 PrevLocation = RadarLocation;
193}
194
195void ARadar::SimulateRadar(float DeltaTime)
196{
197 TRACE_CPUPROFILER_EVENT_SCOPE(ARadar::SimulateRadar);
198
199 CalculateCurrentVelocity(DeltaTime);
200
201 auto TraceParams = FCollisionQueryParams(FName(TEXT("Laser_Trace")), true, this);
202 TraceParams.bTraceComplex = true;
203 TraceParams.bReturnPhysicalMaterial = false;
204
205 constexpr float TO_METERS = 1e-2;
206 const FTransform& ActorTransform = GetActorTransform();
207 const FRotator& TransformRotator = ActorTransform.Rotator();
208 const FVector& RadarLocation = GetActorLocation();
209 const FVector& ForwardVector = GetActorForwardVector();
210 const FVector TransformXAxis = ActorTransform.GetUnitAxis(EAxis::X);
211 const FVector TransformYAxis = ActorTransform.GetUnitAxis(EAxis::Y);
212 const FVector TransformZAxis = ActorTransform.GetUnitAxis(EAxis::Z);
213
214 const float Range = RadarParameters.Range * 100;
215 const float HorizontalFOV = RadarParameters.HorizontalFOV;
216 const float VerticalFOV = RadarParameters.VerticalFOV;
217 const float PointsPerSecond = RadarParameters.PointsPerSecond;
218
219 bool DebugLines = false;
220 EParallelForFlags Flag = EParallelForFlags::Unbalanced;
221 if (DebugLines)
222 {
223 Flag = EParallelForFlags::ForceSingleThread;
224 }
225
226 // Maximum radar radius in horizontal and vertical direction
227 const float MaxRx = FMath::Tan(FMath::DegreesToRadians(HorizontalFOV * 0.5f)) * Range;
228 const float MaxRy = FMath::Tan(FMath::DegreesToRadians(VerticalFOV * 0.5f)) * Range;
229 const int NumPoints = (int)(PointsPerSecond * DeltaTime);
230
231 Rays.clear();
232 Rays.resize(NumPoints);
233 for (int32 i = 0; i < Rays.size(); i++)
234 {
235 Rays[i].Radius = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
236 Rays[i].Angle = 0.0f + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX / (2 * PI - 0.0f)));
237 Rays[i].Hit = false;
238 }
239
240 // Linetrace in parallel
241 ParallelFor(NumPoints, [&](int32 idx)
242 {
243 FHitResult OutHit(ForceInit);
244 const float Radius = Rays[idx].Radius;
245 const float Angle = Rays[idx].Angle;
246
247 float Sin, Cos;
248 FMath::SinCos(&Sin, &Cos, Angle);
249
250 const FVector EndLocation = RadarLocation + TransformRotator.RotateVector({
251 Range,
252 MaxRx * Radius * Cos,
253 MaxRy * Radius * Sin
254 });
255
256 bool hit = false;
257
258#ifdef ParallelLineTraceSingleByChannel_EXISTS
259 hit = World->ParallelLineTraceSingleByChannel(
260 OutHit,
261 RadarLocation,
262 EndLocation,
263 ECC_GameTraceChannel3,
264 TraceParams,
265 FCollisionResponseParams::DefaultResponseParam);
266#else
267 hit = World->LineTraceSingleByChannel(
268 OutHit,
269 RadarLocation,
270 EndLocation,
271 ECC_GameTraceChannel3,
272 TraceParams,
273 FCollisionResponseParams::DefaultResponseParam);
274#endif
275
276#ifdef UE_BUILD_DEBUG
277 if (DebugLines)
278 {
279 DrawDebugLine(World, RadarLocation, EndLocation, FColor::Green, false, 1.0f);
280 }
281#endif
282
283 const TWeakObjectPtr<AActor> Actor = OutHit.GetActor();
284 if (hit && Actor.IsValid())
285 {
286 auto point = OutHit.ImpactPoint;
287 point *= 1e-2;
288 Rays[idx].HitPoint = point;
289 Rays[idx].Hit = true;
290 Rays[idx].RelativeVelocity = CalculateRelativeVelocity(OutHit, RadarLocation, Actor->GetVelocity());
291 Rays[idx].AzimuthAndElevation = FMath::GetAzimuthAndElevation(
292 (EndLocation - RadarLocation).GetSafeNormal() * Range,
293 TransformXAxis,
294 TransformYAxis,
295 TransformZAxis
296 );
297 Rays[idx].Distance = OutHit.Distance * TO_METERS;
298 }
299 }, Flag);
300
301
303 {
304 HitLocations.Empty();
305 }
306
307 // Clear previous detections
308 detections.clear();
309
310 // Write the detections to the output structure
311 for (auto& ray : Rays)
312 {
313 if (ray.Hit)
314 {
315 detections.push_back({
316 ray.RelativeVelocity,
317 (float)ray.AzimuthAndElevation.X,
318 (float)ray.AzimuthAndElevation.Y,
319 ray.Distance
320 });
321
323 {
324 HitLocations.Add(ray.HitPoint);
325 }
326 }
327 }
328
330 {
331 int numberOfHits = HitLocations.Num();
332 NiagaraComponent->SetVariableFloat(NiagaraPointsFloat, float(numberOfHits));
333 NiagaraComponent->SetVariableInt(NiagaraPointsInt, numberOfHits);
334 UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector(NiagaraComponent, NiagaraHitPoints, HitLocations);
335 }
336
338}
339
340float ARadar::CalculateRelativeVelocity(const FHitResult& OutHit, const FVector& RadarLocation, const FVector ActorVelocity)
341{
342 constexpr float TO_METERS = 1e-2;
343
344 const FVector TargetVelocity = ActorVelocity;
345 const FVector TargetLocation = (OutHit.ImpactPoint * TO_METERS);
346 const FVector Direction = (TargetLocation - RadarLocation).GetSafeNormal();
347 const FVector DeltaVelocity = (TargetVelocity - CurrentVelocity);
348 const float V = TO_METERS * FVector::DotProduct(DeltaVelocity, Direction);
349
350 return V;
351}
352
354{
355 UTopic* Topic = GetROSTopic();
356
357 if (!SendDataToROS
358 || !Topic
359 || !RadarMessage.IsValid()
360 || detections.empty())
361 {
362 return;
363 }
364
365 int detectionsSize = detections.size();
366 RadarMessage->width = detectionsSize;
367 RadarMessage->row_step = detectionsSize * RadarMessage->point_step;
368
369 TUniquePtr<uint8> data(new uint8[RadarMessage->row_step]);
370 int pointStep = RadarMessage->point_step;
371
372 int i = 0;
373 for (const FRadarDetection& detection : detections)
374 {
375 RadarData params =
376 {
377 detection.depth * cos(detection.azimuth) * cos(-detection.altitude),
378 detection.depth * sin(-detection.azimuth) * cos(detection.altitude),
379 detection.depth * sin(detection.altitude),
380 detection.depth,
381 detection.velocity,
382 detection.azimuth,
383 detection.altitude
384 };
385
386 memcpy(data.Get() + i * pointStep, &params, sizeof(params));
387 i++;
388 }
389
390 RadarMessage->data_ptr = data.Get();
391
392 Topic->Publish(RadarMessage);
393}
void BeginPlay() override
Definition: Radar.cpp:55
UWorld * World
Definition: Radar.h:134
FVector CurrentVelocity
Definition: Radar.h:107
void ChangeRadarParameters(FRadarParameters Parameters)
Definition: Radar.cpp:152
float CalculateRelativeVelocity(const FHitResult &OutHit, const FVector &RadarLocation, const FVector ActorVelocity)
Definition: Radar.cpp:340
std::vector< RayData > Rays
Definition: Radar.h:131
UNiagaraComponent * NiagaraComponent
Definition: Radar.h:88
FVector PrevLocation
Definition: Radar.h:110
void SendRadarData()
Definition: Radar.cpp:353
TSharedPtr< ROSMessages::sensor_msgs::PointCloud2 > RadarMessage
Definition: Radar.h:149
void SetVisualizeParticles(bool Visualize)
Definition: Radar.cpp:167
virtual void Tick(float DeltaTime) override
Definition: Radar.cpp:134
void Init(FRadarParameters parameters, bool SimulateSensor=true)
Definition: Radar.cpp:22
void SimulateRadar(float DeltaTime)
Definition: Radar.cpp:195
ARadar(const FObjectInitializer &ObjectInitializer)
Definition: Radar.cpp:17
void CalculateCurrentVelocity(const float DeltaTime)
Definition: Radar.cpp:188
FRadarParameters RadarParameters
Definition: Radar.h:95
FRadarParameters TempRadarParameters
Definition: Radar.h:97
std::vector< FRadarDetection > detections
Definition: Radar.h:132
TArray< FVector > HitLocations
Definition: Radar.h:93
void SetRadarParameters(FRadarParameters Parameters)
Definition: Radar.cpp:176
bool RadarParametersChanged
Definition: Radar.h:136
bool VisualizeParticles
Definition: Radar.h:90
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: Radar.cpp:111
static const FName NiagaraHitPoints
Definition: Sensor.h:358
bool CanSimulateSensor() const
Definition: Sensor.h:161
static void HideComponentForAllCameras(UPrimitiveComponent *PrimitiveComponent)
Definition: Sensor.cpp:255
static const FName NiagaraPointsFloat
Definition: Sensor.h:360
void SetSimulateSensor(bool SimulateSensor)
Definition: Sensor.h:151
UTopic * GetROSTopic() const
Definition: Sensor.h:141
bool SendDataToROS
Definition: Sensor.h:344
virtual void CreateROSTopic()
Definition: Sensor.cpp:146
static const FName NiagaraPointsInt
Definition: Sensor.h:357
float velocity
Definition: Radar.h:144