From b00b46b33623f3ea1cccef9b2d8503a9d7d07af7 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Fri, 17 Jul 2026 16:00:05 +1000 Subject: [PATCH 1/2] fix(deps): pin robot_descriptions<3.0, UR5 broke on the unpinned major bump robot_descriptions 3.0.0 (uploaded 2026-07-11) resolves "ur5" to a different upstream repo (Universal_Robots_ROS2_Description) with a different link count/ordering than 2.0.0. UR5.__init__'s gripper_link_index=7 is a raw positional index into the parsed link list - calibrated against the old vendored/local URDF from before this model switched to live robot_descriptions resolution - so it now lands on wrist_2_link (a real arm joint) instead of the intended tool attachment link, silently dropping 2 of 6 joints from self.n and breaking addconfiguration_attr's vector-length check. pyproject.toml never had an upper bound on robot_descriptions, so every fresh install picks up whatever's latest on PyPI at install time - this was going to break CI (and any user's fresh install) regardless of what branch or commit was checked out. Pinned >=2.0,<3.0, same floor+ceiling pattern already used for rtb-data. Not a full fix: compared the raw link lists between both robot_descriptions versions and confirmed all 6 real arm joint names/ isjoint status match exactly across both, and a shared "tool0" link exists in both too (just at a different index) - so a name/structure -based gripper-link lookup would survive this exact upstream change, unlike the hardcoded index. Recorded as its own tech-debt entry rather than bundling a rushed redesign into this pin. Verified: full suite green (620 passed, 72 skipped) with robot_descriptions pinned to 2.0.0. Co-Authored-By: Claude Sonnet 5 --- pyproject.toml | 2 +- tech-debt.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c831d436..46eac8a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "typing_extensions", "colored>2.0.0", "xacrodoc", - "robot_descriptions", + "robot_descriptions>=2.0,<3.0", ] license = { file = "LICENSE" } diff --git a/tech-debt.md b/tech-debt.md index 612bdccb..33f39ca3 100644 --- a/tech-debt.md +++ b/tech-debt.md @@ -1096,3 +1096,64 @@ the real dependency becomes explicit rather than implicit. `/robot`, see the ETS/fknm refactor entry above) is underway — worth revisiting together since the import surface `IK.py` needs from the new package location is the same shape this protocol would formalize. + +## `URDF.UR5`'s `gripper_link_index=7` is a hardcoded position, not a stable identifier + +### Background + +Found 2026-07-17: CI failed on `URDF.UR5` construction (`ValueError: +incorrect vector length: expected 4, got (6,)` from `addconfiguration_attr`) +across every OS/Python combination, while local runs passed. Root cause: +`pyproject.toml`'s `robot_descriptions` dependency had no upper bound, and +`robot_descriptions` 3.0.0 (uploaded 2026-07-11) changed which upstream repo +"ur5" resolves to — `Universal_Robots_ROS2_Description` instead of whatever +2.0.0 pointed at. `UR5.__init__` calls `super().__init__("ur5", ..., +gripper_link_index=7)`, a raw positional index into the parsed link list +meant to mark the gripper/tool attachment link. The two versions parse to +different link counts (11 vs. 13) and orderings, so index `7` now lands on +`wrist_2_link` — a real arm joint — instead of the intended attachment +point, silently dropping 2 of 6 joints from `self.n`. + +`gripper_link_index` was calibrated against `UR5.py`'s older local, vendored +xacro file (before it was switched to the bare name `"ur5"`, which triggers +live `robot_descriptions` resolution) — it was never designed to survive the +upstream source changing under it, which is now possible on every fresh +install. + +**Immediate fix**: pinned `robot_descriptions>=2.0,<3.0` in `pyproject.toml` +(same floor+ceiling pattern already used for `rtb-data`), restoring the +known-working data. This does not fix the underlying fragility, just stops +it from firing today. + +### Confirmed: a name/structure-based fix is viable, not a dead end + +Compared the full raw link lists (`URDF_file("ur5")`, independent of the +gripper-link logic) between the two `robot_descriptions` versions: + +- 2.0.0 (11 links): `world, base_link, shoulder_link, upper_arm_link, + forearm_link, wrist_1_link, wrist_2_link, wrist_3_link, ee_link, base, + tool0` +- 3.0.0 (13 links): `world, base_link, base_link_inertia, shoulder_link, + upper_arm_link, forearm_link, wrist_1_link, wrist_2_link, wrist_3_link, + ft_frame, base, flange, tool0` + +All 6 real arm joints have identical names and `isjoint` status in both. +Only the gripper-attachment link's name differs (`ee_link` in 2.0.0), but +`tool0` exists in **both** — just at a different index (9 vs. 12). So a +name-based lookup (`"tool0"`) or, more robustly, a structural one ("the +first fixed link after the last actuated joint") would survive this exact +upstream change, unlike the index. + +### Proposed fix + +Replace `gripper_link_index: int` with a lookup that doesn't depend on +absolute position — either match by a small set of known tool-frame names +(`tool0`, `ee_link`, `flange`, ...) with a fallback, or derive it +structurally (first non-actuated link following the last actuated one in +the kinematic chain). Worth checking how many other `URDF.*` models pass a +raw `gripper_link_index` the same fragile way before deciding on the +general fix — this UR5 case is likely not unique. + +**Deferred**: not fixed now, since the pin already resolves the immediate +CI break and this needs its own design pass rather than a rushed fix +bundled into a dependency-pin PR. From 2e9f2cd8b698667d9825b9a7944f670ef00a9a1c Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Mon, 20 Jul 2026 14:48:40 +1000 Subject: [PATCH 2/2] docs(fix): update stale roboticstoolbox.robot.ETS references to ets package Sphinx's docs-build job never caught this on PR #553 (the ET/ETS package split) because it's gated behind tests passing, and tests were failing there for the unrelated UR5/robot_descriptions reason - so docs-build never actually ran until this PR fixed that gate. arm_ets.rst and ik.rst still referenced the pre-split roboticstoolbox.robot.ETS.ETS(2) module path; ETS now lives at roboticstoolbox.ets.ETS.ETS and ETS2 at roboticstoolbox.ets.ETS2.ETS2 (separate files). Left autosummary to regenerate docs/source/IK/stubs/ (gitignored) against the corrected path. Verified: sphinx-build succeeds locally (previously a hard failure, ImportExceptionGroup: could not import roboticstoolbox.robot.ETS.ETS); remaining 4 warnings (KinematicCache, SensorBase, a guarded matplotlib Color import) are unrelated and pre-existing. Co-Authored-By: Claude Sonnet 5 --- docs/source/IK/ik.rst | 24 ++++++++++++------------ docs/source/arm_ets.rst | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/source/IK/ik.rst b/docs/source/IK/ik.rst index 87529ba8..2e27013f 100644 --- a/docs/source/IK/ik.rst +++ b/docs/source/IK/ik.rst @@ -67,7 +67,7 @@ There are other arguments which may be unique to the solver, so check the docume C++ Solvers ----------- -These solvers are written in high performance C++ and wrapped in Python methods. The methods are made available within the :py:class:`~roboticstoolbox.robot.ETS.ETS` and :py:class:`~roboticstoolbox.robot.Robot.Robot` classes. Being written in C++, these solvers are extraordinarily fast and typically take 30 to 90 µs. However, these solvers are hard to extend or modify. +These solvers are written in high performance C++ and wrapped in Python methods. The methods are made available within the :py:class:`~roboticstoolbox.ets.ETS.ETS` and :py:class:`~roboticstoolbox.robot.Robot.Robot` classes. Being written in C++, these solvers are extraordinarily fast and typically take 30 to 90 µs. However, these solvers are hard to extend or modify. These methods have been written purely for speed so they do not contain the niceties of the Python alternative. For example, if you give the incorrect length for the ``q0`` vector, you could end up with a ``seg-fault`` or other undetermined behaviour. Therefore, when using these methods it is very important that you understand each of the parameters and the parameters passed are of the correct type and length. @@ -90,9 +90,9 @@ The C++ solvers can be identified as methods which start with ``ik_``. .. autosummary:: :toctree: stubs - ~roboticstoolbox.robot.ETS.ETS.ik_LM - ~roboticstoolbox.robot.ETS.ETS.ik_GN - ~roboticstoolbox.robot.ETS.ETS.ik_NR + ~roboticstoolbox.ets.ETS.ETS.ik_LM + ~roboticstoolbox.ets.ETS.ETS.ik_GN + ~roboticstoolbox.ets.ETS.ETS.ik_NR .. rubric:: Robot C++ IK Methods @@ -115,7 +115,7 @@ In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Pa >>> # Solve the IK problem >>> panda.ik_LM(Tep) -In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Panda` robot and and then get the :py:class:`~roboticstoolbox.robot.ETS.ETS` representation. Subsequently, we use one of the fast IK solvers available within the :py:class:`~roboticstoolbox.robot.ETS.ETS` class. +In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Panda` robot and and then get the :py:class:`~roboticstoolbox.ets.ETS.ETS` representation. Subsequently, we use one of the fast IK solvers available within the :py:class:`~roboticstoolbox.ets.ETS.ETS` class. .. runblock:: pycon @@ -181,7 +181,7 @@ These solvers can be identified as a :py:class:`Class` starting with ``IK_``. .. rubric:: Example -In the following example, we create an IK Solver class and pass an :py:class:`~roboticstoolbox.robot.ETS.ETS` to it to solve the problem. This style may be preferable to experiments where you wish to compare the same solver on different robots. +In the following example, we create an IK Solver class and pass an :py:class:`~roboticstoolbox.ets.ETS.ETS` to it to solve the problem. This style may be preferable to experiments where you wish to compare the same solver on different robots. .. runblock:: pycon @@ -202,7 +202,7 @@ In the following example, we create an IK Solver class and pass an :py:class:`~r .. IK Solvers Available with an ETS .. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Additionally, these :py:class:`Class` based solvers have been implemented as methods within the :py:class:`~roboticstoolbox.robot.ETS.ETS` and :py:class:`~roboticstoolbox.robot.Robot.Robot` classes. The method names start with ``ikine_``. +Additionally, these :py:class:`Class` based solvers have been implemented as methods within the :py:class:`~roboticstoolbox.ets.ETS.ETS` and :py:class:`~roboticstoolbox.robot.Robot.Robot` classes. The method names start with ``ikine_``. .. toctree: @@ -215,10 +215,10 @@ Additionally, these :py:class:`Class` based solvers have been implemented as met .. autosummary:: :toctree: stubs - ~roboticstoolbox.robot.ETS.ETS.ikine_LM - ~roboticstoolbox.robot.ETS.ETS.ikine_QP - ~roboticstoolbox.robot.ETS.ETS.ikine_GN - ~roboticstoolbox.robot.ETS.ETS.ikine_NR + ~roboticstoolbox.ets.ETS.ETS.ikine_LM + ~roboticstoolbox.ets.ETS.ETS.ikine_QP + ~roboticstoolbox.ets.ETS.ETS.ikine_GN + ~roboticstoolbox.ets.ETS.ETS.ikine_NR .. rubric:: Robot Python IK Methods @@ -246,7 +246,7 @@ In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Pa >>> # Solve the IK problem >>> panda.ikine_LM(Tep) -In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Panda` robot and and then get the :py:class:`~roboticstoolbox.robot.ETS.ETS` representation. Subsequently, we use one of the IK solvers available within the :py:class:`~roboticstoolbox.robot.ETS.ETS` class. +In the following example, we create a :py:class:`~roboticstoolbox.models.URDF.Panda` robot and and then get the :py:class:`~roboticstoolbox.ets.ETS.ETS` representation. Subsequently, we use one of the IK solvers available within the :py:class:`~roboticstoolbox.ets.ETS.ETS` class. .. runblock:: pycon diff --git a/docs/source/arm_ets.rst b/docs/source/arm_ets.rst index 251b4449..49a5b33e 100644 --- a/docs/source/arm_ets.rst +++ b/docs/source/arm_ets.rst @@ -69,7 +69,7 @@ The ETS inherits list-like properties and has methods like ``reverse`` and ``pop ETS - 3D -------- -.. autoclass:: roboticstoolbox.robot.ETS.ETS +.. autoclass:: roboticstoolbox.ets.ETS.ETS :members: __str__, __repr__, __mul__, __getitem__, n, m, structure, joints, jointset, split, inv, compile, insert, fkine, jacob0, jacobe, hessian0, hessiane :undoc-members: :show-inheritance: @@ -78,7 +78,7 @@ ETS - 3D ETS - 2D -------- -.. autoclass:: roboticstoolbox.robot.ETS.ETS2 +.. autoclass:: roboticstoolbox.ets.ETS2.ETS2 :members: __str__, __repr__, __mul__, __getitem__, n, m, structure, joints, jointset, split, inv, compile, insert, fkine, jacob0, jacobe :undoc-members: :show-inheritance: