Marcos Duarte

Laboratory of Biomechanics and Motor Control (http://demotu.org/)

Federal University of ABC, Brazil

The kinematics of a rigid body is completely described by its pose, i.e., its position and orientation in space (and the corresponding changes are translation and rotation). The translation and rotation of a rigid body are also known as rigid-body transformations (or simply, rigid transformations).

Remember that in physics, a rigid body is a model (an idealization) for a body in which deformation is neglected, i.e., the distance between every pair of points in the body is considered constant. Consequently, the position and orientation of a rigid body can be completely described by a corresponding coordinate system attached to it. For instance, two (or more) coordinate systems can be used to represent the same rigid body at two (or more) instants or two (or more) rigid bodies in space.

Rigid-body transformations are used in motion analysis (e.g., of the human body) to describe the position and orientation of each segment (using a local (anatomical) coordinate system defined for each segment) in relation to a global coordinate system fixed at the laboratory. Furthermore, one can define an additional coordinate system called technical coordinate system also fixed at the rigid body but not based on anatomical landmarks. In this case, the position of the technical markers is first described in the laboratory coordinate system, and then the technical coordinate system is calculated to recreate the anatomical landmarks position in order to finally calculate the original anatomical coordinate system (and obtain its unknown position and orientation through time).

In what follows, we will study rigid-body transformations by looking at the transformations between two coordinate systems. For simplicity, let's first analyze planar (two-dimensional) rigid-body transformations and later we will extend these concepts to three dimensions (where the study of rotations are more complicated).

Translation and rotation are two examples of affine transformations. Affine transformations preserve straight lines, but not necessarily the distance between points. Other examples of affine transformations are scaling, shear, and reflection. The figure below illustrates different affine transformations in a plane. Note that a 3x3 matrix is shown on top of each transformation; these matrices are known as the transformation matrices and are the mathematical representation of the physical transformations. Next, we will study how to use this approach to describe the translation and rotation of a rigid-body.

In a two-dimensional space, two coordinates and one angle are sufficient to describe the pose of the rigid body, totalizing three degrees of freedom for a rigid body. Let's see first the transformation for translation, then for rotation, and combine them at last.

A pure two-dimensional translation of a coordinate system in relation to other coordinate system and the representation of a point in these two coordinate systems are illustrated in the figure below (remember that this is equivalent to describing a translation between two rigid bodies).

The position of point $\mathbf{P}$ originally described in the local coordinate system but now described in the Global coordinate system in vector form is:

$$ \mathbf{P_G} = \mathbf{L_G} + \mathbf{P_l} $$Or for each component:

$$ \mathbf{P_X} = \mathbf{L_X} + \mathbf{P}_x $$$$ \mathbf{P_Y} = \mathbf{L_Y} + \mathbf{P}_y $$And in matrix form is:

$$ \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \end{bmatrix} = \begin{bmatrix} \mathbf{L_X} \\ \mathbf{L_Y} \end{bmatrix} + \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \end{bmatrix} $$Because position and translation can be treated as vectors, the inverse operation, to describe the position at the local coordinate system in terms of the Global coordinate system, is simply:

$$ \mathbf{P_l} = \mathbf{P_G} -\mathbf{L_G} $$

$$ \begin{bmatrix}
\mathbf{P}_x \\
\mathbf{P}_y
\end{bmatrix} =
\begin{bmatrix}
\mathbf{P_X} \\
\mathbf{P_Y}
\end{bmatrix} -
\begin{bmatrix}
\mathbf{L_X} \\
\mathbf{L_Y}
\end{bmatrix} $$

From classical mechanics, this transformation is an example of Galilean transformation.

For example, if the local coordinate system is translated by $\mathbf{L_G}=[2, 3]$ in relation to the Global coordinate system, a point with coordinates $\mathbf{P_l}=[4, 5]$ at the local coordinate system will have the position $\mathbf{P_G}=[6, 8]$ at the Global coordinate system:

In [1]:

```
# Import the necessary libraries
import numpy as np
```

In [2]:

```
LG = np.array([2, 3]) # (Numpy 1D array with 2 elements)
Pl = np.array([4, 5])
PG = LG + Pl
PG
```

Out[2]:

This operation also works if we have more than one data point (NumPy knows how to handle vectors with different dimensions):

In [3]:

```
Pl = np.array([[4, 5], [6, 7], [8, 9]]) # 2D array with 3 rows and two columns
PG = LG + Pl
PG
```

Out[3]:

A pure two-dimensional rotation of a coordinate system in relation to other coordinate system and the representation of a point in these two coordinate systems are illustrated in the figure below (remember that this is equivalent to describing a rotation between two rigid bodies). The rotation is around an axis orthogonal to this page, not shown in the figure (for a three-dimensional coordinate system the rotation would be around the $\mathbf{Z}$ axis).

Consider we want to express the position of point $\mathbf{P}$ in the Global coordinate system in terms of the local coordinate system knowing only the coordinates at the local coordinate system and the angle of rotation between the two coordinate systems.

There are different ways of deducing that, we will see three of these methods next.

From figure below, the coordinates of point $\mathbf{P}$ in the Global coordinate system can be determined finding the sides of the triangles marked in red.

Then:

$$ \mathbf{P_X} = \mathbf{P}_x \cos \alpha - \mathbf{P}_y \sin \alpha $$$$ \mathbf{P_Y} = \mathbf{P}_x \sin \alpha + \mathbf{P}_y \cos \alpha $$The equations above can be expressed in matrix form:

$$ \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \end{bmatrix} = \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix} \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \end{bmatrix} $$Or simply:

$$ \mathbf{P_G} = \mathbf{R_{Gl}}\mathbf{P_l} $$Where $\mathbf{R_{Gl}}$ is the rotation matrix that rotates the coordinates from the local to the Global coordinate system:

$$ \mathbf{R_{Gl}} = \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix} $$So, given any position at the local coordinate system, with the rotation matrix above we are able to determine the position at the Global coordinate system. Let's check that before looking at other methods to obtain this matrix.

For instance, consider a local coordinate system rotated by $45^o$ in relation to the Global coordinate system, a point in the local coordinate system with position $\mathbf{P_l}=[1, 1]$ will have the following position at the Global coordinate system:

In [4]:

```
RGl = np.array([[np.cos(np.pi/4), -np.sin(np.pi/4)], [np.sin(np.pi/4), np.cos(np.pi/4)]])
Pl = np.array([[1, 1]]).T # transpose the array for correct matrix multiplication
PG = np.dot(RGl, Pl) # the function dot() is used for matrix multiplication of arrays
np.around(PG, 4) # round the number due to floating-point arithmetic errors
```

Out[4]:

We have rounded the number to 4 decimal places due to floating-point arithmetic errors in the computation.

And if we have the points [1,1], [0,1], [1,0] at the local coordinate system, their positions at the Global coordinate system are:

In [5]:

```
Pl = np.array([[1, 1], [0, 1], [1, 0]]).T # transpose array for matrix multiplication
PG = np.dot(RGl, Pl) # the function dot() is used for matrix multiplication with arrays
np.around(PG, 4) # round the number due to floating point arithmetic errors
```

Out[5]:

We have done all the calculations using the array function in NumPy. A NumPy array is different than a matrix, if we want to use explicit matrices in NumPy, the calculation above will be:

In [6]:

```
RGl = np.mat([[np.cos(np.pi/4), -np.sin(np.pi/4)], [np.sin(np.pi/4), np.cos(np.pi/4)]])
Pl = np.mat([[1, 1], [0,1], [1, 0]]).T # 2x3 matrix
PG = RGl*Pl # matrix multiplication in NumPy
np.around(PG, 4) # round the number due to floating point arithmetic errors
```

Out[6]:

Both array and matrix types work in NumPy, but you should choose only one type and not mix them; the array is preferred because it is the standard vector/matrix/tensor type of NumPy.

Another way to determine the rotation matrix is to use the concept of direction cosine.

Direction cosines are the cosines of the angles between any two vectors.

For the present case with two coordinate systems, they are the cosines of the angles between each axis of one coordinate system and each axis of the other coordinate system. The figure below illustrates the directions angles between the two coordinate systems, expressing the local coordinate system in terms of the Global coordinate system.

