roboticstoolbox-python 1.4.0__py3-none-any.whl → 1.4.1__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.
@@ -61,6 +61,9 @@ __all__ = [
61
61
  "rtb_path_to_datafile",
62
62
  "rtb_set_param",
63
63
  "rtb_get_param",
64
+ "TrChainToken",
65
+ "trchain",
66
+ "trchain2",
64
67
  # mobile
65
68
  "VehicleBase",
66
69
  "Bicycle",
@@ -9,7 +9,7 @@ from spatialmath.pose2d import SE2
9
9
  from spatialmath import base
10
10
  from scipy.ndimage import *
11
11
  import matplotlib.pyplot as plt
12
- from matplotlib import cm
12
+ from matplotlib import colormaps
13
13
  from roboticstoolbox.mobile.PlannerBase import PlannerBase
14
14
 
15
15
 
@@ -324,9 +324,7 @@ def distancexform(occgrid, goal, metric="cityblock", animate=False, summary=Fals
324
324
  plt.ylabel("y")
325
325
  ax = plt.gca()
326
326
  plt.pause(0.001)
327
- cmap = cm.get_cmap("gray")
328
- cmap.set_bad("red")
329
- cmap.set_over("white")
327
+ cmap = colormaps.get_cmap("gray").with_extremes(bad="red", over="white")
330
328
  h = plt.imshow(display, cmap=cmap)
331
329
  plt.colorbar(label="distance")
332
330
  else:
@@ -90,6 +90,31 @@ def _load_rd_module(robot_name: str):
90
90
  "URDF model."
91
91
  )
92
92
 
93
+ # robot_descriptions clones a git repository (via GitPython, which shells
94
+ # out to a real git binary) the first time a given model is imported.
95
+ # Pyodide/JupyterLite has no subprocess execution and no git binary, so
96
+ # this always fails there -- not a bug, an environment limitation.
97
+ # Checked up front, before the candidates loop below, rather than caught
98
+ # per-attempt: GitPython's failure in this sandbox surfaces as a plain
99
+ # ImportError (message: "emscripten does not support processes"), which
100
+ # the loop's `except ImportError` treats as "this candidate name doesn't
101
+ # exist, try the next one" -- so after exhausting every candidate it fell
102
+ # through to a misleading "model not found"/"renamed" error instead of
103
+ # this one. The outcome here is deterministic regardless of which
104
+ # candidate name is tried, so there is nothing to gain by attempting the
105
+ # loop at all on this platform.
106
+ if sys.platform == "emscripten":
107
+ raise ValueError(
108
+ f"Toolbox uses {_rd_link()} to provide URDF robot models, "
109
+ "which clones a git repository on first use. That isn't "
110
+ "possible in this browser (Pyodide/JupyterLite) sandbox -- "
111
+ f'this is an expected limitation loading "{robot_name}" '
112
+ "here, not a bug. Try a DH- or ETS-based model instead "
113
+ "(e.g. rtb.models.DH.Panda()), or run this notebook in a "
114
+ "regular Python environment to use robot_descriptions-"
115
+ "backed models."
116
+ )
117
+
93
118
  candidates = [f"{robot_name}_description", f"{robot_name}_official_description"]
94
119
  last_error: ImportError | None = None
95
120
  for candidate in candidates:
@@ -103,26 +128,6 @@ def _load_rd_module(robot_name: str):
103
128
  except ImportError as e:
104
129
  last_error = e
105
130
  continue
106
- except Exception as e:
107
- # robot_descriptions clones a git repository (via GitPython,
108
- # which shells out to a real git binary) the first time a given
109
- # model is imported. Pyodide/JupyterLite has no subprocess
110
- # execution and no git binary, so this always fails there --
111
- # not a bug, an environment limitation. The exact exception type
112
- # depends on how GitPython fails in that sandbox, so this is
113
- # deliberately broad, but only ever intercepts on Pyodide.
114
- if sys.platform == "emscripten":
115
- raise ValueError(
116
- f"Toolbox uses {_rd_link()} to provide URDF robot models, "
117
- "which clones a git repository on first use. That isn't "
118
- "possible in this browser (Pyodide/JupyterLite) sandbox -- "
119
- f'this is an expected limitation loading "{robot_name}" '
120
- "here, not a bug. Try a DH- or ETS-based model instead "
121
- "(e.g. rtb.models.DH.Panda()), or run this notebook in a "
122
- "regular Python environment to use robot_descriptions-"
123
- "backed models."
124
- ) from e
125
- raise
126
131
 
127
132
  renamed_to = _find_rd_rename(robot_name, candidates)
128
133
  if renamed_to is not None:
@@ -2225,11 +2225,13 @@ class BaseRobot(SceneNode, DynamicsMixin, RobotPlottingMPLMixin, ABC, Generic[Li
2225
2225
  already_blocked = env._add_teach_panel(self, q, handle, block)
2226
2226
 
2227
2227
  if vellipse:
2228
- vell = self.vellipse(q, centre="ee", scale=0.5, add=False)
2228
+ vell = self.vellipse(q, centre="ee", add=False)
2229
+ env._teach_vellipse = vell
2229
2230
  env.add(vell)
2230
2231
 
2231
2232
  if fellipse:
2232
2233
  fell = self.fellipse(q, centre="ee", add=False)
2234
+ env._teach_fellipse = fell
2233
2235
  env.add(fell)
2234
2236
 
2235
2237
  # Keep the plot open -- skipped if the backend already blocked
@@ -616,7 +616,7 @@ class DHLink(Link):
616
616
  sa = _sin(self.alpha)
617
617
  ca = _cos(self.alpha)
618
618
 
619
- if self.ets[-1].isflip:
619
+ if self.isflip:
620
620
  q = -q + self.offset
621
621
  else:
622
622
  q = q + self.offset
@@ -1256,21 +1256,23 @@ class DynamicsMixin:
1256
1256
  :returns: Operational space accelerations of the end-effector
1257
1257
  :rtype: ndarray(6,)
1258
1258
 
1259
- ``xdd = accel_x(q, qd, wrench)`` is the operational space acceleration
1259
+ ``xdd = accel_x(q, xd, wrench)`` is the operational space acceleration
1260
1260
  due to ``wrench`` applied to the end-effector of a robot in joint
1261
- configuration ``q`` and joint velocity ``qd``.
1261
+ configuration ``q`` and operational space velocity ``xd``.
1262
1262
 
1263
1263
  .. math::
1264
1264
 
1265
- \ddot{x} = \mathbf{J}(q) \mathbf{M}(q)^{-1} \left(
1266
- \mathbf{J}(q)^T w - \mathbf{C}(q)\dot{q} - \mathbf{g}(q)
1265
+ \ddot{x} = \dot{\mathbf{J}}_a(q, \dot{q})\dot{q}
1266
+ + \mathbf{J}_a(q) \mathbf{M}(q)^{-1} \left(
1267
+ \mathbf{J}_a(q)^T w - \mathbf{C}(q)\dot{q}
1268
+ - \mathbf{g}(q)
1267
1269
  \right)
1268
1270
 
1269
1271
  **Trajectory operation**
1270
1272
 
1271
- If `q`, `qd`, torque are matrices (m,n) then ``qdd`` is a matrix (m,n)
1272
- where each row is the acceleration corresponding to the equivalent rows
1273
- of q, qd, wrench.
1273
+ If ``q`` is a matrix (m,n), and ``xd`` and ``wrench`` are matrices
1274
+ (m,6), then ``xdd`` is a matrix (m,6) where each row is the acceleration
1275
+ corresponding to the equivalent rows of ``q``, ``xd``, and ``wrench``.
1274
1276
 
1275
1277
  .. rubric:: Notes
1276
1278
 
@@ -1294,7 +1296,7 @@ class DynamicsMixin:
1294
1296
  if q.shape[1] != 6:
1295
1297
  pinv = True
1296
1298
 
1297
- xdd = np.zeros((q.shape[0], self.n))
1299
+ xdd = np.zeros((q.shape[0], 6))
1298
1300
 
1299
1301
  for k, (qk, xdk, wk) in enumerate(zip(q, xd, w)):
1300
1302
  Ja = self.jacob0_analytical(qk, representation=representation)
@@ -1322,16 +1324,10 @@ class DynamicsMixin:
1322
1324
 
1323
1325
  # xd = Ja qd
1324
1326
  # xdd = Jad qd + Ja qdd
1325
- #
1326
- # Ja = T J
1327
- # Jad = Td J + T Jd
1328
- # assume Td = 0, not sure how valid that is
1329
-
1330
- # need Jacobian dot
1331
1327
  qdk = Ji @ xdk
1332
- Jd = self.jacob0_dot(qk, qdk, J0=Ja)
1328
+ Jad = self.jacob0_dot(qk, qdk, representation=representation)
1333
1329
 
1334
- xdd[k, :] = T @ (Jd @ qdk + J @ qdd)
1330
+ xdd[k, :] = Jad @ qdk + Ja @ qdd
1335
1331
 
1336
1332
  if q.shape[0] == 1:
1337
1333
  return xdd[0, :]
@@ -62,12 +62,12 @@ class RobotPlottingMPLMixin:
62
62
 
63
63
  """
64
64
 
65
- from matplotlib import cm, colors
65
+ from matplotlib import colormaps, colors
66
66
 
67
- if isinstance(linkcolors, list) and len(linkcolors) == self.n: # type: ignore[attr-defined] # pragma: nocover
67
+ if isinstance(linkcolors, list) and len(linkcolors) == self.n: # type: ignore[attr-defined]
68
68
  return colors.ListedColormap(linkcolors)
69
- else: # pragma: nocover
70
- return cm.get_cmap(linkcolors, 6) # type: ignore[arg-type]
69
+ else:
70
+ return colormaps.get_cmap(linkcolors).resampled(6)
71
71
 
72
72
  # ------------------------------------------------------------------
73
73
  # Ellipse creation
@@ -22,6 +22,7 @@ from roboticstoolbox.tools.data import (
22
22
  )
23
23
  from roboticstoolbox.tools.plot import xplot
24
24
  from roboticstoolbox.tools.params import rtb_set_param, rtb_get_param
25
+ from roboticstoolbox.tools.trchain import TrChainToken, trchain, trchain2
25
26
  from roboticstoolbox.tools.types import ArrayLike, NDArray, PyArrayLike
26
27
 
27
28
  __all__ = [
@@ -48,6 +49,9 @@ __all__ = [
48
49
  "rtb_path_to_datafile",
49
50
  "rtb_set_param",
50
51
  "rtb_get_param",
52
+ "TrChainToken",
53
+ "trchain",
54
+ "trchain2",
51
55
  "PyArrayLike",
52
56
  "ArrayLike",
53
57
  "NDArray",
@@ -0,0 +1,233 @@
1
+ """Evaluate chains of elementary homogeneous transforms."""
2
+
3
+ import ast
4
+ import operator
5
+ import re
6
+ from collections.abc import Callable, Mapping, Sequence
7
+ from typing import NamedTuple
8
+
9
+ import numpy as np
10
+ from spatialmath.base import symbolic as sym
11
+ from spatialmath.base import transl, transl2, trot2, trotx, troty, trotz
12
+
13
+
14
+ class TrChainToken(NamedTuple):
15
+ """A parsed elementary-transform token.
16
+
17
+ :param op: elementary-transform operator
18
+ :param arg: expression inside the operator's parentheses
19
+ :param index: one-based joint index, or zero for a constant expression
20
+ """
21
+
22
+ op: str
23
+ arg: str
24
+ index: int
25
+
26
+
27
+ _TOKEN_RE = re.compile(r"(?P<op>R.?|T.)\(")
28
+ _BINARY_OPERATORS: dict[type[ast.operator], Callable[[object, object], object]] = {
29
+ ast.Add: operator.add,
30
+ ast.Sub: operator.sub,
31
+ ast.Mult: operator.mul,
32
+ ast.Div: operator.truediv,
33
+ ast.FloorDiv: operator.floordiv,
34
+ ast.Mod: operator.mod,
35
+ ast.Pow: operator.pow,
36
+ }
37
+ _UNARY_OPERATORS: dict[type[ast.unaryop], Callable[[object], object]] = {
38
+ ast.UAdd: operator.pos,
39
+ ast.USub: operator.neg,
40
+ }
41
+ _DEFAULT_VALUES = {
42
+ "pi": np.pi,
43
+ "sin": sym.sin,
44
+ "cos": sym.cos,
45
+ "tan": sym.tan,
46
+ "sqrt": sym.sqrt,
47
+ }
48
+
49
+ _Transform = Callable[[object, str], np.ndarray]
50
+ _TRANSFORMS_3D: dict[str, _Transform] = {
51
+ "Rx": lambda value, unit: trotx(value, unit),
52
+ "Ry": lambda value, unit: troty(value, unit),
53
+ "Rz": lambda value, unit: trotz(value, unit),
54
+ "Tx": lambda value, _unit: transl(value, 0, 0),
55
+ "Ty": lambda value, _unit: transl(0, value, 0),
56
+ "Tz": lambda value, _unit: transl(0, 0, value),
57
+ }
58
+ _TRANSFORMS_2D: dict[str, _Transform] = {
59
+ "R": lambda value, unit: trot2(value, unit),
60
+ "Rz": lambda value, unit: trot2(value, unit),
61
+ "Tx": lambda value, _unit: transl2(value, 0),
62
+ "Ty": lambda value, _unit: transl2(0, value),
63
+ }
64
+
65
+
66
+ def _parse(chain: str, qvar: str) -> tuple[TrChainToken, ...]:
67
+ q_re = re.compile(rf"\b{re.escape(qvar)}([1-9][0-9]*)\b")
68
+ tokens = []
69
+ position = 0
70
+
71
+ while match := _TOKEN_RE.search(chain, position):
72
+ depth = 1
73
+ for position in range(match.end(), len(chain)):
74
+ if chain[position] == "(":
75
+ depth += 1
76
+ elif chain[position] == ")":
77
+ depth -= 1
78
+ if depth == 0:
79
+ break
80
+ else:
81
+ raise ValueError(f"unclosed transform {match['op']!r}")
82
+
83
+ arg = chain[match.end() : position]
84
+ indices = q_re.findall(arg)
85
+ if len(indices) > 1:
86
+ raise ValueError("only one joint variable is allowed in each transform")
87
+ tokens.append(TrChainToken(match["op"], arg, int(indices[0]) if indices else 0))
88
+ position += 1
89
+
90
+ return tuple(tokens)
91
+
92
+
93
+ def _eval_expression(expression: str, values: Mapping[str, object]) -> object:
94
+ try:
95
+ tree = ast.parse(expression, mode="eval")
96
+ except SyntaxError as exc:
97
+ raise ValueError(f"cannot evaluate expression {expression!r}") from exc
98
+
99
+ def evaluate(node: ast.AST) -> object:
100
+ if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
101
+ return node.value
102
+ if isinstance(node, ast.Name):
103
+ try:
104
+ return values[node.id]
105
+ except KeyError as exc:
106
+ raise ValueError(
107
+ f"unknown name {node.id!r} in expression {expression!r}"
108
+ ) from exc
109
+ if isinstance(node, ast.BinOp) and type(node.op) in _BINARY_OPERATORS:
110
+ return _BINARY_OPERATORS[type(node.op)](
111
+ evaluate(node.left), evaluate(node.right)
112
+ )
113
+ if isinstance(node, ast.UnaryOp) and type(node.op) in _UNARY_OPERATORS:
114
+ return _UNARY_OPERATORS[type(node.op)](evaluate(node.operand))
115
+ if (
116
+ isinstance(node, ast.Call)
117
+ and isinstance(node.func, ast.Name)
118
+ and not node.keywords
119
+ ):
120
+ function = values.get(node.func.id)
121
+ if callable(function):
122
+ return function(*(evaluate(arg) for arg in node.args))
123
+ raise ValueError(f"unsupported expression {expression!r}")
124
+
125
+ return evaluate(tree.body)
126
+
127
+
128
+ def _evaluate_chain(
129
+ chain: str | Sequence[TrChainToken],
130
+ q: Sequence[object] | np.ndarray | None,
131
+ unit: str,
132
+ qvar: str,
133
+ variables: Mapping[str, object] | None,
134
+ transforms: Mapping[str, _Transform],
135
+ size: int,
136
+ ) -> tuple[np.ndarray, tuple[TrChainToken, ...]]:
137
+ if unit not in {"rad", "deg"}:
138
+ raise ValueError("unit must be 'rad' or 'deg'")
139
+ if not qvar.isidentifier():
140
+ raise ValueError("qvar must be a valid identifier")
141
+
142
+ if isinstance(chain, str):
143
+ tokens = _parse(chain, qvar)
144
+ else:
145
+ tokens = tuple(chain)
146
+ if not all(isinstance(token, TrChainToken) for token in tokens):
147
+ raise TypeError("chain must be a string or a sequence of TrChainToken")
148
+
149
+ q_values = np.asarray(()) if q is None else np.asarray(q).reshape(-1)
150
+ values = {**_DEFAULT_VALUES, **(variables or {})}
151
+ T = np.eye(size)
152
+
153
+ for token in tokens:
154
+ try:
155
+ transform = transforms[token.op]
156
+ except KeyError as exc:
157
+ raise ValueError(f"unknown transform {token.op!r}") from exc
158
+
159
+ token_values = values
160
+ if token.index:
161
+ if token.index > len(q_values):
162
+ raise ValueError("q has insufficient values")
163
+ token_values = {
164
+ **values,
165
+ f"{qvar}{token.index}": q_values[token.index - 1],
166
+ }
167
+
168
+ T = T @ transform(_eval_expression(token.arg, token_values), unit)
169
+
170
+ return T, tokens
171
+
172
+
173
+ def trchain(
174
+ chain: str | Sequence[TrChainToken],
175
+ q: Sequence[object] | np.ndarray | None = None,
176
+ unit: str = "rad",
177
+ *,
178
+ qvar: str = "q",
179
+ variables: Mapping[str, object] | None = None,
180
+ return_tokens: bool = False,
181
+ ) -> np.ndarray | tuple[np.ndarray, tuple[TrChainToken, ...]]:
182
+ """Compound SE(3) elementary transforms from a string.
183
+
184
+ :param chain: transform chain or tokens returned by an earlier call
185
+ :param q: joint values referenced as ``q1``, ``q2``, and so on
186
+ :param unit: angular unit, ``"rad"`` or ``"deg"``
187
+ :param qvar: joint-variable prefix
188
+ :param variables: values and functions used by token expressions
189
+ :param return_tokens: also return the parsed tokens for reuse
190
+ :returns: the SE(3) matrix, optionally followed by its parsed tokens
191
+ :rtype: ndarray(4, 4) or tuple
192
+
193
+ The chain contains ``Rx``, ``Ry``, ``Rz``, ``Tx``, ``Ty``, and ``Tz``
194
+ tokens. Expressions support arithmetic, named values, ``pi``, and direct
195
+ calls to named functions. Names other than joint variables are supplied by
196
+ ``variables``.
197
+
198
+ :seealso: :func:`trchain2`, :class:`roboticstoolbox.ETS`
199
+ """
200
+ result = _evaluate_chain(chain, q, unit, qvar, variables, _TRANSFORMS_3D, 4)
201
+ return result if return_tokens else result[0]
202
+
203
+
204
+ def trchain2(
205
+ chain: str | Sequence[TrChainToken],
206
+ q: Sequence[object] | np.ndarray | None = None,
207
+ unit: str = "rad",
208
+ *,
209
+ qvar: str = "q",
210
+ variables: Mapping[str, object] | None = None,
211
+ return_tokens: bool = False,
212
+ ) -> np.ndarray | tuple[np.ndarray, tuple[TrChainToken, ...]]:
213
+ """Compound SE(2) elementary transforms from a string.
214
+
215
+ :param chain: transform chain or tokens returned by an earlier call
216
+ :param q: joint values referenced as ``q1``, ``q2``, and so on
217
+ :param unit: angular unit, ``"rad"`` or ``"deg"``
218
+ :param qvar: joint-variable prefix
219
+ :param variables: values and functions used by token expressions
220
+ :param return_tokens: also return the parsed tokens for reuse
221
+ :returns: the SE(2) matrix, optionally followed by its parsed tokens
222
+ :rtype: ndarray(3, 3) or tuple
223
+
224
+ The chain contains ``R`` (or ``Rz``), ``Tx``, and ``Ty`` tokens.
225
+ Expressions are evaluated as described by :func:`trchain`.
226
+
227
+ :seealso: :func:`trchain`, :class:`roboticstoolbox.ETS2`
228
+ """
229
+ result = _evaluate_chain(chain, q, unit, qvar, variables, _TRANSFORMS_2D, 3)
230
+ return result if return_tokens else result[0]
231
+
232
+
233
+ __all__ = ["TrChainToken", "trchain", "trchain2"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: roboticstoolbox-python
3
- Version: 1.4.0
3
+ Version: 1.4.1
4
4
  Summary: A Python library for robotics education and research
5
5
  Keywords: python,robotics,robotics-toolbox,kinematics,dynamics,motion-planning,trajectory-generation,jacobian,hessian,control,simulation,robot-manipulator,mobile-robot
6
6
  Author-Email: Jesse Haviland <j.haviland@qut.edu.au>, Peter Corke <rvc@petercorke.com>
@@ -33,6 +33,7 @@ Classifier: Programming Language :: Python :: 3.10
33
33
  Classifier: Programming Language :: Python :: 3.11
34
34
  Classifier: Programming Language :: Python :: 3.12
35
35
  Classifier: Programming Language :: Python :: 3.13
36
+ Classifier: Programming Language :: Python :: 3.14
36
37
  Project-URL: homepage, https://petercorke.github.io/robotics-toolbox-python/
37
38
  Project-URL: documentation, https://petercorke.github.io/robotics-toolbox-python/
38
39
  Project-URL: repository, https://github.com/petercorke/robotics-toolbox-python
@@ -1,4 +1,4 @@
1
- roboticstoolbox/__init__.py,sha256=XBSwVd_-stPceD89wai-tVOg2LXHOek8WLqIYEJgRpw,1921
1
+ roboticstoolbox/__init__.py,sha256=D_IIMDfJ3IFnjkA1BS9ujcmIvBU5fCtJ4ETPNX-o1Uo,1972
2
2
  roboticstoolbox/backends/Connector.py,sha256=oIIoFQdCgCyRvv6dlaCSopYmBRG7h-sUmlT_9Qf6Vkg,2655
3
3
  roboticstoolbox/backends/Dynamixel/README.md,sha256=XSljkUIzB6DIyfih5RS-TlM_MFxHAE3VDr5yzQ8TgOM,289
4
4
  roboticstoolbox/backends/Dynamixel/dynamixel.json,sha256=au-Yh-LPG1Tm9nMRxWrziatPs3ZSziGLdTam98JMVXQ,14181
@@ -480,7 +480,7 @@ roboticstoolbox/ets/fknm.py,sha256=vuZ_eEJiob3pvsjCBgH_Bq8rfc_NVXLCRECkSKTA0G4,1
480
480
  roboticstoolbox/mobile/Animations.py,sha256=Z6VneDvf0fW0gFYQXVLoqBvq3qDKfK-y1cOa18S_cWs,15929
481
481
  roboticstoolbox/mobile/Bug2.py,sha256=tp7NLJBHJcH_W43YQv_zeTBClLfwDbTOjtrHjvDCoOM,14510
482
482
  roboticstoolbox/mobile/CurvaturePolyPlanner.py,sha256=vSqcsPxGwzeDzDbr6itKFUaKByth8jkynQs6UESdBjE,5464
483
- roboticstoolbox/mobile/DistanceTransformPlanner.py,sha256=avs9BIHIUZbGFxUy6VOJuBo5LjFBo5BfVuOKbVfQGk8,12246
483
+ roboticstoolbox/mobile/DistanceTransformPlanner.py,sha256=Nf4E-dBp5StzdLIk__8m_CNFedtbWXgJPVj1rGsPk5I,12224
484
484
  roboticstoolbox/mobile/DstarPlanner.py,sha256=eZvgw3zYUPgc5SjgMzBd7No3m20xCRySgj89u4fnNB0,19219
485
485
  roboticstoolbox/mobile/DubinsPlanner.py,sha256=HH62OZR4QTL1cUUVcrJsy7nCXNqHnikUH0ytp81tnSc,14087
486
486
  roboticstoolbox/mobile/EKF.py,sha256=4ZJvnSNULUQNf67spJVJTmzse9NIC1aeAbsz-kfekJE,52390
@@ -548,7 +548,7 @@ roboticstoolbox/models/URDF/Puma560.py,sha256=1WHxYNXkYgJ6TJ9xJpJzfkZlfHZA2N9HFS
548
548
  roboticstoolbox/models/URDF/UR10.py,sha256=OIw8-f3diIytx9M4FKofNOlduEDRiYTVQZdvZjY5Zo4,1073
549
549
  roboticstoolbox/models/URDF/UR3.py,sha256=T0pd-KtNQYoqmONkfMaqWKZSA6WS3Wq0JzS3NIL1lvg,1066
550
550
  roboticstoolbox/models/URDF/UR5.py,sha256=DDLzthyJ0fyylxijTZ14X_KZIYsCgLO1S6Pk5vecQZ0,1671
551
- roboticstoolbox/models/URDF/URDFRobot.py,sha256=XVL-mVMcj98RGPe6OvTX634cnWBmxN_EDWHFfEYHCJk,15158
551
+ roboticstoolbox/models/URDF/URDFRobot.py,sha256=cEyhWSozFhvQwJsYZ8hhGjkByzChGjHWXahTjiK6a7k,15441
552
552
  roboticstoolbox/models/URDF/Valkyrie.py,sha256=7LLgVRXFZ_NM5lksNW7jx6jugSDEJ4uylqqqfwkbPP0,4133
553
553
  roboticstoolbox/models/URDF/YuMi.py,sha256=IZQWtVrolGdhug08bm7rk5esirIdas4L_VBuWl0yupc,3245
554
554
  roboticstoolbox/models/URDF/__init__.py,sha256=G4PNTfYRiYekdxssVHN8v3zPUWPhbcOrffJR9u6lWDo,1599
@@ -563,11 +563,11 @@ roboticstoolbox/models/URDF/wx250.py,sha256=1vu7dnbnG72P46d0ThP5Yx0I56aCl-sPXYll
563
563
  roboticstoolbox/models/URDF/wx250s.py,sha256=A_BptSxGEBHXLauvB6TepFYHS5owvlHOPxpJbDK8QPA,1254
564
564
  roboticstoolbox/models/__init__.py,sha256=HTx6EusezeH5Qq2wDwDqaI6JCcp9cDWeaYAgQL62VmI,245
565
565
  roboticstoolbox/models/catalog.py,sha256=R-Lc_vHoOfae-afVS1olmoPu9cq7YTDfYqtCJeM3W9g,8694
566
- roboticstoolbox/robot/BaseRobot.py,sha256=yNBzD6XXKZ6Da4tZbQZR-OxhITU_k_qP9CBpqyy9vVg,82209
566
+ roboticstoolbox/robot/BaseRobot.py,sha256=WK2pSU9Oqi_ARQtzv78wA-a4xK3NDyddnjZKqi3hfQo,82276
567
567
  roboticstoolbox/robot/DHFactor.py,sha256=Bf-j7IvBKRA6_k3uev_ceQRMAC79D7W0Ir7kCJt4mec,15981
568
- roboticstoolbox/robot/DHLink.py,sha256=QhVUKdEiqC_dhIc7rwpDP5cGgU7A4dW_WTx4T-de-Ts,27823
568
+ roboticstoolbox/robot/DHLink.py,sha256=_7FtGDuS_dVxFGUQx9-Vqj7EoLsMIhV9SwVDFpWBPWs,27815
569
569
  roboticstoolbox/robot/DHRobot.py,sha256=AzGahOI9zs5A-3H5klUASKyaP_N1on9QGJ_TzTTpIJw,91705
570
- roboticstoolbox/robot/Dynamics.py,sha256=Uqud9jdiujcJAv5Fqxrw-oUL-EIdOkGnWiO85VQhBls,52475
570
+ roboticstoolbox/robot/Dynamics.py,sha256=iCplz4L1wT2OeXuS0acO9x8h4uxsifT67PV4TViUwZw,52479
571
571
  roboticstoolbox/robot/ELink.py,sha256=sgA-1c-U0Gz6TxABSJvfauq2L0Wu_1_pGJVKfLgrGP8,525
572
572
  roboticstoolbox/robot/ERobot.py,sha256=aUnSg91PwHyIHO6ipWtnNBNLYCftKGIxgYz1qX8wghg,567
573
573
  roboticstoolbox/robot/Gripper.py,sha256=CYyXFBYbEp52-RX-b6xGWS3EH-Y2BsYdtaR8Q0yAm3s,6954
@@ -576,7 +576,7 @@ roboticstoolbox/robot/Link.py,sha256=m98nfpv61C5Z1GDrOV_iN6gxyh9qj8URwiju9ssKXxw
576
576
  roboticstoolbox/robot/PoERobot.py,sha256=mByMjb3P5FrW3Z8zyLS5waYcKBy8lpHG44g2uBUyB70,11053
577
577
  roboticstoolbox/robot/Robot.py,sha256=cIixw_nx1ZzpzSVL_GzT6Hlf-HABK9aE856DPFWKGoM,77812
578
578
  roboticstoolbox/robot/RobotKinematics.py,sha256=4aXOGhc2oGOCbr0tB4ruT1Od5nJMV5WL9Q3tmb2IU74,58631
579
- roboticstoolbox/robot/RobotPlottingMPL.py,sha256=uLm3NlaPACty_3M9dfzsu4WGuONGEz7duIPWuBWzza4,14247
579
+ roboticstoolbox/robot/RobotPlottingMPL.py,sha256=tmIGievDZwtLkd0To0Of2MeAB38yvbUd8T0bF0qwLd8,14207
580
580
  roboticstoolbox/robot/RobotProto.py,sha256=os5bJSXp4ZO-ANJjaSF8_Xb-TJxbCg1Ry_obS3A6LqM,4339
581
581
  roboticstoolbox/robot/__init__.py,sha256=RUeji1GTnAL3U3mn0ae6W1lnmvC-CFlUw5U7RTQnvO8,1237
582
582
  roboticstoolbox/robot/cpp-extensions/README.md,sha256=hmXhlk7PVPmaLMXMmM1_IiI-AQpUKFqMyPvdx9kt0OU,1235
@@ -588,7 +588,7 @@ roboticstoolbox/robot/cpp-extensions/vmath.h,sha256=CEYvZMjVCNFrJQXts32iKskcliXh
588
588
  roboticstoolbox/robot/frne.py,sha256=PjUxGEl3dRcPe-PZJwsH3LivGha6bgo7JJjhwOxTcZ8,1810
589
589
  roboticstoolbox/tools/DHFactor.py,sha256=y_UOLTnFEAZJU7Vw2wPBREOxx5zph1VMXxVELGb8zFc,11815
590
590
  roboticstoolbox/tools/Ticker.py,sha256=MxEWyDn1eAd6keZtyhfts2LmMe8rt-AUh3kN9zBtAKc,1204
591
- roboticstoolbox/tools/__init__.py,sha256=iMWqOImcGgR5VhgTNZh9W_AZKwmcoKP0mZOllK3fBzk,1335
591
+ roboticstoolbox/tools/__init__.py,sha256=PRmXLt-qi26A25_c5XwvR6JSAUlkWFlJUI8wWb-cNM8,1460
592
592
  roboticstoolbox/tools/data.py,sha256=ms51jPRCETDr_AGV1-OG-xocGIPOblHUYu-wekkq56o,5550
593
593
  roboticstoolbox/tools/jsingu.py,sha256=4drWqPGNz4VBnMIcTxqkExmTiKu6v49r9yRzC7WDF4Q,1398
594
594
  roboticstoolbox/tools/null.py,sha256=ZBBM4GN9f0d2xBaXpJL8n51_tEjeJuFhxfPjOpUJvNk,1507
@@ -597,14 +597,15 @@ roboticstoolbox/tools/p_servo.py,sha256=KpFoe2sgeYG_plr2kTwb0nRlVfKKCdaGR82NnD-1
597
597
  roboticstoolbox/tools/params.py,sha256=2DRBgmjVFxulh-5u8rAiiqYQQycZEsAEoho4lVX0CoY,151
598
598
  roboticstoolbox/tools/plot.py,sha256=THXEaz_R-J57FhL3gq_rdRVz9B6UykZQYxbIEvtkGv8,2896
599
599
  roboticstoolbox/tools/trajectory.py,sha256=D0gJwuFvpkuYkVJkXwLRJYJaROwwad85-2JWBMkkISA,34214
600
+ roboticstoolbox/tools/trchain.py,sha256=p6MUo8m1Ydnjmu0SBtUWe-ODNO-s4WkLbqu7teI2V6A,8185
600
601
  roboticstoolbox/tools/types.py,sha256=d9Oqs44fio7vI4myf4s4tqgoX83YDLqU_LOH7T4E6FM,231
601
602
  roboticstoolbox/tools/urdf/__init__.py,sha256=tlo9oTXh3evQweOP3Hch-u3GiL_2NWavWHDBnk7BfKY,693
602
603
  roboticstoolbox/tools/urdf/tests/data/ur5.urdf,sha256=9g0uYkRHRsWHWl6vn5lIK_JMHqhaUOi4HaBanuXco4A,11927
603
604
  roboticstoolbox/tools/urdf/tests/test_urdf.py,sha256=MoRP8DolFu7RXxrKkwEWJOSfVygX7UJwEv-hLZkdlKw,3445
604
605
  roboticstoolbox/tools/urdf/urdf.py,sha256=IE5EUSNjkOAZZUOkuhXKDhYRq2AuJGulIvfbsRHy7pI,58743
605
606
  roboticstoolbox/tools/urdf/utils.py,sha256=6g1uuI2u7CUaWhKavI7wDHT3a2T3ijb92JByQ1lNq6w,1483
606
- roboticstoolbox_python-1.4.0.dist-info/METADATA,sha256=16-BiOiSk9VmdYxId4q2mu1qGqHA7kWPscyOI_3tmIo,30164
607
- roboticstoolbox_python-1.4.0.dist-info/WHEEL,sha256=DJBbB-IMWy7eyPvSdm5L2t-BbrULtweJWf0YC-blkl0,95
608
- roboticstoolbox_python-1.4.0.dist-info/entry_points.txt,sha256=m9xEI6rJsYQL0HosyeS-4TUPzxOR3kqx4vbJVVxwo7k,214
609
- roboticstoolbox_python-1.4.0.dist-info/licenses/LICENSE,sha256=oXNGHTPcMKl8PPZWk_4NIWGAtiaQcTgVJ1hpnTcnKFA,1062
610
- roboticstoolbox_python-1.4.0.dist-info/RECORD,,
607
+ roboticstoolbox_python-1.4.1.dist-info/METADATA,sha256=77rLsFDnkDFxePDwO-G0X0KaUElESjFTeSYCTEObUy0,30215
608
+ roboticstoolbox_python-1.4.1.dist-info/WHEEL,sha256=DJBbB-IMWy7eyPvSdm5L2t-BbrULtweJWf0YC-blkl0,95
609
+ roboticstoolbox_python-1.4.1.dist-info/entry_points.txt,sha256=m9xEI6rJsYQL0HosyeS-4TUPzxOR3kqx4vbJVVxwo7k,214
610
+ roboticstoolbox_python-1.4.1.dist-info/licenses/LICENSE,sha256=oXNGHTPcMKl8PPZWk_4NIWGAtiaQcTgVJ1hpnTcnKFA,1062
611
+ roboticstoolbox_python-1.4.1.dist-info/RECORD,,