Skip to content

Commit 8d48e5a

Browse files
committed
Fix rotvelxform gamma
1 parent ae458e9 commit 8d48e5a

File tree

1 file changed

+59
-84
lines changed

1 file changed

+59
-84
lines changed

spatialmath/base/transforms3d.py

+59-84
Original file line numberDiff line numberDiff line change
@@ -805,11 +805,11 @@ def oa2r(o, a=None):
805805
806806
.. note::
807807
808-
- The A vector is the only guaranteed to have the same direction in the
808+
- The A vector is the only guaranteed to have the same direction in the
809809
resulting rotation matrix
810810
- O and A do not have to be unit-length, they are normalized
811811
- O and A do not have to be orthogonal, so long as they are not parallel
812-
- The vectors O and A are parallel to the Y- and Z-axes of the
812+
- The vectors O and A are parallel to the Y- and Z-axes of the
813813
equivalent coordinate frame.
814814
815815
:seealso: :func:`~oa2tr`
@@ -861,7 +861,7 @@ def oa2tr(o, a=None):
861861
- O and A do not have to be unit-length, they are normalized
862862
- O and A do not have to be orthogonal, so long as they are not parallel
863863
- The translational part is zero.
864-
- The vectors O and A are parallel to the Y- and Z-axes of the
864+
- The vectors O and A are parallel to the Y- and Z-axes of the
865865
equivalent coordinate frame.
866866
867867
:seealso: :func:`~oa2r`
@@ -1856,11 +1856,12 @@ def exp2jac(v):
18561856
# (2.106)
18571857
E = (
18581858
np.eye(3)
1859-
+ sk * (1 - np.cos(theta)) / theta ** 2
1860-
+ sk @ sk * (theta - np.sin(theta)) / theta ** 3
1859+
+ sk * (1 - np.cos(theta)) / theta**2
1860+
+ sk @ sk * (theta - np.sin(theta)) / theta**3
18611861
)
18621862
return E
18631863

1864+
18641865
def r2x(R, representation="rpy/xyz"):
18651866
r"""
18661867
Convert SO(3) matrix to angular representation
@@ -1893,14 +1894,15 @@ def r2x(R, representation="rpy/xyz"):
18931894
r = tr2eul(R)
18941895
elif representation.startswith("rpy/"):
18951896
r = tr2rpy(R, order=representation[4:])
1896-
elif representation in ('arm', 'vehicle', 'camera'):
1897+
elif representation in ("arm", "vehicle", "camera"):
18971898
r = tr2rpy(R, order=representation)
18981899
elif representation == "exp":
18991900
r = trlog(R, twist=True)
19001901
else:
19011902
raise ValueError(f"unknown representation: {representation}")
19021903
return r
19031904

1905+
19041906
def x2r(r, representation="rpy/xyz"):
19051907
r"""
19061908
Convert angular representation to SO(3) matrix
@@ -1933,14 +1935,15 @@ def x2r(r, representation="rpy/xyz"):
19331935
R = eul2r(r)
19341936
elif representation.startswith("rpy/"):
19351937
R = rpy2r(r, order=representation[4:])
1936-
elif representation in ('arm', 'vehicle', 'camera'):
1938+
elif representation in ("arm", "vehicle", "camera"):
19371939
R = rpy2r(r, order=representation)
19381940
elif representation == "exp":
19391941
R = trexp(r)
19401942
else:
19411943
raise ValueError(f"unknown representation: {representation}")
19421944
return R
19431945

1946+
19441947
def tr2x(T, representation="rpy/xyz"):
19451948
r"""
19461949
Convert SE(3) to an analytic representation
@@ -1975,9 +1978,10 @@ def tr2x(T, representation="rpy/xyz"):
19751978
r = r2x(R, representation=representation)
19761979
return np.r_[t, r]
19771980

1981+
19781982
def x2tr(x, representation="rpy/xyz"):
19791983
r"""
1980-
Convert analytic representation to SE(3)
1984+
Convert analytic representation to SE(3)
19811985
19821986
:param x: analytic vector representation
19831987
:type x: array_like(6)
@@ -2014,19 +2018,22 @@ def rot2jac(R, representation="rpy/xyz"):
20142018
"""
20152019
DEPRECATED, use :func:`rotvelxform` instead
20162020
"""
2017-
raise DeprecationWarning('use rotvelxform instead')
2021+
raise DeprecationWarning("use rotvelxform instead")
2022+
20182023

