delphyne
SimulationRunner Class Reference

Detailed Description

A wrapper to execute the Drake simulator as a back-end.

This class exposes some of the functionality of the simulator via Ignition Transport services (e.g.: play/pause/step the simulation). At the same time, it has to periodically advance the simulation a certain amount of time. Finally, it notifies any given state changes to the outside world via Ignition Transport messages. All these operations occur in a main loop with the following structure:

  • Process incoming messages: All incoming requests are queued asynchronously when received and in this step we process all of them. This request might involve flag the simulation as paused, insert a new model, remove an existing model, etc.
  • Step the simulation: We advance the simulation a certain amount of time. The amount of time could be the default time step (if we are in play mode), a custom time step (if we are externally stepping) or no time at all if the simulation is paused.
  • Notify changes in the simulation. The simulation will send messages over a a well-known topic every time that a relevant simulation state changed occur. E.g.: When the simulation is paused or a model is removed. These notifications should be used by all the Visualizer instances to update its state/widgets in order to keep them in sync with the back-end.
  • Provides a mechanism to register callbacks to be triggered on each simulation step. Since this was originally conceived to be used with the Python bindings and plain typedef-based functions are not supported (see http://www.boost.org/doc/libs/1_65_1/libs/python/doc/html/faq.html) we need to pass a PyObject as an argument.

Finally, it is important to clarify how the Python-C++ callback mechanism is implemented, so we can understand a possible deadlock. The key elements here are:

  • A typical main program will create a SimulatorRunner instance, configure it and call Start(). Let's call the thread the program runs on the MainThread.
  • Calling Start() will in turn spawn a new thread. This thread will execute a tight loop (see the Run() method), making the simulation itself move forward. We will call this thread the RunThread.
  • When the SimulatorRunner destructor is called, it will try to join the RunThread into the MainThread, to perform a clean shutdown. To do that a boolean flag is set to exit the tight loop in Run() and the join() method is called on the RunThread, effectively blocking the MainThread until RunThread is done.
  • When calling a Python-defined callback function from C++ we need to acquire the GIL (Python's Global Interpreter Lock), which basically gives us thread-safety between the C++ and Python worlds. The typical steps to invoke a Python callback from C++ are:
    • Acquire the GIL.
    • Invoke the callback.
    • Release the GIL. Note that acquiring the GIL is a potentially blocking call, as it is basically getting the lock of a mutex.

Now, the following interleaving may occur when running a python-scripted simulation:

  • Create the simulator runner and configure a python callback.
  • Start the simulator. By this time we have the MainThread (which is the same thread that executes the python code) and the RunThread executing.
  • Stop the simulator from the MainThread (calling the Stop() method) and consider the following events:

    • The RunThread yielded the processor right before acquiring the GIL.
    • The MainTread execute the Stop() method and then the destructor. Now the MainThread is waiting for RunThread to join.
    • Execution is now returned to RunThread, which tries to acquire the GIL. However, the Python thread is blocked on the join(), so we are effectively in a deadlock.

    It is interesting to note however that the lock on the GIL happens only if the code is blocked on the C++ side; in other words, if the code was blocked on the Python side (e.g. due to a sleep call), C++ has no problem to acquire the GIL. Because of this, the current recommendation is to add a sleep after calling stop() from the Python side, so the processor is yielded and the RunThread can finish its current (and last) loop before exiting the while.

#include <src/backend/simulation_runner.h>

Public Types

using AgentCollision = AgentBaseCollision< double >
 
using CollisionCallback = std::function< void(const std::vector< AgentCollision > &)>
 

Public Member Functions

 SimulationRunner (std::unique_ptr< AgentSimulation > sim, double time_step, double realtime_rate, bool paused, bool log, std::string logfile_name)
 Default constructor. More...
 
 SimulationRunner (std::unique_ptr< AgentSimulation > sim, double time_step, bool paused, bool log)
 Simplified constructor that runs the simulation at a real-time rate of 1.0. More...
 
 SimulationRunner (std::unique_ptr< AgentSimulation > sim, double time_step, bool paused, bool log, std::string logfile_name)
 Simplified constructor that runs the simulation at a real-time rate of 1.0. More...
 
 SimulationRunner (std::unique_ptr< AgentSimulation > sim, double time_step, double realtime_rate)
 Simplified constructor that starts the simulator with _paused = false, and log = true. More...
 
 SimulationRunner (std::unique_ptr< AgentSimulation > sim, double time_step)
 Simplified constructor that runs the simulation with _paused = false, a real-time rate of 1.0, and log = true. More...
 
virtual ~SimulationRunner ()
 Default destructor. More...
 
void AddStepCallback (std::function< void()> callable)
 Adds a python callback to be invoked on each simulation step. More...
 
void AddCollisionCallback (CollisionCallback callable)
 Adds a callback to be invoked on agent collision. More...
 
void Start ()
 Spawns a new thread that runs the interactive simulation loop. More...
 
void RunAsyncFor (double duration, std::function< void()> callback)
 Spawns a new thread that runs the interactive simulation loop for the provided time period. More...
 
void RunSyncFor (double duration)
 Runs the interactive simulation loop for the provided time period. More...
 
void Stop ()
 Stops the thread that is running the interactive simulation loop. More...
 
void RequestSimulationStepExecution (unsigned int steps)
 Enqueue a requests for a simulation step to be executed. More...
 
bool IsInteractiveLoopRunning () const
 Returns if the interactive simulation loop is currently running or not. More...
 
void SetRealtimeRate (double realtime_rate)
 See documentation of Simulator::set_target_realtime_rate() More...
 
double GetRealtimeRate () const
 See documentation of Simulator::get_target_realtime_rate() More...
 
bool IsSimulationPaused () const
 Returns the paused state of the simulation. More...
 
void PauseSimulation ()
 Pauses the simulation, no-op if called multiple times. More...
 
void UnpauseSimulation ()
 Unauses the simulation, no-op if called multiple times. More...
 
void EnableCollisions ()
 Enables collisions during the simulation, no-op if called multiple times. More...
 
void DisableCollisions ()
 Disables collisions during the simulation, no-op if called multiple times. More...
 
double GetCurrentSimulationTime () const
 Returns the current simulation time in seconds. More...
 
const AgentSimulationGetSimulation () const
 Returns a reference to the simulation being run. More...
 
AgentSimulationGetMutableSimulation ()
 Returns a mutable reference to the simulation being run. More...
 
const InteractiveSimulationStatsGetStats () const
 Returns the collected interactive simulation statistics. More...
 
double GetTimeStep () const
 Returns the time step of the simulation runner. More...
 
bool IsLogging () const
 Returns the logging state. True indicates that logging is enabled. More...
 
void StartLogging ()
 Start logging to a default named file. More...
 
void StartLogging (const std::string &filename)
 Start logging to a given file or path. More...
 
void StopLogging ()
 Stop logging. More...
 
std::string GetLogFilename () const
 Get the log file name, or empty string if logging has not been started. More...
 

Static Public Attributes

static constexpr char const * kControlService = "/world_control"
 
static constexpr char const * kNotificationsTopic = "/notifications"
 
static constexpr char const * kWorldStatsTopic = "/world_stats"
 
static constexpr char const * kClockTopic = "/clock"
 
static constexpr char const * kSceneRequestServiceName = "/get_scene"
 

Member Typedef Documentation

◆ AgentCollision

◆ CollisionCallback

using CollisionCallback = std::function<void(const std::vector<AgentCollision>&)>

Constructor & Destructor Documentation

◆ SimulationRunner() [1/5]

SimulationRunner ( std::unique_ptr< AgentSimulation sim,
double  time_step,
double  realtime_rate,
bool  paused,
bool  log,
std::string  logfile_name 
)

Default constructor.

Parameters
[in]simA pointer to a simulation. Note that we take ownership of the simulation.
[in]time_stepThe slot of time (seconds) simulated in each simulation step.
[in]realtime_rateDesired rate relative to real time. See documentation of Simulator::set_target_realtime_rate.
[in]pausedA boolean value that if true, will start the simulator in paused mode.
[in]logA boolean value that if true, will log messages to disk.
[in]logfile_nameA string with a custom file name for the log.

◆ SimulationRunner() [2/5]

SimulationRunner ( std::unique_ptr< AgentSimulation sim,
double  time_step,
bool  paused,
bool  log 
)

Simplified constructor that runs the simulation at a real-time rate of 1.0.

Parameters
[in]simA pointer to a simulation. Note that we take ownership of the simulation.
[in]time_stepThe slot of time (seconds) simulated in each simulation step.
[in]pausedA boolean value that if true, will start the simulator in paused mode.
[in]logA boolean value that if true, will log messages to disk.

◆ SimulationRunner() [3/5]

SimulationRunner ( std::unique_ptr< AgentSimulation sim,
double  time_step,
bool  paused,
bool  log,
std::string  logfile_name 
)

Simplified constructor that runs the simulation at a real-time rate of 1.0.

Parameters
[in]simA pointer to a simulation. Note that we take ownership of the simulation.
[in]time_stepThe slot of time (seconds) simulated in each simulation step.
[in]pausedA boolean value that if true, will start the simulator in paused mode.
[in]logA boolean value that if true, will log messages to disk.
[in]logfile_nameA string with a custom file name for the log.

◆ SimulationRunner() [4/5]

SimulationRunner ( std::unique_ptr< AgentSimulation sim,
double  time_step,
double  realtime_rate 
)

Simplified constructor that starts the simulator with _paused = false, and log = true.

Parameters
[in]simA pointer to a simulation. Note that we take ownership of the simulation.
[in]time_stepThe slot of time (seconds) simulated in each simulation step.
[in]realtime_rateDesired rate relative to real time. See documentation of Simulator::set_target_realtime_rate.

◆ SimulationRunner() [5/5]

SimulationRunner ( std::unique_ptr< AgentSimulation sim,
double  time_step 
)

Simplified constructor that runs the simulation with _paused = false, a real-time rate of 1.0, and log = true.

Parameters
[in]simA pointer to a simulation. Note that we take ownership of the simulation.
[in]time_stepThe slot of time (seconds) simulated in each simulation step.

◆ ~SimulationRunner()

~SimulationRunner ( )
virtual

Default destructor.

Member Function Documentation

◆ AddCollisionCallback()

void AddCollisionCallback ( CollisionCallback  callable)

Adds a callback to be invoked on agent collision.

Note
For collisions to be computed in the first place, collision detection must be enabled (via EnableCollisions()).
The simulation step will be effectively blocked during the sequential execution of all registered callbacks.
Parameters
[in]callableThe callback function.

◆ AddStepCallback()

void AddStepCallback ( std::function< void()>  callable)

Adds a python callback to be invoked on each simulation step.

It is important to note that the simulation step will be effectively blocked by this the execution of the callbacks, so please consider this when adding it.

Parameters
[in]callableA pointer to a callback function, coming from the python world.

◆ DisableCollisions()

void DisableCollisions ( )

Disables collisions during the simulation, no-op if called multiple times.

◆ EnableCollisions()

void EnableCollisions ( )

Enables collisions during the simulation, no-op if called multiple times.

Note
Simulation will be paused on collision detection.

◆ GetCurrentSimulationTime()

double GetCurrentSimulationTime ( ) const

Returns the current simulation time in seconds.

◆ GetLogFilename()

std::string GetLogFilename ( ) const

Get the log file name, or empty string if logging has not been started.

◆ GetMutableSimulation()

AgentSimulation* GetMutableSimulation ( )

Returns a mutable reference to the simulation being run.

◆ GetRealtimeRate()

double GetRealtimeRate ( ) const

See documentation of Simulator::get_target_realtime_rate()

◆ GetSimulation()

const AgentSimulation& GetSimulation ( ) const

Returns a reference to the simulation being run.

◆ GetStats()

const InteractiveSimulationStats& GetStats ( ) const

Returns the collected interactive simulation statistics.

◆ GetTimeStep()

double GetTimeStep ( ) const

Returns the time step of the simulation runner.

◆ IsInteractiveLoopRunning()

bool IsInteractiveLoopRunning ( ) const

Returns if the interactive simulation loop is currently running or not.

◆ IsLogging()

bool IsLogging ( ) const

Returns the logging state. True indicates that logging is enabled.

◆ IsSimulationPaused()

bool IsSimulationPaused ( ) const

Returns the paused state of the simulation.

◆ PauseSimulation()

void PauseSimulation ( )

Pauses the simulation, no-op if called multiple times.

◆ RequestSimulationStepExecution()

void RequestSimulationStepExecution ( unsigned int  steps)

Enqueue a requests for a simulation step to be executed.

For this call to succeed the interactive simulation loop must be running and the simulation must be paused.

Precondition
Start() or RunAsyncFor() has been called.
Paused() has been called or the simulation runner has started paused.

◆ RunAsyncFor()

void RunAsyncFor ( double  duration,
std::function< void()>  callback 
)

Spawns a new thread that runs the interactive simulation loop for the provided time period.

Parameters
[in]durationThe duration that the interactive simulation loop should run for. Note that the time stated here is simulation time, expressed in seconds.
[in]callbackA callback function that will be executed when the simulation has finished.

◆ RunSyncFor()

void RunSyncFor ( double  duration)

Runs the interactive simulation loop for the provided time period.

Note that this is a synchronous call and the caller will be blocked until the interactive simulation loop is done.

Parameters
[in]durationThe duration that the interactive simulation loop should run for. Note that the time stated here is simulation time, expressed in seconds.

◆ SetRealtimeRate()

void SetRealtimeRate ( double  realtime_rate)

See documentation of Simulator::set_target_realtime_rate()

◆ Start()

void Start ( )

Spawns a new thread that runs the interactive simulation loop.

Precondition
The simulation loop should not be running.

◆ StartLogging() [1/2]

void StartLogging ( )

Start logging to a default named file.

◆ StartLogging() [2/2]

void StartLogging ( const std::string &  filename)

Start logging to a given file or path.

◆ Stop()

void Stop ( )

Stops the thread that is running the interactive simulation loop.

Precondition
The interactive simulation loop should be running, either because of a call to Start() or RunAsyncFor().

◆ StopLogging()

void StopLogging ( )

Stop logging.

◆ UnpauseSimulation()

void UnpauseSimulation ( )

Unauses the simulation, no-op if called multiple times.

Member Data Documentation

◆ kClockTopic

constexpr char const* kClockTopic = "/clock"
staticconstexpr

◆ kControlService

constexpr char const* kControlService = "/world_control"
staticconstexpr

◆ kNotificationsTopic

constexpr char const* kNotificationsTopic = "/notifications"
staticconstexpr

◆ kSceneRequestServiceName

constexpr char const* kSceneRequestServiceName = "/get_scene"
staticconstexpr

◆ kWorldStatsTopic

constexpr char const* kWorldStatsTopic = "/world_stats"
staticconstexpr

The documentation for this class was generated from the following files: