Skip to content

Commit aa9c92b

Browse files
Extend ROS2 support Step 2: Fine grained ServerSynchronization (#9450)
* Extend ROS2 support Step 2: Fine grained ServerSynchronization Introduced a fine grained ServerSynchronization mechanism, where each synchonization participant is treated independently and interacts with the synchronization of the carla-server individually. If a client is disconnected (or dies) the synchronization state of all participants registered via that client are dropped, i.e. the server will continue running in case the participants of that client were the only ones demanding synchronous mode. The synchronization interface provides means of a time window, up to which the server is allowed to run. Like this, every client can prevent the carla-server to run too fast depending on their individual speed. There is no sync-master anymore. Every client decides for its own if it requires synchronization or not. Drawback of this change: some existing code might have to be changed (see removal of synchronous_master in generate_traffic.py). * Fix Windows build and smoke tests Update RPC lib version for windows build Ensure tick calls are ignored if sync mode is not active. And prevent client changes of fixed_delta_seconds triggering warning message. * Client needs to wait for next tick on non synchronous mode
1 parent 86ebadc commit aa9c92b

File tree

14 files changed

+645
-45
lines changed

14 files changed

+645
-45
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
* Fixed geom::Rotation::RotateVector() rotation directions of pitch and roll
99
* Prepare server for multistream support and ROS2 client calls
1010
* Improved V2X sensor capabilities: send complex custom user-defined data, support V2I sensors not attached to a vehicle
11+
* Introduced fine grained ServerSynchronization mechanism: each client decides for its own if it requires synchronization or not and provides its own synchronization window.
12+
Be aware: some existing code using master/slave sync mechanism might need rework. See also generate_traffic.py.
1113

1214
## CARLA 0.9.16
1315

LibCarla/source/carla/client/World.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ namespace client {
7575
if (tics_correct >= 2)
7676
return id;
7777

78-
Tick(local_timeout);
78+
if (settings.synchronous_mode) {
79+
// tick if synchronous mode is active
80+
Tick(local_timeout);
81+
}
82+
else {
83+
WaitForTick(local_timeout);
84+
}
85+
7986
}
8087

8188
log_warning("World::ApplySettings: After", number_of_attemps, " attemps, the settings were not correctly set. Please check that everything is consistent.");

LibCarla/source/carla/rpc/RpcServerInterface.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "carla/rpc/MapInfo.h"
1515
#include "carla/rpc/MapLayer.h"
1616
#include "carla/rpc/Response.h"
17+
#include "carla/rpc/ServerSynchronizationTypes.h"
1718
#include "carla/rpc/Transform.h"
1819
#include "carla/rpc/VehicleTelemetryData.h"
1920
#include "carla/streaming/detail/Dispatcher.h"
@@ -80,6 +81,27 @@ class RpcServerInterface {
8081
/**
8182
* @}
8283
*/
84+
85+
/**
86+
* @brief synchronization calls
87+
* @{
88+
*/
89+
virtual Response<uint64_t> call_tick(
90+
synchronization_client_id_type const &client_id,
91+
synchronization_participant_id_type const &participant_id,
92+
carla::rpc::SynchronizationTickMode synchronization_tick_mode) = 0;
93+
virtual Response<synchronization_participant_id_type> call_register_synchronization_participant(
94+
synchronization_client_id_type const &client_id,
95+
synchronization_participant_id_type const &participant_id_hint = ALL_PARTICIPANTS) = 0;
96+
virtual Response<bool> call_deregister_synchronization_participant(
97+
synchronization_client_id_type const &client_id, synchronization_participant_id_type const &participant_id) = 0;
98+
virtual Response<bool> call_update_synchronization_window(
99+
synchronization_client_id_type const &client_id, synchronization_participant_id_type const &participant_id,
100+
synchronization_target_game_time const &target_game_time = NO_SYNC_TARGET_GAME_TIME) = 0;
101+
virtual carla::rpc::Response<std::pair< bool , std::vector<carla::rpc::synchronization_window_participant_state> > > call_get_synchronization_window_status() = 0;
102+
/**
103+
* @}
104+
*/
83105
};
84106

85107
} // namespace rpc

LibCarla/source/carla/rpc/Server.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <boost/asio/post.hpp>
1616

1717
#include <rpc/server.h>
18+
#include <rpc/this_session.h>
1819

1920
#include <future>
2021

@@ -65,6 +66,14 @@ namespace rpc {
6566
_server.stop();
6667
}
6768

69+
void BindOnClientConnected(::rpc::server::callback_type callback) {
70+
_server.set_on_connection(callback);
71+
}
72+
73+
void BindOnClientDisconnected(::rpc::server::callback_type callback) {
74+
_server.set_on_disconnection(callback);
75+
}
76+
6877
private:
6978