20192024
def angvelxform(𝚪, inverse=False, full=True, representation="rpy/xyz"):
20202025
"""
20212026
DEPRECATED, use :func:`rotvelxform` instead
20222027
"""
2023-
raise DeprecationWarning('use rotvelxform instead')
2028+
raise DeprecationWarning("use rotvelxform instead")
2029+
20242030

20252031
def angvelxform_dot(𝚪, 𝚪d, full=True, representation="rpy/xyz"):
20262032
"""
20272033
DEPRECATED, use :func:`rotvelxform` instead
20282034
"""
2029-
raise DeprecationWarning('use rotvelxform_inv_dot instead')
2035+
raise DeprecationWarning("use rotvelxform_inv_dot instead")
2036+
20302037

20312038
def rotvelxform(𝚪, inverse=False, full=False, representation="rpy/xyz"):
20322039
r"""
@@ -2069,7 +2076,7 @@ def rotvelxform(𝚪, inverse=False, full=False, representation="rpy/xyz"):
20692076
============================ ========================================
20702077
20712078
If ``inverse==True`` return :math:`\mat{A}^{-1}` computed using
2072-
a closed-form solution rather than matrix inverse.
2079+
a closed-form solution rather than matrix inverse.
20732080
20742081
If ``full=True`` a block diagonal 6x6 matrix is returned which transforms analytic
20752082
velocity to spatial velocity.
@@ -2078,9 +2085,9 @@ def rotvelxform(𝚪, inverse=False, full=False, representation="rpy/xyz"):
20782085
with ``full=False``.
20792086
20802087
The analytical Jacobian is
2081-
2088+
20822089
.. math::
2083-
2090+
20842091
\mat{J}_a(q) = \mat{A}^{-1}(\Gamma)\, \mat{J}(q)
20852092
20862093
where :math:`\mat{A}` is computed with ``inverse==True`` and ``full=True``.
@@ -2099,7 +2106,7 @@ def rotvelxform(𝚪, inverse=False, full=False, representation="rpy/xyz"):
20992106
if smb.isrot(𝚪):
21002107
# passed a rotation matrix
21012108
# convert to the representation
2102-
gamma = r2x(𝚪, representation=representation)
2109+
𝚪 = r2x(𝚪, representation=representation)
21032110

21042111
if sym.issymbol(𝚪):
21052112
C = sym.cos
@@ -2207,19 +2214,16 @@ def rotvelxform(𝚪, inverse=False, full=False, representation="rpy/xyz"):
22072214
# (2.106)
22082215
A = (
22092216
np.eye(3)
2210-
+ sk * (1 - C(theta)) / theta ** 2
2211-
+ sk @ sk * (theta - S(theta)) / theta ** 3
2217+
+ sk * (1 - C(theta)) / theta**2
2218+
+ sk @ sk * (theta - S(theta)) / theta**3
22122219
)
22132220
else:
22142221
# angular velocity -> analytical rates
22152222
# (2.107)
22162223
A = (
22172224
np.eye(3)
22182225
- sk / 2
2219-
+ sk
2220-
@ sk
2221-
/ theta ** 2
2222-
* (1 - (theta / 2) * (S(theta) / (1 - C(theta))))
2226+
+ sk @ sk / theta**2 * (1 - (theta / 2) * (S(theta) / (1 - C(theta))))
22232227
)
22242228

22252229
if full:
@@ -2249,9 +2253,9 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
22492253
22502254
\dvec{x} = \mat{A}^{-1}(\Gamma) \vec{\nu}
22512255
2252-
where :math:`\dvec{x} \in \mathbb{R}^6` is analytic velocity :math:`(\vec{v}, \dvec{\Gamma})`,
2256+
where :math:`\dvec{x} \in \mathbb{R}^6` is analytic velocity :math:`(\vec{v}, \dvec{\Gamma})`,
22532257
:math:`\vec{\nu} \in \mathbb{R}^6` is spatial velocity :math:`(\vec{v}, \vec{\omega})`, and
2254-
:math:`\vec{\Gamma} \in \mathbb{R}^3` is a minimal rotational
2258+
:math:`\vec{\Gamma} \in \mathbb{R}^3` is a minimal rotational
22552259
representation.
22562260
22572261
The relationship between spatial and analytic acceleration is
@@ -2261,8 +2265,8 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
22612265
\ddvec{x} = \dmat{A}^{-1}(\Gamma, \dot{\Gamma) \vec{\nu} + \mat{A}^{-1}(\Gamma) \dvec{\nu}
22622266
22632267
and :math:`\dmat{A}^{-1}(\Gamma, \dot{\Gamma)` is computed by this function.
2264-
2265-
2268+
2269+
22662270
============================ ========================================
22672271
``representation`` Rotational representation
22682272
============================ ========================================
@@ -2303,11 +2307,10 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
23032307
-(
23042308
beta_dot * math.sin(beta) * S(gamma) / C(beta)
23052309
+ gamma_dot * C(gamma)
2306-
) / C(beta),
2307-
(
2308-
beta_dot * S(beta) * C(gamma) / C(beta)
2309-
- gamma_dot * S(gamma)
2310-
) / C(beta),
2310+
)
2311+
/ C(beta),
2312+
(beta_dot * S(beta) * C(gamma) / C(beta) - gamma_dot * S(gamma))
2313+
/ C(beta),
23112314
],
23122315
[0, -gamma_dot * S(gamma), gamma_dot * C(gamma)],
23132316
[
@@ -2328,14 +2331,10 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
23282331
Ainv_dot = np.array(
23292332
[
23302333
[
2331-
(
2332-
beta_dot * S(beta) * C(gamma) / C(beta)
2333-
- gamma_dot * S(gamma)
2334-
) / C(beta),
2335-
(
2336-
beta_dot * S(beta) * S(gamma) / C(beta)
2337-
+ gamma_dot * C(gamma)
2338-
) / C(beta),
2334+
(beta_dot * S(beta) * C(gamma) / C(beta) - gamma_dot * S(gamma))
2335+
/ C(beta),
2336+
(beta_dot * S(beta) * S(gamma) / C(beta) + gamma_dot * C(gamma))
2337+
/ C(beta),
23392338
0,
23402339
],
23412340
[-gamma_dot * C(gamma), -gamma_dot * S(gamma), 0],
@@ -2357,26 +2356,20 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
23572356
Ainv_dot = np.array(
23582357
[
23592358
[
2360-
(beta_dot * S(beta) * S(gamma) / C(beta)
2361-
+ gamma_dot * C(gamma)) / C(beta),
2359+
(beta_dot * S(beta) * S(gamma) / C(beta) + gamma_dot * C(gamma))
2360+
/ C(beta),
23622361
0,
2363-
(beta_dot * S(beta) * C(gamma) / C(beta)
2364-
- gamma_dot * S(gamma)) / C(beta)
2362+
(beta_dot * S(beta) * C(gamma) / C(beta) - gamma_dot * S(gamma))
2363+
/ C(beta),
23652364
],
2365+
[-gamma_dot * S(gamma), 0, -gamma_dot * C(gamma)],
23662366
[
2367-
-gamma_dot * S(gamma),
2367+
beta_dot * S(gamma) / C(beta) ** 2 + gamma_dot * C(gamma) * T(beta),
23682368
0,
2369-
-gamma_dot * C(gamma)
2369+
beta_dot * C(gamma) / C(beta) ** 2 - gamma_dot * S(gamma) * T(beta),
23702370
],
2371-
[
2372-
beta_dot * S(gamma) / C(beta)**2
2373-
+ gamma_dot * C(gamma) * T(beta),
2374-
0,
2375-
beta_dot * C(gamma) / C(beta)**2
2376-
- gamma_dot * S(gamma) * T(beta)
2377-
]
23782371
]
2379-
)
2372+
)
23802373

23812374
elif representation == "eul":
23822375
# autogenerated by symbolic/angvelxform.ipynb
@@ -2394,15 +2387,9 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
23942387
],
23952388
[-phi_dot * C(phi), -phi_dot * S(phi), 0],
23962389
[
2397-
-(
2398-
phi_dot * S(phi)
2399-
+ theta_dot * C(phi) * C(theta) / S(theta)
2400-
)
2390+
-(phi_dot * S(phi) + theta_dot * C(phi) * C(theta) / S(theta))
24012391
/ S(theta),
2402-
(
2403-
phi_dot * C(phi)
2404-
- theta_dot * S(phi) * C(theta) / S(theta)
2405-
)
2392+
(phi_dot * C(phi) - theta_dot * S(phi) * C(theta) / S(theta))
24062393
/ S(theta),
24072394
0,
24082395
],
@@ -2424,15 +2411,10 @@ def rotvelxform_inv_dot(𝚪, 𝚪d, full=False, representation="rpy/xyz"):
24242411
# results are close but different to numerical cross check
24252412
# something wrong in the derivation
24262413
Theta_dot = (
2427-
(
2428-
-theta * C(theta)
2429-
-S(theta) +
2430-
theta * S(theta)**2 / (1 - C(theta))
2431-
) * theta_dot / 2 / (1 - C(theta)) / theta**2
2432-
- (
2433-
2 - theta * S(theta) / (1 - C(theta))
2434-
) * theta_dot / theta**3
2435-
)
2414+
-theta * C(theta) - S(theta) + theta * S(theta) ** 2 / (1 - C(theta))
2415+
) * theta_dot / 2 / (1 - C(theta)) / theta**2 - (
2416+
2 - theta * S(theta) / (1 - C(theta))
2417+
) * theta_dot / theta**3
24362418

24372419
Ainv_dot = -0.5 * skd + 2.0 * sk @ skd * Theta + sk @ sk * Theta_dot
24382420
else:
@@ -2583,11 +2565,7 @@ def trprint(
25832565
# print the angular part in various representations
25842566

25852567
# define some aliases for rpy conventions for arms, vehicles and cameras
2586-
aliases = {
2587-
'arm': 'rpy/xyz',
2588-
'vehicle': 'rpy/zyx',
2589-
'camera': 'rpy/yxz'
2590-
}
2568+
aliases = {"arm": "rpy/xyz", "vehicle": "rpy/zyx", "camera": "rpy/yxz"}
25912569
if orient in aliases:
25922570
orient = aliases[orient]
25932571

@@ -2632,12 +2610,8 @@ def _vec2s(fmt, v):
26322610
return ", ".join([fmt.format(x) for x in v])
26332611

26342612

2635-
2636-
2637-
26382613
def trplot(
26392614
T,
2640-
26412615
color="blue",
26422616
frame=None,
26432617
axislabel=True,
@@ -2657,7 +2631,7 @@ def trplot(
26572631
dims=None,
26582632
d2=1.15,
26592633
flo=(-0.05, -0.05, -0.05),
2660-
**kwargs
2634+
**kwargs,
26612635
):
26622636
"""
26632637
Plot a 3D coordinate frame
@@ -2806,7 +2780,7 @@ def trplot(
28062780

28072781
# unpack the anaglyph parameters
28082782
if anaglyph is True:
2809-
colors = 'rc'
2783+
colors = "rc"
28102784
shift = 0.1
28112785
elif isinstance(anaglyph, tuple):
28122786
colors = anaglyph[0]
@@ -2867,7 +2841,7 @@ def trplot(
28672841
flo=flo,
28682842
anaglyph=anaglyph,
28692843
axislabel=axislabel,
2870-
**kwargs
2844+
**kwargs,
28712845
)
28722846
return
28732847

@@ -3023,6 +2997,7 @@ def trplot(
30232997
if block:
30242998
# calling this at all, causes FuncAnimation to fail so when invoked from tranimate skip this bit
30252999
import matplotlib.pyplot as plt
3000+
30263001
# TODO move blocking into graphics
30273002
plt.show(block=block)
30283003
return ax
@@ -3079,14 +3054,14 @@ def tranimate(T, **kwargs):
30793054

30803055
anim = smb.animate.Animate(**kwargs)
30813056
try:
3082-
del kwargs['dims']
3057+
del kwargs["dims"]
30833058
except KeyError:
30843059
pass
3085-
3060+
30863061
anim.trplot(T, **kwargs)
30873062
anim.run(**kwargs)
30883063

3089-
#plt.show(block=block)
3064+
# plt.show(block=block)
30903065

30913066

30923067
if __name__ == "__main__": # pragma: no cover

0 commit comments

Comments
 (0)