Creating SensorSpawnerActor to spawn custom sensors in the editor. (#7149)

* Added spawning sensor logic on begin play in the editor.

* Moved spawn sensor logic to SensorSpawnerActor. That can be placed on a level to spawn sensors with custom parameters.

* Removing unnecessary logs.

* Updating changelog.
This commit is contained in:
Jose 2024-02-15 11:04:31 +01:00 committed by GitHub
parent 7bcbef736b
commit f6fc4f998d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 254 additions and 4 deletions

View File

@ -1,6 +1,7 @@
## Latest Changes
* Prevent from segfault on failing SignalReference identification when loading OpenDrive files
* Prevent from segfault on failing SignalReference identification when loading OpenDrive files
* Added vehicle doors to the recorder
* Creating SensorSpawnerActor to spawn custom sensors in the editor.
## CARLA 0.9.15

View File

@ -19,4 +19,4 @@ UCarlaGameInstance::UCarlaGameInstance() {
CarlaSettings->LogSettings();
}
UCarlaGameInstance::~UCarlaGameInstance() = default;
UCarlaGameInstance::~UCarlaGameInstance() = default;

View File

@ -130,5 +130,5 @@ private:
UPROPERTY()
FString _MapPath;
};
};

View File

@ -204,6 +204,7 @@ void ACarlaGameModeBase::BeginPlay()
Episode->InitializeAtBeginPlay();
GameInstance->NotifyBeginEpisode(*Episode);
OnEpisodeInitialisedDelegate.Broadcast(Episode);
if (Episode->Weather != nullptr)
{

View File

@ -30,6 +30,8 @@
#include "CarlaGameModeBase.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEpisodeInitialisedDelegate, UCarlaEpisode*, InitialisedEpisode);
/// Base class for the CARLA Game Mode.
UCLASS(HideCategories=(ActorTick))
class CARLA_API ACarlaGameModeBase : public AGameModeBase
@ -111,6 +113,8 @@ public:
const carla::rpc::MaterialParameter& TextureParam);
TArray<FString> GetNamesOfAllActors();
FOnEpisodeInitialisedDelegate OnEpisodeInitialisedDelegate;
protected:

View File

@ -0,0 +1,147 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB). This work is licensed under the terms of the MIT license. For a copy, see <https://opensource.org/licenses/MIT>.
#include "Util/SensorSpawnerActor.h"
#include "Carla/Game/CarlaEpisode.h"
#include "Game/CarlaGameModeBase.h"
#include "Sensor/Sensor.h"
DEFINE_LOG_CATEGORY_STATIC(LogSensorSpawnerActor, Verbose, All);
ASensorSpawnerActor::ASensorSpawnerActor()
{
PrimaryActorTick.bCanEverTick = false;
SceneComp = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComp"));
RootComponent = SceneComp;
}
void ASensorSpawnerActor::BeginPlay()
{
Super::BeginPlay();
// Wait for the CarlaEpisode initialisation. It is done on CarlaGameMode BeginPlay().
if(ACarlaGameModeBase* CarlaGameMode = Cast<ACarlaGameModeBase>(UGameplayStatics::GetGameMode(GetWorld())))
{
CarlaGameMode->OnEpisodeInitialisedDelegate.AddDynamic(this, &ASensorSpawnerActor::OnEpisodeInitialised);
}
}
void ASensorSpawnerActor::OnEpisodeInitialised(UCarlaEpisode* InitialisedEpisode)
{
if(IsValid(InitialisedEpisode))
{
CarlaEpisode = InitialisedEpisode;
// Spawn cameras with initial delay if set.
GetWorldTimerManager().SetTimer(InitialDelaySpawnTimerHandle, this, &ASensorSpawnerActor::SpawnSensors, InitialDelay);
}
}
void ASensorSpawnerActor::SpawnSensors()
{
// Check if we are doing a delayed spawn. If so, don't do nothing.
if(!SensorsToSpawnCopy.IsEmpty())
{
UE_LOG(LogSensorSpawnerActor, Warning, TEXT("Warning: ASensorSpawnerActor::SpawnSensors - Delayed spawn already in progress, wait until it ends"));
return;
}
if(DelayBetweenSpawns > 0.f)
{
SensorsToSpawnCopy = SensorsToSpawn;
GetWorldTimerManager().SetTimer(SpawnSensorsDelayedTimerHandle, this, &ASensorSpawnerActor::SpawnSensorsDelayed, DelayBetweenSpawns, true);
return;
}
for(const auto& SensorStruct : SensorsToSpawn)
{
if(const FActorDefinition* SensorDefinition = GetActorDefinitionByClass(SensorStruct.SensorClass))
{
FActorDescription SensorDescription;
GenerateSensorActorDescription(SensorDefinition, SensorDescription);
for(int i = 0; i < SensorStruct.Amount; i++)
{
SpawnSensorActor(SensorDescription);
}
}
}
}
const FActorDefinition* ASensorSpawnerActor::GetActorDefinitionByClass(const TSubclassOf<AActor> ActorClass) const
{
if(!ActorClass || !IsValid(CarlaEpisode))
{
return nullptr;
}
const TArray<FActorDefinition>& ActorDefinitions = CarlaEpisode->GetActorDefinitions();
// Checks that the class is exactly the same. If we want to allow also child classes use: ActorDef.Class->IsChildOf(ActorClass)
const FActorDefinition* ActorDefinition = ActorDefinitions.FindByPredicate([&](const FActorDefinition& ActorDef){ return ActorDef.Class == ActorClass; });
return ActorDefinition;
}
void ASensorSpawnerActor::SpawnSensorActor(const FActorDescription& SensorDescription) const
{
if(IsValid(CarlaEpisode))
{
FTransform Transform;
GetRandomTransform(Transform);
CarlaEpisode->SpawnActorWithInfo(Transform, SensorDescription);
}
}
void ASensorSpawnerActor::GenerateSensorActorDescription(const FActorDefinition* Definition, FActorDescription& SensorDescription) const
{
SensorDescription.UId = Definition->UId;
SensorDescription.Id = Definition->Id;
SensorDescription.Class = Definition->Class;
SensorDescription.Variations.Reserve(Definition->Variations.Num());
FActorAttribute CreatedAttribute;
for(const FActorVariation& Variation : Definition->Variations)
{
if(Variation.RecommendedValues.IsValidIndex(0))
{
CreatedAttribute.Id = Variation.Id;
CreatedAttribute.Type = Variation.Type;
CreatedAttribute.Value = Variation.RecommendedValues[0];
SensorDescription.Variations.Emplace(CreatedAttribute.Id, CreatedAttribute);
}
}
}
void ASensorSpawnerActor::GetRandomTransform(FTransform &Transform) const
{
Transform = FTransform::Identity;
const float PosX = FMath::FRandRange(MinSpawnLocation.X, MaxSpawnLocation.X);
const float PosY = FMath::FRandRange(MinSpawnLocation.Y, MaxSpawnLocation.Y);
const float PosZ = FMath::FRandRange(MinSpawnLocation.Z, MaxSpawnLocation.Z);
Transform.SetLocation(FVector(PosX, PosY, PosZ));
}
void ASensorSpawnerActor::SpawnSensorsDelayed()
{
if(SensorsToSpawnCopy.IsEmpty())
{
GetWorldTimerManager().ClearTimer(SpawnSensorsDelayedTimerHandle);
return;
}
if(const FActorDefinition* SensorDefinition = GetActorDefinitionByClass(SensorsToSpawnCopy[0].SensorClass))
{
FActorDescription SensorDescription;
GenerateSensorActorDescription(SensorDefinition, SensorDescription);
SpawnSensorActor(SensorDescription);
}
SensorsToSpawnCopy[0].Amount--;
if(SensorsToSpawnCopy[0].Amount <= 0)
{
SensorsToSpawnCopy.RemoveAt(0);
}
}

