diff --git a/CHANGELOG.md b/CHANGELOG.md index c5ed4b6b5..059feb344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ * API extension: `world.tick()` now synchronizes with the simulator and returns the id of the newly started frame * API extension: `world.apply_settings(settings)` now synchronizes with the simulator and returns the id of the frame when the settings took effect * API extension: added `world.remove_on_tick(id)` to allow removing on tick callbacks + * API extension: allow setting fixed frame-rate from client-side, now is part of `carla.WorldSettings` * API change: Rename `frame_count` and `frame_number` as `frame`, old members are kept as deprecated * API change: `world.wait_for_tick()` now returns a `carla.WorldSnapshot` * API change: the callback of `world.on_tick(callback)` now receives a `carla.WorldSnapshot` diff --git a/Docs/configuring_the_simulation.md b/Docs/configuring_the_simulation.md index 0c51b6ce9..cf3e4d3f1 100644 --- a/Docs/configuring_the_simulation.md +++ b/Docs/configuring_the_simulation.md @@ -23,20 +23,26 @@ CARLA can be run in both modes. The simulation tries to keep up with real-time. To do so, the time-step is slightly adjusted each update. Simulations are not repeatable. By default, the -simulator starts in this mode +simulator starts in this mode, but it can be re-enabled if changed with + +```py +settings = world.get_settings() +settings.fixed_delta_seconds = None +world.apply_settings(settings) +```

Fixed time-step

