FirefoxMetzger / python-sdformat

Python Parser for SDFormat files.
BSD 2-Clause "Simplified" License
13 stars 0 forks source link

How to figure out what collision geometry is used #1

Open brean opened 2 years ago

brean commented 2 years ago

Hi!

Thanks a lot for providing python-sdformat, I think it will help me a lot!

My problem with it is that a collision geometry has all parameter set (empty, box, mesh, polyline, sphere, cylinder...). So how do I figure out what is actually used? I for now I convert the data to dict and see if "geometry/mesh", "geometry/box"... are set but that seems like an ugly hack. Is there a better way to check the type?

FirefoxMetzger commented 2 years ago

If I understand the problem correctly, you could inspect SdfElement.children, which is a list of all direct child elements that were explicitly set/modified.

The spec does, technically, allow you to set 0 or 2+ children for Geometry though. I don't know what gazebo will do in this case and I can't think of a scenario where you'd want to do so. Hence, assuming that your SDF is sensible and the geometry element has exactly one type set, you could do the following:

from pysdf import Geometry

my_geometry = Geometry()
my_geometry.ellipsoid.radii = (1, 2, 3)
geometry_type = my_geometry.children[0]
isinstance(geometry_type , Geometry.Ellipsoid)  # True

The downside is that you will lose typed auto-completion because you won't know geometry_type's type until runtime.

If you are looking for something "cleaner", e.g., assert my_geometry.type == "ellipsoid" then this currently doesn't exist. I'm happy to add a feature like this, if you have a concrete suggestion for how this should look like. Does the official C parser have a method for this? If so, how does it look like?

huweiATgithub commented 1 year ago

Hello, thank you for creating such a useful library that enables easy manipulation of models using Python. Following the topic raised in the current issue, I wonder which way is the preferred way to define a Geometry. Reading the codes, I can come up with three different approaches:

FirefoxMetzger commented 1 year ago

@huweiATgithub The short answer is that you can use either of these approaches.

The slightly longer answer is that SDF heavily relies on tags to configure attributes, e.g.. world.gravity or scene.background. Each tag in SDF is represented by a python class in python-sdformat (standard behavior for bindings), and it would be awkward UX to have to create and assign them one-by-one:

scene = Scene()
scene.sky.clouds.speed = 0.6

#  vs.
scene = Scene()
scene,sky = Scene.Sky()
scene.sky.clouds = Scene.Sky.Clouds()
clouds.speed = 0.6  # Speed is a tag, too, but can't have children, so we cant instantiate it 

The latter is perhaps more familiar from a software engineering background and preferred when engineering. However, it is also more verbose and doesn't offer much gain over writing SDF directly:

<scene>
    <sky>
        <clouds>
            <speed>0.6</speed>
        </clouds>
    </sky>
</scene>

My goal with python-sdformat was to make it faster to work with (and/or generate) SDF. SDF is essentially a (complex) config file format, so I am okay with it creating the necessary config (be that objects or tags) under the hood when I say:

scene = Scene()
scene.sky.clouds.speed = 0.6

Regarding auto-completion for dynamic types this is an unfortunate limitation of the project. I used this project to create a faster (and type-hint-guided) way to interact with SDF, but I didn't discover that you can separate definitions (.py files) and type annotations (.pyi files) until much later. If you were to create a .pyi file for the project, you could add type annotations for Radius and other dynamically generated types. I'd be happy to review and merge a contribution on this!

huweiATgithub commented 1 year ago

@FirefoxMetzger Thanks for your reply. I now understand the design. SDF relies heavily on nesting tags. I will take a look at the pyi generation and try to add the annotations.