URDF to DH Frame Conversions¶
Overview¶
Each link in a URDF is connected by joint elements. The joint element describes a homogeneous transformation that will take coordinates in the child link’s coordinate frame and transform them into the parent link’s coordinate frame. The joint elements also give the axis of the joint with respect to the origin of the child link’s coordinate frame.
![Relationship of link and joint URDF elements](http://wiki.ros.org/urdf/XML/joint?action=AttachFile&do=get&target=joint.png)
(From wiki.ros.org/urdf/XML/joint)
Starting with the assumption that the root frame of the URDF and the root frame of the DH parameterization are equivalent, our problem becomes determining the DH parameterization that also describes the child link.
![_images/urdf_to_dh_frame.png](_images/urdf_to_dh_frame.png)
As we create the new DH frames, it becomes clear that each new joint is solved in a similar manner to the first joint. As we add DH frames, we use our knowledge of the previous frame to solve for the next DH frame in the tree.
![_images/next_urdf_to_dh_frame.png](_images/next_urdf_to_dh_frame.png)
Cases¶
Using the framework above, there are four different cases to evaluate. The URDF joint axis with respect to the DH parent frame’s z-axis can be:
- Collinear
- Parallel
- Intersecting
- Skew
Collinear¶
The simplest case, take the z-axis (remember the parent frame is assumed to already be DH parameterized frame) and test if it is collinear given the origin of the child frame and a vector describing the joint axis.
def are_collinear(point1, vec1, point2, vec2):
"""Determine if vectors are collinear."""
# To be collinear, vectors must be parallel
if not are_parallel(vec1, vec2):
return False
# If parallel and point1 is coincident with point2, vectors are collinear
if all(np.isclose(point1, point2)):
return True
# If vectors are parallel, point2 can be defined as p2 = p1 + t * v1
t = np.zeros(3)
for idx in range(0, 3):
if vec1[idx] != 0:
t[idx] = (point2[idx] - point1[idx]) / vec1[idx]
p2 = point1 + t * vec1
return np.allclose(p2, point2)
![_images/collinear_case.png](_images/collinear_case.png)
The DH Parameters for this frame are then:
Parameter | Value |
---|---|
d | Distance to child origin |
\(\theta\) | Free parameter [1] |
a | 0 |
\(\alpha\) | 0 or \(\pi\) |
[1] | Typically left as 0 |