$$ \mathbf{R_{Gl}} = \begin{bmatrix} \cos\mathbf{X}x & \cos\mathbf{X}y \\ \cos\mathbf{Y}x & \cos\mathbf{Y}y \end{bmatrix} = \begin{bmatrix} \cos(\alpha) & \cos(90^o+\alpha) \\ \cos(90^o-\alpha) & \cos(\alpha) \end{bmatrix} = \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix} $$

The same rotation matrix as obtained before.

Note that the order of the direction cosines is because in our convention, the first row is for the $\mathbf{X}$ coordinate and the second row for the $\mathbf{Y}$ coordinate (the outputs). For the inputs, we followed the same order, first column for the $\mathbf{x}$ coordinate, second column for the $\mathbf{y}$ coordinate.

Another way to deduce the rotation matrix is to view the axes of the rotated coordinate system as unit vectors, versors, of a basis as illustrated in the figure below.

A basis is a set of linearly independent vectors that can represent every vector in a given vector space, i.e., a basis defines a coordinate system.

The coordinates of these two versors at the local coordinate system in terms of the Global coordinate system are:

$$ \begin{array}{l l} \mathbf{e}_x = \cos\alpha\:\mathbf{e_X} + \sin\alpha\:\mathbf{e_Y} \\ \mathbf{e}_y = -\sin\alpha\:\mathbf{e_X} + \cos\alpha\:\mathbf{e_Y} \end{array}$$Note that as unit vectors, each of the versors above should have norm (length) equals to one, which indeed is the case.

If we express each versor above as different columns of a matrix, we obtain the rotation matrix again:

$$ \mathbf{R_{Gl}} = \begin{bmatrix} \cos\alpha & -\sin\alpha \\\ \sin\alpha & \cos\alpha \end{bmatrix} $$This means that the rotation matrix can be viewed as the basis of the rotated coordinate system defined by its versors.

This third way to derive the rotation matrix is in fact the method most commonly used in motion analysis because the coordinates of markers (in the Global/laboratory coordinate system) are what we measure with cameras.

Yet another way to deduce the rotation matrix is to define it as the dot product between the versors of the bases related to the two coordinate systems:

$$ \mathbf{R_{Gl}} = \begin{bmatrix} \mathbf{\hat{e}_X}\! \cdot \mathbf{\hat{e}_x} & \mathbf{\hat{e}_X}\! \cdot \mathbf{\hat{e}_y} \\ \mathbf{\hat{e}_Y}\! \cdot \mathbf{\hat{e}_x} & \mathbf{\hat{e}_Y}\! \cdot \mathbf{\hat{e}_y} \end{bmatrix} $$By definition:

$$ \hat{\mathbf{e}}_1\! \cdot \hat{\mathbf{e}}_2 = ||\hat{\mathbf{e}}_1|| \times ||\hat{\mathbf{e}}_2||\cos(e_1,e_2)=\cos(e_1,e_2)$$And the rotation matrix will be equal to the matrix deduced based on the direction cosines.

If we want the inverse operation, to express the position of point $\mathbf{P}$ in the local coordinate system in terms of the Global coordinate system, the figure below illustrates that using trigonometry.

Then:

$$ \mathbf{P}_x = \;\;\mathbf{P_X} \cos \alpha + \mathbf{P_Y} \sin \alpha $$$$ \mathbf{P}_y = -\mathbf{P_X} \sin \alpha + \mathbf{P_Y} \cos \alpha $$And in matrix form:

$$ \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \end{bmatrix} = \begin{bmatrix} \cos\alpha & \sin\alpha \\ -\sin\alpha & \cos\alpha \end{bmatrix} \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \end{bmatrix} $$$$ \mathbf{P_l} = \mathbf{R_{lG}}\mathbf{P_G} $$Where $\mathbf{R_{lG}}$ is the rotation matrix that rotates the coordinates from the Global to the local coordinate system (note the inverse order of the subscripts):

$$ \mathbf{R_{lG}} = \begin{bmatrix} \cos\alpha & \sin\alpha \\ -\sin\alpha & \cos\alpha \end{bmatrix} $$If we use the direction cosines to calculate the rotation matrix, because the axes didn't change, the cosines are the same, only the order changes, now $\mathbf{x, y}$ are the rows (outputs) and $\mathbf{X, Y}$ are the columns (inputs):

$$ \mathbf{R_{lG}} = \begin{bmatrix} \cos\mathbf{X}x & \cos\mathbf{Y}x \\ \cos\mathbf{X}y & \cos\mathbf{Y}y \end{bmatrix} = \begin{bmatrix} \cos(\alpha) & \cos(90^o-\alpha) \\ \cos(90^o+\alpha) & \cos(\alpha) \end{bmatrix} = \begin{bmatrix} \cos\alpha & \sin\alpha \\ -\sin\alpha & \cos\alpha \end{bmatrix} $$And defining the versors of the axes in the Global coordinate system for a basis in terms of the local coordinate system would also produce this latter rotation matrix.

The two sets of equations and matrices for the rotations from Global-to-local and local-to-Global coordinate systems are very similar, this is no coincidence. Each of the rotation matrices we deduced, $\mathbf{R_{Gl}}$ and $\mathbf{R_{lG}}$, perform the inverse operation in relation to the other. Each matrix is the inverse of the other.

In other words, the relation between the two rotation matrices means it is equivalent to instead of rotating the local coordinate system by $\alpha$ in relation to the Global coordinate system, to rotate the Global coordinate system by $-\alpha$ in relation to the local coordinate system; remember that $\cos(-\alpha)=\cos(\alpha)$ and $\sin(-\alpha)=-\sin(\alpha)$.

We can also use the rotation matrix to rotate a vector by a given angle around an axis of the coordinate system as shown in the figure below.

