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;
};