7079
boost::asio::io_context _sync_io_context;
@@ -108,7 +117,9 @@ namespace detail {
108117
template <typename FuncT>
109118
static auto WrapSyncCall(boost::asio::io_context &io, FuncT &&functor) {
110119
return [&io, functor=std::forward<FuncT>(functor)](Metadata metadata, Args... args) -> R {
111-
auto task = std::packaged_task<R()>([functor=std::move(functor), args...]() {
120+
auto const session_id = ::rpc::this_session().id();
121+
auto task = std::packaged_task<R()>([session_id, functor=std::move(functor), args...]() {
122+
::rpc::this_session().set_id(session_id);
112123
return functor(args...);
113124
});
114125
if (metadata.IsResponseIgnored()) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2024 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+
7+
#pragma once
8+
9+
#include <locale>
10+
11+
namespace carla {
12+
namespace rpc {
13+
14+
using synchronization_client_id_type = std::string;
15+
static const carla::rpc::synchronization_client_id_type ALL_CLIENTS{};
16+
17+
using synchronization_participant_id_type = uint32_t;
18+
static constexpr carla::rpc::synchronization_participant_id_type ALL_PARTICIPANTS{0};
19+
20+
using synchronization_target_game_time = double;
21+
static constexpr synchronization_target_game_time NO_SYNC_TARGET_GAME_TIME{0.};
22+
static constexpr synchronization_target_game_time BLOCKING_TARGET_GAME_TIME{1e-6};
23+
24+
struct synchronization_window_participant_state {
25+
synchronization_client_id_type client_id;
26+
synchronization_participant_id_type participant_id;
27+
synchronization_target_game_time target_game_time;
28+
};
29+
30+
enum class SynchronizationTickMode {
31+
FORCE_ENABLE_SYNC,
32+
TICK_ONLY_IF_SYNC_ENABLED
33+
};
34+
35+
} // namespace rpc
36+
} // namespace carla

PythonAPI/examples/generate_traffic.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ def main():
148148
all_id = []
149149
client = carla.Client(args.host, args.port)
150150
client.set_timeout(10.0)
151-
synchronous_master = False
152151
random.seed(args.seed if args.seed is not None else int(time.time()))
153152

153+
original_world_settings = None
154154
try:
155155
world = client.get_world()
156156

@@ -164,23 +164,24 @@ def main():
164164
if args.seed is not None:
165165
traffic_manager.set_random_device_seed(args.seed)
166166

167-
settings = world.get_settings()
167+
original_world_settings = world.get_settings()
168+
print("current_world_settings {}".format(original_world_settings))
169+
settings = original_world_settings
168170
if not args.asynch:
169171
traffic_manager.set_synchronous_mode(True)
170172
if not settings.synchronous_mode:
171-
synchronous_master = True
172173
settings.synchronous_mode = True
173174
settings.fixed_delta_seconds = 0.05
174-
else:
175-
synchronous_master = False
176175
else:
177176
print("You are currently in asynchronous mode. If this is a traffic simulation, \
178177
you could experience some issues. If it's not working correctly, switch to synchronous \
179178
mode by using traffic_manager.set_synchronous_mode(True)")
180179

181180
if args.no_rendering:
182181
settings.no_rendering_mode = True
182+
print("apply_world_settings {}".format(settings))
183183
world.apply_settings(settings)
184+
print("settings applied")
184185

185186
blueprints = get_actor_blueprints(world, args.filterv, args.generationv)
186187
if not blueprints:
@@ -229,7 +230,7 @@ def main():
229230
batch.append(SpawnActor(blueprint, transform)
230231
.then(SetAutopilot(FutureActor, True, traffic_manager.get_port())))
231232

232-
for response in client.apply_batch_sync(batch, synchronous_master):
233+
for response in client.apply_batch_sync(batch, do_tick=True):
233234
if response.error:
234235
logging.error(response.error)
235236
else:
@@ -281,7 +282,7 @@ def main():
281282
print("Walker has no speed")
282283
walker_speed.append(0.0)
283284
batch.append(SpawnActor(walker_bp, spawn_point))
284-
results = client.apply_batch_sync(batch, True)
285+
results = client.apply_batch_sync(batch, do_tick=True)
285286
walker_speed2 = []
286287
for i in range(len(results)):
287288
if results[i].error:
@@ -295,7 +296,7 @@ def main():
295296
walker_controller_bp = world.get_blueprint_library().find('controller.ai.walker')
296297
for i in range(len(walkers_list)):
297298
batch.append(SpawnActor(walker_controller_bp, carla.Transform(), walkers_list[i]["id"]))
298-
results = client.apply_batch_sync(batch, True)
299+
results = client.apply_batch_sync(batch, do_tick=True)
299300
for i in range(len(results)):
300301
if results[i].error:
301302
logging.error(results[i].error)
@@ -308,7 +309,7 @@ def main():
308309
all_actors = world.get_actors(all_id)
309310

310311
# wait for a tick to ensure client receives the last transform of the walkers we have just created
311-
if args.asynch or not synchronous_master:
312+
if args.asynch:
312313
world.wait_for_tick()
313314
else:
314315
world.tick()
@@ -330,18 +331,22 @@ def main():
330331
traffic_manager.global_percentage_speed_difference(30.0)
331332

332333
while True:
333-
if not args.asynch and synchronous_master:
334+
if not args.asynch:
334335
world.tick()
335336
else:
336337
world.wait_for_tick()
337338

338339
finally:
339340

340-
if not args.asynch and synchronous_master:
341-
settings = world.get_settings()
342-
settings.synchronous_mode = False
343-
settings.no_rendering_mode = False
344-
settings.fixed_delta_seconds = None
341+
if not args.asynch:
342+
if original_world_settings:
343+
settings= original_world_settings
344+
else:
345+
settings = world.get_settings()
346+
settings.synchronous_mode = False
347+
settings.no_rendering_mode = False
348+
settings.fixed_delta_seconds = None
349+
print("restore world_settings {}".format(settings))
345350
world.apply_settings(settings)
346351

347352
print('\ndestroying %d vehicles' % len(vehicles_list))

PythonAPI/test/smoke/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def setUp(self):
4848

4949
def tearDown(self):
5050
self.world.apply_settings(self.settings)
51-
self.world.tick()
51+
if self.settings.synchronous_mode:
52+
# tick if synchronous mode is active
53+
self.world.tick()
5254
self.settings = None
5355
super(SyncSmokeTest, self).tearDown()

Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ void FCarlaEngine::NotifyInitGame(const UCarlaSettings &Settings)
202202
Secondary = std::make_shared<carla::multigpu::Secondary>(PrimaryIP, PrimaryPort, CommandExecutor);
203203
Secondary->Connect();
204204
// set this server in synchronous mode
205-
bSynchronousMode = true;
205+
Server.EnableSynchronousMode();
206206
}
207207
else
208208
{
@@ -281,21 +281,24 @@ void FCarlaEngine::OnPreTick(UWorld *, ELevelTick TickType, float DeltaSeconds)
281281

282282
if (bIsPrimaryServer)
283283
{
284-
if (CurrentEpisode && !bSynchronousMode && SecondaryServer->HasClientsConnected())
285-
{
286-
// set synchronous mode
287-
CurrentSettings.bSynchronousMode = true;
288-
CurrentSettings.FixedDeltaSeconds = 1 / 20.0f;
289-
OnEpisodeSettingsChanged(CurrentSettings);
290-
CurrentEpisode->ApplySettings(CurrentSettings);
291-
}
292-
293284
// process RPC commands
294285
do
295286
{
296287
Server.RunSome(1u);
297288
}
298-
while (bSynchronousMode && !Server.TickCueReceived());
289+
while (Server.IsSynchronousModeActive() && !Server.TickCueReceived());
290+
291+
if ( (CurrentEpisode && !Server.IsSynchronousModeActive() && SecondaryServer->HasClientsConnected())
292+
|| ( Server.IsSynchronousModeActive() && (!CurrentSettings.FixedDeltaSeconds || !CurrentSettings.bSynchronousMode) ) )
293+
{
294+
// ensure the delta seconds are also considered in this run
295+
DeltaSeconds = Server.GetTickDeltaSeconds();
296+
297+
CurrentSettings.bSynchronousMode = true;
298+
CurrentSettings.FixedDeltaSeconds = DeltaSeconds;
299+
OnEpisodeSettingsChanged(CurrentSettings);
300+
CurrentEpisode->ApplySettings(CurrentSettings);
301+
}
299302
}
300303
else
301304
{
@@ -382,7 +385,12 @@ void FCarlaEngine::OnEpisodeSettingsChanged(const FEpisodeSettings &Settings)
382385
{
383386
CurrentSettings = FEpisodeSettings(Settings);
384387

385-
bSynchronousMode = Settings.bSynchronousMode;
388+
if (Settings.bSynchronousMode && !Server.IsSynchronousModeActive()) {
389+
Server.EnableSynchronousMode();
390+
}
391+
else if (!Settings.bSynchronousMode && Server.IsSynchronousModeActive()) {
392+
Server.DisableSynchronousMode();
393+
}
386394

387395
if (GEngine && GEngine->GameViewport)
388396
{

Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEngine.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ class FCarlaEngine : private NonCopyable
105105

106106
bool bIsRunning = false;
107107

108-
bool bSynchronousMode = false;
109-
110108
bool bMapChanged = false;
111109

112110
FCarlaServer Server;

0 commit comments

Comments
 (0)