CARLA
Loading...
Searching...
No Matches
Simulator.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8
9#include "carla/Debug.h"
10#include "carla/Exception.h"
11#include "carla/Logging.h"
15#include "carla/client/Map.h"
16#include "carla/client/Sensor.h"
23
24#include <chrono>
25#include <exception>
26#include <thread>
27
28using namespace std::string_literals;
29
30namespace carla {
31namespace client {
32namespace detail {
33
34 // ===========================================================================
35 // -- Static local methods ---------------------------------------------------
36 // ===========================================================================
37
38 static void ValidateVersions(Client &client) {
39 const auto vc = client.GetClientVersion();
40 const auto vs = client.GetServerVersion();
41 if (vc != vs) {
43 "Version mismatch detected: You are trying to connect to a simulator",
44 "that might be incompatible with this API");
45 log_warning("Client API version =", vc);
46 log_warning("Simulator API version =", vs);
47 }
48 }
49
50 static bool SynchronizeFrame(uint64_t frame, const Episode &episode, time_duration timeout) {
51 bool result = true;
52 auto start = std::chrono::system_clock::now();
53 while (frame > episode.GetState()->GetTimestamp().frame) {
54 std::this_thread::sleep_for(std::chrono::microseconds(100));
55 auto end = std::chrono::system_clock::now();
56 auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end-start);
57 if(timeout.to_chrono() < diff) {
58 result = false;
59 break;
60 }
61 }
62 if(result) {
64 }
65
66 return result;
67 }
68
69 // ===========================================================================
70 // -- Constructor ------------------------------------------------------------
71 // ===========================================================================
72
74 const std::string &host,
75 const uint16_t port,
76 const size_t worker_threads,
77 const bool enable_garbage_collection)
78 : LIBCARLA_INITIALIZE_LIFETIME_PROFILER("SimulatorClient("s + host + ":" + std::to_string(port) + ")"),
79 _client(host, port, worker_threads),
80 _light_manager(new LightManager()),
81 _gc_policy(enable_garbage_collection ?
83
85 // Join first, or ~Episode can run on a stream thread while _client dies.
87 }
88
89 // ===========================================================================
90 // -- Load a new episode -----------------------------------------------------
91 // ===========================================================================
92
93 EpisodeProxy Simulator::LoadEpisode(std::string map_name, bool reset_settings, rpc::MapLayer map_layers) {
94 const auto id = GetCurrentEpisode().GetId();
95 _client.LoadEpisode(std::move(map_name), reset_settings, map_layers);
96
97 // We are waiting 50ms for the server to reload the episode.
98 // If in this time we have not detected a change of episode, we try again
99 // 'number_of_attempts' times.
100 // TODO This time is completly arbitrary so we need to improve
101 // this pipeline to not depend in this time because this timeout
102 // could result that the client resume the simulation in different
103 // initial ticks when loading a map in syncronous mode.
104 size_t number_of_attempts = _client.GetTimeout().milliseconds() / 50u;
105
106 for (auto i = 0u; i < number_of_attempts; ++i) {
107 using namespace std::literals::chrono_literals;
110
111 _episode->WaitForState(50ms);
112 auto episode = GetCurrentEpisode();
113
114 if (episode.GetId() != id) {
115 // Wait for the stream thread to reset the Traffic Manager for it.
116 const auto deadline = std::chrono::steady_clock::now() + _client.GetTimeout().to_chrono();
117 while (!_episode->IsEpisodeChangeHandled()) {
118 if (std::chrono::steady_clock::now() > deadline) {
119 log_warning("the Traffic Manager has not finished resetting for the new episode");
120 break;
121 }
122 std::this_thread::sleep_for(10ms);
123 }
124 return episode;
125 }
126 }
127 throw_exception(std::runtime_error("failed to connect to newly created map"));
128 }
129
131 std::string opendrive,
132 const rpc::OpendriveGenerationParameters & params, bool reset_settings) {
133 // The "OpenDriveMap" is an ".umap" located in:
134 // "carla/Unreal/CarlaUnreal/Content/Carla/Maps/"
135 // It will load the last sended OpenDRIVE by client's "LoadOpenDriveEpisode()"
136 constexpr auto custom_opendrive_map = "OpenDriveMap";
137 _client.CopyOpenDriveToServer(std::move(opendrive), params);
138 return LoadEpisode(custom_opendrive_map, reset_settings);
139 }
140
141 // ===========================================================================
142 // -- Access to current episode ----------------------------------------------
143 // ===========================================================================
144
146 if (_episode == nullptr) {
148 _episode = std::make_shared<Episode>(_client, std::weak_ptr<Simulator>(shared_from_this()));
149 _episode->Listen();
150 if (!GetEpisodeSettings().synchronous_mode) {
152 }
153 _light_manager->SetEpisode(WeakEpisodeProxy{shared_from_this()});
154 }
155 }
158 return EpisodeProxy{shared_from_this()};
159 }
160
162 if (!_cached_map) {
163 return true;
164 }
165 if (map_info.name != _cached_map->GetName() ||
166 _open_drive_file.size() != _cached_map->GetOpenDrive().size()) {
167 return true;
168 }
169 return false;
170 }
171
173 DEBUG_ASSERT(_episode != nullptr);
174 // Check-and-refetch must be atomic: the Traffic Manager refreshes the map
175 // from its own thread on an episode change, and a caller that ran while
176 // that refresh was still downloading the OpenDRIVE found the change flag
177 // already consumed and the cache still holding the previous map. The
178 // cache is also keyed on the episode id, so a missed change flag cannot
179 // leave a stale map behind either.
180 std::lock_guard<std::mutex> lock(_cached_map_mutex);
181 const auto episode_id = _episode->GetId();
182 if (!_cached_map || _cached_map_episode_id != episode_id || _episode->HasMapChangedSinceLastCall()) {
183 rpc::MapInfo map_info = _client.GetMapInfo();
184 std::string map_name;
185 std::string map_base_path;
186 bool fill_base_string = false;
187 for (size_t ri = map_info.name.size(); ri > 0; --ri) {
188 size_t i = ri - 1;
189 if (fill_base_string == false && map_info.name[i] != '/') {
190 map_name += map_info.name[i];
191 } else {
192 map_base_path += map_info.name[i];
193 fill_base_string = true;
194 }
195 }
196 std::reverse(map_name.begin(), map_name.end());
197 std::reverse(map_base_path.begin(), map_base_path.end());
198 std::string XODRFolder = map_base_path + "/OpenDrive/" + map_name + ".xodr";
199 if (FileTransfer::FileExists(XODRFolder) == false) _client.GetRequiredFiles();
201 _cached_map = MakeShared<Map>(map_info, _open_drive_file);
202 _cached_map_episode_id = episode_id;
203 }
204
205 return _cached_map;
206 }
207
208 // ===========================================================================
209 // -- Required files ---------------------------------------------------------
210 // ===========================================================================
211
212
213 bool Simulator::SetFilesBaseFolder(const std::string &path) {
214 return _client.SetFilesBaseFolder(path);
215 }
216
217 std::vector<std::string> Simulator::GetRequiredFiles(const std::string &folder, const bool download) const {
218 return _client.GetRequiredFiles(folder, download);
219 }
220
221 void Simulator::RequestFile(const std::string &name) const {
222 _client.RequestFile(name);
223 }
224
225 std::vector<uint8_t> Simulator::GetCacheFile(const std::string &name, const bool request_otherwise) const {
226 return _client.GetCacheFile(name, request_otherwise);
227 }
228
229 // ===========================================================================
230 // -- Tick -------------------------------------------------------------------
231 // ===========================================================================
232
234 DEBUG_ASSERT(_episode != nullptr);
235
236 // tick pedestrian navigation
238
239 auto result = _episode->WaitForState(timeout);
240 if (!result.has_value()) {
242 }
243 return *result;
244 }
245
246 uint64_t Simulator::Tick(time_duration timeout) {
247 DEBUG_ASSERT(_episode != nullptr);
248
249 // tick pedestrian navigation
251
252 // send tick command
253 const auto frame = _client.SendTickCue();
254
255 // waits until new episode is received
256 bool result = SynchronizeFrame(frame, *_episode, timeout);
257 if (!result) {
259 }
260 return frame;
261 }
262
263 // ===========================================================================
264 // -- Access to global objects in the episode --------------------------------
265 // ===========================================================================
266
268 auto defs = _client.GetActorDefinitions();
269 return MakeShared<BlueprintLibrary>(std::move(defs));
270 }
271
275
279
281 if (settings.synchronous_mode && !settings.fixed_delta_seconds) {
283 "synchronous mode enabled with variable delta seconds. It is highly "
284 "recommended to set 'fixed_delta_seconds' when running on synchronous mode.");
285 }
286 else if (settings.synchronous_mode && settings.substepping) {
287 if(settings.max_substeps < 1 || settings.max_substeps > 16) {
289 "synchronous mode and substepping are enabled but the number of substeps is not valid. "
290 "Please be aware that this value needs to be in the range [1-16].");
291 }
292 double n_substeps = settings.fixed_delta_seconds.value() / settings.max_substep_delta_time;
293
294 if (n_substeps > static_cast<double>(settings.max_substeps)) {
296 "synchronous mode and substepping are enabled but the values for the simulation are not valid. "
297 "The values should fulfil fixed_delta_seconds <= max_substep_delta_time * max_substeps. "
298 "Be very careful about that, the time deltas are not guaranteed.");
299 }
300 }
301 const auto frame = _client.SetEpisodeSettings(settings);
302
303 using namespace std::literals::chrono_literals;
304 SynchronizeFrame(frame, *_episode, 1s);
305
306 return frame;
307 }
308
309 // ===========================================================================
310 // -- AI ---------------------------------------------------------------------
311 // ===========================================================================
312
313 std::shared_ptr<WalkerNavigation> Simulator::GetNavigation() {
314 DEBUG_ASSERT(_episode != nullptr);
315 auto nav = _episode->CreateNavigationIfMissing();
316 return nav;
317 }
318
319 // tick pedestrian navigation
321 DEBUG_ASSERT(_episode != nullptr);
322 auto nav = _episode->CreateNavigationIfMissing();
323 nav->Tick(_episode);
324 }
325
327 DEBUG_ASSERT(_episode != nullptr);
328 return _episode->CreateNavigationIfMissing()->IsServerSide();
329 }
330
332 auto walker = controller.GetParent();
333 if (walker == nullptr) {
334 throw_exception(std::runtime_error(controller.GetDisplayId() + ": not attached to walker"));
335 return;
336 }
337 DEBUG_ASSERT(_episode != nullptr);
338 auto nav = _episode->CreateNavigationIfMissing();
339 nav->RegisterWalker(walker->GetId(), controller.GetId());
340 }
341
343 auto walker = controller.GetParent();
344 if (walker == nullptr) {
345 throw_exception(std::runtime_error(controller.GetDisplayId() + ": not attached to walker"));
346 return;
347 }
348 DEBUG_ASSERT(_episode != nullptr);
349 auto nav = _episode->CreateNavigationIfMissing();
350 nav->UnregisterWalker(walker->GetId(), controller.GetId());
351 }
352
353 std::optional<geom::Location> Simulator::GetRandomLocationFromNavigation() {
354 DEBUG_ASSERT(_episode != nullptr);
355 auto nav = _episode->CreateNavigationIfMissing();
356 if (nav->IsServerSide()) {
357 // The server samples the loaded navmesh; the sentinel z = -1e6 means it
358 // found no point (e.g. tiles still streaming in), so try a few times
359 // before giving up like the legacy path does (empty optional).
360 constexpr int attempts = 3;
361 for (int i = 0; i < attempts; ++i) {
363 if (location.z > -9.0e5f) {
364 return location;
365 }
366 }
367 return {};
368 }
369 return nav->GetRandomLocation();
370 }
371
373 DEBUG_ASSERT(_episode != nullptr);
374 auto nav = _episode->CreateNavigationIfMissing();
375 if (nav->IsServerSide()) {
377 return;
378 }
379 nav->SetPedestriansCrossFactor(percentage);
380 }
381
382 void Simulator::SetPedestriansSeed(unsigned int seed) {
383 DEBUG_ASSERT(_episode != nullptr);
384 auto nav = _episode->CreateNavigationIfMissing();
385 if (nav->IsServerSide()) {
387 return;
388 }
389 nav->SetPedestriansSeed(seed);
390 }
391
392 // ===========================================================================
393 // -- General operations with actors -----------------------------------------
394 // ===========================================================================
395
397 const ActorBlueprint &blueprint,
398 const geom::Transform &transform,
399 Actor *parent,
400 rpc::AttachmentType attachment_type,
402 rpc::Actor actor;
403 if (parent != nullptr) {
405 blueprint.MakeActorDescription(),
406 transform,
407 parent->GetId(),
408 attachment_type);
409 } else {
410 actor = _client.SpawnActor(
411 blueprint.MakeActorDescription(),
412 transform);
413 }
414 DEBUG_ASSERT(_episode != nullptr);
415 _episode->RegisterActor(actor);
416 const auto gca = (gc == GarbageCollectionPolicy::Inherit ? _gc_policy : gc);
417 auto result = ActorFactory::MakeActor(GetCurrentEpisode(), actor, gca);
418 // Snapshots up to (and including) this frame predate the spawn; see
419 // Simulator::GetActorState.
420 static_cast<ActorState &>(*result)._spawn_frame = _episode->GetState()->GetFrame();
421 log_debug(
422 result->GetDisplayId(),
423 "created",
424 gca == GarbageCollectionPolicy::Enabled ? "with" : "without",
425 "garbage collection");
426 return result;
427 }
428
430 bool success = true;
431 success = _client.DestroyActor(actor.GetId());
432 if (success) {
433 // Remove it's persistent state so it cannot access the client anymore.
434 actor.GetEpisode().Clear();
435 log_debug(actor.GetDisplayId(), "destroyed.");
436 } else {
437 log_debug("failed to destroy", actor.GetDisplayId());
438 }
439 return success;
440 }
441
442 // ===========================================================================
443 // -- Operations with sensors ------------------------------------------------
444 // ===========================================================================
445
447 const Sensor &sensor,
448 std::function<void(SharedPtr<sensor::SensorData>)> callback) {
449 DEBUG_ASSERT(_episode != nullptr);
452 [cb=std::move(callback), ep=WeakEpisodeProxy{shared_from_this()}](auto buffer) {
453 auto data = sensor::Deserializer::Deserialize(std::move(buffer));
454 data->_episode = ep.TryLock();
455 cb(std::move(data));
456 });
457 }
458
461 // If in the future we need to unsubscribe from each gbuffer individually, it should be done here.
462 }
463
467
471
475
477 Actor &actor,
478 uint32_t gbuffer_id,
479 std::function<void(SharedPtr<sensor::SensorData>)> callback) {
480 _client.SubscribeToGBuffer(actor.GetId(), gbuffer_id,
481 [cb=std::move(callback), ep=WeakEpisodeProxy{shared_from_this()}](auto buffer) {
482 auto data = sensor::Deserializer::Deserialize(std::move(buffer));
483 data->_episode = ep.TryLock();
484 cb(std::move(data));
485 });
486 }
487
488 void Simulator::UnSubscribeFromGBuffer(Actor &actor, uint32_t gbuffer_id) {
489 _client.UnSubscribeFromGBuffer(actor.GetId(), gbuffer_id);
490 }
491
492 void Simulator::Send(const Sensor &sensor, const rpc::CustomV2XBytes &data) {
493 _client.Send(sensor.GetId(), data);
494 }
495
499
500 // =========================================================================
501 /// -- Texture updating operations
502 // =========================================================================
503
505 const std::vector<std::string> &objects_name,
506 const rpc::MaterialParameter& parameter,
507 const rpc::TextureColor& Texture) {
508 _client.ApplyColorTextureToObjects(objects_name, parameter, Texture);
509 }
510
512 const std::vector<std::string> &objects_name,
513 const rpc::MaterialParameter& parameter,
514 const rpc::TextureFloatColor& Texture) {
515 _client.ApplyColorTextureToObjects(objects_name, parameter, Texture);
516 }
517
518 std::vector<std::string> Simulator::GetNamesOfAllObjects() const {
520 }
521
522} // namespace detail
523} // namespace client
524} // namespace carla
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
#define LIBCARLA_INITIALIZE_LIFETIME_PROFILER(display_name)
Contains all the necessary information for spawning an Actor.
rpc::ActorDescription MakeActorDescription() const
Represents an actor in the simulation.
static bool FileExists(std::string_view file)
static SharedPtr< Actor > MakeActor(EpisodeProxy episode, rpc::Actor actor_description, GarbageCollectionPolicy garbage_collection_policy)
Create an Actor based on the provided actor_description.
SharedPtr< Actor > GetParent() const
const rpc::Actor & GetActorDescription() const
const std::string & GetDisplayId() const
Provides communication with the rpc and streaming servers of a CARLA simulator.
bool SetPedestriansSeed(uint32_t seed) const
Seed for the server-side navigation RNG (crosser draw); applies to walkers whose navigation starts af...
const std::string GetEndpoint() const
bool IsEnabledForROS(const streaming::Token &token)
void DisableForROS(const streaming::Token &token)
bool SetFilesBaseFolder(const std::string &path)
void Send(rpc::ActorId ActorId, const rpc::CustomV2XBytes &data)
void RequestFile(const std::string &name) const
void UnSubscribeFromGBuffer(rpc::ActorId ActorId, uint32_t GBufferId)
void UnSubscribeFromStream(const streaming::Token &token)
rpc::VehicleLightStateList GetVehiclesLightStates()
Returns a list of pairs where the firts element is the vehicle ID and the second one is the light sta...
std::vector< std::string > GetRequiredFiles(const std::string &folder="", const bool download=true) const
void LoadEpisode(std::string map_name, bool reset_settings=true, rpc::MapLayer map_layer=rpc::MapLayer::All)
uint64_t SetEpisodeSettings(const rpc::EpisodeSettings &settings)
void SubscribeToStream(const streaming::Token &token, std::function< void(Buffer)> callback)
std::vector< std::string > GetNamesOfAllObjects() const
void CopyOpenDriveToServer(std::string opendrive, const rpc::OpendriveGenerationParameters &params)
void StopStreaming()
Join the streaming threads; no stream callback runs afterwards.
bool DestroyActor(rpc::ActorId actor)
bool SetPedestriansCrossFactor(float percentage) const
Probability that a walker whose navigation starts after this call may cross roads (server-side naviga...
std::vector< uint8_t > GetCacheFile(const std::string &name, const bool request_otherwise=true) const
void EnableForROS(const streaming::Token &token)
std::vector< rpc::ActorDefinition > GetActorDefinitions()
void SubscribeToGBuffer(rpc::ActorId ActorId, uint32_t GBufferId, std::function< void(Buffer)> callback)
geom::Location GetRandomLocationFromNavigation() const
Server-side random point on the loaded navmesh.
void ApplyColorTextureToObjects(const std::vector< std::string > &objects_name, const rpc::MaterialParameter &parameter, const rpc::TextureColor &Texture)
rpc::Actor SpawnActorWithParent(const rpc::ActorDescription &description, const geom::Transform &transform, rpc::ActorId parent, rpc::AttachmentType attachment_type)
rpc::EpisodeSettings GetEpisodeSettings()
rpc::Actor SpawnActor(const rpc::ActorDescription &description, const geom::Transform &transform)
Holds the current episode, and the current episode state.
Definition Episode.h:38
std::shared_ptr< const EpisodeState > GetState() const
Definition Episode.h:56
WorldSnapshot WaitForTick(time_duration timeout)
std::shared_ptr< Episode > _episode
Definition Simulator.h:999
bool ShouldUpdateMap(rpc::MapInfo &map_info)
void DisableForROS(const Sensor &sensor)
void RequestFile(const std::string &name) const
void EnableForROS(const Sensor &sensor)
void RegisterAIController(const WalkerAIController &controller)
SharedPtr< LightManager > _light_manager
Definition Simulator.h:997
std::vector< std::string > GetRequiredFiles(const std::string &folder="", const bool download=true) const
SharedPtr< Actor > MakeActor(rpc::Actor actor_description, GarbageCollectionPolicy gc=GarbageCollectionPolicy::Disabled)
Creates an actor instance out of a description of an existing actor.
Definition Simulator.h:418
bool IsEnabledForROS(const Sensor &sensor)
SharedPtr< BlueprintLibrary > GetBlueprintLibrary()
SharedPtr< Actor > SpawnActor(const ActorBlueprint &blueprint, const geom::Transform &transform, Actor *parent=nullptr, rpc::AttachmentType attachment_type=rpc::AttachmentType::Rigid, GarbageCollectionPolicy gc=GarbageCollectionPolicy::Inherit)
Spawns an actor into the simulation.
Simulator(const std::string &host, uint16_t port, size_t worker_threads=0u, bool enable_garbage_collection=false)
Definition Simulator.cpp:73
const GarbageCollectionPolicy _gc_policy
Definition Simulator.h:1001
rpc::VehicleLightStateList GetVehiclesLightStates()
Returns a list of pairs where the firts element is the vehicle ID and the second one is the light sta...
void UnSubscribeFromGBuffer(Actor &sensor, uint32_t gbuffer_id)
void SubscribeToSensor(const Sensor &sensor, std::function< void(SharedPtr< sensor::SensorData >)> callback)
void UnregisterAIController(const WalkerAIController &controller)
std::vector< uint8_t > GetCacheFile(const std::string &name, const bool request_otherwise) const
void ApplyColorTextureToObjects(const std::vector< std::string > &objects_name, const rpc::MaterialParameter &parameter, const rpc::TextureColor &Texture)
– Texture updating operations
std::shared_ptr< WalkerNavigation > GetNavigation()
bool SetFilesBaseFolder(const std::string &path)
SharedPtr< Actor > GetSpectator()
EpisodeProxy LoadOpenDriveEpisode(std::string opendrive, const rpc::OpendriveGenerationParameters &params, bool reset_settings=true)
void Send(const Sensor &sensor, const rpc::CustomV2XBytes &data)
void SetPedestriansCrossFactor(float percentage)
void FreezeAllTrafficLights(bool frozen)
uint64_t SetEpisodeSettings(const rpc::EpisodeSettings &settings)
void UnSubscribeFromSensor(Actor &sensor)
EpisodeProxy LoadEpisode(std::string map_name, bool reset_settings=true, rpc::MapLayer map_layers=rpc::MapLayer::All)
Definition Simulator.cpp:93
uint64_t Tick(time_duration timeout)
void SubscribeToGBuffer(Actor &sensor, uint32_t gbuffer_id, std::function< void(SharedPtr< sensor::SensorData >)> callback)
std::vector< std::string > GetNamesOfAllObjects() const
SharedPtr< Map > GetCurrentMap()
std::optional< geom::Location > GetRandomLocationFromNavigation()
rpc::EpisodeSettings GetEpisodeSettings()
Definition Simulator.h:271
bool DestroyActor(Actor &actor)
void SetPedestriansSeed(unsigned int seed)
bool IsNavigationServerSide()
True when the current episode's navigation is hosted by the server (World-Partitioned navmesh); walke...
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
streaming::Token GetStreamToken() const
Definition rpc/Actor.h:45
Fixed-size binary payload carried by the custom V2X sensor (sensor.other.v2x_custom).
std::string name
Definition MapInfo.h:21
Positive time duration up to milliseconds resolution.
Definition Time.h:20
static time_duration milliseconds(size_t timeout)
Definition Time.h:27
constexpr auto to_chrono() const
Definition Time.h:54
static void ValidateVersions(Client &client)
Definition Simulator.cpp:38
EpisodeProxyImpl< EpisodeProxyPointerType::Weak > WeakEpisodeProxy
static bool SynchronizeFrame(uint64_t frame, const Episode &episode, time_duration timeout)
Definition Simulator.cpp:50
std::vector< std::pair< ActorId, VehicleLightState::flag_type > > VehicleLightStateList
This file contains definitions of common data structures used in traffic manager.
std::shared_ptr< T > SharedPtr
Definition Memory.h:15
void throw_exception(const ExceptionT &e)
Templated on the static type so the thrown object keeps its dynamic type: taking a const std::excepti...
Definition Exception.h:44
static void log_warning(Args &&... args)
Definition Logging.h:96
static void log_debug(Args &&... args)
Definition Logging.h:68
Seting for map generation from opendrive without additional geometry.