### Install from Source Source: https://xacrodoc.readthedocs.io/en/latest/index.html Method for installing the library directly from the GitHub repository. ```bash git clone --recurse-submodules https://github.com/adamheins/xacrodoc cd xacrodoc pip install . ``` -------------------------------- ### Install Command Line Tool with uv Source: https://xacrodoc.readthedocs.io/en/latest/index.html Recommended method for installing the CLI tool in an isolated environment. ```bash uv tool install xacrodoc # for conversion to MJCF files, use: uv tool install "xacrodoc[mujoco]" ``` -------------------------------- ### Install Command Line Tool with pipx Source: https://xacrodoc.readthedocs.io/en/latest/index.html Alternative method for installing the CLI tool in an isolated environment. ```bash pipx install xacrodoc # or pipx install "xacrodoc[mujoco]" ``` -------------------------------- ### Install Python Library with pip Source: https://xacrodoc.readthedocs.io/en/latest/index.html Standard method for installing the library for use in Python projects. ```bash pip install xacrodoc # or to also include conversion to MJCF files: pip install "xacrodoc[mujoco]" ``` -------------------------------- ### xacrodoc Command Line Examples Source: https://xacrodoc.readthedocs.io/en/latest/index.html Common command line operations for compiling Xacro files, managing package paths, and converting formats. ```bash # compile and print to stdout xacrodoc input.urdf.xacro # compile and output to provided output file xacrodoc input.urdf.xacro -o output.urdf # provide directories in which to look for packages referenced in # input.urdf.xacro (the -d (--pkg-dir) flag is needed before each one to # disambiguate from substitution arguments); a common use-case would be # providing the path to catkin workspaces xacrodoc input.urdf.xacro -d ~/my_pkg_dir -d ~/my_other_pkg_dir # alternatively, package name/path mappings can be directly supplied using the # -p (--pkg-path) flag; := is used to separate the name and path: xacrodoc input.urdf.xacro -p my_pkg:=~/path/to/my_pkg -p other_pkg:=/home/foo/packages/other_pkg # substitution arguments also use := notation, like xacro xacrodoc input.urdf.xacro mass:=1 # you can also remove file:// protocols prefixed to asset paths with # -s (--strip-protocols) or convert paths from absolute to relative using # -r (--relative-paths) xacrodoc input.urdf.xacro -s -r output.urdf # alternatively, convert to MJCF (requires Mujoco) # the -c (--copy-assets-to) option with a directory name is required when # converting to MJCF; it copies all assets to that directory and updates their # file paths (-c is optional when converting to URDF) # relative file paths are always used when converting to MJCF xacrodoc input.urdf.xacro --mjcf -c assets -o output.xml ``` -------------------------------- ### Compile URDF from Xacro Source: https://xacrodoc.readthedocs.io/en/latest/index.html Basic usage for loading xacro files and converting them to URDF strings or files. Includes examples for managing temporary URDF files. ```python import os from xacrodoc import XacroDoc doc = XacroDoc.from_file("robot.urdf.xacro") # or relative to a ROS package # e.g., for a file located at some_ros_package/urdf/robot.urdf.xacro: doc = XacroDoc.from_package_file("some_ros_package", "urdf/robot.urdf.xacro") # convert to a string of URDF urdf_str = doc.to_urdf_string() # or write to a file doc.to_urdf_file("robot.urdf") # or just work with a temp file # this is useful for working with libraries that expect a URDF *file* (rather # than a string) with doc.temp_urdf_file_path() as path: # do stuff with URDF file located at `path`... # file is cleaned up once context manager is exited # you can also manage the temp file yourself if you don't want to clean it up # right away path = doc.to_temp_urdf_file() # ...do stuff with path... # manually delete the temp file os.remove(path) ``` -------------------------------- ### Convert to MJCF File Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Writes the model to a Mujoco MJCF XML file. Requires the mujoco module to be installed. ```python def to_mjcf_file(self, path, **kwargs): """Convert and write to a Mujoco MJCF XML file. This requires the ``mujoco`` module to be installed and available for import. Parameters ---------- path : str or Path The path to the MJCF XML file to be written. **kwargs : dict All keyword arguments are passed as Mujoco compiler options; see https://mujoco.readthedocs.io/en/stable/modeling.html#curdf """ path = Path(path) spec = self._to_mjcf_spec(path, **kwargs) try: spec.to_file(path.as_posix()) except AttributeError: # Python 3.8 with open(path, "w") as f: f.write(spec.to_xml()) ``` -------------------------------- ### Build Documentation Source: https://xacrodoc.readthedocs.io/en/latest/index.html Generate HTML documentation using Sphinx within the docs directory. ```bash make html ``` -------------------------------- ### Initialize Submodules Source: https://xacrodoc.readthedocs.io/en/latest/index.html Run this command to clone and initialize required submodules before running tests. ```bash git submodule update --init --recursive ``` -------------------------------- ### PackageFinder.walk_up_from Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Configures the finder to search for packages by traversing up the directory tree from a specified path. ```APIDOC ## walk_up_from(path, priority=0) ### Description Look for packages by walking up the directory tree from the provided path, checking for package.xml or manifest.xml files. ### Parameters - **path** (str or pathlib.Path) - Required - The path at which to start looking for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### walk_up_from(path, priority=0) Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Looks for packages by walking up the directory tree from a specified path. ```APIDOC ## walk_up_from(path, priority=0) ### Description Look for packages by walking up the directory tree from `path`. ### Parameters - **path** (str or pathlib.Path) - Required - The path at which to start looking for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### xacrodoc.packages.walk_up_from(path, priority=0) Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Configures the package finder to search for packages by walking up the directory tree from a specified path. ```APIDOC ## xacrodoc.packages.walk_up_from(path, priority=0) ### Description Look for packages by walking up the directory tree from `path`. ### Parameters - **path** (str or pathlib.Path) - Required - The path at which to start looking for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### Run Test Suite Source: https://xacrodoc.readthedocs.io/en/latest/index.html Navigate to the tests directory and execute the pytest suite. ```bash cd tests pytest . ``` -------------------------------- ### Resolve Package Paths Source: https://xacrodoc.readthedocs.io/en/latest/index.html Syntax for package-relative paths and how xacrodoc expands them. ```text package:///relative/path/to/mesh ``` ```text file:///abs/path/to/mesh ``` -------------------------------- ### Run Tests with uv Source: https://xacrodoc.readthedocs.io/en/latest/index.html Execute tests against specific Python versions and include optional dependencies like mujoco. ```bash # for example uv run --locked --isolated --extra mujoco --python=3.9 pytest ``` -------------------------------- ### Convert Xacro to MJCF format Source: https://xacrodoc.readthedocs.io/en/latest/index.html Demonstrates converting a Xacro document to MJCF, including options for asset path handling and localizing assets to a specific directory. ```python from xacrodoc import XacroDoc doc = XacroDoc.from_file("input.urdf.xacro") # keep absolute paths to assets doc.to_mjcf_file("output.xml", strippath="false") # copy all the referenced assets to the directory `assets` before conversion # name collisions between assets with the same filename are automatically # resolved by appending numbers as needed doc.localize_assets("assets") doc.to_mjcf_file("output.xml", strippath="true", meshdir="assets") # if desired, one can also just produce an MJCF string: xml = doc.to_mjcf_string() ``` -------------------------------- ### Initialize xacrodoc instance Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Constructor for the xacrodoc class. ```python def __init__( self, dom, rootdir=None, ): self.dom = dom self.rootdir = rootdir ``` -------------------------------- ### look_in(paths, priority=0) Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Adds additional directories to search for packages. ```APIDOC ## look_in(paths, priority=0) ### Description Add additional directories to search for packages. Internally, these are passed to `rospkg.RosPack`. ### Parameters - **paths** (Iterable) - Required - A list of paths in which to search for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### Add Search Directories Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Registers additional directories for package searching with a specified priority. ```python def look_in(paths, priority=0): """Add additional directories to search for packages. Parameters ---------- paths : Iterable A list of paths in which to search for packages. Internally, these are passed to `rospkg.RosPack`. priority : int Priority for the search: lower means the package is looked for using this method earlier. """ _finder.look_in(paths, priority=priority) ``` -------------------------------- ### Search Packages via Directory Walk Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Initiates a package search by traversing upwards from the provided directory path. ```python def walk_up_from(path, priority=0): """Look for packages by walking up the directory tree from ``path``. Parameters ---------- path : str or pathlib.Path The path at which to start looking for packages. priority : int Priority for the search: lower means the package is looked for using this method earlier. """ _finder.walk_up_from(path, priority=priority) ``` -------------------------------- ### xacrodoc.packages.look_in(paths, priority=0) Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Registers additional directories to be searched when looking for ROS packages. ```APIDOC ## xacrodoc.packages.look_in(paths, priority=0) ### Description Add additional directories to search for packages. ### Parameters - **paths** (Iterable) - Required - A list of paths in which to search for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### Build URDF from includes Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Creates a URDF document by aggregating multiple xacro include directives. ```python @classmethod def from_includes(cls, includes, name="robot", **kwargs): """Build the URDF document from xacro includes. Parameters ---------- includes : list A list of strings representing files to include in a new, otherwise empty, xacro file. Any string that can be used in a xacro include directive is valid, so look-ups like ``$(find package)`` are valid. name : str The name attribute of the top-level robot tag. **kwargs : dict Additional keyword arguments are passed to the ``.from_string`` method. """ s = _xacro_header(name) for incl in includes: s += _xacro_include(incl) s += "" return cls.from_string(s, **kwargs) ``` -------------------------------- ### xacrodoc.from_includes Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Builds a URDF document from a list of xacro includes. ```APIDOC ## xacrodoc.from_includes(includes, name="robot", **kwargs) ### Description Build the URDF document from xacro includes. ### Parameters - **includes** (list) - Required - A list of strings representing files to include. - **name** (str) - Optional - The name attribute of the top-level robot tag. - **kwargs** (dict) - Optional - Additional keyword arguments passed to .from_string. ``` -------------------------------- ### Pass Substitution Arguments Source: https://xacrodoc.readthedocs.io/en/latest/index.html Provide arguments to xacro files programmatically, equivalent to command-line arguments. ```bash xacro robot_base.urdf.xacro -o robot_base.urdf mass:=2 ``` ```python from xacrodoc import XacroDoc doc = XacroDoc.from_file("robot.urdf.xacro", subargs={"mass": "2"}) ``` -------------------------------- ### PackageFinder.look_in Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Adds additional directories to the search path for finding ROS packages. ```APIDOC ## look_in(paths, priority=0) ### Description Adds additional directories to search for packages. These paths are used to initialize a `rospkg.RosPack` instance for searching. ### Parameters - **paths** (Iterable or str or Path) - Required - A path or list of paths in which to search for packages. - **priority** (int) - Optional - Priority for the search: lower means the package is looked for using this method earlier. ``` -------------------------------- ### Load URDF from file Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Loads a URDF document from a specified file path. ```python @classmethod def from_file(cls, path, walk_up=True, **kwargs): """Load the URDF document from a xacro or plain URDF file. Parameters ---------- path : str or Path The path to the xacro file. walk_up : bool If ``True``, look for packages by walking up the directory tree from ``path``. **kwargs : dict Additional keyword arguments are passed to the ``.from_string`` constructor method. """ if walk_up: packages.walk_up_from(path) with open(path) as f: text = f.read() rootdir = Path(path).parent return cls.from_string(text, rootdir=rootdir, **kwargs) ``` -------------------------------- ### Retrieve Package Path Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Resolves the filesystem path for a given package name, raising PackageNotFoundError if not found. ```python def get_path(pkg): """Attempt to get the path of a package. Parameters ---------- pkg : str The name of the package. Returns ------- : str The path to the package. Raises ------ PackageNotFoundError If the package could not be found. """ return _finder.get_path(pkg) ``` -------------------------------- ### Use temp_urdf_file_path as a context manager Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Utilize the context manager to temporarily write a URDF file for external program consumption. ```python with doc.temp_urdf_file_path() as path: load_file_from_path(path) ``` -------------------------------- ### xacrodoc.from_package_file Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Loads a URDF document from a xacro file in a ROS package. ```APIDOC ## xacrodoc.from_package_file(package_name, relative_path, **kwargs) ### Description Load the URDF document from a xacro file in a ROS package. ### Parameters - **package_name** (str) - Required - The name of the ROS package. - **relative_path** (str or Path) - Required - The path of the xacro file relative to the ROS package. - **kwargs** (dict) - Optional - Additional keyword arguments passed to .from_file. ``` -------------------------------- ### Compose Multiple Xacro Files Source: https://xacrodoc.readthedocs.io/en/latest/index.html Programmatically build a URDF by including multiple xacro files. Supports $(find ...) directives within the include list. ```python import xacrodoc as xd # setup where to look for packages, if needed; for example: xd.packages.look_in(["somewhere/I/keep/packages"]) # specify files to compose (using xacro include directives) includes = ["robot_base.urdf.xacro", "robot_arm.urdf.xacro", "tool.urdf.xacro"] doc = xd.XacroDoc.from_includes(includes) # includes can also use $(find ...) directives: includes = [ "$(find my_ros_package)/urdf/robot_base.urdf.xacro", "$(find another_ros_package)/urdf/robot_arm.urdf.xacro", "tool.urdf.xacro" ] doc = xd.XacroDoc.from_includes(includes) ``` -------------------------------- ### XacroDoc.from_file Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Loads a URDF document from a xacro or plain URDF file. ```APIDOC ## XacroDoc.from_file(path, walk_up=True, **kwargs) ### Description Load the URDF document from a xacro or plain URDF file. ### Parameters - **path** (str or Path) - Required - The path to the xacro file. - **walk_up** (bool) - Optional - If True, look for packages by walking up the directory tree from path. - **kwargs** (dict) - Optional - Additional keyword arguments passed to the .from_string constructor method. ``` -------------------------------- ### xacrodoc.packages.get_path(pkg) Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Attempts to resolve the filesystem path of a given ROS package. ```APIDOC ## xacrodoc.packages.get_path(pkg) ### Description Attempt to get the path of a package. ### Parameters - **pkg** (str) - Required - The name of the package. ### Returns - **str** - The path to the package. ### Raises - **PackageNotFoundError** - If the package could not be found. ``` -------------------------------- ### get_path(pkg) Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Attempts to retrieve the filesystem path of a given package. ```APIDOC ## get_path(pkg) ### Description Attempt to get the path of a package. ### Parameters - **pkg** (str) - Required - The name of the package. ### Returns - **str** - The path to the package. ### Raises - **PackageNotFoundError** - If the package could not be found. ``` -------------------------------- ### PackageFinder.get_path Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Attempts to resolve the filesystem path of a given ROS package name. ```APIDOC ## get_path(pkg) ### Description Attempts to get the path of a package using the configured search methods. Results are cached for subsequent calls. ### Parameters - **pkg** (str) - Required - The name of the package. ### Returns - **str** - The path to the package. ### Raises - **PackageNotFoundError** - If the package could not be found. ``` -------------------------------- ### XACRO Compilation and Path Resolution Utilities Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Core functions for compiling XACRO strings to URDF and resolving package-based file paths. ```python from contextlib import contextmanager import os from pathlib import Path import re import shutil import tempfile from xml.dom.minidom import parseString import warnings import xacro from xacro import substitution_args from xacro.color import warning from . import packages FILE_PROTOCOL_PREFIX = "file://" PACKAGE_PROTOCOL_PREFIX = "package://" # monkey patch to replace xacro's package finding infrastructure # NOTE: we include a vendored version of xacro here to ensure we always have # compatibility even if the user has a different version of xacro installed substitution_args._eval_find = lambda pkg: packages.get_path(pkg) def _xacro_include(path): return f'' def _xacro_header(name): return f""" """.strip() def _compile_xacro_file(text, subargs=None, max_runs=10): """Compile xacro string until a fixed point is reached. Parameters ---------- text : str The xacro string. subargs : dict or None Optional dictionary of substitution arguments to pass into xacro. max_runs : int Maximum number of compilation runs. Raises ------ ValueError If ``max_runs`` compilations are exceeded. Returns ------- : xml.dom.minidom.Document The URDF XML document. """ if subargs is None: subargs = {} dom = xacro.parse(text) s1 = dom.toxml() # keep compiling until a fixed point is reached (i.e., the document doesn't # change anymore) run = 1 while run < max_runs: xacro.process_doc(dom, mappings=subargs) # modifies dom in place s2 = dom.toxml() if s1 == s2: break s1 = s2 run += 1 if run >= max_runs: raise ValueError("URDF file did not converge.") return dom def _urdf_elements_with_filenames(dom): """Get all elements in the URDF document with a filename attribute. Parameters ---------- dom : xml.dom.minidom.Document The XML document. Returns ------- : list List of elements which have a filename attribute. """ elements = dom.getElementsByTagName("mesh") + dom.getElementsByTagName( "material" ) return [e for e in elements if e.hasAttribute("filename")] def _resolve_packages(dom): """Convert all filenames specified with package:// to a full absolute path with a file:// prefix. Parameters ---------- dom : xml.dom.minidom.Document The XML document, which is modified in place. """ pkg_regex = re.compile(PACKAGE_PROTOCOL_PREFIX + r"([ \w-]+)/") for e in _urdf_elements_with_filenames(dom): filename = e.getAttribute("filename") if filename.startswith(PACKAGE_PROTOCOL_PREFIX): pkg = pkg_regex.search(filename).group(1) # ROS doesn't support spaces in package names, so neither do we; # explicitly check and tell the user about this if " " in pkg: raise ValueError( f"Package name '{pkg}' contains spaces, which is not allowed." ) abspath = Path(packages.get_path(pkg)).absolute().as_posix() filename = re.sub( f"{PACKAGE_PROTOCOL_PREFIX}{pkg}", f"{FILE_PROTOCOL_PREFIX}{abspath}", filename, ) e.setAttribute("filename", filename) def _set_mjcf_compile_options(dom, **kwargs): """Add or set compiler options in the Mujoco extension. Parameters ---------- dom : xml.dom.minidom.Document The XML document, which is modified in place. **kwargs : dict All keyword arguments are interpreted as Mujoco compiler options. """ mujoco_nodes = dom.getElementsByTagName("mujoco") if len(mujoco_nodes) > 1: raise ValueError("Multiple elements found.") if len(mujoco_nodes) == 0: mujoco_node = dom.createElement("mujoco") dom.documentElement.appendChild(mujoco_node) else: mujoco_node = mujoco_nodes[0] compiler_nodes = mujoco_node.getElementsByTagName("compiler") if len(compiler_nodes) > 1: raise ValueError("Multiple elements found.") if len(compiler_nodes) == 0: compiler_node = dom.createElement("compiler") mujoco_node.appendChild(compiler_node) else: compiler_node = compiler_nodes[0] for k, v in kwargs.items(): if type(v) != str: raise TypeError( f"MJCF compile option values must be strings, but got {v} which is of type {type(v)}." ) compiler_node.setAttribute(str(k), v) def _make_name_unique(name, existing_names): """Make a unique name for a file name. ``` -------------------------------- ### Reset Package Finder Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Resets the global package finder to its default lookup configuration. ```python def reset(): """Reset the package finder to the default look-up methods.""" global _finder _finder = PackageFinder() ``` -------------------------------- ### Load URDF from string Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Compiles a xacro string into a URDF document. ```python @classmethod def from_string( cls, text, subargs=None, max_runs=10, resolve_packages=True, rootdir=None, ): """Load the URDF document from a xacro or plain URDF string. Parameters ---------- text : str The xacro text to compile into a URDF document. subargs : dict or None Optional dict of substitution arguments; i.e., the values of ```` directives. Equivalent to writing ``value:=foo`` on the command line. max_runs : int The text is repeated compiled until a fixed point is reached; i.e., compilation of the text just returns the same text. ``max_runs`` is the maximum number of times compilation will be performed. resolve_packages : bool If ``True``, resolve package protocol URIs in the compiled URDF. Otherwise, they are left unchanged. rootdir : str or Path or None Root directory to resolve relative asset filenames with respect to. """ dom = _compile_xacro_file( text=text, subargs=subargs, max_runs=max_runs, ) if resolve_packages: _resolve_packages(dom) return cls(dom=dom, rootdir=rootdir) ``` -------------------------------- ### reset() Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Resets the global package finder to the default look-up methods. ```APIDOC ## reset() ### Description Resets the package finder to the default look-up methods. ``` -------------------------------- ### Configure ROS Package Resolution Source: https://xacrodoc.readthedocs.io/en/latest/index.html Methods for locating ROS packages when resolving $(find ) directives. Useful when working outside standard ROS workspaces. ```python import xacrodoc as xd # `from_file` automatically resolves packages by looking in each parent # directory of the given path to check for required ROS packages (as marked by # a package.xml file) doc = xd.XacroDoc.from_file("robot.urdf.xacro") # if you want to disable this, pass `walk_up=False`: doc = xd.XacroDoc.from_file("robot.urdf.xacro", walk_up=False) # we can also tell xacrodoc to walk up a directory tree manually xd.packages.walk_up_from("some/other/path") # or we can give paths to directories to search for packages # packages can be located multiple levels deep from the specified directories, # just like in a ROS workspace - the same package search logic is used (since # we actually use rospkg under the hood) xd.packages.look_in(["somewhere/I/keep/packages", "another/directory/with/packages"]) # you can bypass ROS conventions entirely by directly providing a dict of # package names and paths; no package.xml is required to identify packages in # this case xd.packages.update_package_cache({"my_package": "path/to/my_package"}) ``` -------------------------------- ### XacroDoc.localize_assets Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Copies all assets to a local directory and updates filenames in the document. ```APIDOC ## XacroDoc.localize_assets(asset_dir, exist_ok=True) ### Description Copy all assets to a local directory and update all filenames in the document accordingly. This is useful when converting to MJCF format. ### Parameters - **asset_dir** (str or Path) - Required - The path to the local directory where assets should be copied. - **exist_ok** (bool) - Optional - If False, an error is raised if asset_dir exists. ``` -------------------------------- ### Package Resolution Utilities Source: https://xacrodoc.readthedocs.io/en/latest/index.html Utility functions for managing how xacrodoc finds ROS packages. ```APIDOC ## xacrodoc.packages.walk_up_from(path) ### Description Manually triggers a search for ROS packages by walking up the directory tree from the given path. ## xacrodoc.packages.look_in(paths) ### Description Adds directories to the search path for ROS packages. ### Parameters - **paths** (list) - Required - List of directory paths to search. ## xacrodoc.packages.update_package_cache(mapping) ### Description Manually defines a mapping of package names to their absolute paths, bypassing standard ROS discovery. ### Parameters - **mapping** (dict) - Required - Dictionary where keys are package names and values are paths. ``` -------------------------------- ### Resolve File Path in Package Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Constructs an absolute path to a file located within a specific ROS package. ```python def get_file_path(pkg, relative_path): """Get the path to a file within a ROS package. Parameters ---------- pkg : str The name of the ROS package. relative_path : str or Path The path of the file relative to the package root. Returns ------- : str The file path. """ pkgpath = Path(get_path(pkg)) filepath = pkgpath / relative_path return filepath.as_posix() ``` -------------------------------- ### xacrodoc.packages.reset() Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Resets the package finder configuration to the default lookup methods. ```APIDOC ## xacrodoc.packages.reset() ### Description Reset the package finder to the default look-up methods. ``` -------------------------------- ### XacroDoc.from_string Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Loads a URDF document from a xacro or plain URDF string. ```APIDOC ## XacroDoc.from_string(text, subargs=None, max_runs=10, resolve_packages=True, rootdir=None) ### Description Load the URDF document from a xacro or plain URDF string. ### Parameters - **text** (str) - Required - The xacro text to compile into a URDF document. - **subargs** (dict or None) - Optional - Optional dict of substitution arguments. - **max_runs** (int) - Optional - Maximum number of times compilation will be performed to reach a fixed point. - **resolve_packages** (bool) - Optional - If True, resolve package protocol URIs. - **rootdir** (str or Path or None) - Optional - Root directory to resolve relative asset filenames. ``` -------------------------------- ### Define PackageFinder and locate ROS packages Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html The PackageFinder class initializes with support for ament (ROS 2) or rospkg (ROS 1) and provides methods to resolve package paths. ```python from pathlib import Path from xml.dom import minidom import rospkg [docs] class PackageNotFoundError(Exception): """Raised when a package could not be found.""" def __str__(self): return f"Package not found: {' '.join(self.args)}" class PackageFinder: """Utility to find ROS packages. But without relying too much on ROS infrastructure. """ def __init__(self): # list of tuples (func, err): func(pkg) looks for the path to package # `pkg`, raises error of type `err` if it is not found self.finder_funcs = [] self.package_cache = {} try: # ament (ROS 2) from ament_index_python.packages import ( get_package_share_directory, PackageNotFoundError, ) self.finder_funcs.append( (get_package_share_directory, PackageNotFoundError) ) except ImportError: # if no ament, fall back to rospkg (ROS 1) rospack = rospkg.RosPack() self.finder_funcs.append( (rospack.get_path, rospkg.common.ResourceNotFound) ) def look_in(self, paths, priority=0): """Add additional directories to search for packages. Parameters ---------- paths : Iterable or str or Path A path or list of paths (represented as `str` or `Path`) in which to search for packages. Internally, these are passed to `rospkg.RosPack`. priority : int Priority for the search: lower means the package is looked for using this method earlier. """ if not isinstance(paths, (list, tuple)): paths = [paths] rospack = rospkg.RosPack(ros_paths=paths) self.finder_funcs.insert( priority, (rospack.get_path, rospkg.common.ResourceNotFound) ) def walk_up_from(self, path, priority=0): """Look for packages by walking up the directory tree from ``path``. Parameters ---------- path : str or pathlib.Path The path at which to start looking for packages. priority : int Priority for the search: lower means the package is looked for using this method earlier. """ resolved = Path(path).resolve() def finder_func(pkg): path = resolved while path != path.parent: # check for package.xml, get the package name from it package_xml_path = path / rospkg.common.PACKAGE_FILE if package_xml_path.exists(): with open(package_xml_path) as f: doc = minidom.parse(f) names = doc.getElementsByTagName("name") if len(names) != 1: raise ValueError( f"Expected one name in {package_xml_path}, but found {len(names)}" ) if names[0].firstChild.data == pkg: return path.as_posix() # check for manifest.xml (used in the old, deprecated rosbuild # system); if it exists, the package name is the directory name manifest_xml_path = path / rospkg.common.MANIFEST_FILE if manifest_xml_path.exists(): return path.as_posix() # go up to the next directory path = path.parent raise PackageNotFoundError(pkg) self.finder_funcs.insert(priority, (finder_func, PackageNotFoundError)) def get_path(self, pkg): """Attempt to get the path of a package. Parameters ---------- pkg : str The name of the package. Returns ------- : str The path to the package. Raises ------ PackageNotFoundError If the package could not be found. """ # try the cache first if pkg in self.package_cache: return self.package_cache[pkg] # otherwise try a lookup for func, err in self.finder_funcs: try: path = func(pkg) self.package_cache[pkg] = path # add to cache return path except err: continue raise PackageNotFoundError(pkg) def update_package_cache(self, pkgpaths): """Update the package cache. ``` -------------------------------- ### to_mjcf_file Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Converts and writes the document to a Mujoco MJCF XML file. ```APIDOC ## to_mjcf_file(path, **kwargs) ### Description Convert and write to a Mujoco MJCF XML file. This requires the `mujoco` module to be installed. ### Parameters - **path** (str or Path) - Required - The path to the MJCF XML file to be written. - **kwargs** (dict) - Optional - All keyword arguments are passed as Mujoco compiler options. ``` -------------------------------- ### Load URDF from ROS package Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Loads a URDF document from a file located within a ROS package. ```python @classmethod def from_package_file(cls, package_name, relative_path, **kwargs): """Load the URDF document from a xacro file in a ROS package. Parameters ---------- package_name : str The name of the ROS package. relative_path : str or Path The path of the xacro file relative to the ROS package. **kwargs : dict Additional keyword arguments are passed to the ``.from_file`` constructor method. """ path = packages.get_file_path(package_name, relative_path) return cls.from_file(path, **kwargs) ``` -------------------------------- ### Copy DOM Tree Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Creates a deep copy of an XML DOM tree using serialization. ```python def _copy_dom(dom): """Make a deep copy of a DOM tree. Parameters ---------- dom : xml.dom.minidom.Document The XML document to copy. Returns ------- : xml.dom.minidom.Document The copied XML document. """ # NOTE: this is not quite as efficient as doing a deep clone of the node # and copying it into a new document, but it is very simple return parseString(dom.toxml()) ``` -------------------------------- ### Update Package Cache Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Manually updates the package cache with a mapping of package names to filesystem paths. ```python def update_package_cache(pkgpaths): """Update the package cache. This allows the user to manually specify the location of packages on the filesystem. The path does not actually need to be a proper ROS package, meaning that there is no need to have a package.xml file there. Parameters ---------- pkgpaths : dict Map from package names to package paths. The paths are resolved, made absolute, and converted to strings. """ _finder.update_package_cache(pkgpaths) ``` -------------------------------- ### Convert to MJCF String Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Converts the model to a string in Mujoco MJCF XML format. ```python def to_mjcf_string(self, **kwargs): """Convert to a string in Mujoco MJCF XML format. This requires the ``mujoco`` module to be installed and available for import. All ``kwargs`` are used as Mujoco compiler options; see https://mujoco.readthedocs.io/en/stable/modeling.html#curdf Parameters ---------- **kwargs : dict All keyword arguments are passed as Mujoco compiler options; see https://mujoco.readthedocs.io/en/stable/modeling.html#curdf Returns ------- : str The string of XML. """ return self._to_mjcf_spec(".", **kwargs).to_xml() ``` -------------------------------- ### temp_urdf_file_path Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Context manager for temporary URDF file usage. ```APIDOC ## temp_urdf_file_path(verbose=False) ### Description Provides a context manager that creates a temporary URDF file and automatically deletes it after the block execution. ### Parameters - **verbose** (bool) - Optional - If True, prints compilation information. ``` -------------------------------- ### xacrodoc.packages.update_package_cache(pkgpaths) Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Manually updates the package cache with a mapping of package names to their filesystem paths. ```APIDOC ## xacrodoc.packages.update_package_cache(pkgpaths) ### Description Update the package cache. This allows the user to manually specify the location of packages on the filesystem. ### Parameters - **pkgpaths** (dict) - Required - Map from package names to package paths. ``` -------------------------------- ### Copy DOM with Modified Asset Paths Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Deep copies a DOM tree while updating asset filenames to be relative or absolute based on provided directory paths. ```python def _copy_dom_change_paths( dom, use_protocols=True, paths_relative_to=None, rootdir=None ): """Make a deep copy of the DOM tree with modified asset paths. Parameters ---------- dom : xml.dom.minidom.Document The XML document to copy. use_protocols : bool If ``True``, all asset filenames that do not currently have a protocol (``file://`` or ``package://``) will have the ``file://`` protocol prepended. If ``False``, asset filenames will have any protocol removed. paths_relative_to : str or Path or None If not ``None``, all asset filenames will be made relative to this path. Otherwise, paths are changed according to the value of ``rootdir`` (see below). Note that some applications may not support relative paths. This parameter has no effect on filenames with the ``package://`` protocol prefix. rootdir : str or Path or None If ``paths_relative_to`` is ``None`` but ``rootdir`` is not, make all asset filenames absolute by resolving relative paths relative to ``rootdir``. If the filename is already absolute or if ``paths_relative_to`` and ``rootdir`` are both ``None``, the filename is left unchanged. This argument has no effect if ``paths_relative_to`` is not ``None``. Returns ------- : xml.dom.minidom.Document The copied XML document with modified asset paths. """ dom = _copy_dom(dom) for e in _urdf_elements_with_filenames(dom): path = e.getAttribute("filename") protocol, path = _split_path_protocol(path) path = Path(path) # we don't modify any paths with a package:// protocol if protocol != PACKAGE_PROTOCOL_PREFIX: if paths_relative_to is not None: start = Path(paths_relative_to) # we always want to be relative to a directory if start.is_file(): start = start.parent path = os.path.relpath(path, start=start) elif rootdir is not None: # if the path is not absolute, resolve it relative to rootdir if not path.is_absolute(): path = (Path(rootdir) / path).resolve() if use_protocols and protocol == "": protocol = FILE_PROTOCOL_PREFIX elif not use_protocols: if protocol == PACKAGE_PROTOCOL_PREFIX: raise ValueError( "Protocol prefixes are being stripped because " f"use_protocols=True, but a {PACKAGE_PROTOCOL_PREFIX} " "prefix was found. You should resolve all package " "references to file paths (path resolve_packages=True to " "the XacroDoc constructors) before stripping protocols." ) protocol = "" e.setAttribute("filename", f"{protocol}{path}") return dom ``` -------------------------------- ### get_file_path(pkg, relative_path) Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Retrieves the absolute path to a file within a ROS package. ```APIDOC ## get_file_path(pkg, relative_path) ### Description Get the path to a file within a ROS package. ### Parameters - **pkg** (str) - Required - The name of the ROS package. - **relative_path** (str or Path) - Required - The path of the file relative to the package root. ### Returns - **str** - The file path. ``` -------------------------------- ### Split Path Protocol Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Separates a path string into its protocol prefix and the remaining path component. ```python def _split_path_protocol(path, delim="://"): """Split a path into its protocol prefix and the rest of the path. Parameters ---------- path : str The path to split. delim : str The delimiter to split on. Returns ------- : tuple A tuple of (protocol, path) where protocol is the protocol prefix including the delimiter, or an empty string if there is no protocol. """ parts = path.split(delim, maxsplit=1) path = parts[-1] protocol = parts[0] + delim if len(parts) == 2 else "" return protocol, path ``` -------------------------------- ### update_package_cache(pkgpaths) Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/packages.html Manually updates the package cache with specific package paths. ```APIDOC ## update_package_cache(pkgpaths) ### Description Update the package cache. This allows the user to manually specify the location of packages on the filesystem. ### Parameters - **pkgpaths** (dict) - Required - Map from package names to package paths. The paths are resolved, made absolute, and converted to strings. ``` -------------------------------- ### Count unique assets Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Calculates the number of unique asset files referenced in the document. ```python @classmethod def count_assets(self): """Count the number of unique asset files referenced in the document. Returns ------- : int The number of unique asset files. """ paths = set() for e in self._elements_with_filenames(): path = e.getAttribute("filename") _, path = _split_path_protocol(path) paths.add(path) return len(paths) ``` -------------------------------- ### xacrodoc.count_assets Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Counts unique asset files referenced in the document. ```APIDOC ## xacrodoc.count_assets() ### Description Count the number of unique asset files referenced in the document. ### Returns - (int) - The number of unique asset files. ``` -------------------------------- ### to_mjcf_string Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Converts the document to a string in Mujoco MJCF XML format. ```APIDOC ## to_mjcf_string(**kwargs) ### Description Convert to a string in Mujoco MJCF XML format. This requires the `mujoco` module to be installed. ### Parameters - **kwargs** (dict) - Optional - All keyword arguments are passed as Mujoco compiler options. ### Returns - **str** - The string of XML. ``` -------------------------------- ### Write URDF to File Source: https://xacrodoc.readthedocs.io/en/latest/_modules/xacrodoc/xacrodoc.html Writes the current URDF representation to a specified file path. ```python def to_urdf_file( self, path, compare_existing=True, use_protocols=True, relative_paths=False, verbose=False, ): """Write the URDF to a file. ``` -------------------------------- ### to_urdf_string Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Retrieves the URDF as a formatted string. ```APIDOC ## to_urdf_string(use_protocols=True, paths_relative_to=None, pretty=True) ### Description Get the URDF as a string. ### Parameters - **use_protocols** (bool) - Optional - If `True`, prepends `file://` to asset filenames without protocols. - **paths_relative_to** (str or Path or None) - Optional - If provided, makes asset filenames relative to this path. - **pretty** (bool) - Optional - Set `True` to format the string for human readability. ### Returns - **str** - The URDF represented as a string. ``` -------------------------------- ### to_urdf_file Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Writes the URDF to a specified file path. ```APIDOC ## to_urdf_file(path, compare_existing=True, use_protocols=True, relative_paths=False, verbose=False) ### Description Write the URDF to a file. ### Parameters - **path** (str or Path) - Required - The path to the URDF file to be written. - **compare_existing** (bool) - Optional - If `True`, only write if the parsed URDF is different than the existing file content. - **use_protocols** (bool) - Optional - If `True`, prepends `file://` to asset filenames without protocols. - **relative_paths** (bool) - Optional - If `True`, convert asset filenames to be relative to the output path. - **verbose** (bool) - Optional - Set to `True` to print information about xacro compilation. ``` -------------------------------- ### to_temp_urdf_file Source: https://xacrodoc.readthedocs.io/en/latest/reference.html Writes the URDF to a temporary file. ```APIDOC ## to_temp_urdf_file(verbose=False) ### Description Write the URDF to a temporary file. Clean-up is left to the user. ### Parameters - **verbose** (bool) - Optional - Set to `True` to print information about xacro compilation. ### Returns - **str** - The path to the temporary URDF file. ```