We will not prove that we use the same rotation matrix, but think that in this case the vector position rotates by the same angle instead of the coordinate system. The new coordinates of the vector position $\mathbf{P'}$ rotated by an angle $\alpha$ is simply the rotation matrix (for the angle $\alpha$) multiplied by the coordinates of the vector position $\mathbf{P}$:

$$ \mathbf{P'} = \mathbf{R}_\alpha\mathbf{P} $$Consider for example that $\mathbf{P}=[2,1]$ and $\alpha=30^o$; the coordinates of $\mathbf{P'}$ are:

In [7]:

```
a = np.pi/6
R = np.array([[np.cos(a), -np.sin(a)], [np.sin(a), np.cos(a)]])
P = np.array([[2, 1]]).T
Pl = np.dot(R, P)
print("P':\n", Pl)
```

**See here for a review about matrix and its main properties**.

A nice property of the rotation matrix is that its inverse is the transpose of the matrix (because the columns/rows are mutually orthogonal and have norm equal to one).

This property can be shown with the rotation matrices we deduced:

This means that if we have a rotation matrix, we know its inverse.

The transpose and inverse operators in NumPy are methods of the array:

In [8]:

```
RGl = np.mat([[np.cos(np.pi/4), -np.sin(np.pi/4)], [np.sin(np.pi/4), np.cos(np.pi/4)]])
print('Orthogonal matrix (RGl):\n', np.around(RGl, 4))
print('Transpose (RGl.T):\n', np.around(RGl.T, 4))
print('Inverse (RGl.I):\n', np.around(RGl.I, 4))
```

Using the inverse and the transpose mathematical operations, the coordinates at the local coordinate system given the coordinates at the Global coordinate system and the rotation matrix can be obtained by:

$$ \begin{array}{l l} \mathbf{P_G} = \mathbf{R_{Gl}}\mathbf{P_l} \implies \\ \\ \mathbf{R_{Gl}^{-1}}\mathbf{P_G} = \mathbf{R_{Gl}^{-1}}\mathbf{R_{Gl}}\mathbf{P_l} \implies \\ \\ \mathbf{R_{Gl}^{-1}}\mathbf{P_G} = \mathbf{I}\:\mathbf{P_l} \implies \\ \\ \mathbf{P_l} = \mathbf{R_{Gl}^{-1}}\mathbf{P_G} = \mathbf{R_{Gl}^T}\mathbf{P_G} \quad \text{or} \quad \mathbf{P_l} = \mathbf{R_{lG}}\mathbf{P_G} \end{array} $$Where we referred the inverse of $\mathbf{R_{Gl}}\;(\:\mathbf{R_{Gl}^{-1}})$ as $\mathbf{R_{lG}}$ (note the different order of the subscripts).

Let's show this calculation in NumPy:

In [9]:

```
RGl = np.array([[np.cos(np.pi/4), -np.sin(np.pi/4)], [np.sin(np.pi/4), np.cos(np.pi/4)]])
print('Rotation matrix (RGl):\n', np.around(RGl, 4))
Pl = np.array([[1, 1]]).T # transpose the array for correct matrix multiplication
print('Position at the local coordinate system (Pl):\n', Pl)
PG = np.dot(RGl, Pl) # the function dot() is used for matrix multiplication with arrays
print('Position at the Global coordinate system (PG=RGl*Pl):\n', np.around(PG,2))
Pl = np.dot(RGl.T, PG)
print('Position at the local coordinate system using the inverse of RGl (Pl=RlG*PG):\n', Pl)
```

**In summary, some of the properties of the rotation matrix are:**

- The columns of the rotation matrix form a basis of (independent) unit vectors (versors) and the rows are also independent versors since the transpose of the rotation matrix is another rotation matrix.
- The rotation matrix is orthogonal. There is no linear combination of one of the lines or columns of the matrix that would lead to the other row or column, i.e., the lines and columns of the rotation matrix are independent, orthogonal, to each other (this is property 1 rewritten). Because each row and column have norm equal to one, this matrix is also sometimes said to be orthonormal.
- The determinant of the rotation matrix is equal to one (or equal to -1 if a left-hand coordinate system was used, but you should rarely use that). For instance, the determinant of the rotation matrix we deduced is $cos\alpha cos\alpha - sin\alpha(-sin\alpha)=1$.
- The inverse of the rotation matrix is equals to its transpose.

**On the different meanings of the rotation matrix:**

- It represents the coordinate transformation between the coordinates of a point expressed in two different coordinate systems.
- It describes the rotation between two coordinate systems. The columns are the direction cosines (versors) of the axes of the rotated coordinate system in relation to the other coordinate system and the rows are also direction cosines (versors) for the inverse rotation.
- It is an operator for the calculation of the rotation of a vector in a coordinate system.
- Rotation matrices provide a means of numerically representing rotations without appealing to angular specification.

**Which matrix to use, from local to Global or Global to local?**

- A typical use of the transformation is in movement analysis, where there are the fixed Global (laboratory) coordinate system and the local (moving, e.g. anatomical) coordinate system attached to each body segment. Because the movement of the body segment is measured in the Global coordinate system, using cameras for example, and we want to reconstruct the coordinates of the markers at the anatomical coordinate system, we want the transformation leading from the Global coordinate system to the local coordinate system.
- Of course, if you have one matrix, it is simple to get the other; you just have to pay attention to use the right one.

Consider now the case where the local coordinate system is translated and rotated in relation to the Global coordinate system and a point is described in both coordinate systems as illustrated in the figure below (once again, remember that this is equivalent to describing a translation and a rotation between two rigid bodies).

The position of point $\mathbf{P}$ originally described in the local coordinate system, but now described in the Global coordinate system in vector form is:

$$ \mathbf{P_G} = \mathbf{L_G} + \mathbf{R_{Gl}}\mathbf{P_l} $$And in matrix form:

$$ \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \end{bmatrix} = \begin{bmatrix} \mathbf{L_{X}} \\\ \mathbf{L_{Y}} \end{bmatrix} + \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix} \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \end{bmatrix} $$This means that we first *disrotate* the local coordinate system and then correct for the translation between the two coordinate systems. Note that we can't invert this order: the point position is expressed in the local coordinate system and we can't add this vector to another vector expressed in the Global coordinate system, first we have to convert the vectors to the same coordinate system.

If now we want to find the position of a point at the local coordinate system given its position in the Global coordinate system, the rotation matrix and the translation vector, we have to invert the expression above:

$$ \begin{array}{l l} \mathbf{P_G} = \mathbf{L_G} + \mathbf{R_{Gl}}\mathbf{P_l} \implies \\ \\ \mathbf{R_{Gl}^{-1}}(\mathbf{P_G} - \mathbf{L_G}) = \mathbf{R_{Gl}^{-1}}\mathbf{R_{Gl}}\mathbf{P_l} \implies \\ \\ \mathbf{P_l} = \mathbf{R_{Gl}^{-1}}\left(\mathbf{P_G}-\mathbf{L_G}\right) = \mathbf{R_{Gl}^T}\left(\mathbf{P_G}-\mathbf{L_G}\right) \quad \text{or} \quad \mathbf{P_l} = \mathbf{R_{lG}}\left(\mathbf{P_G}-\mathbf{L_G}\right) \end{array} $$The expression above indicates that to perform the inverse operation, to go from the Global to the local coordinate system, we first translate and then rotate the coordinate system.

