roboticstoolbox-python 1.4.2__py3-none-any.whl → 1.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roboticstoolbox/bin/rtbtool.py +1 -1
- roboticstoolbox/ets/cpp-extensions/ik.cpp +47 -3
- roboticstoolbox/models/DH/UR10.py +5 -0
- roboticstoolbox/models/URDF/Fetch.py +3 -0
- roboticstoolbox/models/URDF/Frankie.py +11 -2
- roboticstoolbox/models/URDF/FrankieOmni.py +10 -2
- roboticstoolbox/models/URDF/Jaco.py +3 -0
- roboticstoolbox/models/URDF/PR2.py +3 -0
- roboticstoolbox/models/URDF/Panda.py +29 -9
- roboticstoolbox/models/URDF/UR10.py +3 -0
- roboticstoolbox/models/URDF/UR3.py +3 -0
- roboticstoolbox/models/URDF/UR5.py +13 -1
- roboticstoolbox/models/URDF/URDFRobot.py +28 -2
- roboticstoolbox/models/URDF/Valkyrie.py +3 -0
- roboticstoolbox/models/URDF/YuMi.py +3 -0
- roboticstoolbox/robot/DHRobot.py +1 -1
- roboticstoolbox/robot/IK.py +68 -27
- roboticstoolbox/robot/Robot.py +117 -65
- roboticstoolbox/robot/RobotKinematics.py +59 -16
- {roboticstoolbox_python-1.4.2.dist-info → roboticstoolbox_python-1.4.4.dist-info}/METADATA +3 -122
- {roboticstoolbox_python-1.4.2.dist-info → roboticstoolbox_python-1.4.4.dist-info}/RECORD +24 -24
- {roboticstoolbox_python-1.4.2.dist-info → roboticstoolbox_python-1.4.4.dist-info}/WHEEL +0 -0
- {roboticstoolbox_python-1.4.2.dist-info → roboticstoolbox_python-1.4.4.dist-info}/entry_points.txt +0 -0
- {roboticstoolbox_python-1.4.2.dist-info → roboticstoolbox_python-1.4.4.dist-info}/licenses/LICENSE +0 -0
roboticstoolbox/bin/rtbtool.py
CHANGED
|
@@ -51,8 +51,52 @@ static void _IK_loop(
|
|
|
51
51
|
|
|
52
52
|
if (*E < tol)
|
|
53
53
|
{
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Preserve translations and coordinates already within their limits.
|
|
55
|
+
// Other revolute coordinates may move only by whole turns, so the
|
|
56
|
+
// converged end-effector pose is unchanged (see IKSolver._normalise_q).
|
|
57
|
+
int j = 0;
|
|
58
|
+
for (int i = 0; i < ets->m; i++)
|
|
59
|
+
{
|
|
60
|
+
ET *et = ets->ets[i];
|
|
61
|
+
if (!et->isjoint)
|
|
62
|
+
continue;
|
|
63
|
+
|
|
64
|
+
double lower = ets->qlim_l[j];
|
|
65
|
+
double upper = ets->qlim_h[j];
|
|
66
|
+
if (et->axis < 3 && !(lower <= q(j) && q(j) <= upper))
|
|
67
|
+
{
|
|
68
|
+
// Avoid rounding a principal angle across a nearby limit.
|
|
69
|
+
double angle = q(j);
|
|
70
|
+
if (!(angle >= -PI && angle < PI))
|
|
71
|
+
{
|
|
72
|
+
// Unlike Python's %, fmod can return a negative remainder.
|
|
73
|
+
angle = std::fmod(angle + PI, PI_x2);
|
|
74
|
+
if (angle < 0)
|
|
75
|
+
angle += PI_x2;
|
|
76
|
+
angle -= PI;
|
|
77
|
+
}
|
|
78
|
+
if (angle < lower)
|
|
79
|
+
angle += PI_x2 * std::ceil((lower - angle) / PI_x2);
|
|
80
|
+
else if (angle > upper)
|
|
81
|
+
angle -= PI_x2 * std::ceil((angle - upper) / PI_x2);
|
|
82
|
+
if (!(lower <= angle && angle <= upper))
|
|
83
|
+
{
|
|
84
|
+
// Recover rounded endpoints only if whole turns
|
|
85
|
+
// reconstruct the original coordinate exactly.
|
|
86
|
+
for (double bound : {lower, upper})
|
|
87
|
+
{
|
|
88
|
+
double turns = std::round((q(j) - bound) / PI_x2);
|
|
89
|
+
if (turns != 0 && bound + turns * PI_x2 == q(j))
|
|
90
|
+
{
|
|
91
|
+
angle = bound;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
q(j) = angle;
|
|
97
|
+
}
|
|
98
|
+
j++;
|
|
99
|
+
}
|
|
56
100
|
*solution = reject_jl ? _check_lim(ets, q) : 1;
|
|
57
101
|
break;
|
|
58
102
|
}
|
|
@@ -315,4 +359,4 @@ extern "C"
|
|
|
315
359
|
// return q;
|
|
316
360
|
}
|
|
317
361
|
|
|
318
|
-
} /* extern "C" */
|
|
362
|
+
} /* extern "C" */
|
|
@@ -27,6 +27,11 @@ class UR10(DHRobot):
|
|
|
27
27
|
|
|
28
28
|
.. note::
|
|
29
29
|
- SI units are used.
|
|
30
|
+
- The last link's inertia tensor has :math:`I_{xx} = 0` -- that's not
|
|
31
|
+
a bug in this model, it's what the manufacturer's own reference
|
|
32
|
+
(below) publishes. It makes the robot's inertia matrix near-singular
|
|
33
|
+
(``det(robot.inertia(q)) ~ 2e-6``), so dynamics work that needs a
|
|
34
|
+
well-conditioned inertia matrix should account for this.
|
|
30
35
|
|
|
31
36
|
:References:
|
|
32
37
|
|
|
@@ -47,6 +47,9 @@ class Fetch(URDFRobot):
|
|
|
47
47
|
from a URDF file. The model describes its kinematic and graphical
|
|
48
48
|
characteristics.
|
|
49
49
|
|
|
50
|
+
The model is loaded via the `robot_descriptions
|
|
51
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
52
|
+
|
|
50
53
|
.. runblock:: pycon
|
|
51
54
|
|
|
52
55
|
>>> import roboticstoolbox as rtb
|
|
@@ -13,6 +13,9 @@ class Frankie(URDFRobot):
|
|
|
13
13
|
from a URDF file. The model describes its kinematic and graphical
|
|
14
14
|
characteristics.
|
|
15
15
|
|
|
16
|
+
The model is loaded via the `robot_descriptions
|
|
17
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
18
|
+
|
|
16
19
|
.. runblock:: pycon
|
|
17
20
|
|
|
18
21
|
>>> import roboticstoolbox as rtb
|
|
@@ -23,8 +26,14 @@ class Frankie(URDFRobot):
|
|
|
23
26
|
|
|
24
27
|
- qz, zero joint angle configuration, 'L' shaped configuration
|
|
25
28
|
- qr, vertical 'READY' configuration
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
|
|
30
|
+
Loaded via the `robot_descriptions
|
|
31
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package
|
|
32
|
+
(real inertial data, plain-mesh collision geometry), unlike
|
|
33
|
+
:class:`~roboticstoolbox.models.URDF.Panda.Panda`'s bundled default. See
|
|
34
|
+
the wiki's `Panda models
|
|
35
|
+
<https://github.com/petercorke/robotics-toolbox-python/wiki/Panda-models>`_
|
|
36
|
+
page for why the two differ.
|
|
28
37
|
|
|
29
38
|
.. codeauthor:: Jesse Haviland
|
|
30
39
|
.. sectionauthor:: Peter Corke
|
|
@@ -27,8 +27,16 @@ class FrankieOmni(Robot):
|
|
|
27
27
|
|
|
28
28
|
- qz, zero joint angle configuration, 'L' shaped configuration
|
|
29
29
|
- qr, vertical 'READY' configuration
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
|
|
31
|
+
The arm is loaded from the same bundled ``qut_frankie_description`` xacro
|
|
32
|
+
as :class:`~roboticstoolbox.models.URDF.Panda.Panda`'s default (no
|
|
33
|
+
inertial data), spliced by hand onto a separately-loaded
|
|
34
|
+
``clearpath_ridgeback_description`` mobile base -- it has no
|
|
35
|
+
``use_robot_descriptions``-style option, since the base+arm splice is
|
|
36
|
+
done manually rather than through :class:`URDFRobot`'s own loader. See
|
|
37
|
+
the wiki's `Panda models
|
|
38
|
+
<https://github.com/petercorke/robotics-toolbox-python/wiki/Panda-models>`_
|
|
39
|
+
page for the full picture.
|
|
32
40
|
|
|
33
41
|
.. codeauthor:: Jesse Haviland
|
|
34
42
|
.. sectionauthor:: Peter Corke
|
|
@@ -12,6 +12,9 @@ class Jaco(URDFRobot):
|
|
|
12
12
|
from a URDF file. The model describes its kinematic and graphical
|
|
13
13
|
characteristics.
|
|
14
14
|
|
|
15
|
+
The model is loaded via the `robot_descriptions
|
|
16
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
17
|
+
|
|
15
18
|
.. runblock:: pycon
|
|
16
19
|
|
|
17
20
|
>>> import roboticstoolbox as rtb
|
|
@@ -12,6 +12,9 @@ class PR2(URDFRobot):
|
|
|
12
12
|
from a URDF file. The model describes its kinematic and graphical
|
|
13
13
|
characteristics.
|
|
14
14
|
|
|
15
|
+
The model is loaded via the `robot_descriptions
|
|
16
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
17
|
+
|
|
15
18
|
.. runblock:: pycon
|
|
16
19
|
|
|
17
20
|
>>> import roboticstoolbox as rtb
|
|
@@ -23,20 +23,40 @@ class Panda(URDFRobot):
|
|
|
23
23
|
|
|
24
24
|
- qz, zero joint angle configuration, 'L' shaped configuration
|
|
25
25
|
- qr, vertical 'READY' configuration
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
|
|
27
|
+
:param use_robot_descriptions: if ``True``, load the Panda URDF from the
|
|
28
|
+
`robot_descriptions <https://github.com/robot-descriptions/robot_descriptions.py>`_
|
|
29
|
+
package instead of the toolbox's own bundled ``qut_frankie_description``
|
|
30
|
+
xacro (the default, ``False``). The bundled model has real collision
|
|
31
|
+
geometry (a hand-built capsule approximation) but no inertial
|
|
32
|
+
(mass/CoM/inertia) data at all -- ``rne()``/``inertia()``/``coriolis()``/
|
|
33
|
+
``gravload()`` are all silently zero. The ``robot_descriptions`` model
|
|
34
|
+
has real inertial data, but its collision geometry is plain meshes,
|
|
35
|
+
which are roughly an order of magnitude slower to collision-check
|
|
36
|
+
against than the bundled model's capsules -- noticeable in a
|
|
37
|
+
real-time reactive-avoidance loop (see ``examples/neo.py``). See the
|
|
38
|
+
wiki's `Panda models <https://github.com/petercorke/robotics-toolbox-python/wiki/Panda-models>`_
|
|
39
|
+
page for the full comparison and rationale.
|
|
40
|
+
:type use_robot_descriptions: bool
|
|
28
41
|
|
|
29
42
|
.. codeauthor:: Jesse Haviland
|
|
30
43
|
.. sectionauthor:: Peter Corke
|
|
31
44
|
"""
|
|
32
45
|
|
|
33
|
-
def __init__(self):
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
def __init__(self, use_robot_descriptions: bool = False):
|
|
47
|
+
|
|
48
|
+
if use_robot_descriptions:
|
|
49
|
+
super().__init__(
|
|
50
|
+
"panda",
|
|
51
|
+
manufacturer="Franka Emika",
|
|
52
|
+
gripper_link_index=9,
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
super().__init__(
|
|
56
|
+
"qut_frankie_description/robots/panda_arm_hand.urdf.xacro",
|
|
57
|
+
manufacturer="Franka Emika",
|
|
58
|
+
gripper_link_index=9,
|
|
59
|
+
)
|
|
40
60
|
|
|
41
61
|
self.grippers[0].tool = SE3(0, 0, 0.1034)
|
|
42
62
|
|
|
@@ -12,6 +12,9 @@ class UR10(URDFRobot):
|
|
|
12
12
|
definition from a URDF file. The model describes its kinematic and
|
|
13
13
|
graphical characteristics.
|
|
14
14
|
|
|
15
|
+
The model is loaded via the `robot_descriptions
|
|
16
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
17
|
+
|
|
15
18
|
.. runblock:: pycon
|
|
16
19
|
|
|
17
20
|
>>> import roboticstoolbox as rtb
|
|
@@ -12,6 +12,9 @@ class UR3(URDFRobot):
|
|
|
12
12
|
definition from a URDF file. The model describes its kinematic and
|
|
13
13
|
graphical characteristics.
|
|
14
14
|
|
|
15
|
+
The model is loaded via the `robot_descriptions
|
|
16
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
17
|
+
|
|
15
18
|
.. runblock:: pycon
|
|
16
19
|
|
|
17
20
|
>>> import roboticstoolbox as rtb
|
|
@@ -13,6 +13,9 @@ class UR5(URDFRobot):
|
|
|
13
13
|
definition from a URDF file. The model describes its kinematic and
|
|
14
14
|
graphical characteristics.
|
|
15
15
|
|
|
16
|
+
The model is loaded via the `robot_descriptions
|
|
17
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
18
|
+
|
|
16
19
|
.. runblock:: pycon
|
|
17
20
|
|
|
18
21
|
>>> import roboticstoolbox as rtb
|
|
@@ -30,7 +33,16 @@ class UR5(URDFRobot):
|
|
|
30
33
|
|
|
31
34
|
def __init__(self):
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
# Name-based lookup, not a raw positional index: robot_descriptions
|
|
37
|
+
# 3.0.0 changed which upstream repo "ur5" resolves to, reordering
|
|
38
|
+
# links so index 7 silently pointed at a real arm joint instead of
|
|
39
|
+
# the tool attachment link (#578). tool0/ee_link/flange are stable
|
|
40
|
+
# names across the versions checked.
|
|
41
|
+
super().__init__(
|
|
42
|
+
"ur5",
|
|
43
|
+
manufacturer="Universal Robotics",
|
|
44
|
+
gripper_link_name=["tool0", "ee_link", "flange"],
|
|
45
|
+
)
|
|
34
46
|
|
|
35
47
|
# for link in links:
|
|
36
48
|
# print(link)
|
|
@@ -383,10 +383,16 @@ class URDFRobot(Robot):
|
|
|
383
383
|
super().__init__(
|
|
384
384
|
"ur5",
|
|
385
385
|
manufacturer="Universal Robotics",
|
|
386
|
-
|
|
386
|
+
gripper_link_name=["tool0", "ee_link"],
|
|
387
387
|
)
|
|
388
388
|
self.qz = np.zeros(6)
|
|
389
389
|
...
|
|
390
|
+
|
|
391
|
+
``gripper_link_name`` (a name, or a list of candidate names tried in
|
|
392
|
+
order) survives an upstream URDF reordering that would silently break a
|
|
393
|
+
raw positional ``gripper_link_index`` -- see #578. Prefer it for new
|
|
394
|
+
models; ``gripper_link_index`` remains for models where a stable,
|
|
395
|
+
well-known link name isn't available.
|
|
390
396
|
"""
|
|
391
397
|
|
|
392
398
|
def __init__(
|
|
@@ -394,6 +400,7 @@ class URDFRobot(Robot):
|
|
|
394
400
|
urdf_path: "str | Path",
|
|
395
401
|
manufacturer: str = "",
|
|
396
402
|
gripper_link_index: "int | None" = None,
|
|
403
|
+
gripper_link_name: "str | list[str] | None" = None,
|
|
397
404
|
patch: "Callable[[str], str] | None" = None,
|
|
398
405
|
extra_packages: "dict[str, str] | None" = None,
|
|
399
406
|
**kwargs,
|
|
@@ -401,7 +408,26 @@ class URDFRobot(Robot):
|
|
|
401
408
|
elinks, name, filepath = URDF_file(
|
|
402
409
|
urdf_path, patch=patch, extra_packages=extra_packages
|
|
403
410
|
)
|
|
404
|
-
if
|
|
411
|
+
if gripper_link_name is not None:
|
|
412
|
+
candidates = (
|
|
413
|
+
[gripper_link_name]
|
|
414
|
+
if isinstance(gripper_link_name, str)
|
|
415
|
+
else gripper_link_name
|
|
416
|
+
)
|
|
417
|
+
for candidate in candidates:
|
|
418
|
+
match = next(
|
|
419
|
+
(link for link in elinks if link.name == candidate), None
|
|
420
|
+
)
|
|
421
|
+
if match is not None:
|
|
422
|
+
kwargs["gripper_links"] = match
|
|
423
|
+
break
|
|
424
|
+
else:
|
|
425
|
+
raise ValueError(
|
|
426
|
+
f"none of the candidate gripper link names {candidates!r} "
|
|
427
|
+
f"were found in the parsed URDF for {name!r} (have: "
|
|
428
|
+
f"{[link.name for link in elinks]})"
|
|
429
|
+
)
|
|
430
|
+
elif gripper_link_index is not None:
|
|
405
431
|
kwargs["gripper_links"] = elinks[gripper_link_index]
|
|
406
432
|
super().__init__(elinks, name=name, manufacturer=manufacturer, **kwargs)
|
|
407
433
|
self._urdf_filepath = str(filepath) if filepath is not None else ""
|
|
@@ -41,6 +41,9 @@ class Valkyrie(Robot):
|
|
|
41
41
|
from a URDF file. The model describes its kinematic and graphical
|
|
42
42
|
characteristics.
|
|
43
43
|
|
|
44
|
+
The model is loaded via the `robot_descriptions
|
|
45
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
46
|
+
|
|
44
47
|
.. runblock:: pycon
|
|
45
48
|
|
|
46
49
|
>>> import roboticstoolbox as rtb
|
|
@@ -15,6 +15,9 @@ class YuMi(Robot):
|
|
|
15
15
|
from a URDF file. The model describes its kinematic and graphical
|
|
16
16
|
characteristics.
|
|
17
17
|
|
|
18
|
+
The model is loaded via the `robot_descriptions
|
|
19
|
+
<https://github.com/robot-descriptions/robot_descriptions.py>`_ package.
|
|
20
|
+
|
|
18
21
|
.. runblock:: pycon
|
|
19
22
|
|
|
20
23
|
>>> import roboticstoolbox as rtb
|
roboticstoolbox/robot/DHRobot.py
CHANGED
roboticstoolbox/robot/IK.py
CHANGED
|
@@ -293,43 +293,25 @@ class IKSolver(ABC):
|
|
|
293
293
|
linalg_error = 0
|
|
294
294
|
|
|
295
295
|
# Initialise variables
|
|
296
|
-
E =
|
|
296
|
+
E = np.inf
|
|
297
297
|
q = q0[0]
|
|
298
298
|
|
|
299
299
|
for search in range(self.slimit):
|
|
300
300
|
q = q0[search].copy()
|
|
301
301
|
i = 0
|
|
302
302
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
# update. An undamped update (GN/NR) can overshoot, so if E is
|
|
308
|
-
# already below tol we must return this pre-step q, not the
|
|
309
|
-
# mutated one step() hands back - otherwise we can report
|
|
310
|
-
# success with a q whose actual residual is far above tol.
|
|
311
|
-
q_prev = q.copy()
|
|
312
|
-
|
|
313
|
-
# Attempt a step
|
|
314
|
-
try:
|
|
315
|
-
E, q[ets.jindices] = self.step(ets, Tep, q)
|
|
316
|
-
|
|
317
|
-
except np.linalg.LinAlgError:
|
|
318
|
-
# Abandon search and try again
|
|
319
|
-
linalg_error += 1
|
|
320
|
-
break
|
|
303
|
+
# Check the initial configuration before attempting an update.
|
|
304
|
+
# An exact or already-converged q0 must not enter a solver step,
|
|
305
|
+
# which can be singular even though the requested pose is solved.
|
|
306
|
+
_, E = self.error(ets.eval(q), Tep)
|
|
321
307
|
|
|
322
|
-
|
|
308
|
+
while True:
|
|
309
|
+
# Check convergence for the current q before another update.
|
|
323
310
|
if E < self.tol:
|
|
324
|
-
q
|
|
325
|
-
|
|
326
|
-
# Wrap q to be within +- 180 deg
|
|
327
|
-
# If your robot has larger than 180 deg range on a joint
|
|
328
|
-
# this line should be modified in incorporate the extra range
|
|
329
|
-
q = (q + np.pi) % (2 * np.pi) - np.pi
|
|
311
|
+
self._normalise_q(ets, q)
|
|
330
312
|
|
|
331
313
|
# Check if we have violated joint limits
|
|
332
|
-
jl_valid = self._check_jl(ets, q)
|
|
314
|
+
jl_valid = self._check_jl(ets, q[ets.jindices])
|
|
333
315
|
|
|
334
316
|
if not jl_valid and self.joint_limits:
|
|
335
317
|
# Abandon search and try again
|
|
@@ -344,6 +326,21 @@ class IKSolver(ABC):
|
|
|
344
326
|
residual=E,
|
|
345
327
|
reason="Success",
|
|
346
328
|
)
|
|
329
|
+
|
|
330
|
+
if i >= self.ilimit:
|
|
331
|
+
break
|
|
332
|
+
|
|
333
|
+
i += 1
|
|
334
|
+
|
|
335
|
+
# Attempt a step. step() reports E for the updated q.
|
|
336
|
+
try:
|
|
337
|
+
E, q[ets.jindices] = self.step(ets, Tep, q)
|
|
338
|
+
|
|
339
|
+
except np.linalg.LinAlgError:
|
|
340
|
+
# Abandon search and try again
|
|
341
|
+
linalg_error += 1
|
|
342
|
+
break
|
|
343
|
+
|
|
347
344
|
total_i += i
|
|
348
345
|
|
|
349
346
|
# If we make it here, then we have failed
|
|
@@ -450,6 +447,46 @@ class IKSolver(ABC):
|
|
|
450
447
|
|
|
451
448
|
return q
|
|
452
449
|
|
|
450
|
+
def _normalise_q(self, ets: "rtb.ETS", q: np.ndarray) -> None:
|
|
451
|
+
"""
|
|
452
|
+
Choose equivalent revolute coordinates without changing prismatic joints.
|
|
453
|
+
|
|
454
|
+
:param ets: The ETS defining the joint types and limits
|
|
455
|
+
:param q: Joint coordinates, indexed by joint jindex, modified in place
|
|
456
|
+
:returns: None
|
|
457
|
+
|
|
458
|
+
Preserve coordinates already within their limits, including revolute joints
|
|
459
|
+
outside the principal interval. Otherwise prefer the principal angle, shifted
|
|
460
|
+
by whole turns towards the limits if necessary. If no equivalent angle lies
|
|
461
|
+
within the limits, the subsequent joint-limit check will reject the solution.
|
|
462
|
+
"""
|
|
463
|
+
qlim = ets.qlim
|
|
464
|
+
for i, joint in enumerate(ets.joints()):
|
|
465
|
+
j = joint.jindex
|
|
466
|
+
lower, upper = qlim[:, i]
|
|
467
|
+
if not joint.isrotation or lower <= q[j] <= upper:
|
|
468
|
+
continue
|
|
469
|
+
|
|
470
|
+
# Avoid rounding a principal angle across a nearby joint limit.
|
|
471
|
+
angle = q[j]
|
|
472
|
+
if not -np.pi <= angle < np.pi:
|
|
473
|
+
angle = (angle + np.pi) % (2 * np.pi) - np.pi
|
|
474
|
+
if angle < lower:
|
|
475
|
+
angle += 2 * np.pi * np.ceil((lower - angle) / (2 * np.pi))
|
|
476
|
+
elif angle > upper:
|
|
477
|
+
angle -= 2 * np.pi * np.ceil((angle - upper) / (2 * np.pi))
|
|
478
|
+
if not lower <= angle <= upper:
|
|
479
|
+
# Argument reduction can round a limit plus whole turns just
|
|
480
|
+
# outside the closed interval. Accept that endpoint only when
|
|
481
|
+
# it reconstructs the original coordinate exactly, without an
|
|
482
|
+
# epsilon that could admit an unrelated out-of-limit angle.
|
|
483
|
+
for bound in (lower, upper):
|
|
484
|
+
turns = np.rint((q[j] - bound) / (2 * np.pi))
|
|
485
|
+
if turns != 0 and bound + turns * (2 * np.pi) == q[j]:
|
|
486
|
+
angle = bound
|
|
487
|
+
break
|
|
488
|
+
q[j] = angle
|
|
489
|
+
|
|
453
490
|
def _check_jl(self, ets: "rtb.ETS", q: np.ndarray) -> bool:
|
|
454
491
|
"""
|
|
455
492
|
Checks if the joints are within their respective limits
|
|
@@ -709,6 +746,7 @@ class IK_NR(IKSolver):
|
|
|
709
746
|
else:
|
|
710
747
|
q[ets.jindices] += np.linalg.inv(J) @ e + qnull
|
|
711
748
|
|
|
749
|
+
_, E = self.error(ets.eval(q), Tep)
|
|
712
750
|
return E, q[ets.jindices]
|
|
713
751
|
|
|
714
752
|
|
|
@@ -941,6 +979,7 @@ class IK_LM(IKSolver):
|
|
|
941
979
|
|
|
942
980
|
q[ets.jindices] += np.linalg.inv(J.T @ self.We @ J + Wn) @ g + qnull
|
|
943
981
|
|
|
982
|
+
_, E = self.error(ets.eval(q), Tep)
|
|
944
983
|
return E, q[ets.jindices]
|
|
945
984
|
|
|
946
985
|
|
|
@@ -1121,6 +1160,7 @@ class IK_GN(IKSolver):
|
|
|
1121
1160
|
else:
|
|
1122
1161
|
q[ets.jindices] += np.linalg.inv(J) @ e + qnull
|
|
1123
1162
|
|
|
1163
|
+
_, E = self.error(ets.eval(q), Tep)
|
|
1124
1164
|
return E, q[ets.jindices]
|
|
1125
1165
|
|
|
1126
1166
|
|
|
@@ -1388,6 +1428,7 @@ class IK_QP(IKSolver):
|
|
|
1388
1428
|
|
|
1389
1429
|
q += xd[: ets.n]
|
|
1390
1430
|
|
|
1431
|
+
_, E = self.error(ets.eval(q), Tep)
|
|
1391
1432
|
return E, q
|
|
1392
1433
|
|
|
1393
1434
|
|