18#include "GeoReferencingSystem.h"
19#include "Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
20#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
21#include "Math/UnrealMathUtility.h"
22#include "NiagaraFunctionLibrary.h"
23#include "Misc/FileHelper.h"
24#include "NiagaraComponent.h"
25#include "Components/PrimitiveComponent.h"
27#include "Async/ParallelFor.h"
28#include "Engine/GameViewportClient.h"
29#include "Engine/Engine.h"
30#include "Async/Async.h"
31#include "Engine/World.h"
32#include "CollisionQueryParams.h"
36ALidar::ALidar(
const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
39 PrimaryActorTick.bCanEverTick =
false;
62 UWorld* World = GetWorld();
66 for (
const auto& ColorEntry : Colors)
68 FName SemanticName = FName(*ColorEntry.Key);
75 CachedNoneColor = (NoneColor.R << 16) | (NoneColor.G << 8) | (NoneColor.B << 0);
76 CachedSnowflakeColor = (SnowflakeColor.R << 16) | (SnowflakeColor.G << 8) | (SnowflakeColor.B << 0);
93 UNiagaraSystem* NS = LoadObject<UNiagaraSystem>(
nullptr, TEXT(
"/Game/Agrarsense/Particles/PointCloud/NS_PointCloudSemanticFast"),
nullptr, LOAD_None,
nullptr);
96 NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(World, NS, GetActorLocation());
113 UE_LOG(LogTemp, Warning, TEXT(
"Lidar.cpp: Couldn't find Niagara particle system."));
120 Super::EndPlay(EndPlayReason);
122 UWorld* World = GetWorld();
179 Points.resize(NumberOfLasers);
185 for (int32 i = 0; i < NumberOfLasers; ++i)
197 SimulatorLog::Log(
"Lidar sensor: It's highly advisable to turn off VisualizePointcloud when PointsPerSecond exceed 1 million.");
221 TArray<FVector> EmptyHitpoints;
222 TArray<FLinearColor> EmptyColors;
245 NiagaraComponent->SetVariableFloat(FName(
"User.LifeTime"), ParticleLifeTime);
253 std::vector<FPointData> HitPoints;
261 const FTransform& ActorTransform = GetTransform();
262 const FVector LidarBodyLocation = ActorTransform.GetLocation();
263 const FRotator LidarBodyRotation = ActorTransform.Rotator();
264 const FQuat LidarQuat = FQuat(LidarBodyRotation);
271 const float AngleDistanceOfLaserMeasure = AngleDistanceOfTick / PointsToScanWithOneChannel;
274 const FColor Color = FColor(255, 255, 255);
275 const int32 RgbDefault = (Color.R << 16) | (Color.G << 8) | (Color.B << 0);
277 FCollisionQueryParams TraceParams = FCollisionQueryParams(FName(TEXT(
"Laser_Trace")),
true,
this);
278 TraceParams.bReturnPhysicalMaterial =
false;
279 TraceParams.bReturnFaceIndex =
false;
301 const float HorizontalFOVHalf = HorizontalFOV / 2;
303 const UWorld* World = GetWorld();
308 ParallelFor(ChannelCount, [&](int32 Channel)
310 std::vector<FPointData>& ChannelPoints =
Points[Channel];
312 int32 RGB = RgbDefault;
313 const bool UseHorizontalNoise = FMath::RandBool();
315 FHitResult HitResult;
318 const float VerticalRad = FMath::DegreesToRadians(VerticalAngle);
319 const float VCos = FMath::Cos(VerticalRad);
320 const float VSin = FMath::Sin(VerticalRad);
325 for (int32 i = 0; i < PointsToScanWithOneChannel; i++)
329 const float HorizontalRad = FMath::DegreesToRadians(LaserHorizontalAngle);
330 const FVector LocalDirection(VCos * FMath::Cos(HorizontalRad), VCos * FMath::Sin(HorizontalRad), VSin);
331 const FVector EndTrace = LidarBodyLocation + Range * LidarQuat.RotateVector(LocalDirection);
333 if (
ShootLaser(World, HitResult, EndTrace, TraceParams, LidarBodyLocation, Channel, Semantic, RgbDefault, UseLidarNoiseModel))
340 if (NeedsExtraProcessing)
346 const FVector& HitPoint = HitResult.ImpactPoint;
347 ChannelPoints.emplace_back(HitPoint.X, HitPoint.Y, HitPoint.Z, RGB);
356 }, EParallelForFlags::Unbalanced);
379 if (UseSnowTerrainAdjustment)
398 float NoiseValue = Distribution(
Generator);
399 const FVector DirectionToSensor = ((LidarBodyLocation * 1e-2) - HitResult.ImpactPoint).GetSafeNormal();
400 const FVector Noise = DirectionToSensor * NoiseValue;
401 HitResult.ImpactPoint += Noise;
406 const FVector ForwardVector = (HitResult.ImpactPoint - LidarBodyLocation).GetSafeNormal();
407 const FVector RightVector = FVector::CrossProduct(ForwardVector, FVector::UpVector).GetSafeNormal();
409 FVector ChosenDirection;
410 if (UseHorizontalNoise)
412 ChosenDirection = RightVector;
416 FVector UpVector = FVector::CrossProduct(ForwardVector, RightVector).GetSafeNormal();
417 ChosenDirection = UpVector;
421 const float NoiseValue = Distribution(
Generator);
423 HitResult.ImpactPoint += ChosenDirection * NoiseValue;
428 AActor* HitActor = HitResult.GetActor();
431 if (HitActor && HitActor->Tags.Contains(
TerrainTag))
447 if (GEngine && GEngine->GameViewport && GEngine->GameViewport->bDisableWorldRendering)
454 const int32 NumberOfHits = HitPoints.size();
456 TArray<FVector> HitLocations;
457 HitLocations.SetNumUninitialized(NumberOfHits);
459 TArray<FLinearColor> Colors;
460 Colors.Init(FLinearColor::Green, NumberOfHits);
462 FVector* RESTRICT HitLocationPtr = HitLocations.GetData();
463 FLinearColor* RESTRICT ColorsPtr = Colors.GetData();
464 const FPointData* RESTRICT HitPointPtr = HitPoints.data();
466 for (int32 i = 0; i < NumberOfHits; ++i)
470 HitLocationPtr[i].
X = Point.
X;
471 HitLocationPtr[i].Y = -Point.
Y;
472 HitLocationPtr[i].Z = Point.
Z;
476 const uint32 RGB = Point.
RGB;
477 const uint8 R = (RGB >> 16) & 0xFF;
478 const uint8 G = (RGB >> 8) & 0xFF;
479 const uint8 B = RGB & 0xFF;
481 ColorsPtr[i].R = R / 255.0f;
482 ColorsPtr[i].G = G / 255.0f;
483 ColorsPtr[i].B = B / 255.0f;
499 size_t TotalSize = 0;
500 for (
const std::vector<FPointData>& Vec :
Points)
502 TotalSize += Vec.size();
506 for (std::vector<FPointData>& HitPoints :
Points)
566 auto* HitComponent = HitResult.Component.Get();
570 TArray<FName>& CompTags = HitComponent->ComponentTags;
571 if (!CompTags.IsEmpty())
573 FName Tag = CompTags[0];
575 return (Color.R << 16) | (Color.G << 8) | (Color.B << 0);
588bool ALidar::ShootLaser(
const UWorld* World, FHitResult& HitResult,
const FVector& EndTrace,
const FCollisionQueryParams& TraceParams,
589 const FVector& LidarBodyLocation,
const int32 Channel,
const bool Semantic,
const uint32 RGBDefault,
const bool UseLidarNoiseModel)
591#ifdef ParallelLineTraceSingleByChannel_EXISTS
594 World->ParallelLineTraceSingleByChannel(
598 ECC_GameTraceChannel3,
600 FCollisionResponseParams::DefaultResponseParam);
603 World->LineTraceSingleByChannel(
607 ECC_GameTraceChannel3,
609 FCollisionResponseParams::DefaultResponseParam);
612 bool BlockingHit = HitResult.IsValidBlockingHit();
613 if (!BlockingHit && !UseLidarNoiseModel)
619 HitResult.ImpactPoint *= 1e-2;
622 HitResult.ImpactPoint.Y *= -1;
627 FHitResult OriginalHitResultCopy = HitResult;
628 uint32 RGB = RGBDefault;
634 const FVector& HitPoint = OriginalHitResultCopy.ImpactPoint;
635 FPointData point = { HitPoint.
X, HitPoint.Y, HitPoint.Z, RGB };
639 if (UseLidarNoiseModel)
643 BlockingHit = HitResult.bBlockingHit || HitSnowFlake;
659 AsyncTask(ENamedThreads::GameThread, [
this, CopiedPoints]()
676 ImpactPoints.clear();
677 ImpactPoints.reserve(MaxPointsPerChannel);
681 for (std::vector<FPointData>& ImpactPoints :
Points)
683 ImpactPoints.clear();
684 ImpactPoints.reserve(MaxPointsPerChannel);
703 const size_t PointsSize = points.size();
704 const size_t DataSize = PointsSize *
sizeof(
FPointData);
728 std::vector<FPointData> PointsWithOutNoiseModel;
730 size_t totalSize = 0;
733 totalSize += Vec.size();
735 PointsWithOutNoiseModel.reserve(totalSize);
739 PointsWithOutNoiseModel.insert(PointsWithOutNoiseModel.end(), Vec.begin(), Vec.end());
743 const FString PreprocessedFilename = FString::Printf(TEXT(
"%sLidar_%d_without_noise_model.ply"), *
FileSavePath,
LidarSaves);
768 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
771 const FString FileName =
"lidar_metadata";
778 WriteToLogFile(
"timestamp, pointcloud_name, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation, GPS latitude, GPS longitude, GPS altitude");
782 WriteToLogFile(
"timestamp, pointcloud_name, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation");
795 const FVector ActorPosition = GetActorLocation();
796 const FRotator ActorRotation = GetActorRotation();
804 MetaData = FString::Printf(TEXT(
"%s, %s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.8f, %.8f, %.8f"),
805 *TimeStamp, *FileName,
806 ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
807 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll,
808 GeoCoordinates.Latitude, GeoCoordinates.Longitude, GeoCoordinates.Altitude);
812 MetaData = FString::Printf(TEXT(
"%s, %s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f"),
813 *TimeStamp, *FileName,
814 ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
815 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll);
void AddRaycastLidar(ALidar *lidarPtr)
void RemoveRaycastLidar(ALidar *lidarPtr)
UNiagaraComponent * NiagaraComponent
TMap< FName, FColor > SemanticColors
void SaveLidarMetaDataToDisk(const FString &FileName)
FORCEINLINE bool ShootLaser(const UWorld *World, FHitResult &HitResult, const FVector &EndTrace, const FCollisionQueryParams &TraceParams, const FVector &LidarBodyLocation, const int32 Channel, const bool Semantic, const uint32 RGBDefault, const bool UseLidarNoiseModel)
FWeatherParameters CurrentWeatherParameters
std::random_device RandomDevice
FORCEINLINE uint32 GetSemanticData(const FHitResult &HitResult) const
void VisualizeLidarParticles(std::vector< FPointData > HitPoints)
void Init(FLidarParameters parameters, bool SimulateSensor=true)
void UpdateLidarParticles(int32 NumberOfHits, const TArray< FVector > &HitPoints, const TArray< FLinearColor > &Colors)
void SendData(const float DeltaTime)
void AddLateralNoise(FHitResult &HitResult, const FVector &LidarBodyLocation, const bool UseHorizontalNoise)
void ChangeLidarParameters(FLidarParameters newLidarParameters)
bool CanSendData(const float DeltaTime)
std::vector< std::vector< FPointData > > PreProcessedHitImpactPoints
void ResetRecordedHits(int32 MaxPointsPerChannel)
ALidar(const FObjectInitializer &ObjectInitializer)
uint32 CachedSnowflakeColor
std::vector< float > LaserAngles
void SetParticleLifeTime(float ParticleLifeTime)
bool LidarParametersChanged
void OnWeatherChanged(FWeatherParameters WeatherParams)
virtual void BeginPlay() override
FLidarParameters TempLidarParameters
void AddDistanceNoise(FHitResult &HitResult, const FVector &LidarBodyLocation)
AGeoReferencingSystem * GeoReferencingSystem
void SetNiagaraRendering(bool Enabled)
std::vector< std::vector< FPointData > > Points
std::vector< FPointData > SimulateRaycastLidar(const float DeltaTime)
bool NeedToSavePointCloudWithoutNoise
std::vector< FPointData > PointsFlattened
void SetVisualizeLidarParticles(bool Visualize)
TSharedPtr< ROSMessages::sensor_msgs::PointCloud2 > PointCloudMessage
void SendDataToTopic(const std::vector< FPointData > &points)
void AddSnowTerrainAdjustment(FHitResult &HitResult, const FVector &LidarBodyLocation)
void CreateLogFile() override
FLidarParameters LidarParameters
void AddProcessingToHitResult(FHitResult &HitResult, const FVector &LidarBodyLocation, const bool UseHorizontalNoise, const bool UseSnowTerrainAdjustment)
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
float CurrentHorizontalAngle
bool SaveCurrentPointCloudToDiskRequested
static const FName NiagaraHitPoints
bool CanSimulateSensor() const
static void HideComponentForAllCameras(UPrimitiveComponent *PrimitiveComponent)
static const FName NiagaraPointsFloat
virtual void CreateDataSavePath()
void SetSimulateSensor(bool SimulateSensor)
UTopic * GetROSTopic() const
static const FName NiagaraHitColors
FString CreateTimeStampString() const
void WriteToLogFile(const FString &Message)
FORCEINLINE bool IsROSConnected() const
virtual void CreateROSTopic()
static const FName NiagaraPointsInt
static TMap< FString, FColor > GetSemanticColors()
const FWeatherParameters & GetCurrentWeather() const
FLevelEventDelegate_WeatherChanged OnWeatherChanged
static bool CheckSnowflakeHit(FHitResult &HitInfo, const FVector EndTrace, const FVector LidarLocation, const FWeatherParameters &WeatherParameters)
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static AWeather * GetWeatherActor(const UObject *WorldContextObject)
static ALidarManager * GetLidarManager(const UObject *WorldContextObject)
static FGeographicCoordinates UnrealToGeographicCoordinates(AGeoReferencingSystem *GeoReferencingSystem, const FVector &Position)
void Create(const FString &FileNameWithoutExtension, FLogFileSettings Settings)
static void SaveVectorArrayAsPlyAsync(FString FullFileName, const std::vector< FPointData > points)
float DistanceNoiseStdDev
bool SendDataAtRotationFrequency
bool SendDataToCombinedROSTopic
bool SavePointcloudWithoutNoiseModel
bool UseComplexCollisionTrace
bool UseTerrainSnowHitAdjustment
FFileWriteOptions FileWriteOptions
FFileCreationOptions FileCreationOptions
bool IsWinterSnowCondition()
bool IsLidarNoiseModelCondition()