View File

@ -0,0 +1,97 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB). This work is licensed under the terms of the MIT license. For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SensorSpawnerActor.generated.h"
class ASensor;
class USceneComponent;
class UCarlaEpisode;
USTRUCT(BlueprintType)
struct FSensorTuple
{
GENERATED_BODY()
// sensor class to spawn.
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<ASensor> SensorClass;
// Number of sensors to spawn of the SensorClass.
UPROPERTY(EditAnywhere, BlueprintReadOnly)
int Amount = 1;
};
UCLASS(Blueprintable)
class CARLA_API ASensorSpawnerActor : public AActor
{
GENERATED_BODY()
public:
ASensorSpawnerActor();
// Called OnBeginPlay().
UFUNCTION(BlueprintCallable)
void SpawnSensors();
protected:
// Called when the game starts or when spawned.
virtual void BeginPlay() override;
// Root
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Components")
USceneComponent* SceneComp;
// Array with sensors to spawn
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config")
TArray<FSensorTuple> SensorsToSpawn;
// Initial delay until the sensors start spawning.
UPROPERTY(EditAnywhere, BlueprintReadOnly, meta=(ClampMin=0.f, ClampMax=40.f), Category="Config")
float InitialDelay = 6.f;
// Delay between spawns. Set to 0 for no delay.
UPROPERTY(EditAnywhere, BlueprintReadOnly, meta=(ClampMin=0.f, ClampMax=40.f), Category="Config")
float DelayBetweenSpawns = 0.f;
// Max spawn location of the sensor. Spawn at random location between MaxSpawnLocation and MinSpawnLocation.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config")
FVector MaxSpawnLocation {-110000.f, -100000.f, 9600.f};
// Min spawn location of the sensor. Spawn at random location between MaxSpawnLocation and MinSpawnLocation.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config")
FVector MinSpawnLocation {-120000.f, -110000.f, 9300.f};
private:
UFUNCTION()
void OnEpisodeInitialised(UCarlaEpisode* InitialisedEpisode);
UFUNCTION()
void SpawnSensorsDelayed();
void GenerateSensorActorDescription(const FActorDefinition* Definition, FActorDescription& SensorDescription) const;
// Gets a transform with a random location between MaxSpawnLocation and MinSpawnLocation.
void GetRandomTransform(FTransform &Transform) const;
void SpawnSensorActor(const FActorDescription& SensorDescription) const;
const FActorDefinition* GetActorDefinitionByClass(const TSubclassOf<AActor> ActorClass) const;
FTimerHandle InitialDelaySpawnTimerHandle;
UPROPERTY()
UCarlaEpisode* CarlaEpisode;
// Used for delayed spawn
FTimerHandle SpawnSensorsDelayedTimerHandle;
// Used for delayed spawn
TArray<FSensorTuple> SensorsToSpawnCopy;
};