How can I parse "package://" in a URDF file path?

Question:

I have a robot URDF that points to mesh files using "package://".

  <geometry>
    <mesh filename="package://a1_rw/meshes/hip.dae" scale="1 1 1"/>
  </geometry>

I would like to use urdfpy to parse this URDF. However, it is unable to interpret the meaning of "package://".

import os
from urdfpy import URDF

a1_rw = {
    "model": "a1",
    "csvpath": "a1_rw/urdf/a1_rw.csv",
    "urdfpath": "a1_rw/urdf/a1_rw.urdf"
}

model = a1_rw
curdir = os.getcwd()
path_parent = os.path.dirname(curdir)
print("path parent = ", path_parent)
model_path = model["urdfpath"]
robot = URDF.load(os.path.join(path_parent, model_path))

Here is the error message:

$ python3.8 calc_parallax.py
path parent =  /home/ben/Documents/git_workspace/a1_test
Traceback (most recent call last):
  File "calc_parallax.py", line 18, in <module>
    robot = URDF.load(os.path.join(path_parent, model_path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 3729, in load
    return URDF._from_xml(node, path)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 3926, in _from_xml
    kwargs = cls._parse(node, path)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in _parse_simple_elements
    v = [t._from_xml(n, path) for n in vs]
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in <listcomp>
    v = [t._from_xml(n, path) for n in vs]
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 181, in _from_xml
    return cls(**cls._parse(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in _parse_simple_elements
    v = [t._from_xml(n, path) for n in vs]
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in <listcomp>
    v = [t._from_xml(n, path) for n in vs]
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 1146, in _from_xml
    kwargs = cls._parse(node, path)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 127, in _parse_simple_elements
    v = t._from_xml(v, path)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 181, in _from_xml
    return cls(**cls._parse(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 127, in _parse_simple_elements
    v = t._from_xml(v, path)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/urdf.py", line 581, in _from_xml
    meshes = load_meshes(fn)
  File "/home/ben/.local/lib/python3.8/site-packages/urdfpy/utils.py", line 225, in load_meshes
    meshes = trimesh.load(filename)
  File "/home/ben/.local/lib/python3.8/site-packages/trimesh/exchange/load.py", line 111, in load
    ) = parse_file_args(file_obj=file_obj,
  File "/home/ben/.local/lib/python3.8/site-packages/trimesh/exchange/load.py", line 623, in parse_file_args
    raise ValueError('string is not a file: {}'.format(file_obj))
ValueError: string is not a file: /home/ben/Documents/git_workspace/a1_test/a1_rw/urdf/package://a1_rw/meshes/trunk.dae

Is there any way to get urdfpy (or another urdf parser) to parse this correctly?

Asked By: Locknutbushing

||

Answers:

The behavior you’re observing is expected. The documentation for urdfpy.URDF.load() specifically states:

Any paths in the URDF should be specified as relative paths to the .urdf file instead of as ROS resources.

If you want to keep using the same library, the only way to sort this out is to replace the strings in the .urdf file. To do so I suggest using a temporary file so that it is automatically discarded once you’ve loaded your URDF and doesn’t actually impact your original urdf.

from pathlib import Path
from urdfpy import URDF
import tempfile

# URDF file (pathlib is a little nicer but not mandatory)
urdf_file_path = Path("path/to/my/file.urdf")

# Define how you replace your string. Adjust it so it fits your file organization
ros_url_prefix = "package://"
abs_path_prefix = "/path/to/my/meshes/folder"

# Start with openning a temp dir (context manger makes it easy to handle)
with tempfile.TemporaryDirectory() as tmpdirname:
  
    # Where your tmpfile will be
    tmp_file_path = Path(tmpdirname)/urdf_file_path.name

    # Write each line in fout replacing the ros url prefix with abs path
    with open(urdf_file_path, 'r') as fin:
        with open(tmp_file_path, 'w') as fout:
            for line in fin:
                fout.write(line.replace(ros_url_prefix, abs_path_prefix))

    # Load the urdf from the corrected tmp file
    robot_urdf = load(tmp_file_path)

# Here we get out of tmpfile context manager, so the tmpdir and all its content is erased
robot_urdf.show() # The robot urdf is still accessible
Answered By: LNiederha
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.