// 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 . #include "carla/client/detail/Simulator.h" #include "carla/Debug.h" #include "carla/Exception.h" #include "carla/Logging.h" #include "carla/RecurrentSharedFuture.h" #include "carla/client/BlueprintLibrary.h" #include "carla/client/FileTransfer.h" #include "carla/client/Map.h" #include "carla/client/Sensor.h" #include "carla/client/TimeoutException.h" #include "carla/client/WalkerAIController.h" #include "carla/client/detail/ActorFactory.h" #include "carla/client/detail/WalkerNavigation.h" #include "carla/trafficmanager/TrafficManager.h" #include "carla/sensor/Deserializer.h" #include #include using namespace std::string_literals; namespace carla { namespace client { namespace detail { // =========================================================================== // -- Static local methods --------------------------------------------------- // =========================================================================== static void ValidateVersions(Client &client) { const auto vc = client.GetClientVersion(); const auto vs = client.GetServerVersion(); if (vc != vs) { log_warning( "Version mismatch detected: You are trying to connect to a simulator", "that might be incompatible with this API"); log_warning("Client API version =", vc); log_warning("Simulator API version =", vs); } } static bool SynchronizeFrame(uint64_t frame, const Episode &episode, time_duration timeout) { bool result = true; auto start = std::chrono::system_clock::now(); while (frame > episode.GetState()->GetTimestamp().frame) { std::this_thread::yield(); auto end = std::chrono::system_clock::now(); auto diff = std::chrono::duration_cast(end-start); if(timeout.to_chrono() < diff) { result = false; break; } } if(result) { carla::traffic_manager::TrafficManager::Tick(); } return result; } // =========================================================================== // -- Constructor ------------------------------------------------------------ // =========================================================================== Simulator::Simulator( const std::string &host, const uint16_t port, const size_t worker_threads, const bool enable_garbage_collection) : LIBCARLA_INITIALIZE_LIFETIME_PROFILER("SimulatorClient("s + host + ":" + std::to_string(port) + ")"), _client(host, port, worker_threads), _light_manager(new LightManager()), _gc_policy(enable_garbage_collection ? GarbageCollectionPolicy::Enabled : GarbageCollectionPolicy::Disabled) {} // =========================================================================== // -- Load a new episode ----------------------------------------------------- // =========================================================================== EpisodeProxy Simulator::LoadEpisode(std::string map_name, bool reset_settings, rpc::MapLayer map_layers) { const auto id = GetCurrentEpisode().GetId(); _client.LoadEpisode(std::move(map_name), reset_settings, map_layers); // We are waiting 50ms for the server to reload the episode. // If in this time we have not detected a change of episode, we try again // 'number_of_attempts' times. // TODO This time is completly arbitrary so we need to improve // this pipeline to not depend in this time because this timeout // could result that the client resume the simulation in different // initial ticks when loading a map in syncronous mode. size_t number_of_attempts = _client.GetTimeout().milliseconds() / 50u; for (auto i = 0u; i < number_of_attempts; ++i) { using namespace std::literals::chrono_literals; if (_client.GetEpisodeSettings().synchronous_mode) _client.SendTickCue(); _episode->WaitForState(50ms); auto episode = GetCurrentEpisode(); if (episode.GetId() != id) { return episode; } } throw_exception(std::runtime_error("failed to connect to newly created map")); } EpisodeProxy Simulator::LoadOpenDriveEpisode( std::string opendrive, const rpc::OpendriveGenerationParameters & params, bool reset_settings) { // The "OpenDriveMap" is an ".umap" located in: // "carla/Unreal/CarlaUE4/Content/Carla/Maps/" // It will load the last sended OpenDRIVE by client's "LoadOpenDriveEpisode()" constexpr auto custom_opendrive_map = "OpenDriveMap"; _client.CopyOpenDriveToServer(std::move(opendrive), params); return LoadEpisode(custom_opendrive_map, reset_settings); } // =========================================================================== // -- Access to current episode ---------------------------------------------- // =========================================================================== void Simulator::GetReadyCurrentEpisode() { if (_episode == nullptr) { ValidateVersions(_client); _episode = std::make_shared(_client, std::weak_ptr(shared_from_this())); _episode->Listen(); if (!GetEpisodeSettings().synchronous_mode) { WaitForTick(_client.GetTimeout()); } _light_manager->SetEpisode(WeakEpisodeProxy{shared_from_this()}); } } EpisodeProxy Simulator::GetCurrentEpisode() { GetReadyCurrentEpisode(); return EpisodeProxy{shared_from_this()}; } bool Simulator::ShouldUpdateMap(rpc::MapInfo& map_info) { if (!_cached_map) { return true; } if (map_info.name != _cached_map->GetName() || _open_drive_file.size() != _cached_map->GetOpenDrive().size()) { return true; } return false; } SharedPtr Simulator::GetCurrentMap() { DEBUG_ASSERT(_episode != nullptr); if (!_cached_map || _episode->HasMapChangedSinceLastCall()) { rpc::MapInfo map_info = _client.GetMapInfo(); std::string map_name; std::string map_base_path; bool fill_base_string = false; for (int i = map_info.name.size() - 1; i >= 0; --i) { if (fill_base_string == false && map_info.name[i] != '/') { map_name += map_info.name[i]; } else { map_base_path += map_info.name[i]; fill_base_string = true; } } std::reverse(map_name.begin(), map_name.end()); std::reverse(map_base_path.begin(), map_base_path.end()); std::string XODRFolder = map_base_path + "/OpenDrive/" + map_name + ".xodr"; if (FileTransfer::FileExists(XODRFolder) == false) _client.GetRequiredFiles(); _open_drive_file = _client.GetMapData(); _cached_map = MakeShared(map_info, _open_drive_file); } return _cached_map; } // =========================================================================== // -- Required files --------------------------------------------------------- // =========================================================================== bool Simulator::SetFilesBaseFolder(const std::string &path) { return _client.SetFilesBaseFolder(path); } std::vector Simulator::GetRequiredFiles(const std::string &folder, const bool download) const { return _client.GetRequiredFiles(folder, download); } void Simulator::RequestFile(const std::string &name) const { _client.RequestFile(name); } std::vector Simulator::GetCacheFile(const std::string &name, const bool request_otherwise) const { return _client.GetCacheFile(name, request_otherwise); } // =========================================================================== // -- Tick ------------------------------------------------------------------- // =========================================================================== WorldSnapshot Simulator::WaitForTick(time_duration timeout) { DEBUG_ASSERT(_episode != nullptr); // tick pedestrian navigation NavigationTick(); auto result = _episode->WaitForState(timeout); if (!result.has_value()) { throw_exception(TimeoutException(_client.GetEndpoint(), timeout)); } return *result; } uint64_t Simulator::Tick(time_duration timeout) { DEBUG_ASSERT(_episode != nullptr); // tick pedestrian navigation NavigationTick(); // send tick command const auto frame = _client.SendTickCue(); // waits until new episode is received bool result = SynchronizeFrame(frame, *_episode, timeout); if (!result) { throw_exception(TimeoutException(_client.GetEndpoint(), timeout)); } return frame; } // =========================================================================== // -- Access to global objects in the episode -------------------------------- // =========================================================================== SharedPtr Simulator::GetBlueprintLibrary() { auto defs = _client.GetActorDefinitions(); return MakeShared(std::move(defs)); } rpc::VehicleLightStateList Simulator::GetVehiclesLightStates() { return _client.GetVehiclesLightStates(); } SharedPtr Simulator::GetSpectator() { return MakeActor(_client.GetSpectator()); } uint64_t Simulator::SetEpisodeSettings(const rpc::EpisodeSettings &settings) { if (settings.synchronous_mode && !settings.fixed_delta_seconds) { log_warning( "synchronous mode enabled with variable delta seconds. It is highly " "recommended to set 'fixed_delta_seconds' when running on synchronous mode."); } else if (settings.synchronous_mode && settings.substepping) { if(settings.max_substeps < 1 || settings.max_substeps > 16) { log_warning( "synchronous mode and substepping are enabled but the number of substeps is not valid. " "Please be aware that this value needs to be in the range [1-16]."); } double n_substeps = settings.fixed_delta_seconds.get() / settings.max_substep_delta_time; if (n_substeps > static_cast(settings.max_substeps)) { log_warning( "synchronous mode and substepping are enabled but the values for the simulation are not valid. " "The values should fulfil fixed_delta_seconds <= max_substep_delta_time * max_substeps. " "Be very careful about that, the time deltas are not guaranteed."); } } const auto frame = _client.SetEpisodeSettings(settings); using namespace std::literals::chrono_literals; SynchronizeFrame(frame, *_episode, 1s); return frame; } // =========================================================================== // -- AI --------------------------------------------------------------------- // =========================================================================== std::shared_ptr Simulator::GetNavigation() { DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); return nav; } // tick pedestrian navigation void Simulator::NavigationTick() { DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); nav->Tick(_episode); } void Simulator::RegisterAIController(const WalkerAIController &controller) { auto walker = controller.GetParent(); if (walker == nullptr) { throw_exception(std::runtime_error(controller.GetDisplayId() + ": not attached to walker")); return; } DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); nav->RegisterWalker(walker->GetId(), controller.GetId()); } void Simulator::UnregisterAIController(const WalkerAIController &controller) { auto walker = controller.GetParent(); if (walker == nullptr) { throw_exception(std::runtime_error(controller.GetDisplayId() + ": not attached to walker")); return; } DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); nav->UnregisterWalker(walker->GetId(), controller.GetId()); } boost::optional Simulator::GetRandomLocationFromNavigation() { DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); return nav->GetRandomLocation(); } void Simulator::SetPedestriansCrossFactor(float percentage) { DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); nav->SetPedestriansCrossFactor(percentage); } void Simulator::SetPedestriansSeed(unsigned int seed) { DEBUG_ASSERT(_episode != nullptr); auto nav = _episode->CreateNavigationIfMissing(); nav->SetPedestriansSeed(seed); } // =========================================================================== // -- General operations with actors ----------------------------------------- // =========================================================================== SharedPtr Simulator::SpawnActor( const ActorBlueprint &blueprint, const geom::Transform &transform, Actor *parent, rpc::AttachmentType attachment_type, GarbageCollectionPolicy gc) { rpc::Actor actor; if (parent != nullptr) { actor = _client.SpawnActorWithParent( blueprint.MakeActorDescription(), transform, parent->GetId(), attachment_type); } else { actor = _client.SpawnActor( blueprint.MakeActorDescription(), transform); } DEBUG_ASSERT(_episode != nullptr); _episode->RegisterActor(actor); const auto gca = (gc == GarbageCollectionPolicy::Inherit ? _gc_policy : gc); auto result = ActorFactory::MakeActor(GetCurrentEpisode(), actor, gca); log_debug( result->GetDisplayId(), "created", gca == GarbageCollectionPolicy::Enabled ? "with" : "without", "garbage collection"); return result; } bool Simulator::DestroyActor(Actor &actor) { bool success = true; success = _client.DestroyActor(actor.GetId()); if (success) { // Remove it's persistent state so it cannot access the client anymore. actor.GetEpisode().Clear(); log_debug(actor.GetDisplayId(), "destroyed."); } else { log_debug("failed to destroy", actor.GetDisplayId()); } return success; } // =========================================================================== // -- Operations with sensors ------------------------------------------------ // =========================================================================== void Simulator::SubscribeToSensor( const Sensor &sensor, std::function)> callback) { DEBUG_ASSERT(_episode != nullptr); _client.SubscribeToStream( sensor.GetActorDescription().GetStreamToken(), [cb=std::move(callback), ep=WeakEpisodeProxy{shared_from_this()}](auto buffer) { auto data = sensor::Deserializer::Deserialize(std::move(buffer)); data->_episode = ep.TryLock(); cb(std::move(data)); }); } void Simulator::UnSubscribeFromSensor(Actor &sensor) { _client.UnSubscribeFromStream(sensor.GetActorDescription().GetStreamToken()); // If in the future we need to unsubscribe from each gbuffer individually, it should be done here. } void Simulator::EnableForROS(const Sensor &sensor) { _client.EnableForROS(sensor.GetActorDescription().GetStreamToken()); } void Simulator::DisableForROS(const Sensor &sensor) { _client.DisableForROS(sensor.GetActorDescription().GetStreamToken()); } bool Simulator::IsEnabledForROS(const Sensor &sensor) { return _client.IsEnabledForROS(sensor.GetActorDescription().GetStreamToken()); } void Simulator::SubscribeToGBuffer( Actor &actor, uint32_t gbuffer_id, std::function)> callback) { _client.SubscribeToGBuffer(actor.GetId(), gbuffer_id, [cb=std::move(callback), ep=WeakEpisodeProxy{shared_from_this()}](auto buffer) { auto data = sensor::Deserializer::Deserialize(std::move(buffer)); data->_episode = ep.TryLock(); cb(std::move(data)); }); } void Simulator::UnSubscribeFromGBuffer(Actor &actor, uint32_t gbuffer_id) { _client.UnSubscribeFromGBuffer(actor.GetId(), gbuffer_id); } void Simulator::FreezeAllTrafficLights(bool frozen) { _client.FreezeAllTrafficLights(frozen); } // ========================================================================= /// -- Texture updating operations // ========================================================================= void Simulator::ApplyColorTextureToObjects( const std::vector &objects_name, const rpc::MaterialParameter& parameter, const rpc::TextureColor& Texture) { _client.ApplyColorTextureToObjects(objects_name, parameter, Texture); } void Simulator::ApplyColorTextureToObjects( const std::vector &objects_name, const rpc::MaterialParameter& parameter, const rpc::TextureFloatColor& Texture) { _client.ApplyColorTextureToObjects(objects_name, parameter, Texture); } std::vector Simulator::GetNamesOfAllObjects() const { return _client.GetNamesOfAllObjects(); } } // namespace detail } // namespace client } // namespace carla