It is possible to combine the translation and rotation operations in only one matrix, called the transformation matrix (also referred as homogeneous transformation matrix):

$$ \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \\ 1 \end{bmatrix} = \begin{bmatrix} \cos\alpha & -\sin\alpha & \mathbf{L_{X}} \\ \sin\alpha & \cos\alpha & \mathbf{L_{Y}} \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \\ 1 \end{bmatrix} $$Or simply:

$$ \mathbf{P_G} = \mathbf{T_{Gl}}\mathbf{P_l} $$The inverse operation, to express the position at the local coordinate system in terms of the Global coordinate system, is:

$$ \mathbf{P_l} = \mathbf{T_{Gl}^{-1}}\mathbf{P_G} $$However, because $\mathbf{T_{Gl}}$ is not orthonormal when there is a translation, its inverse is not its transpose. Its inverse in matrix form is given by:

$$ \begin{bmatrix} \mathbf{P}_x \\ \mathbf{P}_y \\ 1 \end{bmatrix} = \begin{bmatrix} \mathbf{R^{-1}_{Gl}} & \cdot & - \mathbf{R^{-1}_{Gl}}\mathbf{L_{G}} \\ \cdot & \cdot & \cdot \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} \mathbf{P_X} \\ \mathbf{P_Y} \\ 1 \end{bmatrix} $$A typical scenario in motion analysis is to calculate the rotation matrix using the position of markers placed on the moving rigid body. With the markers' positions, we create a local basis, which by definition is the rotation matrix for the rigid body with respect to the Global (laboratory) coordinate system. To define a coordinate system using a basÃs, we also will need to define an origin.

Let's see how to calculate a basis given the markers' positions.

