Skip to content

Proposal to add support for managing simulation worlds #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(msg_files
"msg/SimulatorFeatures.msg"
"msg/Spawnable.msg"
"msg/TagsFilter.msg"
"msg/WorldResource.msg"
)

set(srv_files
Expand All @@ -47,6 +48,10 @@ set(srv_files
"srv/SetSimulationState.srv"
"srv/SpawnEntity.srv"
"srv/StepSimulation.srv"
"srv/LoadWorld.srv"
"srv/UnloadWorld.srv"
"srv/GetCurrentWorld.srv"
"srv/GetAvailableWorlds.srv"
)

set(action_files
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ Some interfaces represent optional utility and are considered lower priority:
- [GetNamedPoseBounds](srv/GetNamedPoseBounds.srv)
- [GetNamedPoses](srv/GetNamedPoses.srv)
- [GetSpawnables](srv/GetSpawnables.srv)
- [SetEntityInfo](srv/SetEntityInfo.srv)
- [SetEntityInfo](srv/SetEntityInfo.srv)
- [GetAvailableWorlds](srv/GetAvailableWorlds.srv)
- [LoadWorld](srv/LoadWorld.srv)
- [UnloadWorld](srv/UnloadWorld.srv)
- [GetCurrentWorld](srv/GetCurrentWorld.srv)
2 changes: 2 additions & 0 deletions msg/SimulationState.msg
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ uint8 STATE_QUITTING = 3 # Closing the simulator application. Swit
# Running simulation application is outside of the simulation interfaces as
# there is no service to handle the call when the simulator is not up.

uint8 STATE_NO_WORLD = 4 # Simulation world is currently unloaded and a new world has not finished loading yet.
# The simulation is inactive and cannot be started, stopped, or paused.
Comment on lines +16 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like NO_WORLD is a little too specific in this context and STATE_TRANSITIONAL would be better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint8 STATE_NO_WORLD = 4 # Simulation world is currently unloaded and a new world has not finished loading yet.
# The simulation is inactive and cannot be started, stopped, or paused.
uint8 STATE_NO_WORLD = 4 # Simulation world is currently unloaded, and/or a new world has not finished loading yet.
# The simulation is inactive and cannot be started, stopped, or paused.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure with my English here. Would TRANSITIONAL also semantically cover the instant where no world is loaded and no world is commanded to be loading?

uint8 state
6 changes: 6 additions & 0 deletions msg/SimulatorFeatures.msg
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ uint8 STEP_SIMULATION_MULTIPLE = 32 # Supports multi-stepping through simu
# service or through SimulateSteps action.
uint8 STEP_SIMULATION_ACTION = 33 # Supports SimulateSteps action interface.

uint8 WORLD_LOADING = 40 # Supports LoadWorld interface
uint8 WORLD_RESOURCE_STRING = 41 # Supports LoadWorld resource_string field
uint8 WORLD_TAGS = 42 # Supports world tags and tag filtering
uint8 WORLD_UNLOADING = 43 # Supports UnloadWorld interface
uint8 WORLD_INFO_GETTING = 44 # Supports GetCurrentWorld interface
uint8 AVAILABLE_WORLDS = 45 # Supports GetAvailableWorlds interface

uint16[] features # A list of simulation features as specified by the list above.

Expand Down
18 changes: 18 additions & 0 deletions msg/WorldResource.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# World is a virtual environment in which the simulation happens.
# Worlds are also known as scenes or levels in some simulators.
# Depending on the world format, loading of a world might be associated with changes
# in certain parameters, including physics settings such as gravity.
# World resources may be defined in standard or simulation-specific formats,
# and, depending on the simulator, loaded from local or remote repositories.

# World name, which is not necessarily unique.
string name

# URI which will be accepted by LoadWorld service, unique per world.
string uri
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we already have the Spawnable.msg in this package that has a string uri, string description and a spawn_bounds. I think this would be cleaner if we eliminated the non-unique name from this message.

Copy link
Author

@ayushgnv ayushgnv May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think semantically any Spawnable object is spawned while simulation is running. Whereas a world would be something you load by stopping sim, opening a new world which resets the current simulation. We could look into renaming or updating the descriptions in Spawnable.msg if we would still like to reuse this message type for loading worlds.

@adamdbrw @peci1 @mjcarroll any thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what repeats here is uri and description, which could be abstracted into "Resource", but a world is essentially quite a different type of thing - something that is required for spawning anything (in it).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its fine to have Spawnable and WorldResource. My point was to highlight the difference that both have a uri and description, but only worlds have names.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also okay with keeping this separate message. However, see further.


# Optional description of the world
string description

# Optional tags describing the world (e.g., "indoor", "outdoor", "warehouse")
string[] tags
18 changes: 18 additions & 0 deletions srv/GetAvailableWorlds.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Return a list of available world resources which can be used with LoadWorld.
# Support for this interface is indicated through the AVAILABLE_WORLDS value in GetSimulatorFeatures.

# Optional field for additional sources (local or remote) to search,
# specified as standard URIs if possible.
string[] sources
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string[] sources
string[] additional_sources

Also, it's not clear to me what should I type in this field. Is it https://app.gazebosim.org/fuel/worlds or https://fuel.gazebosim.org/1.0 or something else?

Last, what to do if there are standard online source (like the Fuel database), but we'd like to allow these use-cases without the user needing to look up docs for some particular URI?

  1. Search only locally available worlds, not connecting to the Internet
  2. Search also the default online locations

As one possibility, I think the worlds could get a virtual tag LOCAL or ONLINE which would tell whether they are locally available or not. The implementations could then first check for this particular tag filter and skip the online search when it is not needed. But what if the implementation doesn't support world tags?


# Only get worlds with tags matching the filter. The filter is optional and matches everything by default.
# This feature is supported if WORLD_TAGS feature is included in output of GetSimulatorFeatures.
TagsFilter filter

---

# Standard result message
Result result

# Available world resources.
WorldResource[] worlds
12 changes: 12 additions & 0 deletions srv/GetCurrentWorld.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Get information about the currently loaded world in the simulation.
# Support for this interface is indicated through the WORLD_INFO_GETTING value in GetSimulatorFeatures.

---

uint8 NO_WORLD_LOADED = 101 # No world is loaded at the moment.

# Standard result message
Result result

# Information about the currently loaded world. Only valid if result is RESULT_OK.
WorldResource world
33 changes: 33 additions & 0 deletions srv/LoadWorld.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Load a simulation world from a file or resource.
# This means clearing the current scene (removing all entities), loading the new world and setting the simulation to the stopped state.
# Support for this interface is indicated through the WORLD_LOADING value in GetSimulatorFeatures.
# resource_string field support is indicated through the WORLD_RESOURCE_STRING value in GetSimulatorFeatures.
# Currently loaded worlds will be unloaded before attempting to load a new one.
# Any previously spawned entities will be removed. Once a world is loaded, simulation will be left in a Stopped state.

# Resource such as SDF, USD, MJCF, or other simulator-native format world file.
string uri

# Simulation world passed as a string. This field is used if the uri field is empty.
string resource_string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense to have two separate services.

  1. Add World, which takes in a resource_string and makes it available for use
  2. Load World, which only takes a uri

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would does "makes it available for use" entail for the simulator? Is there any pre-processing that needs to happen before loading a world in your case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "adding" have a value from the perspective of external interface node / automation? I am of opinion that it is an implementation detail, some resources need to be downloaded and other will be available; I would expect the simulator to handle that.

Perhaps it would be useful to document that loading can take a bit and make things clearer through error codes, suggesting async service call etc, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So from an abstract perspective, here's what I imagine.

As an external client, I call GetAvailableWorlds.srv and get a list of unique world uri.

worlds: [
  - {uri: 'factory1'}
  - {uri: 'competition_2'}
  - {uri: 'moon}
]

I could then call LoadWorld.srv with uri: 'moon'. However, if instead I wanted to load in some other world that is not specified yet, I could call AddWorld.srv with the resource_string (which I assume would be SDF or similar), and then that new world be listed in the available worlds.

The core issue here is that I think it is semantically cleaner to run separate Add/Load calls than it is to have the single Load call with different behavior if the uri field is empty or not. Also, what happens when uri is nonempty and resource_string is nonempty?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me it could be beneficial to create a Resource message like this:

string uri
string resource_string  # only used if uri is empty

This could then be used in Spawnable, WorldResource and here.

The proposal with AddWorld/LoadWorld seems too complicated to me.


# Fail on unsupported elements (such as SDFormat sub-tags). By default, such elements are ignored.
bool fail_on_unsupported_element

# Ignore missing or unsupported assets. By default, missing or unsupported assets result in failure.
bool ignore_missing_or_unsupported_assets

---

uint8 UNSUPPORTED_FORMAT = 101 # Format for uri or resource string is unsupported.
uint8 NO_RESOURCE = 102 # Both uri and resource string are empty.
uint8 RESOURCE_PARSE_ERROR = 103 # Resource file or string failed to parse.
uint8 MISSING_ASSETS = 104 # At least one of resource assets (such as meshes) was not found.
uint8 UNSUPPORTED_ASSETS = 105 # At least one of resource assets (such as meshes) is not supported.
uint8 UNSUPPORTED_ELEMENTS = 106 # At least one of world definition elements such as format tags is unsupported.

# Standard result message
Result result

# Information about the loaded world. Only valid if result is RESULT_OK.
WorldResource world
10 changes: 10 additions & 0 deletions srv/UnloadWorld.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Unload the current simulation world.
# Support for this interface is indicated through the WORLD_UNLOADING value in GetSimulatorFeatures.
# Any previously spawned entities will be removed.

---

uint8 NO_WORLD_LOADED = 101 # No world is loaded at the moment.

# Standard result message
Result result