Skip to content

Commit 671fbbf

Browse files
committed
Correct joint and link definition
Sorry this is a very big commit, I couldn't think of how to simplify it. Signed-off-by: elian-WSL22H <elian.neppel@posteo.eu>
1 parent 307fad9 commit 671fbbf

File tree

1 file changed

+135
-28
lines changed

1 file changed

+135
-28
lines changed

roboticstoolbox/tools/urdf/urdf.py

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@author (Adapted by) Jesse Haviland
55
"""
66

7+
from typing import Optional, Tuple
78
import numpy as np
89
import roboticstoolbox as rtb
910
import spatialgeometry as gm
@@ -121,6 +122,78 @@ def _find_standard_joint(joint: "Joint") -> "rtb.ET | None":
121122
std_joint = rtb.ET.SE3(SE3())
122123
return std_joint
123124

125+
def _find_joint_ets(
126+
joint: "Joint",
127+
parent_from_Rx_to_axis: Optional[SE3] = None,
128+
) -> Tuple["rtb.ETS", SE3]:
129+
"""
130+
Finds the ETS of a urdf joint object to be used by a Link.
131+
132+
This is based on the following fomula:
133+
ets(N) = axis(N-1).inv() * transl(N) * rot(N) * axis(N) * [Rx]
134+
where N is the current joint, and (N-1) the parent joint.
135+
136+
Attributes
137+
----------
138+
joint: Joint
139+
Joint from the urdf.
140+
Used to deduce: transl(N), rot(N), axis(N), [Rx]
141+
parent_from_Rx_to_axis: Optional[SE3]
142+
SE3 resulting from the axis orientation of the parent joint
143+
Used to deduce: axis(N-1)
144+
145+
Returns
146+
-------
147+
ets: ETS
148+
ETS representing the joint. It ends with a joint.
149+
from_Rx_to_axis: SE3
150+
SE3 representing the rotation of the axis attribute of the joint.
151+
"""
152+
joint_trans = SE3(joint.origin).t
153+
joint_rot = joint.rpy
154+
if parent_from_Rx_to_axis is None:
155+
parent_from_Rx_to_axis = SE3()
156+
157+
joint_without_axis = SE3(joint_trans) * SE3.RPY(joint_rot)
158+
159+
std_joint = _find_standard_joint(joint)
160+
is_simple_joint = std_joint is not None
161+
162+
if is_simple_joint:
163+
from_Rx_to_axis = SE3()
164+
pure_joint = std_joint
165+
else: # rotates a Rx joint onto right axis
166+
joint_axis = joint.axis
167+
axis_of_Rx = np.array([1, 0, 0], dtype=float)
168+
169+
rotation_from_Rx_to_axis = rotation_fromVec_toVec(
170+
from_this_vector=axis_of_Rx, to_this_vector=joint_axis
171+
)
172+
from_Rx_to_axis = SE3(rotation_from_Rx_to_axis)
173+
174+
if joint.joint_type in ("revolute", "continuous"):
175+
pure_joint = rtb.ET.Rx(flip=0)
176+
elif joint.joint_type == "prismatic": # I cannot test this case
177+
pure_joint = rtb.ET.tx(flip=0)
178+
else:
179+
pure_joint = rtb.ET.SE3(SE3())
180+
181+
listET = []
182+
emptySE3 = SE3()
183+
184+
# skips empty SE3
185+
if not parent_from_Rx_to_axis == emptySE3:
186+
listET.append(rtb.ET.SE3(parent_from_Rx_to_axis.inv()))
187+
listET.append(rtb.ET.SE3(joint_without_axis))
188+
if not from_Rx_to_axis == emptySE3:
189+
listET.append(rtb.ET.SE3(from_Rx_to_axis))
190+
if not joint.joint_type == "fixed":
191+
listET.append(pure_joint)
192+
193+
ets = rtb.ETS(listET)
194+
195+
return ets, from_Rx_to_axis
196+
124197
class URDFType(object):
125198
"""Abstract base class for all URDF types.
126199
This has useful class methods for automatic parsing/unparsing
@@ -1797,34 +1870,10 @@ def __init__(
17971870
# constant part of link transform
17981871
trans = SE3(joint.origin).t
17991872
rot = joint.rpy
1800-
1801-
# Check if axis of rotation/tanslation is not 1
1802-
if np.count_nonzero(joint.axis) < 2:
1803-
ets = rtb.ET.SE3(SE3(trans) * SE3.RPY(rot))
1804-
else:
1805-
# Normalise the joint axis to be along or about z axis
1806-
# Convert rest to static ETS
1807-
v = joint.axis
1808-
u, n = unitvec_norm(v)
1809-
R = angvec2r(n, u)
1810-
1811-
R_total = SE3.RPY(joint.rpy) * R
1812-
rpy = tr2rpy(R_total)
1813-
1814-
ets = rtb.ET.SE3(SE3(trans) * SE3.RPY(rpy))
1815-
1816-
joint.axis = [0, 0, 1]
1817-
1818-
var = _find_standard_joint(joint)
1819-
1820-
is_not_fixed = joint.joint_type != "fixed"
1821-
if var is not None and is_not_fixed:
1822-
ets = ets * var
1823-
1824-
if isinstance(ets, rtb.ET):
1825-
ets = rtb.ETS(ets)
1826-
1827-
childlink.ets = ets
1873+
1874+
# childlink.ets will be set later.
1875+
# This is because the fully defined parent link is required
1876+
# and this loop does not follow the parent-child order.
18281877

18291878
# joint limit
18301879
try:
@@ -1855,6 +1904,64 @@ def __init__(
18551904
# TODO, why did you put the base_link on the end?
18561905
# easy to do it here
18571906

1907+
# the childlink.ets will be set in there
1908+
self._recursive_axis_definition()
1909+
return
1910+
1911+
def _recursive_axis_definition(
1912+
self,
1913+
parentname: Optional[str] = None,
1914+
parent_from_Rx_to_axis: Optional[SE3] = None,
1915+
) -> None:
1916+
"""
1917+
Recursively sets the ets of all elinks (in place).
1918+
1919+
The ets of a link depends on the previous joint axis orientation.
1920+
In a URDF a joint is defined as the following ets:
1921+
ets = translation * rotation * axis * [Rx] * axis.inv()
1922+
where Rx is the variable joint ets, and "axis" rotates the variable joint
1923+
axis, BUT NOT the next link. Hence why Rx is rotated onto the axis, then
1924+
the rotation is canceled by axis.inv().
1925+
1926+
A Link is requiered to end with a variable ets -- this is our [Rx].
1927+
The previous formula must therefore be changed and requires recursion:
1928+
ets(N) = axis(N-1).inv() * transl(N) * rot(N) * axis(N) * [Rx]
1929+
where N is the current joint, and (N-1) the parent joint.
1930+
Chaining the ets of the second formula is equivalent to the first formula.
1931+
1932+
Attributes
1933+
----------
1934+
parentname: Optional[str]
1935+
Name of the parent link.
1936+
parent_from_Rx_to_axis: Optional[SE3]
1937+
SE3 resulting from the axis orientation of the parent joint
1938+
"""
1939+
if parentname is None:
1940+
base_link_exists = "base_link" in [j.name for j in self.elinks]
1941+
if base_link_exists:
1942+
parentname = "base_link"
1943+
else:
1944+
parentname = self.elinks[0].name
1945+
if parent_from_Rx_to_axis is None:
1946+
parent_from_Rx_to_axis = SE3()
1947+
1948+
for joint in self._joints: # search all joint with identical parent
1949+
is_parent = joint.parent == parentname
1950+
if not is_parent:
1951+
continue # skips to next joint
1952+
1953+
ets, from_Rx_to_axis = _find_joint_ets(joint, parent_from_Rx_to_axis)
1954+
1955+
for child in self.elinks: # search all link with identical child
1956+
is_child = joint.child == child.name
1957+
if not is_child:
1958+
continue # skips to next link
1959+
1960+
child.ets = ets # sets the ets of the joint
1961+
self._recursive_axis_definition(
1962+
parentname=child.name, parent_from_Rx_to_axis=from_Rx_to_axis
1963+
)
1964+
18581965
@property
18591966
def name(self):
18601967
"""str : The name of the URDF."""

0 commit comments

Comments
 (0)