-
Notifications
You must be signed in to change notification settings - Fork 27
Description
SDFormat v1.5 introduced the ability to nest models within itself (the //model/model
element). As part of my effort to test my python SDFormat bindings, I created a simple world with a nested model, which loads fine in gazebo itself, but fails to load in gym-ignition due to its inability to determine the canonical link (which I specify explicitly).
Here is the full SDF
SDF
<?xml version="1.0"?>
<sdf version="1.8">
<world name="pose_world">
<model name="parent" canonical_link="box::box_link">
<frame name="table2">
<pose>0.794 0 0 0 -0 1.5708</pose>
</frame>
<frame name="tabletop_2">
<pose relative_to="table2">0 0 1.015 0 0 0</pose>
</frame>
<model name="box">
<pose relative_to="tabletop_2">-0.2379753249844183 -0.036526411138213755 0.025 0 0 0</pose>
<link name="box_link"></link>
<link name="B">
<pose relative_to="box_link">1 0 0 0 0 0</pose>
</link>
</model>
</model>
</world>
</sdf>
And here is a MWE python script to reproduce the behavior
SDF
import tempfile
from scenario import gazebo as scenario_gazebo
from scipy.spatial.transform import Rotation as R
import numpy as np
# Allocate the simulator
simulator = scenario_gazebo.GazeboSimulator()
world_without_model_string = """
<?xml version="1.0" ?>
<sdf version="1.8">
<world name="pose_world">
</world>
</sdf>"""
with tempfile.NamedTemporaryFile(mode="r+") as f:
# Write the world file
f.write(world_without_model_string)
# Insert the world file
f.seek(0)
assert simulator.insert_world_from_sdf(f.name)
assert simulator.initialize()
world = simulator.get_world("pose_world")
# Insert the physics
# ==> OTHERWISE THE POSES ARE NOT UPDATED <==
if not world.set_physics_engine(scenario_gazebo.PhysicsEngine_dart):
raise RuntimeError("Failed to insert the physics plugin")
model_string_A = """
<?xml version="1.0" ?>
<sdf version="1.8">
<model name="parent" canonical_link="box::box_link">
<frame name="table2">
<pose>0.794 0 0 0 -0 1.5708</pose>
</frame>
<frame name="tabletop_2">
<pose relative_to="table2">0 0 1.015 0 0 0</pose>
</frame>
<model name="box">
<pose relative_to="tabletop_2">-0.2379753249844183 -0.036526411138213755 0.025 0 0 0</pose>
<link name="box_link"></link>
<link name="B">
<pose relative_to="box_link">1 0 0 0 0 0</pose>
</link>
</model>
</model>
</sdf>"""
assert world.insert_model_from_string(model_string_A)
# A paused run should suffice
assert simulator.run(paused=True)
for model_name in world.model_names():
model = world.get_model(model_name)
print(f"Model: {model_name}")
print(f" Base link: {model.base_frame()}")
for name in model.link_names():
position = model.get_link(name).position()
orientation_wxyz = np.asarray(model.get_link(name).orientation())
orientation = R.from_quat(orientation_wxyz[[1, 2, 3, 0]]).as_euler("xyz")
print(f" {name}:", (*position, *tuple(orientation)))
On my machine, this produces:
Model: parent
Traceback (most recent call last):
File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/home/sebastian/.vscode/extensions/ms-python.python-2021.2.633441544/pythonFiles/lib/python/debugpy/__main__.py", line 45, in <module>
cli.main()
File "/home/sebastian/.vscode/extensions/ms-python.python-2021.2.633441544/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 444, in main
run()
File "/home/sebastian/.vscode/extensions/ms-python.python-2021.2.633441544/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 285, in run_file
runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
File "/usr/lib/python3.8/runpy.py", line 265, in run_path
return _run_module_code(code, init_globals, run_name,
File "/usr/lib/python3.8/runpy.py", line 97, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/home/sebastian/coding_projects/lila-legibility/lila/test_poses2.py", line 60, in <module>
print(f" Base link: {model.base_frame()}")
File "/home/sebastian/.local/lib/python3.8/site-packages/scenario/bindings/core.py", line 2724, in base_frame
return _core.Model_base_frame(self)
RuntimeError: [parent] Failed to find the canonical link
Is this a known limitation of gym-ignition, and if so, what are the plans to supporting nested models? For me, they are useful in this particular context, because I want to position a model @relative_to
something, which only works if I can insert a full world with models (currently not possible because models should be inserted dynamically after physics are loaded) or if they are inside a nested model.
This is because due to SDF scoping rules, which prevent using @relative_to
on a model that is a direct child of //sdf
(since there are no other frames within that scope).