From 32d995910483ca14e5404f60cd38b3b1ac25a332 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sun, 5 Jul 2026 17:27:29 +1000 Subject: [PATCH] fix(models): make KinovaGen3 load via robot_descriptions, add XACRO_ARGS support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KinovaGen3.py pointed at "kortex_description/robots/gen3.xacro" in the bundled rtb-data tree, which doesn't exist there (no kortex_description folder at all) — always raised FileNotFoundError. robot_descriptions does have a working gen3_description entry, but its xacro file requires a substitution argument (XACRO_ARGS = {"dof": "7"}) to select the 6dof/7dof arm variant; without it xacro fails on an unresolved property reference. _load_urdf_from_RD only ever read URDF_PATH/XACRO_PATH and silently ignored XACRO_ARGS — a gap that likely affects other RD models exposing the same attribute (several UR/xArm/FR3 variants), even though this is the first one this toolbox actually hits. Thread XACRO_ARGS through as XacroDoc's subargs, and switch KinovaGen3 to the bare "gen3" RD lookup instead of the missing bundled path. Co-Authored-By: Claude Sonnet 5 --- src/roboticstoolbox/models/URDF/KinovaGen3.py | 2 +- src/roboticstoolbox/models/URDF/URDFRobot.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/roboticstoolbox/models/URDF/KinovaGen3.py b/src/roboticstoolbox/models/URDF/KinovaGen3.py index 638c57c2f..454ed5a6f 100644 --- a/src/roboticstoolbox/models/URDF/KinovaGen3.py +++ b/src/roboticstoolbox/models/URDF/KinovaGen3.py @@ -31,7 +31,7 @@ class KinovaGen3(URDFRobot): def __init__(self): super().__init__( - "kortex_description/robots/gen3.xacro", + "gen3", manufacturer="Kinova", gripper_link_index=10, ) diff --git a/src/roboticstoolbox/models/URDF/URDFRobot.py b/src/roboticstoolbox/models/URDF/URDFRobot.py index 565c05500..5f7977db8 100644 --- a/src/roboticstoolbox/models/URDF/URDFRobot.py +++ b/src/roboticstoolbox/models/URDF/URDFRobot.py @@ -116,7 +116,7 @@ def _load_rd_module(robot_name: str): ) from last_error -def _load_urdf_from_RD(robot_name: str) -> Path: +def _load_urdf_from_RD(robot_name: str) -> "tuple[Path, dict | None]": """Fetch the URDF/xacro path from robot_descriptions and register its packages. robot_descriptions models expose different path attributes depending on their @@ -125,6 +125,13 @@ def _load_urdf_from_RD(robot_name: str) -> Path: - XACRO_PATH — a xacro source that must be processed by xacrodoc (e.g. j2n6s200, ur5, kinova family) Both are passed to XacroDoc.from_file(), which handles either format. + + Some xacro-based models also expose ``XACRO_ARGS`` — substitution + values required to compile the file at all (e.g. Kinova Gen3's + ``{"dof": "7"}``, which selects the 6dof/7dof arm variant; several UR + and xArm variants have the same pattern). Returned alongside the path + so the caller can forward them to xacrodoc as ``subargs`` — without + them, xacro fails on an unresolved ``$(arg ...)``/property reference. """ module = _load_rd_module(robot_name) @@ -138,7 +145,7 @@ def _load_urdf_from_RD(robot_name: str) -> Path: "URDF_PATH nor XACRO_PATH." ) _register_rd_packages(urdf_path) - return urdf_path + return urdf_path, getattr(module, "XACRO_ARGS", None) def _parse_urdf(urdf_str: str): @@ -272,17 +279,18 @@ def URDF_file(file: "str | Path | TextIO", model: "str | None" = None) -> tuple: pkg_map = {d.name: str(d) for d in xacro_root.iterdir() if d.is_dir()} packages.update_package_cache(pkg_map) + xacro_args = None if isinstance(file, str): file = Path(file) if file.suffix not in (".urdf", ".xacro"): - file = Path(_load_urdf_from_RD(str(file))) + file, xacro_args = _load_urdf_from_RD(str(file)) resolved_path = None if isinstance(file, Path): if not file.is_absolute(): file = xacro_root / file resolved_path = file - doc = XacroDoc.from_file(file) + doc = XacroDoc.from_file(file, subargs=xacro_args) else: doc = XacroDoc.from_string(file.read())