The simulation runs as fast as possible, simulating the same time increment on -each step. To run the simulator this way you need to pass two parameters in the -command-line, one to enable the fixed time-step mode, and the second to specify -the FPS of the simulation (i.e. the inverse of the time step). For instance, to -run the simulation at a fixed time-step of 0.1 seconds we execute +each step. To enable this mode set a fixed delta seconds in the world settings. +For instance, to run the simulation at a fixed time-step of 0.05 seconds (20 +FPS) apply the following settings - $ ./CarlaUE4.sh -benchmark -fps=10 - -It is important to note that this mode can only be enabled when launching the -simulator since this is actually a feature of Unreal Engine. +```py +settings = world.get_settings() +settings.fixed_delta_seconds = 0.05 +world.apply_settings(settings) +``` !!! important **Do not decrease the frame-rate below 10 FPS.**
diff --git a/Docs/python_api.md b/Docs/python_api.md index d01f268b0..1aaf8885d 100644 --- a/Docs/python_api.md +++ b/Docs/python_api.md @@ -49,6 +49,7 @@ - `synchronous_mode` - `no_rendering_mode` +- `fixed_delta_seconds` - `__eq__(other)` - `__ne__(other)` diff --git a/LibCarla/source/carla/client/detail/Simulator.cpp b/LibCarla/source/carla/client/detail/Simulator.cpp index afde90c39..ed32ad050 100644 --- a/LibCarla/source/carla/client/detail/Simulator.cpp +++ b/LibCarla/source/carla/client/detail/Simulator.cpp @@ -136,6 +136,11 @@ namespace detail { } 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."); + } const auto frame = _client.SetEpisodeSettings(settings); SynchronizeFrame(frame, *_episode); return frame; diff --git a/LibCarla/source/carla/rpc/EpisodeSettings.h b/LibCarla/source/carla/rpc/EpisodeSettings.h index 1f0010910..1956acc9b 100644 --- a/LibCarla/source/carla/rpc/EpisodeSettings.h +++ b/LibCarla/source/carla/rpc/EpisodeSettings.h @@ -7,11 +7,14 @@ #pragma once #include "carla/MsgPack.h" +#include "carla/MsgPackAdaptors.h" #ifdef LIBCARLA_INCLUDED_FROM_UE4 # include "Carla/Settings/EpisodeSettings.h" #endif // LIBCARLA_INCLUDED_FROM_UE4 +#include + namespace carla { namespace rpc { @@ -26,7 +29,9 @@ namespace rpc { bool no_rendering_mode = false; - MSGPACK_DEFINE_ARRAY(synchronous_mode, no_rendering_mode); + boost::optional fixed_delta_seconds; + + MSGPACK_DEFINE_ARRAY(synchronous_mode, no_rendering_mode, fixed_delta_seconds); // ========================================================================= // -- Constructors --------------------------------------------------------- @@ -36,9 +41,12 @@ namespace rpc { EpisodeSettings( bool synchronous_mode, - bool no_rendering_mode) + bool no_rendering_mode, + double fixed_delta_seconds = 0.0) : synchronous_mode(synchronous_mode), - no_rendering_mode(no_rendering_mode) {} + no_rendering_mode(no_rendering_mode), + fixed_delta_seconds( + fixed_delta_seconds > 0.0 ? fixed_delta_seconds : boost::optional{}) {} // ========================================================================= // -- Comparison operators ------------------------------------------------- @@ -47,7 +55,8 @@ namespace rpc { bool operator==(const EpisodeSettings &rhs) const { return (synchronous_mode == rhs.synchronous_mode) && - (no_rendering_mode == rhs.no_rendering_mode); + (no_rendering_mode == rhs.no_rendering_mode) && + (fixed_delta_seconds == rhs.fixed_delta_seconds); } bool operator!=(const EpisodeSettings &rhs) const { @@ -61,13 +70,18 @@ namespace rpc { #ifdef LIBCARLA_INCLUDED_FROM_UE4 EpisodeSettings(const FEpisodeSettings &Settings) - : synchronous_mode(Settings.bSynchronousMode), - no_rendering_mode(Settings.bNoRenderingMode) {} + : EpisodeSettings( + Settings.bSynchronousMode, + Settings.bNoRenderingMode, + Settings.FixedDeltaSeconds.Get(0.0)) {} operator FEpisodeSettings() const { FEpisodeSettings Settings; Settings.bSynchronousMode = synchronous_mode; Settings.bNoRenderingMode = no_rendering_mode; + if (fixed_delta_seconds.has_value()) { + Settings.FixedDeltaSeconds = *fixed_delta_seconds; + } return Settings; } diff --git a/PythonAPI/carla/source/libcarla/World.cpp b/PythonAPI/carla/source/libcarla/World.cpp index 236271c39..86d3c201a 100644 --- a/PythonAPI/carla/source/libcarla/World.cpp +++ b/PythonAPI/carla/source/libcarla/World.cpp @@ -96,11 +96,20 @@ void export_world() { ; class_("WorldSettings") - .def(init( + .def(init( (arg("synchronous_mode")=false, - arg("no_rendering_mode")=false))) + arg("no_rendering_mode")=false, + arg("fixed_delta_seconds")=0.0))) .def_readwrite("synchronous_mode", &cr::EpisodeSettings::synchronous_mode) .def_readwrite("no_rendering_mode", &cr::EpisodeSettings::no_rendering_mode) + .add_property("fixed_delta_seconds", + +[](const cr::EpisodeSettings &self) { + return OptionalToPythonObject(self.fixed_delta_seconds); + }, + +[](cr::EpisodeSettings &self, object value) { + double fds = (value == object{} ? 0.0 : extract(value)); + self.fixed_delta_seconds = fds > 0.0 ? fds : boost::optional{}; + }) .def("__eq__", &cc::Timestamp::operator==) .def("__ne__", &cc::Timestamp::operator!=) .def(self_ns::str(self_ns::self)) diff --git a/PythonAPI/carla/source/libcarla/libcarla.cpp b/PythonAPI/carla/source/libcarla/libcarla.cpp index f0d24a6da..6867acdd5 100644 --- a/PythonAPI/carla/source/libcarla/libcarla.cpp +++ b/PythonAPI/carla/source/libcarla/libcarla.cpp @@ -12,6 +12,11 @@ #include #include +template +static boost::python::object OptionalToPythonObject(OptionalT &optional) { + return optional.has_value() ? boost::python::object(*optional) : boost::python::object(); +} + // Convenient for requests without arguments. #define CALL_WITHOUT_GIL(cls, fn) +[](cls &self) { \ carla::PythonUtil::ReleaseGIL unlock; \ @@ -88,12 +93,12 @@ #define CALL_RETURNING_OPTIONAL(cls, fn) +[](const cls &self) { \ auto optional = self.fn(); \ - return optional.has_value() ? boost::python::object(*optional) : boost::python::object(); \ + return OptionalToPythonObject(optional); \ } #define CALL_RETURNING_OPTIONAL_1(cls, fn, T1_) +[](const cls &self, T1_ t1) { \ auto optional = self.fn(std::forward(t1)); \ - return optional.has_value() ? boost::python::object(*optional) : boost::python::object(); \ + return OptionalToPythonObject(optional); \ } #define CALL_RETURNING_OPTIONAL_WITHOUT_GIL(cls, fn) +[](const cls &self) { \ diff --git a/PythonAPI/test/smoke/__init__.py b/PythonAPI/test/smoke/__init__.py index adf0e9bcf..1cf227b56 100644 --- a/PythonAPI/test/smoke/__init__.py +++ b/PythonAPI/test/smoke/__init__.py @@ -39,7 +39,8 @@ class SyncSmokeTest(SmokeTest): self.settings = self.world.get_settings() settings = carla.WorldSettings( no_rendering_mode=False, - synchronous_mode=True) + synchronous_mode=True, + fixed_delta_seconds=0.05) self.world.apply_settings(settings) def tearDown(self): diff --git a/PythonAPI/test/smoke/test_world.py b/PythonAPI/test/smoke/test_world.py new file mode 100644 index 000000000..40f9b299a --- /dev/null +++ b/PythonAPI/test/smoke/test_world.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019 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 . + + +from . import SmokeTest + + +class TestWorld(SmokeTest): + def test_fixed_delta_seconds(self): + world = self.client.get_world() + settings = world.get_settings() + self.assertFalse(settings.synchronous_mode) + for expected_delta_seconds in [0.1, 0.066667, 0.05, 0.033333, 0.016667, 0.011112]: + settings.fixed_delta_seconds = expected_delta_seconds + world.apply_settings(settings) + for _ in range(0, 20): + delta_seconds = world.wait_for_tick().timestamp.delta_seconds + self.assertAlmostEqual(expected_delta_seconds, delta_seconds) + settings.fixed_delta_seconds = None + world.apply_settings(settings) diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp index 60a21e352..bceab577a 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp @@ -12,13 +12,34 @@ #include "Carla/Settings/CarlaSettings.h" #include "Carla/Settings/EpisodeSettings.h" +#include "Runtime/Core/Public/Misc/App.h" + #include -static uint32 GetNumberOfThreadsForRPCServer() +// ============================================================================= +// -- Static local methods ----------------------------------------------------- +// ============================================================================= + +static uint32 FCarlaEngine_GetNumberOfThreadsForRPCServer() { return std::max(std::thread::hardware_concurrency(), 4u) - 2u; } +static TOptional FCarlaEngine_GetFixedDeltaSeconds() +{ + return FApp::IsBenchmarking() ? FApp::GetFixedDeltaTime() : TOptional{}; +} + +static void FCarlaEngine_SetFixedDeltaSeconds(TOptional FixedDeltaSeconds) +{ + FApp::SetBenchmarking(FixedDeltaSeconds.IsSet()); + FApp::SetFixedDeltaTime(FixedDeltaSeconds.Get(0.0)); +} + +// ============================================================================= +// -- FCarlaEngine ------------------------------------------------------------- +// ============================================================================= + FCarlaEngine::~FCarlaEngine() { if (bIsRunning) @@ -35,7 +56,7 @@ void FCarlaEngine::NotifyInitGame(const UCarlaSettings &Settings) { const auto StreamingPort = Settings.StreamingPort.Get(Settings.RPCPort + 1u); auto BroadcastStream = Server.Start(Settings.RPCPort, StreamingPort); - Server.AsyncRun(GetNumberOfThreadsForRPCServer()); + Server.AsyncRun(FCarlaEngine_GetNumberOfThreadsForRPCServer()); WorldObserver.SetStream(BroadcastStream); @@ -55,6 +76,7 @@ void FCarlaEngine::NotifyInitGame(const UCarlaSettings &Settings) void FCarlaEngine::NotifyBeginEpisode(UCarlaEpisode &Episode) { + Episode.EpisodeSettings.FixedDeltaSeconds = FCarlaEngine_GetFixedDeltaSeconds(); CurrentEpisode = &Episode; Server.NotifyBeginEpisode(Episode); } @@ -91,4 +113,6 @@ void FCarlaEngine::OnEpisodeSettingsChanged(const FEpisodeSettings &Settings) { GEngine->GameViewport->bDisableWorldRendering = Settings.bNoRenderingMode; } + + FCarlaEngine_SetFixedDeltaSeconds(Settings.FixedDeltaSeconds); } diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.cpp index 5af86d7c7..dc3f317c7 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.cpp @@ -158,7 +158,7 @@ void UCarlaEpisode::InitializeAtBeginPlay() { auto World = GetWorld(); check(World != nullptr); - auto PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); + auto PlayerController = UGameplayStatics::GetPlayerController(World, 0); if (PlayerController == nullptr) { UE_LOG(LogCarla, Error, TEXT("Can't find player controller!")); diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/EpisodeSettings.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/EpisodeSettings.h index 21c21d7df..f87c6d311 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/EpisodeSettings.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Settings/EpisodeSettings.h @@ -18,4 +18,6 @@ struct CARLA_API FEpisodeSettings UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bNoRenderingMode = false; + + TOptional FixedDeltaSeconds; };