Skip to content

Commit 965f6fe

Browse files
committed
Merge branch 'future' of github.com:petercorke/robotics-toolbox-python into future
2 parents aef672d + deb416f commit 965f6fe

File tree

4 files changed

+166
-147
lines changed

4 files changed

+166
-147
lines changed

roboticstoolbox/models/URDF/YuMi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def __init__(self):
5353
l_gripper_links = [link for link in links if link.parent == gripper_l_base]
5454

5555
# New intermediate links
56-
r_gripper = ELink(name="r_gripper", parent=gripper_l_base)
57-
l_gripper = ELink(name="l_gripper", parent=gripper_r_base)
56+
r_gripper = ELink(name="r_hand", parent=gripper_l_base)
57+
l_gripper = ELink(name="l_hand", parent=gripper_r_base)
5858
links.append(r_gripper)
5959
links.append(l_gripper)
6060

roboticstoolbox/robot/IK.py

Lines changed: 88 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import numpy as np
77
from collections import namedtuple
8+
89
# from roboticstoolbox.tools.null import null
910
import roboticstoolbox as rtb
1011
from spatialmath import base
@@ -13,6 +14,7 @@
1314
import math
1415
import qpsolvers as qp
1516
from roboticstoolbox.tools.p_servo import p_servo
17+
1618
# iksol = namedtuple("IKsolution", "q, success, reason, iterations, residual",
1719
# defaults=(None, False, None, None, None)) # Py >= 3.7 only
1820
iksol = namedtuple("IKsolution", "q, success, reason, iterations, residual")
@@ -21,10 +23,7 @@
2123

2224

2325
class IKMixin:
24-
25-
def ikine_mmc(
26-
self, T,
27-
q0=None):
26+
def ikine_mmc(self, T, q0=None):
2827

2928
arrived = False
3029

@@ -104,20 +103,22 @@ def ikine_mmc(
104103

105104
return q
106105

107-
# --------------------------------------------------------------------- #
106+
# --------------------------------------------------------------------- #
108107

109108
def ikine_LM(
110-
self, T,
111-
q0=None,
112-
mask=None,
113-
ilimit=500,
114-
rlimit=100,
115-
tol=1e-10,
116-
L=0.1,
117-
Lmin=0,
118-
search=False,
119-
slimit=100,
120-
transpose=None):
109+
self,
110+
T,
111+
q0=None,
112+
mask=None,
113+
ilimit=500,
114+
rlimit=100,
115+
tol=1e-10,
116+
L=0.1,
117+
Lmin=0,
118+
search=False,
119+
slimit=100,
120+
transpose=None,
121+
):
121122
"""
122123
Numerical inverse kinematics by Levenberg-Marquadt optimization
123124
(Robot superclass)
@@ -245,7 +246,7 @@ def ikine_LM(
245246
""" # noqa E501
246247

247248
if not isinstance(T, SE3):
248-
raise TypeError('argument must be SE3')
249+
raise TypeError("argument must be SE3")
249250

250251
solutions = []
251252

