Full pipeline for spawning actors from Python

This commit is contained in:
nsubiron 2018-07-23 20:40:59 +02:00
parent 03c5ccc9d2
commit 0f636e84fd
15 changed files with 236 additions and 41 deletions

View File

@ -86,11 +86,11 @@ namespace client {
void ActorAttribute::Validate() const {
switch (_attribute.type) {
case rpc::ActorAttributeType::Bool: As<rpc::ActorAttributeType::Bool>();
case rpc::ActorAttributeType::Int: As<rpc::ActorAttributeType::Int>();
case rpc::ActorAttributeType::Float: As<rpc::ActorAttributeType::Float>();
case rpc::ActorAttributeType::String: As<rpc::ActorAttributeType::String>();
case rpc::ActorAttributeType::RGBColor: As<rpc::ActorAttributeType::RGBColor>();
case rpc::ActorAttributeType::Bool: As<rpc::ActorAttributeType::Bool>(); break;
case rpc::ActorAttributeType::Int: As<rpc::ActorAttributeType::Int>(); break;
case rpc::ActorAttributeType::Float: As<rpc::ActorAttributeType::Float>(); break;
case rpc::ActorAttributeType::String: As<rpc::ActorAttributeType::String>(); break;
case rpc::ActorAttributeType::RGBColor: As<rpc::ActorAttributeType::RGBColor>(); break;
default:
LIBCARLA_THROW_INVALID_VALUE("invalid value type");
}

View File

@ -58,10 +58,13 @@ namespace client {
SharedPtr<World> GetWorld();
SharedPtr<BlueprintLibrary> GetBlueprintLibrary() {
return MakeShared<BlueprintLibrary>(Call<std::vector<carla::rpc::ActorDefinition>>("get_blueprints"));
return MakeShared<BlueprintLibrary>(
Call<std::vector<carla::rpc::ActorDefinition>>("get_actor_definitions"));
}
SharedPtr<Actor> SpawnActor(const ActorBlueprint &blueprint, const Transform &transform);
SharedPtr<Actor> SpawnActor(
const ActorBlueprint &blueprint,
const Transform &transform);
void ApplyControlToActor(
const Actor &actor,

View File

@ -29,7 +29,7 @@ namespace rpc {
#ifdef LIBCARLA_INCLUDED_FROM_UE4
operator FActorDescription() const {
FActorVariation Description;
FActorDescription Description;
Description.UId = uid;
Description.Id = ToFString(id);
Description.Variations.Reserve(attributes.size());

View File

@ -11,6 +11,7 @@
#include <boost/asio/io_service.hpp>
#include <rpc/server.h>
#include <rpc/this_handler.h>
#include <future>
@ -108,6 +109,11 @@ namespace detail {
_server.stop();
}
template <typename T>
static void RespondError(T &&error_object) {
::rpc::this_handler().respond_error(std::forward<T>(error_object));
}
private:
boost::asio::io_service _sync_io_service;

View File

@ -0,0 +1,26 @@
// 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 <rpc/rpc_error.h>
#include <boost/python.hpp>
#include <sstream>
void translator(rpc::rpc_error e) {
std::stringstream ss;
ss << e.what()
<< " in function " << e.get_function_name()
<< ": " << e.get_error().as<std::string>();
PyErr_SetString(PyExc_RuntimeError, ss.str().c_str());
}
void export_exception() {
using namespace boost::python;
namespace cc = carla::client;
register_exception_translator<rpc::rpc_error>(translator);
}

View File

@ -10,6 +10,7 @@
#include "Blueprint.cpp"
#include "Client.cpp"
#include "Control.cpp"
#include "Exception.cpp"
#include "Transform.cpp"
#include "World.cpp"
@ -22,4 +23,5 @@ BOOST_PYTHON_MODULE(libcarla) {
export_actor();
export_world();
export_client();
export_exception();
}

View File

@ -34,21 +34,18 @@ void FActorDispatcher::Bind(IActorSpawner &ActorSpawner)
}
}
AActor *FActorDispatcher::SpawnActor(
TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
const FTransform &Transform,
const FActorDescription &Description)
{
if ((Description.UId == 0) || (Description.UId > SpawnFunctions.Num()))
{
UE_LOG(LogCarla, Error, TEXT("Invalid ActorDescription \"%s\" (UId=%d)"), *Description.Id, Description.UId);
return nullptr;
return MakeTuple(EActorSpawnResultStatus::InvalidDescription, FActorView());
}
auto *Actor = SpawnFunctions[Description.UId](Transform, Description);
if (Actor != nullptr)
{
Registry.Register(*Actor);
}
return Actor;
auto Result = SpawnFunctions[Description.UId - 1](Transform, Description);
auto View = Result.IsValid() ? Registry.Register(*Result.Actor) : FActorView();
return MakeTuple(Result.Status, View);
}
void FActorDispatcher::DestroyActor(AActor *Actor)

View File

@ -9,6 +9,7 @@
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorRegistry.h"
#include "Carla/Actor/ActorSpawnResult.h"
#include "Containers/Array.h"
#include "Templates/Function.h"
@ -21,7 +22,7 @@ class FActorDispatcher
{
public:
using SpawnFunctionType = TFunction<AActor*(const FTransform &, const FActorDescription &)>;
using SpawnFunctionType = TFunction<FActorSpawnResult(const FTransform &, const FActorDescription &)>;
/// Bind a definition to a spawn function. When SpawnActor is called with a
/// matching description @a Functor is called.
@ -37,8 +38,10 @@ public:
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
/// despawn an actor created with this function call DestroyActor.
///
/// Return nullptr on failure.
AActor *SpawnActor(
/// @return A pair containing the result of the spawn function and a view over
/// the actor and its properties. If the status is different of Success the
/// view is invalid.
TPair<EActorSpawnResultStatus, FActorView> SpawnActor(
const FTransform &Transform,
const FActorDescription &ActorDescription);

View File

@ -0,0 +1,30 @@
// 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 "Carla.h"
#include "Carla/Actor/ActorSpawnResult.h"
FString FActorSpawnResult::StatusToString(EActorSpawnResultStatus InStatus)
{
static_assert(
static_cast<uint8>(EActorSpawnResultStatus::SIZE) == 4u,
"If you add a new status, please update this function.");
switch (InStatus)
{
case EActorSpawnResultStatus::Success:
return TEXT("Success");
case EActorSpawnResultStatus::InvalidDescription:
return TEXT("Spawn failed because of invalid actor description");
case EActorSpawnResultStatus::Collision:
return TEXT("Spawn failed because due to a collision at spawn position");
case EActorSpawnResultStatus::UnknownError:
default:
return TEXT("Unknown error while trying to spawn actor");
}
}

View File

@ -0,0 +1,41 @@
// 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 "ActorSpawnResult.generated.h"
/// List of valid types for actor attributes.
UENUM(BlueprintType)
enum class EActorSpawnResultStatus : uint8
{
Success UMETA(DisplayName = "Success"),
InvalidDescription UMETA(DisplayName = "Invalid actor description"),
Collision UMETA(DisplayName = "Failed because collision at spawn position"),
UnknownError UMETA(DisplayName = "Unknown Error"),
SIZE UMETA(Hidden)
};
/// Result of an actor spawn function.
USTRUCT(BlueprintType)
struct FActorSpawnResult
{
GENERATED_BODY()
static FString StatusToString(EActorSpawnResultStatus Status);
bool IsValid() const
{
return (Actor != nullptr) && (Status == EActorSpawnResultStatus::Success);
}
UPROPERTY(EditAnywhere, BlueprintReadWrite)
AActor *Actor = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EActorSpawnResultStatus Status = EActorSpawnResultStatus::UnknownError;
};

View File

@ -8,6 +8,7 @@
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorSpawnResult.h"
#include "Containers/Array.h"
#include "GameFramework/Actor.h"
@ -26,7 +27,7 @@ public:
///
/// @pre ActorDescription is expected to be derived from one of the
/// definitions retrieved with MakeDefinitions.
virtual AActor *SpawnActor(
virtual FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) = 0;
};

View File

@ -7,6 +7,7 @@
#pragma once
#include "Carla/Actor/ActorSpawner.h"
#include "Carla/Actor/ActorSpawnResult.h"
#include "ActorSpawnerBlueprintBase.generated.h"
@ -28,13 +29,13 @@ public:
return GenerateDefinitions();
}
virtual AActor *SpawnActor(
virtual FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) final
{
AActor *Actor = nullptr;
SpawnActor(SpawnAtTransform, ActorDescription, Actor);
return Actor;
FActorSpawnResult Result;
SpawnActor(SpawnAtTransform, ActorDescription, Result);
return Result;
}
protected:
@ -46,5 +47,5 @@ protected:
void SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription,
AActor *&Actor);
FActorSpawnResult &SpawnResult);
};

View File

@ -8,7 +8,7 @@
class AActor;
/// An view over an actor and its properties.
/// A view over an actor and its properties.
class FActorView
{
public:

View File

@ -47,13 +47,28 @@ public:
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
/// despawn an actor created with this function call DestroyActor.
///
/// Return nullptr on failure.
/// @return A pair containing the result of the spawn function and a view over
/// the actor and its properties. If the status is different of Success the
/// view is invalid.
TPair<EActorSpawnResultStatus, FActorView> SpawnActorWithInfo(
const FTransform &Transform,
const FActorDescription &ActorDescription)
{
return ActorDispatcher.SpawnActor(Transform, ActorDescription);
}
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
/// despawn an actor created with this function call DestroyActor.
///
/// @return nullptr on failure.
///
/// @note Special overload for blueprints.
UFUNCTION(BlueprintCallable)
AActor *SpawnActor(
const FTransform &Transform,
const FActorDescription &ActorDescription)
{
return ActorDispatcher.SpawnActor(Transform, ActorDescription);
return SpawnActorWithInfo(Transform, ActorDescription).Value.GetActor();
}
/// Destroys an actor, properly removing it from the registry.

View File

@ -9,20 +9,99 @@
#include <compiler/disable-ue4-macros.h>
#include <carla/Version.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/ActorDefinition.h>
#include <carla/rpc/ActorDescription.h>
#include <carla/rpc/Server.h>
#include <carla/rpc/Transform.h>
#include <compiler/enable-ue4-macros.h>
#include <vector>
// =============================================================================
// -- Static local functions ---------------------------------------------------
// =============================================================================
template <typename T, typename Other>
static std::vector<T> MakeVectorFromTArray(const TArray<Other> &Array)
{
return {Array.GetData(), Array.GetData() + Array.Num()};
}
// =============================================================================
// -- FTheNewCarlaServer::FPimpl -----------------------------------------------
// =============================================================================
class FTheNewCarlaServer::FPimpl
{
public:
FPimpl(uint16_t port) : Server(port) {}
FPimpl(uint16_t port) : Server(port) {
BindActions();
}
carla::rpc::Server Server;
UCarlaEpisode *CurrentEpisode = nullptr;
UCarlaEpisode *Episode = nullptr;
private:
void BindActions();
void RespondErrorStr(const std::string &ErrorMessage) {
UE_LOG(LogCarlaServer, Log, TEXT("Responding error, %s"), *carla::rpc::ToFString(ErrorMessage));
carla::rpc::Server::RespondError(ErrorMessage);
}
void RespondError(const FString &ErrorMessage) {
RespondErrorStr(carla::rpc::FromFString(ErrorMessage));
}
void RequireEpisode()
{
if (Episode == nullptr)
{
RespondErrorStr("episode not ready");
}
}
};
// =============================================================================
// -- FTheNewCarlaServer::FPimpl Bind Actions ----------------------------------
// =============================================================================
void FTheNewCarlaServer::FPimpl::BindActions()
{
namespace cr = carla::rpc;
Server.BindAsync("ping", []() { return true; });
Server.BindAsync("version", []() { return std::string(carla::version()); });
Server.BindSync("get_actor_definitions", [this]() {
RequireEpisode();
return MakeVectorFromTArray<cr::ActorDefinition>(Episode->GetActorDefinitions());
});
Server.BindSync("spawn_actor", [this](
const cr::Transform &Transform,
const cr::ActorDescription &Definition) {
RequireEpisode();
auto Result = Episode->SpawnActorWithInfo(Transform, Definition);
if (Result.Key != EActorSpawnResultStatus::Success)
{
RespondError(FActorSpawnResult::StatusToString(Result.Key));
}
cr::Actor actor;
actor.id = Result.Value.GetActorId();
return actor;
});
}
// =============================================================================
// -- FTheNewCarlaServer -------------------------------------------------------
// =============================================================================
FTheNewCarlaServer::FTheNewCarlaServer() : Pimpl(nullptr) {}
FTheNewCarlaServer::~FTheNewCarlaServer() {}
@ -30,27 +109,18 @@ FTheNewCarlaServer::~FTheNewCarlaServer() {}
void FTheNewCarlaServer::Start(uint16_t Port)
{
UE_LOG(LogCarlaServer, Log, TEXT("Initializing rpc-server at port %d"), Port);
Pimpl = MakeUnique<FPimpl>(Port);
namespace cr = carla::rpc;
auto &srv = Pimpl->Server;
srv.BindAsync("ping", []() { return true; });
srv.BindAsync("version", []() { return std::string(carla::version()); });
}
void FTheNewCarlaServer::NotifyBeginEpisode(UCarlaEpisode &Episode)
{
UE_LOG(LogCarlaServer, Log, TEXT("New episode '%s' started"), *Episode.GetMapName());
Pimpl->CurrentEpisode = &Episode;
Pimpl->Episode = &Episode;
}
void FTheNewCarlaServer::NotifyEndEpisode()
{
Pimpl->CurrentEpisode = nullptr;
Pimpl->Episode = nullptr;
}
void FTheNewCarlaServer::AsyncRun(uint32 NumberOfWorkerThreads)