clemense / yourdfpy

Python parser for URDFs
MIT License
122 stars 13 forks source link
kinematics robotics ros urdf urdf-descriptions urdf-models urdf-parser urdf-visualizer

Build Status Documentation Status Coverage Status PyPI version

yourdfpy

Yet anOther URDF parser for Python. Yup, it's another one. Deal with it.

Yourdfpy is a simpler and easier-to-use library for loading, manipulating, validating, saving, and visualizing URDF files.

Installation

You can install yourdfpy directly from pip:

pip install yourdfpy

Visualization

Once installed, you can visualize a URDF model from the command line:

yourdfpy ./my_description/urdf/robot.urdf

You can use the following keyboard shortcuts to inspect your model:

But why another one?!?

Why are you wasting not only your but also our time? you might ask. Fair point. There are already urdfpy and urdf_parser_py that deal with URDFs. Unfortunately, none of these solutions allow customizable URDF parsing that is fully independent of validation and mesh loading. Dealing with filenames, outdated dependencies, open bug reports, and limited flexibility when it comes to serialization are other disadvantages. As shown in the table below, yourdfpy is the most robust one when it comes to loading URDFs in the wild.

Example URDFs

urdfpy urdf_parser_py yourdfpy
Decouple parsing from validation :heavy_check_mark:
Decouple parsing from loading meshes :heavy_check_mark: :heavy_check_mark:
Visualize URDF :heavy_check_mark: :heavy_check_mark:
Forward Kinematics :heavy_check_mark: :heavy_check_mark:
Robustness test: loading 12 URDF files from here 4/12 6/12 12/12
Avg. loading time per file (w/ mesh loading) 480 ms 370 ms
(w/o mesh loading) 3.2 ms 6.2 ms
Test on 4 URDF files on which urdfpy succeeds 347.5 ms 203 ms
Test on 6 URDF files on which urdf_parser_py succeeds 2.6 ms 3.8 ms
Click to expand code listing that produces the above table entries. ```python robot_assets = ['robot-assets/urdfs/robots/barret_hand/bhand_model.URDF', 'robot-assets/urdfs/robots/robotiq_gripper/robotiq_arg85_description.URDF', 'robot-assets/urdfs/robots/anymal/anymal.urdf', 'robot-assets/urdfs/robots/franka_panda/panda.urdf', 'robot-assets/urdfs/robots/ginger_robot/gingerurdf.urdf', 'robot-assets/urdfs/robots/halodi/eve_r3.urdf', 'robot-assets/urdfs/robots/kinova/kinova.urdf', 'robot-assets/urdfs/robots/kuka_iiwa/model.urdf', 'robot-assets/urdfs/robots/pr2/pr2.urdf', 'robot-assets/urdfs/robots/ur10/ur10_robot.urdf', 'robot-assets/urdfs/robots/ur5/ur5_gripper.urdf', 'robot-assets/urdfs/robots/yumi/yumi.urdf'] import urdfpy import urdf_parser_py import yourdfpy from functools import partial def load_urdfs(fnames, load_fn): results = {fname: None for fname in fnames} for fname in fnames: try: x = load_fn(fname) results[fname] = x except: print("Problems loading: ", fname) pass print(sum([1 for x, y in results.items() if y is not None]), "/", len(fnames)) return results # parsing success rate load_urdfs(robot_assets, urdfpy.URDF.load) load_urdfs(robot_assets, urdf_parser_py.urdf.URDF.load) load_urdfs(robot_assets, yourdfpy.URDF.load) # parsing times %timeit load_urdfs(robot_assets, urdfpy.URDF.load) %timeit load_urdfs(robot_assets, urdf_parser_py.urdf.URDF.load) %timeit load_urdfs(robot_assets, yourdfpy.URDF.load) %timeit load_urdfs(robot_assets, partial(yourdfpy.URDF.load, load_meshes=False, build_scene_graph=False)) # fairer comparison with yourdfpy urdfpy_fnames = [x for x, y in load_urdfs(robot_assets, urdfpy.URDF.load).items() if y is not None] %timeit load_urdfs(urdfpy_fnames, yourdfpy.URDF.load) # fairer comparison with urdf_parser_py urdfparser_fnames = [x for x, y in load_urdfs(robot_assets, urdf_parser_py.urdf.URDF.from_xml_file).items() if y is not None] %timeit load_urdfs(urdfparser_fnames, functools.partial(yourdfpy.URDF.load, load_meshes=False, build_scene_graph=False)) ```