From b0d244866f668590cd5bf7577f6b2759543b5b8e Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:09:09 +1000 Subject: [PATCH 1/6] fix(ik): add IKSolution.__getitem__/__repr__, fix docstring typos IKSolution had __iter__ but no __getitem__, so positional indexing (sol[0], sol[1], ...) -- the pattern every existing caller and the old bare-tuple return used -- raised TypeError. Add __getitem__ matching __iter__'s order, and a __repr__ matching the existing custom __str__ instead of the verbose default dataclass repr. Also fixes "Levemberg-Marquadt"/"Marquadt" -> "Levenberg-Marquardt" and "progamming" -> "programming", present throughout this file's docstrings. Co-Authored-By: Claude Sonnet 5 --- src/roboticstoolbox/robot/IK.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/roboticstoolbox/robot/IK.py b/src/roboticstoolbox/robot/IK.py index 075412b3c..676042a42 100644 --- a/src/roboticstoolbox/robot/IK.py +++ b/src/roboticstoolbox/robot/IK.py @@ -46,6 +46,12 @@ class IKSolution: .. versionchanged:: 1.0.3 Added IKSolution dataclass to replace the IKsolution named tuple + .. versionchanged:: 1.4.2 + Added ``__getitem__`` (positional indexing matching ``__iter__``'s + order) and a ``__repr__`` matching ``__str__``, so ``ik_LM``/``ik_NR``/ + ``ik_GN`` (which now also return ``IKSolution``, see :meth:`ETS.ik_LM`) + remain compatible with code that indexed the old bare tuple return + """ q: np.ndarray @@ -67,6 +73,12 @@ def __iter__(self): ) ) + def __getitem__(self, i): + return tuple(self)[i] + + def __repr__(self): + return str(self) + def __str__(self): if self.q is not None: q_str = np.array2string( @@ -134,7 +146,7 @@ class IKSolver(ABC): :class:`IK_GN` Implements this class using the Gauss-Newton method - :class:`IK_LM` Implements this class using the Levemberg-Marquadt method + :class:`IK_LM` Implements this class using the Levenberg-Marquardt method :class:`IK_QP` Implements this class using a quadratic programming approach @@ -601,7 +613,7 @@ class IK_NR(IKSolver): :class:`IK_GN` Implements the IKSolver class using the Gauss-Newton method - :class:`IK_LM` Implements the IKSolver class using the Levemberg-Marquadt method + :class:`IK_LM` Implements the IKSolver class using the Levenberg-Marquardt method :class:`IK_QP` Implements the IKSolver class using a quadratic programming approach @@ -691,10 +703,10 @@ def step( class IK_LM(IKSolver): r""" - Levemberg-Marquadt Numerical Inverse Kinematics Solver + Levenberg-Marquardt Numerical Inverse Kinematics Solver A class which provides functionality to perform numerical inverse kinematics (IK) - using the Levemberg-Marquadt method. See ``step`` method for mathematical description. + using the Levenberg-Marquardt method. See ``step`` method for mathematical description. :param name: The name of the IK algorithm :param ilimit: How many iterations are allowed within a search before a new search @@ -771,7 +783,7 @@ class IK_LM(IKSolver): :class:`IK_QP` Implements the IKSolver class using a quadratic programming approach .. versionchanged:: 1.0.3 - Added the Levemberg-Marquadt IK solver class + Added the Levenberg-Marquardt IK solver class """ @@ -829,7 +841,7 @@ def __init__( def step(self, ets: "rtb.ETS", Tep: np.ndarray, q: np.ndarray): r""" - Performs a single iteration of the Levenberg-Marquadt optimisation + Performs a single iteration of the Levenberg-Marquardt optimisation :param ets: The ETS representing the manipulators kinematics :param Tep: The desired end-effector pose @@ -997,7 +1009,7 @@ class IK_GN(IKSolver): :class:`IK_NR` Implements IKSolver using the Newton-Raphson method - :class:`IK_LM` Implements IKSolver using the Levemberg-Marquadt method + :class:`IK_LM` Implements IKSolver using the Levenberg-Marquardt method :class:`IK_QP` Implements IKSolver using a quadratic programming approach @@ -1106,7 +1118,7 @@ class IK_QP(IKSolver): Quadratic Progamming Numerical Inverse Kinematics Solver A class which provides functionality to perform numerical inverse kinematics (IK) - using a quadratic progamming approach. See `step` method for mathematical + using a quadratic programming approach. See `step` method for mathematical description. :param name: The name of the IK algorithm @@ -1172,7 +1184,7 @@ class IK_QP(IKSolver): :class:`IK_GN` Implements IKSolver class using the Gauss-Newton method - :class:`IK_LM` Implements IKSolver class using the Levemberg-Marquadt method + :class:`IK_LM` Implements IKSolver class using the Levenberg-Marquardt method .. versionchanged:: 1.0.3 Added the Quadratic Programming IK solver class From edf102f38fcc9805e5a8bfb036e8dff0811d537b Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:09:24 +1000 Subject: [PATCH 2/6] fix(ik): unify ik_LM/ik_NR/ik_GN's return type with IKSolution ik_LM/ik_NR/ik_GN (the fast C++-backed solvers) returned a bare 5-tuple while ikine_LM/ikine_NR/ikine_GN (the pure-Python solvers) already returned IKSolution -- same family of methods, two different return shapes. Wrap the C++ tuple in IKSolution in both ETS.ik_LM/ ik_NR/ik_GN and their RobotKinematics forwarders, and update the return-type annotations and :returns:/:rtype: docstring fields to match (ikine_LM/ikine_NR/ikine_GN/ikine_QP were missing :returns:/ :rtype: entirely -- added those too). Also: - bidirectionally cross-reference each ik_XX with its ikine_XX counterpart (previously only cross-referenced their C++ siblings) - add a loud warning to ik_LM/ik_NR/ik_GN's docstrings that they require the compiled C++ extension and raise RuntimeError without it (e.g. pure-Python builds, Pyodide/JupyterLite) - fix several copy-paste bugs in RobotKinematics.py found while doing this: ik_GN's own "See Also" listed itself instead of ik_LM/ik_NR, ikine_GN's and ikine_QP's listed the wrong solver class entirely (IK_NR instead of IK_GN/IK_QP), and two runblock examples said "ikine_GN"/"ikine_LM" while actually calling ik_NR/ik_LM/ik_GN - fix "Levemberg-Marquadt"/"Marquadt" -> "Levenberg-Marquardt" and "deined" -> "defined" throughout both files Co-Authored-By: Claude Sonnet 5 --- src/roboticstoolbox/ets/ETS.py | 97 ++++++++++--- src/roboticstoolbox/robot/RobotKinematics.py | 136 ++++++++++++------- 2 files changed, 160 insertions(+), 73 deletions(-) diff --git a/src/roboticstoolbox/ets/ETS.py b/src/roboticstoolbox/ets/ETS.py index aaa71e3fd..8a82540e8 100644 --- a/src/roboticstoolbox/ets/ETS.py +++ b/src/roboticstoolbox/ets/ETS.py @@ -23,7 +23,7 @@ getmatrix, ) from roboticstoolbox.tools.params import rtb_get_param -from roboticstoolbox.robot.IK import IK_GN, IK_LM, IK_NR, IK_QP +from roboticstoolbox.robot.IK import IK_GN, IK_LM, IK_NR, IK_QP, IKSolution from roboticstoolbox.ets.fknm import ( ETS_init, @@ -1000,7 +1000,7 @@ def ik_LM( joint_limits: bool = True, k: float = 1.0, method: L["chan", "wampler", "sugihara"] = "chan", - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" Fast Levenberg-Marquardt numerical inverse kinematics solver @@ -1013,8 +1013,18 @@ def ik_LM( :param joint_limits: reject solutions with joint limit violations :param k: gain value for the damping matrix Wn :param method: one of ``"chan"`` (default), ``"sugihara"`` or ``"wampler"`` - :returns: tuple (q, success, iterations, searches, residual) - :rtype: tuple + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches`` and ``residual`` error value (``reason`` is + always empty -- this fast C++ solver doesn't produce a granular failure + reason string, unlike :meth:`ikine_LM`) + :rtype: IKSolution + + .. warning:: + + This method requires the compiled C++ extension. It raises + ``RuntimeError`` if that extension is unavailable, e.g. in a + pure-Python build/wheel or under Pyodide/JupyterLite. Use + :meth:`ikine_LM` instead in those environments. A method which provides functionality to perform numerical inverse kinematics (IK) using the Levenberg-Marquardt method. This is a fast solver implemented in C++. @@ -1024,7 +1034,7 @@ def ik_LM( The operation is defined by the choice of the ``method`` kwarg. - The step is deined as + The step is defined as .. math:: @@ -1112,16 +1122,23 @@ def ik_LM( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ik_NR` :meth:`ik_GN` + .. seealso:: :meth:`ik_NR` :meth:`ik_GN` :meth:`ikine_LM` .. versionchanged:: 1.0.4 Merged the Levenberg-Marquardt IK solvers into the ik_LM method """ - return IK_LM_c( + q, success, iterations, searches, residual = IK_LM_c( self._fknm, Tep, q0, ilimit, slimit, tol, joint_limits, mask, k, method ) + return IKSolution( + q=q, + success=bool(success), + iterations=iterations, + searches=searches, + residual=residual, + ) def ik_NR( self, @@ -1134,7 +1151,7 @@ def ik_NR( joint_limits: bool = True, pinv: int = True, pinv_damping: float = 0.0, - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" Fast numerical inverse kinematics using Newton-Raphson optimisation @@ -1147,8 +1164,18 @@ def ik_NR( :param joint_limits: reject solutions with invalid joint configurations :param pinv: use the pseudo-inverse instead of the normal matrix inverse :param pinv_damping: damping factor for the pseudo-inverse - :returns: tuple (q, success, iterations, searches, residual) - :rtype: tuple + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches`` and ``residual`` error value (``reason`` is + always empty -- this fast C++ solver doesn't produce a granular failure + reason string, unlike :meth:`ikine_NR`) + :rtype: IKSolution + + .. warning:: + + This method requires the compiled C++ extension. It raises + ``RuntimeError`` if that extension is unavailable, e.g. in a + pure-Python build/wheel or under Pyodide/JupyterLite. Use + :meth:`ikine_NR` instead in those environments. ``sol = ets.ik_NR(Tep)`` are the joint coordinates (n) corresponding to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. @@ -1195,11 +1222,11 @@ def ik_NR( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ik_LM` :meth:`ik_GN` + .. seealso:: :meth:`ik_LM` :meth:`ik_GN` :meth:`ikine_NR` """ - return IK_NR_c( + q, success, iterations, searches, residual = IK_NR_c( self._fknm, Tep, q0, @@ -1211,6 +1238,13 @@ def ik_NR( pinv, pinv_damping, ) + return IKSolution( + q=q, + success=bool(success), + iterations=iterations, + searches=searches, + residual=residual, + ) def ik_GN( self, @@ -1223,7 +1257,7 @@ def ik_GN( joint_limits: bool = True, pinv: int = True, pinv_damping: float = 0.0, - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" Fast numerical inverse kinematics by Gauss-Newton optimisation @@ -1299,11 +1333,11 @@ def ik_GN( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ik_LM` :meth:`ik_NR` + .. seealso:: :meth:`ik_LM` :meth:`ik_NR` :meth:`ikine_GN` """ - return IK_GN_c( + q, success, iterations, searches, residual = IK_GN_c( self._fknm, Tep, q0, @@ -1315,6 +1349,13 @@ def ik_GN( pinv, pinv_damping, ) + return IKSolution( + q=q, + success=bool(success), + iterations=iterations, + searches=searches, + residual=residual, + ) def ikine_LM( self, @@ -1351,7 +1392,10 @@ def ikine_LM( :param km: gain for manipulability maximisation (0.0 disables) :param ps: minimum joint approach distance to limit (radians or metres) :param pi: null-space influence distance (radians or metres) - :returns: IK solution + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution A method which provides functionality to perform numerical inverse kinematics (IK) using the Levenberg-Marquardt method. @@ -1449,7 +1493,7 @@ def ikine_LM( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ikine_NR` :meth:`ikine_GN` :meth:`ikine_QP` + .. seealso:: :meth:`ikine_NR` :meth:`ikine_GN` :meth:`ikine_QP` :meth:`ik_LM` .. versionchanged:: 1.0.4 Added the Levenberg-Marquardt IK solver method on the `ETS` class @@ -1510,7 +1554,10 @@ def ikine_NR( :param km: gain for manipulability maximisation (0.0 disables) :param ps: minimum joint approach distance to limit (radians or metres) :param pi: null-space influence distance (radians or metres) - :returns: IK solution + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution A method which provides functionality to perform numerical inverse kinematics (IK) using the Newton-Raphson method. @@ -1554,7 +1601,7 @@ def ikine_NR( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ikine_LM` :meth:`ikine_GN` :meth:`ikine_QP` + .. seealso:: :meth:`ikine_LM` :meth:`ikine_GN` :meth:`ikine_QP` :meth:`ik_NR` .. versionchanged:: 1.0.4 Added the Newton-Raphson IK solver method on the `ETS` class @@ -1614,7 +1661,10 @@ def ikine_GN( :param km: gain for manipulability maximisation (0.0 disables) :param ps: minimum joint approach distance to limit (radians or metres) :param pi: null-space influence distance (radians or metres) - :returns: IK solution + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution A method which provides functionality to perform numerical inverse kinematics (IK) using the Gauss-Newton method. @@ -1673,7 +1723,7 @@ def ikine_GN( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - .. seealso:: :meth:`ikine_LM` :meth:`ikine_NR` :meth:`ikine_QP` + .. seealso:: :meth:`ikine_LM` :meth:`ikine_NR` :meth:`ikine_QP` :meth:`ik_GN` .. versionchanged:: 1.0.4 Added the Gauss-Newton IK solver method on the `ETS` class @@ -1735,7 +1785,10 @@ def ikine_QP( :param km: gain for manipulability maximisation (0.0 disables) :param ps: minimum joint approach distance to limit (radians or metres) :param pi: null-space influence distance (radians or metres) - :returns: IK solution + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution :raises ImportError: if the package ``qpsolvers`` is not installed A method that provides functionality to perform numerical inverse kinematics diff --git a/src/roboticstoolbox/robot/RobotKinematics.py b/src/roboticstoolbox/robot/RobotKinematics.py index 3fc793e9c..cddc121ee 100644 --- a/src/roboticstoolbox/robot/RobotKinematics.py +++ b/src/roboticstoolbox/robot/RobotKinematics.py @@ -6,6 +6,7 @@ from roboticstoolbox.tools.types import ArrayLike, NDArray from roboticstoolbox.robot.Link import Link from roboticstoolbox.robot.Gripper import Gripper +from roboticstoolbox.robot.IK import IKSolution from spatialmath import SE3 from typing import Literal as L, overload @@ -505,9 +506,9 @@ def ik_LM( joint_limits: bool = True, k: float = 1.0, method: L["chan", "wampler", "sugihara"] = "chan", - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" - Fast levenberg-Marquadt Numerical Inverse Kinematics Solver + Fast Levenberg-Marquardt Numerical Inverse Kinematics Solver :param Tep: The desired end-effector pose :param end: the link considered as the end-effector @@ -523,10 +524,21 @@ def ik_LM( :param k: Sets the gain value for the damping matrix Wn in the next iteration :param method: One of "chan", "sugihara" or "wampler". Defines which method is used to calculate the damping matrix Wn in the ``step`` method - :returns: tuple (q, success, iterations, searches, residual) + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches`` and ``residual`` error value (``reason`` is + always empty -- this fast C++ solver doesn't produce a granular failure + reason string, unlike :meth:`ikine_LM`) + :rtype: IKSolution + + .. warning:: + + This method requires the compiled C++ extension. It raises + ``RuntimeError`` if that extension is unavailable, e.g. in a + pure-Python build/wheel or under Pyodide/JupyterLite. Use + :meth:`ikine_LM` instead in those environments. A method which provides functionality to perform numerical inverse kinematics (IK) - using the Levemberg-Marquadt method. This + using the Levenberg-Marquardt method. This is a fast solver implemented in C++. See the :ref:`Inverse Kinematics Docs Page ` for more details and for a @@ -534,7 +546,7 @@ def ik_LM( The operation is defined by the choice of the ``method`` kwarg. - The step is deined as + The step is defined as .. math:: @@ -594,13 +606,13 @@ def ik_LM( -------- The following example makes a ``panda`` robot object, makes a goal pose ``Tep``, and then solves for the joint coordinates which result in the pose - ``Tep`` using the `ikine_LM` method. + ``Tep`` using the `ik_LM` method. .. runblock:: pycon >>> import roboticstoolbox as rtb >>> panda = rtb.models.Panda() >>> Tep = panda.fkine([0, -0.3, 0, -2.2, 0, 2, 0.7854]) - >>> panda.ikine_LM(Tep) + >>> panda.ik_LM(Tep) .. rubric:: Notes @@ -621,16 +633,10 @@ def ik_LM( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - See Also - -------- - ik_NR - A fast numerical inverse kinematics solver using Newton-Raphson optimisation - ik_GN - A fast numerical inverse kinematics solver using Gauss-Newton optimisation - + .. seealso:: :meth:`ik_NR` :meth:`ik_GN` :meth:`ikine_LM` .. versionchanged:: 1.0.4 - Merged the Levemberg-Marquadt IK solvers into the ik_LM method + Merged the Levenberg-Marquardt IK solvers into the ik_LM method """ @@ -659,7 +665,7 @@ def ik_NR( joint_limits: bool = True, pinv: int = True, pinv_damping: float = 0.0, - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" Fast numerical inverse kinematics using Newton-Raphson optimization @@ -679,7 +685,18 @@ def ik_NR( another search up to the slimit) :param pinv: Use the pseudo-inverse instead of the normal matrix inverse :param pinv_damping: Damping factor for the pseudo-inverse - :returns: tuple (q, success, iterations, searches, residual) + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches`` and ``residual`` error value (``reason`` is + always empty -- this fast C++ solver doesn't produce a granular failure + reason string, unlike :meth:`ikine_NR`) + :rtype: IKSolution + + .. warning:: + + This method requires the compiled C++ extension. It raises + ``RuntimeError`` if that extension is unavailable, e.g. in a + pure-Python build/wheel or under Pyodide/JupyterLite. Use + :meth:`ikine_NR` instead in those environments. ``sol = ets.ik_NR(Tep)`` are the joint coordinates (n) corresponding to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. @@ -693,19 +710,20 @@ def ik_NR( When using this method with redundant robots (>6 DoF), ``pinv`` must be set to ``True`` - The return value ``sol`` is a tuple with elements: + The return value ``sol`` is an ``IKSolution`` with fields: ============== ========== =============================================== - Element Type Description + Field Type Description ============== ========== =============================================== ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found + ``success`` bool whether a solution was found ``iterations`` int total number of iterations ``searches`` int total number of searches ``residual`` float final value of cost function + ``reason`` str always empty for this C++ solver ============== ========== =============================================== - If ``success == 0`` the ``q`` values will be valid numbers, but the + If ``success == False`` the ``q`` values will be valid numbers, but the solution will be in error. The amount of error is indicated by the ``residual``. @@ -719,7 +737,7 @@ def ik_NR( -------- The following example gets a ``panda`` robot object, makes a goal pose ``Tep``, and then solves for the joint coordinates which result in the pose - ``Tep`` using the `ikine_GN` method. + ``Tep`` using the `ik_NR` method. .. runblock:: pycon >>> import roboticstoolbox as rtb @@ -739,12 +757,7 @@ def ik_NR( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - See Also - -------- - ik_LM - A fast numerical inverse kinematics solver using Levenberg-Marquadt optimisation - ik_GN - A fast numerical inverse kinematics solver using Gauss-Newton optimisation + .. seealso:: :meth:`ik_LM` :meth:`ik_GN` :meth:`ikine_NR` """ @@ -773,7 +786,7 @@ def ik_GN( joint_limits: bool = True, pinv: int = True, pinv_damping: float = 0.0, - ) -> tuple[NDArray, int, int, int, float]: + ) -> IKSolution: r""" Fast numerical inverse kinematics by Gauss-Newton optimization @@ -793,7 +806,18 @@ def ik_GN( another search up to the slimit) :param pinv: Use the pseudo-inverse instead of the normal matrix inverse :param pinv_damping: Damping factor for the pseudo-inverse - :returns: tuple (q, success, iterations, searches, residual) + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches`` and ``residual`` error value (``reason`` is + always empty -- this fast C++ solver doesn't produce a granular failure + reason string, unlike :meth:`ikine_GN`) + :rtype: IKSolution + + .. warning:: + + This method requires the compiled C++ extension. It raises + ``RuntimeError`` if that extension is unavailable, e.g. in a + pure-Python build/wheel or under Pyodide/JupyterLite. Use + :meth:`ikine_GN` instead in those environments. ``sol = ets.ik_GN(Tep)`` are the joint coordinates (n) corresponding to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. @@ -807,19 +831,20 @@ def ik_GN( When using this method with redundant robots (>6 DoF), ``pinv`` must be set to ``True`` - The return value ``sol`` is a tuple with elements: + The return value ``sol`` is an ``IKSolution`` with fields: ============== ========== =============================================== - Element Type Description + Field Type Description ============== ========== =============================================== ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found + ``success`` bool whether a solution was found ``iterations`` int total number of iterations ``searches`` int total number of searches ``residual`` float final value of cost function + ``reason`` str always empty for this C++ solver ============== ========== =============================================== - If ``success == 0`` the ``q`` values will be valid numbers, but the + If ``success == False`` the ``q`` values will be valid numbers, but the solution will be in error. The amount of error is indicated by the ``residual``. @@ -848,7 +873,7 @@ def ik_GN( -------- The following example gets a ``panda`` robot object, makes a goal pose ``Tep``, and then solves for the joint coordinates which result in the pose - ``Tep`` using the `ikine_GN` method. + ``Tep`` using the `ik_GN` method. .. runblock:: pycon >>> import roboticstoolbox as rtb @@ -868,12 +893,7 @@ def ik_GN( - J. Haviland, and P. Corke. "Manipulator Differential Kinematics Part II: Acceleration and Advanced Applications." arXiv preprint arXiv:2207.01794 (2022). - See Also - -------- - ik_NR - A fast numerical inverse kinematics solver using Newton-Raphson optimisation - ik_GN - A fast numerical inverse kinematics solver using Gauss-Newton optimisation + .. seealso:: :meth:`ik_LM` :meth:`ik_NR` :meth:`ikine_GN` """ @@ -910,7 +930,7 @@ def ikine_LM( **kwargs, ): r""" - Levenberg-Marquadt Numerical Inverse Kinematics Solver + Levenberg-Marquardt Numerical Inverse Kinematics Solver :param Tep: The desired end-effector pose :param end: the link considered as the end-effector @@ -936,16 +956,20 @@ def ikine_LM( allowed to approach to its limit :param pi: The influence angle/distance (in radians or metres) in null space motion becomes active + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution A method which provides functionality to perform numerical inverse kinematics (IK) - using the Levemberg-Marquadt method. + using the Levenberg-Marquardt method. See the :ref:`Inverse Kinematics Docs Page ` for more details and for a **tutorial** on numerical IK, see `here `_. The operation is defined by the choice of the ``method`` kwarg. - The step is deined as + The step is defined as .. math:: @@ -1035,17 +1059,19 @@ def ikine_LM( See Also -------- :py:class:`~roboticstoolbox.robot.IK.IK_LM` - An IK Solver class which implements the Levemberg Marquadt optimisation technique + An IK Solver class which implements the Levenberg-Marquardt optimisation technique ikine_NR Implements the :py:class:`~roboticstoolbox.robot.IK.IK_NR` class as a method within the :py:class:`Robot` class ikine_GN Implements the :py:class:`~roboticstoolbox.robot.IK.IK_GN` class as a method within the :py:class:`Robot` class ikine_QP Implements the :py:class:`~roboticstoolbox.robot.IK.IK_QP` class as a method within the :py:class:`Robot` class + :meth:`ik_LM` + The fast, C++-backed equivalent of this method (requires the compiled extension) .. versionchanged:: 1.0.4 - Added the Levemberg-Marquadt IK solver method on the `Robot` class + Added the Levenberg-Marquardt IK solver method on the `Robot` class """ @@ -1166,6 +1192,8 @@ def ikine_NR( Implements the :py:class:`~roboticstoolbox.robot.IK.IK_GN` class as a method within the :py:class:`ETS` class ikine_QP Implements the :py:class:`~roboticstoolbox.robot.IK.IK_QP` class as a method within the :py:class:`ETS` class + :meth:`ik_NR` + The fast, C++-backed equivalent of this method (requires the compiled extension) .. versionchanged:: 1.0.4 @@ -1296,14 +1324,16 @@ def ikine_GN( See Also -------- - :py:class:`~roboticstoolbox.robot.IK.IK_NR` - An IK Solver class which implements the Newton-Raphson optimisation technique + :py:class:`~roboticstoolbox.robot.IK.IK_GN` + An IK Solver class which implements the Gauss-Newton optimisation technique ikine_LM Implements the :py:class:`~roboticstoolbox.robot.IK.IK_LM` class as a method within the :py:class:`ETS` class ikine_NR Implements the :py:class:`~roboticstoolbox.robot.IK.IK_NR` class as a method within the :py:class:`ETS` class ikine_QP Implements the :py:class:`~roboticstoolbox.robot.IK.IK_QP` class as a method within the :py:class:`ETS` class + :meth:`ik_GN` + The fast, C++-backed equivalent of this method (requires the compiled extension) .. versionchanged:: 1.0.4 @@ -1375,9 +1405,13 @@ def ikine_QP( :param pi: The influence angle/distance (in radians or metres) in null space motion becomes active :raises ImportError: If the package ``qpsolvers`` is not installed + :returns: an IKSolution containing joint coordinates ``q``, ``success`` flag, + ``iterations``, ``searches``, ``residual`` error value, and ``reason`` + string if applicable + :rtype: IKSolution A method that provides functionality to perform numerical inverse kinematics - (IK) using a quadratic progamming approach. + (IK) using a quadratic programming approach. See the :ref:`Inverse Kinematics Docs Page ` for more details and for a **tutorial** on numerical IK, see `here `_. @@ -1471,8 +1505,8 @@ def ikine_QP( See Also -------- - :py:class:`~roboticstoolbox.robot.IK.IK_NR` - An IK Solver class which implements the Newton-Raphson optimisation technique + :py:class:`~roboticstoolbox.robot.IK.IK_QP` + An IK Solver class which implements a quadratic programming approach ikine_LM Implements the :py:class:`~roboticstoolbox.robot.IK.IK_LM` class as a method within the :py:class:`ETS` class ikine_GN From 96411e7fb8293bdf83109f71d1b4b157486eb12c Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:09:32 +1000 Subject: [PATCH 3/6] fix(dhrobot): remove 5 completely broken dead IK methods DHRobot.ik_lm_chan/ik_lm_wampler/ik_lm_sugihara/ik_nr/ik_gn all forwarded to self.ets().(...), but ETS has never had methods by these names (only the unified ik_LM/ik_NR/ik_GN, each taking a method= kwarg where relevant) -- every one of these five methods raises AttributeError unconditionally on any call. Their docstrings even have literal ":seealso: TODO" placeholders. No test exercises any of them. Confirmed dead: nothing in tests/ or docs/ references any of the five; the only caller was examples/ik_exp.py (fixed separately). Co-Authored-By: Claude Sonnet 5 --- src/roboticstoolbox/robot/DHRobot.py | 526 --------------------------- 1 file changed, 526 deletions(-) diff --git a/src/roboticstoolbox/robot/DHRobot.py b/src/roboticstoolbox/robot/DHRobot.py index 5a04c8490..44f583f30 100644 --- a/src/roboticstoolbox/robot/DHRobot.py +++ b/src/roboticstoolbox/robot/DHRobot.py @@ -1996,532 +1996,6 @@ def config_validate(self, config, allowables): # -------------------------------------------------------------------------- # - def ik_lm_chan( - self, - Tep: np.ndarray | SE3, - q0: np.ndarray | None = None, - ilimit: int = 30, - slimit: int = 100, - tol: float = 1e-6, - reject_jl: bool = True, - we: np.ndarray | None = None, - λ: float = 1.0, - ) -> tuple[np.ndarray, int, int, int, float]: - """ - Numerical inverse kinematics by Levenberg-Marquadt optimization (Chan's Method) - - :param Tep: The desired end-effector pose or pose trajectory - :param q0: initial joint configuration (default to random valid joint - configuration constrained by the joint limits of the robot) - :param ilimit: maximum number of iterations per search - :param slimit: maximum number of search attempts - :param tol: final error tolerance - :param reject_jl: constrain the solution to being within the joint limits of - the robot (reject solution with invalid joint configurations and perform - another search up to the slimit) - :param we: a mask vector which weights the end-effector error priority. - Corresponds to translation in X, Y and Z and rotation about X, Y and Z - respectively - :param λ: value of lambda for the damping matrix Wn - - :return: inverse kinematic solution - :rtype: tuple (q, success, iterations, searches, residual) - - ``sol = ets.ik_lm_chan(Tep)`` are the joint coordinates (n) corresponding - to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. - This method can be used for robots with any number of degrees of freedom. - The return value ``sol`` is a tuple with elements: - - ============== ========== =============================================== - Element Type Description - ============== ========== =============================================== - ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found - ``iterations`` int total number of iterations - ``searches`` int total number of searches - ``residual`` float final value of cost function - ============== ========== =============================================== - - If ``success == 0`` the ``q`` values will be valid numbers, but the - solution will be in error. The amount of error is indicated by - the ``residual``. - - **Joint Limits**: - - ``sol = robot.ikine_LM(T, slimit=100)`` which is the deafualt for this method. - The solver will initialise a solution attempt with a random valid q0 and - perform a maximum of ilimit steps within this attempt. If a solution is not - found, this process is repeated up to slimit times. - - **Global search**: - - ``sol = robot.ikine_LM(T, reject_jl=True)`` is the deafualt for this method. - By setting reject_jl to True, the solver will discard any solution which - violates the defined joint limits of the robot. The solver will then - re-initialise with a new random q0 and repeat the process up to slimit times. - Note that finding a solution with valid joint coordinates takes longer than - without. - - **Underactuated robots:** - - For the case where the manipulator has fewer than 6 DOF the - solution space has more dimensions than can be spanned by the - manipulator joint coordinates. - - In this case we specify the ``we`` option where the ``we`` vector - (6) specifies the Cartesian DOF (in the wrist coordinate frame) that - will be ignored in reaching a solution. The we vector has six - elements that correspond to translation in X, Y and Z, and rotation - about X, Y and Z respectively. The value can be 0 (for ignore) - or above to assign a priority relative to other Cartesian DoF. The number - of non-zero elements must equal the number of manipulator DOF. - - For example when using a 3 DOF manipulator tool orientation might - be unimportant, in which case use the option ``we=[1, 1, 1, 0, 0, 0]``. - - - - .. note:: - - - See `Toolbox kinematics wiki page `_ - - Implements a Levenberg-Marquadt variable-damping solver. - - The tolerance is computed on the norm of the error between - current and desired tool pose. This norm is computed from - distances and angles without any kind of weighting. - - The inverse kinematic solution is generally not unique, and - depends on the initial guess ``q0``. - - :references: - TODO - - :seealso: - TODO - """ - - return self.ets().ik_lm_chan(Tep, q0, ilimit, slimit, tol, reject_jl, we, λ) # type: ignore[attr-defined] - - def ik_lm_wampler( - self, - Tep: np.ndarray | SE3, - q0: np.ndarray | None = None, - ilimit: int = 30, - slimit: int = 100, - tol: float = 1e-6, - reject_jl: bool = True, - we: np.ndarray | None = None, - λ: float = 1.0, - ) -> tuple[np.ndarray, int, int, int, float]: - """ - Numerical inverse kinematics by Levenberg-Marquadt optimization (Wamplers's Method) - - :param Tep: The desired end-effector pose or pose trajectory - :param q0: initial joint configuration (default to random valid joint - configuration constrained by the joint limits of the robot) - :param ilimit: maximum number of iterations per search - :param slimit: maximum number of search attempts - :param tol: final error tolerance - :param reject_jl: constrain the solution to being within the joint limits of - the robot (reject solution with invalid joint configurations and perform - another search up to the slimit) - :param we: a mask vector which weights the end-effector error priority. - Corresponds to translation in X, Y and Z and rotation about X, Y and Z - respectively - :param λ: value of lambda for the damping matrix Wn - - :return: inverse kinematic solution - :rtype: tuple (q, success, iterations, searches, residual) - - ``sol = ets.ik_lm_chan(Tep)`` are the joint coordinates (n) corresponding - to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. - This method can be used for robots with any number of degrees of freedom. - The return value ``sol`` is a tuple with elements: - - ============== ========== =============================================== - Element Type Description - ============== ========== =============================================== - ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found - ``iterations`` int total number of iterations - ``searches`` int total number of searches - ``residual`` float final value of cost function - ============== ========== =============================================== - - If ``success == 0`` the ``q`` values will be valid numbers, but the - solution will be in error. The amount of error is indicated by - the ``residual``. - - **Joint Limits**: - - ``sol = robot.ikine_LM(T, slimit=100)`` which is the deafualt for this method. - The solver will initialise a solution attempt with a random valid q0 and - perform a maximum of ilimit steps within this attempt. If a solution is not - found, this process is repeated up to slimit times. - - **Global search**: - - ``sol = robot.ikine_LM(T, reject_jl=True)`` is the deafualt for this method. - By setting reject_jl to True, the solver will discard any solution which - violates the defined joint limits of the robot. The solver will then - re-initialise with a new random q0 and repeat the process up to slimit times. - Note that finding a solution with valid joint coordinates takes longer than - without. - - **Underactuated robots:** - - For the case where the manipulator has fewer than 6 DOF the - solution space has more dimensions than can be spanned by the - manipulator joint coordinates. - - In this case we specify the ``we`` option where the ``we`` vector - (6) specifies the Cartesian DOF (in the wrist coordinate frame) that - will be ignored in reaching a solution. The we vector has six - elements that correspond to translation in X, Y and Z, and rotation - about X, Y and Z respectively. The value can be 0 (for ignore) - or above to assign a priority relative to other Cartesian DoF. The number - of non-zero elements must equal the number of manipulator DOF. - - For example when using a 3 DOF manipulator tool orientation might - be unimportant, in which case use the option ``we=[1, 1, 1, 0, 0, 0]``. - - - - .. note:: - - - See `Toolbox kinematics wiki page `_ - - Implements a Levenberg-Marquadt variable-damping solver. - - The tolerance is computed on the norm of the error between - current and desired tool pose. This norm is computed from - distances and angles without any kind of weighting. - - The inverse kinematic solution is generally not unique, and - depends on the initial guess ``q0``. - - :references: - TODO - - :seealso: - TODO - """ - - return self.ets().ik_lm_wampler(Tep, q0, ilimit, slimit, tol, reject_jl, we, λ) # type: ignore[attr-defined] - - def ik_lm_sugihara( - self, - Tep: np.ndarray | SE3, - q0: np.ndarray | None = None, - ilimit: int = 30, - slimit: int = 100, - tol: float = 1e-6, - reject_jl: bool = True, - we: np.ndarray | None = None, - λ: float = 1.0, - ) -> tuple[np.ndarray, int, int, int, float]: - """ - Numerical inverse kinematics by Levenberg-Marquadt optimization (Sugihara's Method) - - :param Tep: The desired end-effector pose or pose trajectory - :param q0: initial joint configuration (default to random valid joint - configuration constrained by the joint limits of the robot) - :param ilimit: maximum number of iterations per search - :param slimit: maximum number of search attempts - :param tol: final error tolerance - :param reject_jl: constrain the solution to being within the joint limits of - the robot (reject solution with invalid joint configurations and perform - another search up to the slimit) - :param we: a mask vector which weights the end-effector error priority. - Corresponds to translation in X, Y and Z and rotation about X, Y and Z - respectively - :param λ: value of lambda for the damping matrix Wn - - :return: inverse kinematic solution - :rtype: tuple (q, success, iterations, searches, residual) - - ``sol = ets.ik_lm_chan(Tep)`` are the joint coordinates (n) corresponding - to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. - This method can be used for robots with any number of degrees of freedom. - The return value ``sol`` is a tuple with elements: - - ============== ========== =============================================== - Element Type Description - ============== ========== =============================================== - ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found - ``iterations`` int total number of iterations - ``searches`` int total number of searches - ``residual`` float final value of cost function - ============== ========== =============================================== - - If ``success == 0`` the ``q`` values will be valid numbers, but the - solution will be in error. The amount of error is indicated by - the ``residual``. - - **Joint Limits**: - - ``sol = robot.ikine_LM(T, slimit=100)`` which is the deafualt for this method. - The solver will initialise a solution attempt with a random valid q0 and - perform a maximum of ilimit steps within this attempt. If a solution is not - found, this process is repeated up to slimit times. - - **Global search**: - - ``sol = robot.ikine_LM(T, reject_jl=True)`` is the deafualt for this method. - By setting reject_jl to True, the solver will discard any solution which - violates the defined joint limits of the robot. The solver will then - re-initialise with a new random q0 and repeat the process up to slimit times. - Note that finding a solution with valid joint coordinates takes longer than - without. - - **Underactuated robots:** - - For the case where the manipulator has fewer than 6 DOF the - solution space has more dimensions than can be spanned by the - manipulator joint coordinates. - - In this case we specify the ``we`` option where the ``we`` vector - (6) specifies the Cartesian DOF (in the wrist coordinate frame) that - will be ignored in reaching a solution. The we vector has six - elements that correspond to translation in X, Y and Z, and rotation - about X, Y and Z respectively. The value can be 0 (for ignore) - or above to assign a priority relative to other Cartesian DoF. The number - of non-zero elements must equal the number of manipulator DOF. - - For example when using a 3 DOF manipulator tool orientation might - be unimportant, in which case use the option ``we=[1, 1, 1, 0, 0, 0]``. - - - - .. note:: - - - See `Toolbox kinematics wiki page `_ - - Implements a Levenberg-Marquadt variable-damping solver. - - The tolerance is computed on the norm of the error between - current and desired tool pose. This norm is computed from - distances and angles without any kind of weighting. - - The inverse kinematic solution is generally not unique, and - depends on the initial guess ``q0``. - - :references: - TODO - - :seealso: - TODO - """ - - return self.ets().ik_lm_sugihara(Tep, q0, ilimit, slimit, tol, reject_jl, we, λ) # type: ignore[attr-defined] - - def ik_nr( - self, - Tep: np.ndarray | SE3, - q0: np.ndarray | None = None, - ilimit: int = 30, - slimit: int = 100, - tol: float = 1e-6, - reject_jl: bool = True, - we: np.ndarray | None = None, - use_pinv: int = True, - pinv_damping: float = 0.0, - ) -> tuple[np.ndarray, int, int, int, float]: - """ - Numerical inverse kinematics by Levenberg-Marquadt optimization (Newton-Raphson Method) - - :param Tep: The desired end-effector pose or pose trajectory - :param q0: initial joint configuration (default to random valid joint - configuration constrained by the joint limits of the robot) - :param ilimit: maximum number of iterations per search - :param slimit: maximum number of search attempts - :param tol: final error tolerance - :param reject_jl: constrain the solution to being within the joint limits of - the robot (reject solution with invalid joint configurations and perform - another search up to the slimit) - :param we: a mask vector which weights the end-effector error priority. - Corresponds to translation in X, Y and Z and rotation about X, Y and Z - respectively - :param λ: value of lambda for the damping matrix Wn - - :return: inverse kinematic solution - :rtype: tuple (q, success, iterations, searches, residual) - - ``sol = ets.ik_lm_chan(Tep)`` are the joint coordinates (n) corresponding - to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. - This method can be used for robots with any number of degrees of freedom. - The return value ``sol`` is a tuple with elements: - - ============== ========== =============================================== - Element Type Description - ============== ========== =============================================== - ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found - ``iterations`` int total number of iterations - ``searches`` int total number of searches - ``residual`` float final value of cost function - ============== ========== =============================================== - - If ``success == 0`` the ``q`` values will be valid numbers, but the - solution will be in error. The amount of error is indicated by - the ``residual``. - - **Joint Limits**: - - ``sol = robot.ikine_LM(T, slimit=100)`` which is the deafualt for this method. - The solver will initialise a solution attempt with a random valid q0 and - perform a maximum of ilimit steps within this attempt. If a solution is not - found, this process is repeated up to slimit times. - - **Global search**: - - ``sol = robot.ikine_LM(T, reject_jl=True)`` is the deafualt for this method. - By setting reject_jl to True, the solver will discard any solution which - violates the defined joint limits of the robot. The solver will then - re-initialise with a new random q0 and repeat the process up to slimit times. - Note that finding a solution with valid joint coordinates takes longer than - without. - - **Underactuated robots:** - - For the case where the manipulator has fewer than 6 DOF the - solution space has more dimensions than can be spanned by the - manipulator joint coordinates. - - In this case we specify the ``we`` option where the ``we`` vector - (6) specifies the Cartesian DOF (in the wrist coordinate frame) that - will be ignored in reaching a solution. The we vector has six - elements that correspond to translation in X, Y and Z, and rotation - about X, Y and Z respectively. The value can be 0 (for ignore) - or above to assign a priority relative to other Cartesian DoF. The number - of non-zero elements must equal the number of manipulator DOF. - - For example when using a 3 DOF manipulator tool orientation might - be unimportant, in which case use the option ``we=[1, 1, 1, 0, 0, 0]``. - - - - .. note:: - - - See `Toolbox kinematics wiki page `_ - - Implements a Levenberg-Marquadt variable-damping solver. - - The tolerance is computed on the norm of the error between - current and desired tool pose. This norm is computed from - distances and angles without any kind of weighting. - - The inverse kinematic solution is generally not unique, and - depends on the initial guess ``q0``. - - :references: - TODO - - :seealso: - TODO - """ - - return self.ets().ik_nr( # type: ignore[attr-defined] - Tep, q0, ilimit, slimit, tol, reject_jl, we, use_pinv, pinv_damping - ) - - def ik_gn( - self, - Tep: np.ndarray | SE3, - q0: np.ndarray | None = None, - ilimit: int = 30, - slimit: int = 100, - tol: float = 1e-6, - reject_jl: bool = True, - we: np.ndarray | None = None, - use_pinv: int = True, - pinv_damping: float = 0.0, - ) -> tuple[np.ndarray, int, int, int, float]: - """ - Numerical inverse kinematics by Levenberg-Marquadt optimization (Gauss-Newton Method) - - :param Tep: The desired end-effector pose or pose trajectory - :param q0: initial joint configuration (default to random valid joint - configuration constrained by the joint limits of the robot) - :param ilimit: maximum number of iterations per search - :param slimit: maximum number of search attempts - :param tol: final error tolerance - :param reject_jl: constrain the solution to being within the joint limits of - the robot (reject solution with invalid joint configurations and perform - another search up to the slimit) - :param we: a mask vector which weights the end-effector error priority. - Corresponds to translation in X, Y and Z and rotation about X, Y and Z - respectively - :param λ: value of lambda for the damping matrix Wn - - :return: inverse kinematic solution - :rtype: tuple (q, success, iterations, searches, residual) - - ``sol = ets.ik_lm_chan(Tep)`` are the joint coordinates (n) corresponding - to the robot end-effector pose ``Tep`` which is an ``SE3`` or ``ndarray`` object. - This method can be used for robots with any number of degrees of freedom. - The return value ``sol`` is a tuple with elements: - - ============== ========== =============================================== - Element Type Description - ============== ========== =============================================== - ``q`` ndarray(n) joint coordinates in units of radians or metres - ``success`` int whether a solution was found - ``iterations`` int total number of iterations - ``searches`` int total number of searches - ``residual`` float final value of cost function - ============== ========== =============================================== - - If ``success == 0`` the ``q`` values will be valid numbers, but the - solution will be in error. The amount of error is indicated by - the ``residual``. - - **Joint Limits**: - - ``sol = robot.ikine_LM(T, slimit=100)`` which is the deafualt for this method. - The solver will initialise a solution attempt with a random valid q0 and - perform a maximum of ilimit steps within this attempt. If a solution is not - found, this process is repeated up to slimit times. - - **Global search**: - - ``sol = robot.ikine_LM(T, reject_jl=True)`` is the deafualt for this method. - By setting reject_jl to True, the solver will discard any solution which - violates the defined joint limits of the robot. The solver will then - re-initialise with a new random q0 and repeat the process up to slimit times. - Note that finding a solution with valid joint coordinates takes longer than - without. - - **Underactuated robots:** - - For the case where the manipulator has fewer than 6 DOF the - solution space has more dimensions than can be spanned by the - manipulator joint coordinates. - - In this case we specify the ``we`` option where the ``we`` vector - (6) specifies the Cartesian DOF (in the wrist coordinate frame) that - will be ignored in reaching a solution. The we vector has six - elements that correspond to translation in X, Y and Z, and rotation - about X, Y and Z respectively. The value can be 0 (for ignore) - or above to assign a priority relative to other Cartesian DoF. The number - of non-zero elements must equal the number of manipulator DOF. - - For example when using a 3 DOF manipulator tool orientation might - be unimportant, in which case use the option ``we=[1, 1, 1, 0, 0, 0]``. - - - - .. note:: - - - See `Toolbox kinematics wiki page `_ - - Implements a Levenberg-Marquadt variable-damping solver. - - The tolerance is computed on the norm of the error between - current and desired tool pose. This norm is computed from - distances and angles without any kind of weighting. - - The inverse kinematic solution is generally not unique, and - depends on the initial guess ``q0``. - - :references: - TODO - - :seealso: - TODO - """ - - return self.ets().ik_gn( # type: ignore[attr-defined] - Tep, q0, ilimit, slimit, tol, reject_jl, we, use_pinv, pinv_damping - ) - def ikine_LM( self, Tep: np.ndarray | SE3, From fc5ca49330655c054f312b0a9976e84dd8c539e7 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:09:58 +1000 Subject: [PATCH 4/6] fix(examples): repair ik_exp.py to use the real ik_NR/ik_GN/ik_LM API Called the now-removed dead ets.ik_nr/ik_gn/ik_lm_chan/ik_lm_wampler/ ik_lm_sugihara methods, which never existed on ETS in the first place (same root cause as the DHRobot dead-method removal). Rewired to the real ik_NR/ik_GN/ik_LM(method=...) API with matching parameter names, switched from raw 5-tuple unpacking to IKSolution attribute access (the old unpacking would have silently broken now that these methods return a 6-field IKSolution instead of a 5-tuple), and dropped several entirely unused imports (fknm, swift, spatialgeometry, sys, and unused typing names). Co-Authored-By: Claude Sonnet 5 --- examples/ik_exp.py | 160 +++++++++------------------------------------ 1 file changed, 30 insertions(+), 130 deletions(-) diff --git a/examples/ik_exp.py b/examples/ik_exp.py index bb2693bb6..d83d91e18 100644 --- a/examples/ik_exp.py +++ b/examples/ik_exp.py @@ -1,17 +1,7 @@ import numpy as np import roboticstoolbox as rtb -import spatialmath as sm -import fknm -import time -import swift -import spatialgeometry as sg -import sys from ansitable import ANSITable -from numpy import ndarray -from spatialmath import SE3 -from typing import Union, overload, List, Set - # Our robot and ETS robot = rtb.models.Panda() ets = robot.ets() @@ -42,7 +32,7 @@ tol = 1e-6 # Reject solutions with invalid joint limits -reject_jl = True +joint_limits = True class IK: @@ -64,164 +54,74 @@ def __init__(self, name, solve, problems=problems): solvers = [ - # IK( - # "Newton Raphson", - # lambda Tep: ets.ik_nr( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # use_pinv=False, - # pinv_damping=0.0, - # ), - # ), - # IK( - # "Gauss Newton", - # lambda Tep: ets.ik_gn( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # use_pinv=False, - # pinv_damping=0.0, - # ), - # ), IK( "Newton Raphson Pinv", - lambda Tep: ets.ik_nr( + lambda Tep: ets.ik_NR( Tep, q0=None, ilimit=ilimit, slimit=slimit, tol=tol, - reject_jl=reject_jl, - we=we, - use_pinv=True, + joint_limits=joint_limits, + mask=we, + pinv=True, pinv_damping=0.0, ), ), IK( "Gauss Newton Pinv", - lambda Tep: ets.ik_gn( + lambda Tep: ets.ik_GN( Tep, q0=None, ilimit=ilimit, slimit=slimit, tol=tol, - reject_jl=reject_jl, - we=we, - use_pinv=True, + joint_limits=joint_limits, + mask=we, + pinv=True, pinv_damping=0.0, ), ), IK( "LM Chan 0.1", - lambda Tep: ets.ik_lm_chan( + lambda Tep: ets.ik_LM( Tep, q0=None, ilimit=ilimit, slimit=slimit, tol=tol, - reject_jl=reject_jl, - we=we, - λ=0.1, + joint_limits=joint_limits, + mask=we, + k=0.1, + method="chan", ), ), - # IK( - # "LM Chan 1.0", - # lambda Tep: ets.ik_lm_chan( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # λ=1.0, - # ), - # ), - # IK( - # "LM Wampler", - # lambda Tep: ets.ik_lm_wampler( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # λ=1e-2, - # ), - # ), IK( "LM Wampler 1e-4", - lambda Tep: ets.ik_lm_wampler( + lambda Tep: ets.ik_LM( Tep, q0=None, ilimit=ilimit, slimit=slimit, tol=tol, - reject_jl=reject_jl, - we=we, - λ=1e-4, + joint_limits=joint_limits, + mask=we, + k=1e-4, + method="wampler", ), ), - # IK( - # "LM Wampler 1e-6", - # lambda Tep: ets.ik_lm_wampler( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # λ=1e-6, - # ), - # ), - # IK( - # "LM Sugihara 0.001", - # lambda Tep: ets.ik_lm_sugihara( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # λ=0.001, - # ), - # ), - # IK( - # "LM Sugihara 0.01", - # lambda Tep: ets.ik_lm_sugihara( - # Tep, - # q0=None, - # ilimit=ilimit, - # slimit=slimit, - # tol=tol, - # reject_jl=reject_jl, - # we=we, - # λ=0.01, - # ), - # ), IK( "LM Sugihara 0.1", - lambda Tep: ets.ik_lm_sugihara( + lambda Tep: ets.ik_LM( Tep, q0=None, ilimit=ilimit, slimit=slimit, tol=tol, - reject_jl=reject_jl, - we=we, - λ=0.1, + joint_limits=joint_limits, + mask=we, + k=0.1, + method="sugihara", ), ), ] @@ -230,13 +130,13 @@ def __init__(self, name, solve, problems=problems): print(i + 1) for solver in solvers: - _, success, iterations, searches, residual = solver.solve(Tep[i]) + sol = solver.solve(Tep[i]) - if success: - solver.success[i] = success - solver.iterations[i] = iterations - solver.searches[i] = searches - solver.residual[i] = residual + if sol.success: + solver.success[i] = sol.success + solver.iterations[i] = sol.iterations + solver.searches[i] = sol.searches + solver.residual[i] = sol.residual solver.total_iterations += solver.iterations[i] solver.total_searches += solver.searches[i] else: From d9d4603ba0fd107ef867a18c20e3e1f8991bfe38 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:10:03 +1000 Subject: [PATCH 5/6] docs: fix Levenberg-Marquardt spelling in IK docs Co-Authored-By: Claude Sonnet 5 --- docs/source/IK/ik_lm.rst | 4 ++-- docs/source/intro.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/IK/ik_lm.rst b/docs/source/IK/ik_lm.rst index 41d337add..b047b7e4e 100644 --- a/docs/source/IK/ik_lm.rst +++ b/docs/source/IK/ik_lm.rst @@ -1,5 +1,5 @@ -IK_LM - Levemberg-Marquadt Numerical IK ---------------------------------------- +IK_LM - Levenberg-Marquardt Numerical IK +---------------------------------------- .. currentmodule:: roboticstoolbox.robot.IK diff --git a/docs/source/intro.rst b/docs/source/intro.rst index 1e09fb779..0d789771b 100644 --- a/docs/source/intro.rst +++ b/docs/source/intro.rst @@ -103,7 +103,7 @@ All robots can generate a random joint configuration informed by joint limits, i >>> puma.random_q() ``ikine_LM`` is a generalised iterative numerical solution based on -Levenberg-Marquadt minimization, and additional status results are also +Levenberg-Marquardt minimization, and additional status results are also returned as part of a named tuple. .. warning:: From c3449bb718154795c0628fb348411d41a06c54b9 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Wed, 26 Aug 2026 21:10:10 +1000 Subject: [PATCH 6/6] test(ik): add regression tests for IKSolution/ik_XX return-type changes Covers IKSolution.__getitem__/__repr__, and that ik_LM/ik_NR/ik_GN now return real IKSolution instances rather than a bare tuple. Co-Authored-By: Claude Sonnet 5 --- tests/test_IK.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_IK.py b/tests/test_IK.py index 7c5858fef..a6627d726 100644 --- a/tests/test_IK.py +++ b/tests/test_IK.py @@ -828,6 +828,51 @@ def test_sol_print5(self): self.assertEqual(s, ans) + def test_getitem_iksol(self): + sol = rtb.IKSolution( + np.array([1.0, 2.0, 3.0]), + success=True, + iterations=10, + searches=100, + residual=0.1, + reason="ok", + ) + + nt.assert_almost_equal(sol[0], np.array([1.0, 2.0, 3.0])) # type: ignore + self.assertEqual(sol[1], True) + self.assertEqual(sol[2], 10) + self.assertEqual(sol[3], 100) + self.assertEqual(sol[4], 0.1) + self.assertEqual(sol[5], "ok") + + def test_repr_iksol(self): + sol = rtb.IKSolution(np.array([1.0, 2.0, 3.0]), success=True) + self.assertEqual(repr(sol), str(sol)) + + def test_ik_LM_returns_iksolution(self): + panda = rtb.models.Panda().ets() + Tep = panda.eval([0, -0.3, 0, -2.2, 0, 2.0, np.pi / 4]) + + sol = panda.ik_LM(Tep) + self.assertIsInstance(sol, rtb.IKSolution) + self.assertIsInstance(sol.success, bool) + + def test_ik_NR_returns_iksolution(self): + panda = rtb.models.Panda().ets() + Tep = panda.eval([0, -0.3, 0, -2.2, 0, 2.0, np.pi / 4]) + + sol = panda.ik_NR(Tep) + self.assertIsInstance(sol, rtb.IKSolution) + self.assertIsInstance(sol.success, bool) + + def test_ik_GN_returns_iksolution(self): + panda = rtb.models.Panda().ets() + Tep = panda.eval([0, -0.3, 0, -2.2, 0, 2.0, np.pi / 4]) + + sol = panda.ik_GN(Tep) + self.assertIsInstance(sol, rtb.IKSolution) + self.assertIsInstance(sol.success, bool) + def test_iter_iksol(self): sol = rtb.IKSolution( np.array([1.0, 2.0, 3.0]),