Purpose
This page details the system architecture for Gazebo v1.0.
Requirements
Speed
- Simulate indoor environments with multi-robots in real time
- Environments with: multi-room (~3), multiple objects, 1 PR2, multiple Turtlebots
- Physics requirements: Manipulate household objects while maintaining stability
- Simulate indoor environments with multi-robots in real time
Usability
- Graphically create worlds: XML editing should not be required
- Import common meshes (tables, chairs, etc), such as from 3D Warehouse.
- Create models: create joints, set physical properties
- Create environments: click+drag to build walls, rooms, and place objects
- Controls
- Manipulate joints graphically
- Apply forces and torques to models
- Position objects intelligently: snap to ground, other objects, or a grid
Visualization & Debugging: These features have performance implications, but are disabled by default
- Show sensor output: lasers, camera rendering, contacts
- Hooks for user generated visualization markers, similar to rviz
- Built in performance diagnostics
Hooks for Hudson plot plugin
- Plot performance of key components at run time
- Graphically create worlds: XML editing should not be required
Programmatic control
- Create, modify and control bodies and joints at runtime.
- Interface provided via: ROS, custom plugin, or communication through Gazebo communication system
- Scriptable objects
- Attach scripts to objects in the environment
- Will support event triggers, and manipulation of dynamic objects (doors, people, lights)
- Create, modify and control bodies and joints at runtime.
Portability
- Run on any laptop regardless of hardware and OS (linux, mac, windows)
- Step 1: Support visualization on alternative platforms
- Step 2: Support physics simulation on alternative platforms
- Run on any laptop regardless of hardware and OS (linux, mac, windows)
Current Implementation
The current version of Gazebo is a monolithic implementation that glues together a physics engine (ODE), a rendering engine (OGRE), and a GUI (QT4). User access to sensors and controllers are provided via a shared memory interface.
Two threads run in Gazebo. The first manages the GUI and rendering engine, and the second thread manages the physics engine. With this setup it is possible to step the physics engine multiple times between rendering updates.
Issues
These issues are in relation to the speed and flexibility of Gazebo. The user interface requirements can be addressed independently of the underlying architecture.
- Generating camera sensor data forces the physics engine to pause
- This prevents a truly decoupled system where sensor generation can take place independently of physics simulation
- Difficult to use individual parts of Gazebo (just physics, or sensor generation)
- Difficult to run remotely if user also wants GUI
- Can't take advantage of distributed systems
- Monolithic architecture is less flexible
Proposed System
In order to address the requirements and resolve the current issues with Gazebo, we propose a distributed architecture. Gazebo will be broken into libraries for physics simulation, rendering, user interface, communication, and sensor generation. Three different processes will be provided: physics_sim, sensor_gen, gui, and a master for coordination.
Communication Between Processes
Communication between each process will use a combination of google::protobufs and sockets. The communication architecture will mimic that of ROS nodes. A simulated world will publish body pose updates, and sensor generation and GUI will consume these messages to produce output.
This mechanism will allow for introspection of a running simulation, and provide a convenient mechanism to control aspects of Gazebo.
Why google::protobufs instead of ROS messages
Gazebo is a standalone application, just as OpenCV and PCL are libraries independent of ROS. In order to use ROS messages, Gazebo would have to exist within the ROS eco-system. Until it's possible to link against specific parts of ROS, it's more practical to use google::protobufs.
Benefits
- Speed
- Decoupling of physics, sensor generation, and gui into separate processes which can be run independently
- Modularity
- User can pick and choose which processes to run and where to run them
- Each library has a simple API for potential use outside of gazebo
- Internal API allow for swapping of physics and collision engines
- Communication system allows a user to develop applications with minimal dependencies
example: A render_server that server just images is trivial to implement
- example: A web server can potentially interact with a running simulation
- Portability
- User can run complete sim on cluster and run gui on local machine
Potential Complications
- Synchronization between processes
We will use timestamped messages or give user complete control via run_once functionality
- Network traffic can become a potential bottleneck
- More difficult to run
- We can establish scripts for common situations
- Development and maintenance:
- Complexity leads to a longer debugging process
System Architecture
Gazebo Master
This is a stripped down clone of the ROS master. It provides namelookup, and topic management. A single master can handle multiple physics simulations, sensor generators, and GUIs.
Communication Library
Dependencies: Protobuf and boost::ASIO
External API:
bool get_master_uri(std::string &master_host, unsigned short &master_port);
- Get URI of the gazebo master, as defined by the GAZEBO_MASTER_URI
- master_host: Gets set to the master hostname
- master_port: Gets set to the master port
- Return true on success
bool init(const std::string &master_host="", unsigned short master_port=0);
- Initialize the communication system
- master_host: Hostname of the master, leave empty to use the default
- master_port: Port of the master, leave empty to use the default
- Return true on success
void set_topic_namespace(const std::string &space);
Set the namespace for all topics ( /gazebo/<space>/ )
- space: Name of the topic namespace
transport::PublisherPtr advertise(const std::string &topic);
- Advertise a new topic
- topic: Name of the topic
- Return a pointer to a publisher
transport::SubscriberPtr subscribe(const std::string &topic, void(T::*fp)(const boost::shared_ptr<M const> &), T *obj);
- Subscribe to a topic
- topic: Name of the topic
- fp: Calback function pointer
- obj: Object containing the callback function
- Return a Subscriber pointer
transport::SubscriberPtr subscribe(const std::string &topic, void(*fp)(const boost::shared_ptr<M const> &));
- Subscribe to a topic
- fp: A function pointer callback
- Return a Subscriber pointer
Internal API: None
Advertised Topics: None
Subscribed Topics: None
This library is used by almost all subsequent libraries. It acts as the communication and transport mechanism for Gazebo. It currently supports only publish/subscribe, but it's possible to use RPC with minimal effort.
Protobuf Messages
- Point { double:x, double:y, double:z }
- Quaternion { double:x, double:y, double:z, double:u }
Pose { Point:position, Quaternion:orientation }
Twist { Point:linear_velocity, Point:euler_angle_velocity }
Wrench { Point:force, Point:torque }
- Time {uint32:seconds, uint32:nanoseconds}
- Mass {double:mass, double[]:inertia}
EntityState { string:entity_name, Pose:pose, Twist:twist, Wrench:wrench }
WorldStatistics {Time:sim_time, Time:pause_time, Time:real_time, uint32:iterations, int32:model_count}
WorldState { EntityState[]:entity_states, WorldStatistics:stats, Point:gravity, double:time_step}
Physics Library
Dependencies: Dynamics engine, and Collision Library
External API: Provides a simple and generic interface to physics simulation
bool init();
- Initialize the physics engine
- Return True if successful
WorldPtr create_world_from_file(const std::string &filename);
- Create a world based on a configuration file
- filename: The name of the configuration file
- Return a pointer to the world
WorldPtr create_world_from_string(const std::string &xml_string);
- Create a world based on a configuration string
- filename: The name of the configuration string
- Return a pointer to the world
void run();
- Run all the worlds, this blocks
void run_once();
- Run all the worlds one step
Internal API: Defines a fundamental interface to the physics library for 3rd party dynamic engines.
Advertised Topics:
/gazebo/<world_name>/entity_state_update : Update of the entity states. Only those entities that have changed will be published
Msg Type: EntityStateUpdate { EntityState[]:entity_states }
/gazebo/<world_name>/state : The current state of the entire world. Published when a new subscriber connects to the world
Msg Type: WorldState
/gazebo/<world_name>/world_stats: Publish statistics about the physics simulation
Msg Type: WorldStatistics
Subscribed Topics:
/gazebo/<world_name>/control : Provides external users with control over the running simulation
Msg Type: PhysicsControl {bool:pause, int:step_count}
/gazebo/<world_name>/set_entity_prop : Set the properties of an entity
Msg Type: EntityProp { string:entity_name, Mass:mass, double:velocity_damping, double:friction_coefficient }
/gazebo/<world_name>/set_entity_state : Set the state of an entity
Msg Type: EntityState { string:entity_name, Pose:pose, Twist:twist, Wrench:wrench }
/gazebo/<world_name>/set_joint_property : Set the properties of a joint
Msg Type: JointProp { string:joint_name, double:damping, Point:offset, double:max_force, double[]:lower_limits, double[]:upper_limits }
/gazebo/<world_name>/set_joint_state: Set the state of a joint
Msg Type: JointState { string:joint_name, double[]:angle, double[]:velocity, double[]:effort }
/gazebo/<world_name>/insert_entity : Allows new entities to be inserted into the simulation
Msg Type: Entity { string:entity_name, Pose:pose, Mass:mass, CollisionObj:coll, Visual:vis }
The physics library can use any dynamic engine that conforms to the internal API (TBD). It also presents a simple external interface in order to establish a work physics simulation.
Collision Library
Dependencies: 3rd party collision engine
External API: TBD
Internal API: Generic interface for collision engines
Advertised Topics: None
Subscribed Topics: None
This is an abstraction library to handle different collision engines, and provide a simple external interface to the user.
Rendering Library
Dependencies: OGRE
External API: Allows for loading, initialization, and scene creation
bool init(const std::string &world_name, bool no_gui);
- Initialize the rendering engine
- world_name: The world which is performing physics sim
- no_gui: Set to true if a GUI is not present or if the rendering engine should not expect an external render window
bool load_from_file(const std::string &filename);
- Load the rendering engine from a file.
- filename: Filename containing XML configuration
- Return True if load was successful
bool load_from_string(const std::string &xml_string);
- Load the rendering engine from a file.
- xml_string: A string containing the XML configuration specification
- Return True if load was successful
Internal API: None, we are going to use only OGRE.
Advertised Topics: None
Subscribed Topics:
/gazebo/<world_name>/state : Get the current state of the entire world
Msg Type: WorldState
/gazebo/<world_name>/entity_state_update : Get the entity updates and change the scene appropriately
Msg Type: EntityStateUpdate { EntityState[]:entity_states }
The rendering library provides a simple interface to both the GUI and sensor generation. We are currently sticking with OGRE since we don't have a better alternative. It will be possible to write plugins for the rendering engine.
Note on RVE
Significant work has been done on RVE, and we believe there may be potential to combine our efforts. In the next few days/weeks we will hash out this possibility, and how to proceed.
Sensor Generation
Dependencies: Rendering Library, Collision Library
External API: Provide functionality to initialize and run a set of sensors
bool sensors::init(const std::string &world_name);
- Initialize the sensor library
- world_name: The name of the world in which to simulate the sensors
- Return True if initialization was successful
SensorPtr sensors::create_sensor(const std::string &type);
- Create a sensor of a certain type.
- type: A string name that defines the type of sensor, ex: camera, laser
- Return a pointer to the new sensor.
void sensors::run();
- Update all the sensors, this blocks.
void sensors::run_once(bool force=true);
- Update all the sensors once
- force: If true, all sensors will be updated regardless of their Hz rate.
Internal API: TBD
Advertised Topics: This depends on the sensors that are created.
Subscribed Topics:
/gazebo/<world_name>/state : Get the current state of the entire world
Msg Type: WorldState
/gazebo/<world_name>/entity_state_update : Get the entity updates and change the scene appropriately
Msg Type: EntityStateUpdate { EntityState[]:entity_states }
The sensor generation library implements all the various types of sensors, listens to world state updates from a physics simulator and produces output specified by the instantiated sensors.
GUI
Dependencies: Rendering Library, QT4
External API: TBD
Internal API: TBD
Advertised Topics:
/gazebo/<world_name>/set_entity_prop : Set the properties of an entity
Msg Type: EntityProp { string:entity_name, Mass:mass, double:velocity_damping, double:friction_coefficient }
/gazebo/<world_name>/set_entity_state : Set the state of an entity
Msg Type: EntityState { string:entity_name, Pose:pose, Twist:twist, Wrench:wrench }
/gazebo/<world_name>/set_joint_property : Set the properties of a joint
Msg Type: JointProp { string:joint_name, double:damping, Point:offset, double:max_force, double[]:lower_limits, double[]:upper_limits }
/gazebo/<world_name>/set_joint_state: Set the state of a joint
Msg Type: JointState { string:joint_name, double[]:angle, double[]:velocity, double[]:effort }
/gazebo/<world_name>/insert_entity : Allows new entities to be inserted into the simulation
Msg Type: Entity { string:entity_name, Pose:pose, Mass:mass, CollisionObj:coll, Visual:vis }
Subscribed Topics:
/gazebo/<world_name>/entity_state_update : Update of the entity states. Only those entities that have changed will be published
Msg Type: EntityStateUpdate { EntityState[]:entity_states }
The primary function of the GUI involves displaying the current state of the simulation and providing a convenient means for user input. There is no need for an internal or external API since we are sticking with wxWidgets.
Plugins
The physics, sensor, and rendering libraries will support plugins. These plugins will provide users with access to the respective libraries without using the communication system.