diff --git a/docs/source/conf.py b/docs/source/conf.py
index 42e18296c..d06799bd0 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -198,6 +198,7 @@
:format: html
.. |BlockOptions| replace:: :raw-html:`common Block options`
.. |GraphicsBlockOptions| replace:: :raw-html:`common GraphicsBlock options`
+.. |ikargs| replace:: additional keyword arguments accepted by the underlying numerical IK solver -- ``ilimit``, ``slimit``, ``tol``, ``mask``, ``joint_limits``, ``seed``, ``k``, ``method``, ``kq``, ``km`` -- see :meth:`~roboticstoolbox.ETS.ikine_LM` for details of each
"""
# -------- Suppress common noisy warnings ----------------------------------------#
diff --git a/examples/ikine_evaluate.py b/examples/ikine_evaluate.py
index d4c4c2f80..8888a8051 100644
--- a/examples/ikine_evaluate.py
+++ b/examples/ikine_evaluate.py
@@ -31,31 +31,25 @@
# - the statement to execute for timeit
ikfuncs = [
(
- robot.ikine_LM, # Levenberg-Marquadt
+ lambda T, q0: robot.ikine_LM(T, q0=q0), # Levenberg-Marquadt
"ikine_LM",
- "sol = robot.ikine_LM(T, q0)",
+ "sol = robot.ikine_LM(T, q0=q0)",
),
(
- robot.ikine_LMS, # Levenberg-Marquadt (Sugihara)
- "ikine_LMS",
- "sol = robot.ikine_LMS(T, q0)",
+ lambda T, q0: robot.ikine_LM(T, q0=q0, method="sugihara", k=0.0001),
+ "ikine_LM (sugihara)",
+ "sol = robot.ikine_LM(T, q0=q0, method='sugihara', k=0.0001)",
),
(
- robot.ikine_min, # numerical solution with no constraints
- "ikine_min(qlim=False)",
- "sol = robot.ikine_min(T, q0)",
+ lambda T, q0: robot.ikine_LM(T, q0=q0, joint_limits=False),
+ "ikine_LM(qlim=False)",
+ "sol = robot.ikine_LM(T, q0=q0, joint_limits=False)",
),
(
- lambda T, q0: robot.ikine_min(
- T, q0, qlim=True
- ), # numerical solution with constraints
- "ikine_min(qlim=True)",
- "sol = robot.ikine_min(T, q0, qlim=True)",
+ lambda T, q0: robot.ikine_LM(T, q0=q0, joint_limits=True),
+ "ikine_LM(qlim=True)",
+ "sol = robot.ikine_LM(T, q0=q0, joint_limits=True)",
),
- # (robot.ikine_mmc, #numerical solution with no constraints
- # "ikine_min(qlim=False)",
- # "sol = robot.ikine_min(T, q0)"
- # ),
]
if hasattr(robot, "ikine_a"):
a = (
diff --git a/examples/readme.py b/examples/readme.py
index ddfc1f790..86563ff1c 100644
--- a/examples/readme.py
+++ b/examples/readme.py
@@ -11,7 +11,9 @@
# IK
T = SE3(0.7, 0.2, 0.1) * SE3.OA([0, 1, 0], [0, 0, -1])
-sol = robot.ikine_LMS(T) # solve IK, ignore additional outputs
+sol = robot.ikine_LM(
+ T, method="sugihara", k=0.0001
+) # solve IK, ignore additional outputs
print(sol.q) # display joint angles
# FK shows that desired end-effector pose was achieved
print(robot.fkine(sol.q))
diff --git a/src/roboticstoolbox/robot/DHRobot.py b/src/roboticstoolbox/robot/DHRobot.py
index 5a04c8490..d1c0133fb 100644
--- a/src/roboticstoolbox/robot/DHRobot.py
+++ b/src/roboticstoolbox/robot/DHRobot.py
@@ -2526,23 +2526,24 @@ def ikine_LM(
self,
Tep: np.ndarray | SE3,
q0: ArrayLike | None = None,
- ilimit: int = 30,
- slimit: int = 100,
- tol: float = 1e-6,
- joint_limits: bool = False,
- mask: ArrayLike | None = None,
- seed: int | None = None,
+ **ikargs,
):
- return self.ets().ikine_LM(
- Tep=Tep,
- q0=q0,
- ilimit=ilimit,
- slimit=slimit,
- tol=tol,
- joint_limits=joint_limits,
- mask=mask,
- seed=seed,
- )
+ """
+ Numerical inverse kinematics by Levenberg-Marquadt optimization
+
+ :param Tep: The desired end-effector pose
+ :param q0: The initial joint coordinate vector
+ :param ikargs: |ikargs|
+ :returns: An IKSolution containing joint coordinates ``q``, ``success`` flag, ``iterations``, ``searches``, ``residual`` error value, and ``reason`` string if applicable
+ :rtype: IKSolution
+
+ This is a thin wrapper around :meth:`ETS.ikine_LM` -- see that
+ method for the full description of the Levenberg-Marquadt solver
+ and its ``method``/``kq``/``km`` variants.
+
+ .. seealso:: :meth:`ikine_LM` (:class:`~roboticstoolbox.ETS`)
+ """
+ return self.ets().ikine_LM(Tep=Tep, q0=q0, **ikargs)
class SerialLink(DHRobot):
diff --git a/tests/test_DHRobot.py b/tests/test_DHRobot.py
index 6b2357fe3..ea058fc3d 100644
--- a/tests/test_DHRobot.py
+++ b/tests/test_DHRobot.py
@@ -1029,14 +1029,18 @@ def test_ikine_LM(self):
self.assertTrue(sol.success)
self.assertAlmostEqual(np.linalg.norm(T - puma.fkine(sol.q)), 0, places=4)
- # def test_ikine_LMS(self):
- # puma = rp.models.DH.Puma560()
+ def test_ikine_LM_ikargs_forwarding(self):
+ # DHRobot.ikine_LM forwards **ikargs straight through to
+ # ETS.ikine_LM -- confirm a non-default method/k actually reaches
+ # the underlying solver (regression: DHRobot.ikine_LM used to have
+ # its own narrow signature that silently dropped method/k/kq/km).
+ puma = rp.models.DH.Puma560()
- # T = puma.fkine(puma.qn)
+ T = puma.fkine(puma.qn)
- # sol = puma.ikine_LM(T)
- # self.assertTrue(sol.success)
- # self.assertAlmostEqual(np.linalg.norm(T - puma.fkine(sol.q)), 0, places=6)
+ sol = puma.ikine_LM(T, method="sugihara", k=0.0001, tol=1e-10, seed=0)
+ self.assertTrue(sol.success)
+ self.assertAlmostEqual(np.linalg.norm(T - puma.fkine(sol.q)), 0, places=4)
# def test_ikine_unc(self):
# puma = rp.models.DH.Puma560()