From 75e77160403a66f3e41938e5fa8347677e1755c2 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Wed, 19 Aug 2026 08:47:09 +0900 Subject: [PATCH] fix: honour flip in DHLink.A() when the joint ET is not last DHLink.A() decided whether to negate the joint variable by checking self.ets[-1].isflip. For a link with a nonzero a, d or alpha, _to_ets() places the joint ET before the constant ETs, so self.ets[-1] is a constant ET whose isflip is always False. As a result flip=True was silently ignored for such links and q was not negated, so DHRobot.fkine disagreed with the link's own ETS. For example RevoluteDH(a=1.0, flip=True).A(0.5).A[1, 0] returned +sin(0.5) instead of -sin(0.5). Check self.isflip (the joint ET's flip, via Link.isflip -> self.v.isflip) instead of the last ET. When the joint ET is last (a = d = alpha = 0) the two are equal, so previously-correct links are unaffected. Fixes #563. --- src/roboticstoolbox/robot/DHLink.py | 2 +- tests/test_DHRobot.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/roboticstoolbox/robot/DHLink.py b/src/roboticstoolbox/robot/DHLink.py index 4b570901e..fbe2921c6 100644 --- a/src/roboticstoolbox/robot/DHLink.py +++ b/src/roboticstoolbox/robot/DHLink.py @@ -616,7 +616,7 @@ def A(self, q: float) -> SE3: sa = _sin(self.alpha) ca = _cos(self.alpha) - if self.ets[-1].isflip: + if self.isflip: q = -q + self.offset else: q = q + self.offset diff --git a/tests/test_DHRobot.py b/tests/test_DHRobot.py index f76706561..8c9cf42d6 100644 --- a/tests/test_DHRobot.py +++ b/tests/test_DHRobot.py @@ -1564,6 +1564,17 @@ def test_alpha(self): nt.assert_array_almost_equal(r0.alpha, np.r_[1, 0, -1, 1, -1, 0] * math.pi / 2) + def test_flip_with_nonzero_a(self): + # Issue #563: DHLink.A() honoured flip only when the joint ET happened + # to be the last ET. For a link with a nonzero a/d/alpha the joint ET is + # not last, so flip=True was silently ignored and q was not negated. + q = 0.5 + L = rp.RevoluteDH(a=1.0, flip=True) + # A(q) must agree with the link's own ETS evaluation, which honours flip. + nt.assert_array_almost_equal(L.A(q).A, L.ets.eval([q])) + # The rotation uses the negated angle, so the [1, 0] entry is sin(-q). + nt.assert_almost_equal(L.A(q).A[1, 0], math.sin(-q)) + def test_ets(self): panda = rp.models.DH.Panda() panda.ets()