@@ -259,7 +260,7 @@ def ikine_LM(
259260
for k in range(slimit):
260261
# choose a random joint coordinate
261262
q0_k = np.random.rand(self.n) * qspan + qlim[0, :]
262-
print('search starts at ', q0_k)
263+
print("search starts at ", q0_k)
263264

264265
# recurse into the solver
265266
solution = self.ikine_LM(
@@ -273,7 +274,8 @@ def ikine_LM(
273274
Lmin,
274275
False,
275276
slimit,
276-
transpose)
277+
transpose,
278+
)
277279

278280
if solution.success:
279281
q0 = solution.q
@@ -295,13 +297,15 @@ def ikine_LM(
295297
if mask is not None:
296298
mask = base.getvector(mask, 6)
297299
if not self.n >= np.sum(mask):
298-
raise ValueError('Number of robot DOF must be >= the number '
299-
'of 1s in the mask matrix')
300+
raise ValueError(
301+
"Number of robot DOF must be >= the number "
302+
"of 1s in the mask matrix"
303+
)
300304
else:
301305
mask = np.ones(6)
302306
W = np.diag(mask)
303307

304-
tcount = 0 # Total iteration count
308+
tcount = 0 # Total iteration count
305309
rejcount = 0 # Rejected step count
306310
nm = 0
307311
revolutes = self.revolutejoints
@@ -332,17 +336,20 @@ def ikine_LM(
332336

333337
if transpose is not None:
334338
# Do the simple Jacobian transpose with constant gain
335-
dq = transpose * J.T @ e # lgtm [py/multiple-definition]
339+
dq = transpose * J.T @ e # lgtm [py/multiple-definition]
336340
q += dq
337341
else:
338342
# Do the damped inverse Gauss-Newton with
339343
# Levenberg-Marquadt
340344
# dq = np.linalg.inv(
341345
# JtJ + ((Li + Lmin) * np.eye(self.n))
342346
# ) @ J.T @ W @ e
343-
dq = np.linalg.inv(
344-
JtJ + ((Li + Lmin) * np.diag(np.diag(JtJ)))
345-
) @ J.T @ W @ e
347+
dq = (
348+
np.linalg.inv(JtJ + ((Li + Lmin) * np.diag(np.diag(JtJ))))
349+
@ J.T
350+
@ W
351+
@ e
352+
)
346353
# print(J.T @ W @ e)
347354

348355
# Compute possible new value of
@@ -371,7 +378,7 @@ def ikine_LM(
371378
q[k] -= 2 * np.pi
372379

373380
k = np.logical_and(q < -np.pi, revolutes)
374-
q[k] += + 2 * np.pi
381+
q[k] += +2 * np.pi
375382

376383
nm = np.linalg.norm(W @ e)
377384
# qs = ", ".join(["{:8.3f}".format(qi) for qi in q])
@@ -392,20 +399,12 @@ def ikine_LM(
392399
np.array([sol.success for sol in solutions]),
393400
[sol.reason for sol in solutions],
394401
np.array([sol.iterations for sol in solutions]),
395-
np.array([sol.residual for sol in solutions])
402+
np.array([sol.residual for sol in solutions]),
396403
)
397404

398-
# --------------------------------------------------------------------- #
405+
# --------------------------------------------------------------------- #
399406

400-
def ikine_LMS(
401-
self, T,
402-
q0=None,
403-
mask=None,
404-
ilimit=500,
405-
tol=1e-10,
406-
wN=1e-3,
407-
Lmin=0
408-
):
407+
def ikine_LMS(self, T, q0=None, mask=None, ilimit=500, tol=1e-10, wN=1e-3, Lmin=0):
409408
"""
410409
Numerical inverse kinematics by Levenberg-Marquadt optimization
411410
(Robot superclass)
@@ -511,7 +510,7 @@ def ikine_LMS(
511510
""" # noqa E501
512511

513512
if not isinstance(T, SE3):
514-
raise TypeError('argument must be SE3')
513+
raise TypeError("argument must be SE3")
515514

516515
solutions = []
517516

@@ -523,8 +522,10 @@ def ikine_LMS(
523522
if mask is not None:
524523
mask = base.getvector(mask, 6)
525524
if not self.n >= np.sum(mask):
526-
raise ValueError('Number of robot DOF must be >= the number '
527-
'of 1s in the mask matrix')
525+
raise ValueError(
526+
"Number of robot DOF must be >= the number "
527+
"of 1s in the mask matrix"
528+
)
528529
else:
529530
mask = np.ones(6)
530531
W = np.diag(mask)
@@ -555,7 +556,7 @@ def ikine_LMS(
555556
J = self.jacob0(q)
556557
WN = E * np.eye(self.n) + wN * np.eye(self.n)
557558
H = J.T @ W @ J + WN # n x n
558-
g = J.T @ W @ e # n x 1
559+
g = J.T @ W @ e # n x 1
559560

560561
# Compute new value of q
561562
q += np.linalg.inv(H) @ g # n x 1
@@ -570,7 +571,7 @@ def ikine_LMS(
570571
q[k] -= 2 * np.pi
571572

572573
k = np.logical_and(q < -np.pi, revolutes)
573-
q[k] += + 2 * np.pi
574+
q[k] += +2 * np.pi
574575

575576
# qs = ", ".join(["{:8.3f}".format(qi) for qi in q])
576577
# print(f"|e|={E:8.2g}, det(H)={np.linalg.det(H)}: q={qs}")
@@ -590,14 +591,23 @@ def ikine_LMS(
590591
np.array([sol.success for sol in solutions]),
591592
[sol.reason for sol in solutions],
592593
np.array([sol.iterations for sol in solutions]),
593-
np.array([sol.residual for sol in solutions])
594+
np.array([sol.residual for sol in solutions]),
594595
)
595596

596-
# --------------------------------------------------------------------- #
597+
# --------------------------------------------------------------------- #
597598

598599
def ikine_min(
599-
self, T, q0=None, qlim=False, ilimit=1000,
600-
tol=1e-16, method=None, stiffness=0, costfun=None, options={}):
600+
self,
601+
T,
602+
q0=None,
603+
qlim=False,
604+
ilimit=1000,
605+
tol=1e-16,
606+
method=None,
607+
stiffness=0,
608+
costfun=None,
609+
options={},
610+
):
601611
r"""
602612
Inverse kinematics by optimization with joint limits (Robot superclass)
603613
@@ -703,7 +713,7 @@ def ikine_min(
703713
""" # noqa E501
704714

705715
if not isinstance(T, SE3):
706-
raise TypeError('argument must be SE3')
716+
raise TypeError("argument must be SE3")
707717

708718
if q0 is None:
709719
q0 = np.zeros((self.n))
@@ -715,35 +725,35 @@ def ikine_min(
715725
wr = 1 / self.reach
716726
weight = np.r_[wr, wr, wr, 1, 1, 1]
717727

718-
optdict = {'maxiter': ilimit}
728+
optdict = {"maxiter": ilimit}
719729
if options is not None and isinstance(options, dict):
720730
optdict.update(options)
721731
else:
722-
raise ValueError('options must be a dict')
732+
raise ValueError("options must be a dict")
723733

724734
if qlim:
725735
# dealing with joint limits
726736
bounds = opt.Bounds(self.qlim[0, :], self.qlim[1, :])
727737

728738
if method is None:
729-
method = 'trust-constr'
739+
method = "trust-constr"
730740
else:
731741
# no joint limits
732742
if method is None:
733-
method = 'SLSQP'
743+
method = "SLSQP"
734744
bounds = None
735745

736746
def cost(q, T, weight, costfun, stiffness):
737747
# T, weight, costfun, stiffness = args
738748
e = _angle_axis(self.fkine(q).A, T) * weight
739-
E = (e**2).sum()
749+
E = (e ** 2).sum()
740750

741751
if stiffness > 0:
742752
# Enforce a continuity constraint on joints, minimum bend
743-
E += np.sum(np.diff(q)**2) * stiffness
753+
E += np.sum(np.diff(q) ** 2) * stiffness
744754

745755
if costfun is not None:
746-
E += (e**2).sum() + costfun(q)
756+
E += (e ** 2).sum() + costfun(q)
747757

748758
return E
749759

@@ -755,7 +765,7 @@ def cost(q, T, weight, costfun, stiffness):
755765
bounds=bounds,
756766
method=method,
757767
tol=tol,
758-
options=options
768+
options=options,
759769
)
760770

761771
# trust-constr seems to work better than L-BFGS-B which often
@@ -772,11 +782,11 @@ def cost(q, T, weight, costfun, stiffness):
772782
else:
773783
return solutions
774784

775-
# --------------------------------------------------------------------- #
785+
# --------------------------------------------------------------------- #
776786

777787
def ikine_global(
778-
self, T, qlim=False, ilimit=1000,
779-
tol=1e-16, method=None, options={}):
788+
self, T, qlim=False, ilimit=1000, tol=1e-16, method=None, options={}
789+
):
780790
r"""
781791
.. warning:: Experimental code for using SciPy global optimizers.
782792
@@ -792,7 +802,7 @@ def ikine_global(
792802
# dual_annealing: bounds
793803

794804
if not isinstance(T, SE3):
795-
raise TypeError('argument must be SE3')
805+
raise TypeError("argument must be SE3")
796806

797807
solutions = []
798808

@@ -802,29 +812,32 @@ def ikine_global(
802812
optdict = {}
803813

804814
if method is None:
805-
method = 'differential-evolution'
815+
method = "differential-evolution"
806816

807-
if method == 'brute':
817+
if method == "brute":
808818
# requires a tuple of tuples
809-
optdict['ranges'] = tuple([tuple(li.qlim) for li in self])
819+
optdict["ranges"] = tuple([tuple(li.qlim) for li in self])
810820
else:
811-
optdict['bounds'] = tuple([tuple(li.qlim) for li in self])
821+
optdict["bounds"] = tuple([tuple(li.qlim) for li in self])
812822

813-
if method not in ['basinhopping', 'brute', 'differential_evolution',
814-
'shgo', 'dual_annealing']:
815-
raise ValueError('unknown global optimizer requested')
823+
if method not in [
824+
"basinhopping",
825+
"brute",
826+
"differential_evolution",
827+
"shgo",
828+
"dual_annealing",
829+
]:
830+
raise ValueError("unknown global optimizer requested")
816831

817832
global_minimizer = opt.__dict__[method]
818833

819834
def cost(q, T, weight):
820835
# T, weight, costfun, stiffness = args
821836
e = _angle_axis(self.fkine(q).A, T) * weight
822-
return (e**2).sum()
837+
return (e ** 2).sum()
823838

824839
for _ in T:
825-
res = global_minimizer(
826-
cost,
827-
**optdict)
840+
res = global_minimizer(cost, **optdict)
828841

829842
solution = iksol(res.x, res.success, res.message, res.nit, res.fun)
830843
solutions.append(solution)
@@ -875,14 +888,11 @@ def _angle_axis_sekiguchi(T, Td):
875888
if R[1, 0] > 0 and R[2, 1] > 0 and R[0, 2] > 0:
876889
a = np.pi / np.sqrt(2) * np.sqrt(np.diag(R) + 1)
877890
elif R[1, 0] > 0: # (2)
878-
a = np.pi / np.sqrt(2) * np.sqrt(
879-
np.diag(R) @ np.r_[1, 1, -1] + 1)
891+
a = np.pi / np.sqrt(2) * np.sqrt(np.diag(R) @ np.r_[1, 1, -1] + 1)
880892
elif R[0, 2] > 0: # (3)
881-
a = np.pi / np.sqrt(2) * np.sqrt(
882-
np.diag(R) @ np.r_[1, -1, 1] + 1)
893+
a = np.pi / np.sqrt(2) * np.sqrt(np.diag(R) @ np.r_[1, -1, 1] + 1)
883894
elif R[2, 1] > 0: # (4)
884-
a = np.pi / np.sqrt(2) * np.sqrt(
885-
np.diag(R) @ np.r_[-1, 1, 1] + 1)
895+
a = np.pi / np.sqrt(2) * np.sqrt(np.diag(R) @ np.r_[-1, 1, 1] + 1)
886896
else:
887897
# non-diagonal matrix case
888898
ln = base.norm(li)
@@ -916,4 +926,4 @@ def _angle_axis_sekiguchi(T, Td):
916926
# print(robot.fkine(sol.q))
917927
# robot.plot(sol.q)
918928

919-
sol = robot.ikine_global(T, method='brute')
929+
sol = robot.ikine_global(T, method="brute")

0 commit comments

Comments
 (0)