Consider the markers at m1=[1,1]`, m2=[1,2] and m3=[-1,1] measured in the Global coordinate system as illustrated in the figure below:

A possible local coordinate system with origin at the position of m1 is also illustrated in the figure above. Intentionally, the three markers were chosen to form orthogonal vectors.

The translation vector between the two coordinate system is:

The vectors expressing the axes of the local coordinate system are:

$$ x = m_2 - m_1 = [1,2] - [1,1] = [0,1] $$$$ y = m_3 - m_1 = [-1,1] - [1,1] = [-2,0] $$Note that these two vectors do not form a basis yet because they are not unit vectors (in fact, only *y* is not a unit vector). Let's normalize these vectors:

Beware that the versors above are not exactly the same as the ones shown in the right plot of the last figure, the versors above if plotted will start at the origin of the coordinate system, not at [1,1] as shown in the figure.

We could have done this calculation in NumPy (we will need to do that when dealing with real data later):

In [10]:

```
m1 = np.array([1.,1.]) # marker 1
m2 = np.array([1.,2.]) # marker 2
m3 = np.array([-1.,1.]) # marker 3
x = m2 - m1 # vector x
y = m3 - m1 # vector y
vx = x/np.linalg.norm(x) # versor x
vy = y/np.linalg.norm(y) # verson y
print("x =", x, ", y =", y, "\nex=", vx, ", ey=", vy)
```

Now, both $\mathbf{e}_x$ and $\mathbf{e}_y$ are unit vectors (versors) and they are orthogonal, a basis can be formed with these two versors, and we can represent the rotation matrix using this basis (just place the versors of this basis as columns of the rotation matrix):

$$ \mathbf{R_{Gl}} = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} $$This rotation matrix makes sense because from the figure above we see that the local coordinate system we defined is rotated by 90$^o$ in relation to the Global coordinate system and if we use the general form for the rotation matrix:

$$ \mathbf{R} = \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix} = \begin{bmatrix} \cos90^o & -\sin90^o \\ \sin90^o & \cos90^o \end{bmatrix} = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} $$So, the position of any point in the local coordinate system can be represented in the Global coordinate system by:

$$ \begin{array}{l l} \mathbf{P_G} =& \mathbf{L_{Gl}} + \mathbf{R_{Gl}}\mathbf{P_l} \\ \\ \mathbf{P_G} =& \begin{bmatrix} 1 \\ 1 \end{bmatrix} + \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} \mathbf{P_l} \end{array} $$For example, the point $\mathbf{P_l}=[1,1]$ has the following position at the Global coordinate system:

In [11]:

```
LGl = np.array([[1, 1]]).T
print('Translation vector:\n', LGl)
RGl = np.array([[0, -1], [1, 0]])
print('Rotation matrix:\n', RGl)
Pl = np.array([[1, 1]]).T
print('Position at the local coordinate system:\n', Pl)
PG = LGl + np.dot(RGl, Pl)
print('Position at the Global coordinate system, PG = LGl + RGl*Pl:\n', PG)
```

If we didn't know the angle of rotation between the two coordinate systems, which is the typical situation in motion analysis, we simply would equate one of the terms of the two-dimensional rotation matrix in its algebraic form to its correspondent value in the numerical rotation matrix we calculated.

For instance, taking the first term of the rotation matrices above: $\cos\alpha = 0$ implies that $\theta$ is 90$^o$ or 270$^o$, but combining with another matrix term, $\sin\alpha = 1$, implies that $\alpha=90^o$. We can solve this problem in one step using the tangent $(\sin\alpha/\cos\alpha)$ function with two terms of the rotation matrix and calculating the angle with the `arctan2(y, x)`

function:

In [12]:

```
ang = np.arctan2(RGl[1, 0], RGl[0, 0])*180/np.pi
print('The angle is:', ang)
```

And this procedure would be repeated for each segment and for each instant of the analyzed movement to find the rotation of each segment.

In the notebook about two-dimensional angular kinematics, we calculated segment and joint angles using simple trigonometric relations. We can also calculate these two-dimensional angles using what we learned here about the rotation matrix.

The segment angle will be given by the matrix representing the rotation from the laboratory coordinate system (G) to a coordinate system attached to the segment and the joint angle will be given by the matrix representing the rotation from one segment coordinate system (l1) to the other segment coordinate system (l2). So, we have to calculate two basis now, one for each segment and the joint angle will be given by the product between the two rotation matrices.

To define a two-dimensional basis, we need to calculate vectors perpendicular to each of these lines. Here is a way of doing that. First, let's find three non-collinear points for each basis:

In [13]:

```
x1, y1, x2, y2 = 0, 0, 1, 1 # points at segment 1
x3, y3, x4, y4 = 1.1, 1, 2.1, 0 # points at segment 2
#The slope of the perpendicular line is minus the inverse of the slope of the line
xl1 = x1 - (y2-y1); yl1 = y1 + (x2-x1) # point at the perpendicular line 1
xl2 = x4 - (y3-y4); yl2 = y4 + (x3-x4) # point at the perpendicular line 2
```

With these three points, we can create a basis and the corresponding rotation matrix:

In [14]:

```
b1x = np.array([x2-x1, y2-y1])
b1x = b1x/np.linalg.norm(b1x) # versor x of basis 1
b1y = np.array([xl1-x1, yl1-y1])
b1y = b1y/np.linalg.norm(b1y) # versor y of basis 1
b2x = np.array([x3-x4, y3-y4])
b2x = b2x/np.linalg.norm(b2x) # versor x of basis 2
b2y = np.array([xl2-x4, yl2-y4])
b2y = b2y/np.linalg.norm(b2y) # versor y of basis 2
RGl1 = np.array([b1x, b1y]).T # rotation matrix from segment 1 to the laboratory
RGl2 = np.array([b2x, b2y]).T # rotation matrix from segment 2 to the laboratory
```

Now, the segment and joint angles are simply matrix operations:

In [15]:

```
print('Rotation matrix for segment 1:\n', np.around(RGl1, 4))
print('\nRotation angle of segment 1:', np.arctan2(RGl1[1,0], RGl1[0,0])*180/np.pi)
print('\nRotation matrix for segment 2:\n', np.around(RGl2, 4))
print('\nRotation angle of segment 2:', np.arctan2(RGl1[1,0], RGl2[0,0])*180/np.pi)
Rl1l2 = np.dot(RGl1.T, RGl2) # Rl1l2 = Rl1G*RGl2
print('\nJoint rotation matrix (Rl1l2 = Rl1G*RGl2):\n', np.around(Rl1l2, 4))
print('\nJoint angle:', np.arctan2(Rl1l2[1,0], Rl1l2[0,0])*180/np.pi)
```

Same result as obtained in Angular kinematics in a plane (2D).

The fact that we simply multiplied the rotation matrices to calculate the rotation matrix of one segment in relation to the other is powerful and can be generalized for any number of segments: given a serial kinematic chain with links 1, 2, ..., n and 0 is the base/laboratory, the rotation matrix between the base and last link is: $\mathbf{R_{n,n-1}R_{n-1,n-2} \dots R_{2,1}R_{1,0}}$, where each matrix in this product (calculated from right to left) is the rotation of one link with respect to the next one.

For instance, consider a kinematic chain with two links, the link 1 is rotated by $\alpha_1$ with respect to the base (0) and the link 2 is rotated by $\alpha_2$ with respect to the link 1.

Using Sympy, the rotation matrices for link 2 w.r.t. link 1 $(R_{12})$ and for link 1 w.r.t. base 0 $(R_{01})$ are:

In [16]:

```
from IPython.display import display, Math
from sympy import sin, cos, Matrix, simplify, latex, symbols
from sympy.interactive import printing
printing.init_printing()
```

In [17]:

```
a1, a2 = symbols('alpha1 alpha2')
R12 = Matrix([[cos(a2), -sin(a2)], [sin(a2), cos(a2)]])
display(Math(latex(r'\mathbf{R_{12}}=') + latex(R12)))
R01 = Matrix([[cos(a1), -sin(a1)], [sin(a1), cos(a1)]])
display(Math(latex(r'\mathbf{R_{01}}=') + latex(R01)))
```

The rotation matrix of link 2 w.r.t. the base $(R_{02})$ is given simply by $R_{01}*R_{12}$:

In [18]:

```
R02 = R01*R12
display(Math(latex(r'\mathbf{R_{02}}=') + latex(R02)))
```

Which simplifies to:

In [19]:

```
display(Math(latex(r'\mathbf{R_{02}}=') + latex(simplify(R02))))
```

As expected.

The typical use of all these concepts is in the three-dimensional motion analysis where we will have to deal with angles in different planes, which needs a special manipulation as we will see next.

A local coordinate system is rotated 30$^o$ clockwise in relation to the Global reference system.

A. Determine the matrices for rotating one coordinate system to another (two-dimensional).

B. What are the coordinates of the point [1, 1] (local coordinate system) at the global coordinate system?

C. And if this point is at the Global coordinate system and we want the coordinates at the local coordinate system?

D. Consider that the local coordinate system, besides the rotation is also translated by [2, 2]. What are the matrices for rotation, translation, and transformation from one coordinate system to another (two-dimensional)?

E. Repeat B and C considering this translation.Consider a local coordinate system U rotated 45$^o$ clockwise in relation to the Global reference system and another local coordinate system V rotated 45$^o$ clockwise in relation to the local reference system U.

A. Determine the rotation matrices of all possible transformations between the coordinate systems.

B. For the point [1, 1] in the coordinate system U, what are its coordinates in coordinate system V and in the Global coordinate system?Using the rotation matrix, deduce the new coordinates of a square figure with coordinates [0, 0], [1, 0], [1, 1], and [0, 1] when rotated by 0$^o$, 45$^o$, 90$^o$, 135$^o$, and 180$^o$ (always clockwise).

Solve the problem 2 of Angular kinematics in a plane (2D) but now using the concept of two-dimensional transformations.

- Robertson G, Caldwell G, Hamill J, Kamen G (2013) Research Methods in Biomechanics. 2nd Edition. Human Kinetics.
- Ruina A, Rudra P (2013) Introduction to Statics and Dynamics. Oxford University Press.
- Winter DA (2009) Biomechanics and motor control of human movement. 4 ed. Hoboken, EUA: Wiley.
- Zatsiorsky VM (1997) Kinematics of Human Motion. Champaign, Human Kinetics.