Chapter 2: Rigid-Body Motion and Lie Groups
Lesson 2: Rotation Matrices and SO(3) Properties
In this lesson we formalize 3D rotations as matrices, define the special orthogonal group \( \mathrm{SO}(3) \), and prove its key algebraic and geometric properties. We connect these ideas to coordinate transformations between rigid frames, and implement rotation matrices in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica using robotics-relevant libraries.
1. Geometric Definition of Rotation and Rotation Matrices
Consider a rigid body in \( \mathbb{R}^3 \) with a body-fixed frame \( \{B\} \) and an inertial frame \( \{A\} \). From Lesson 1, physical vectors are independent of coordinates, but their coordinate representations depend on the chosen frame. A map \( R : \mathbb{R}^3 \to \mathbb{R}^3 \) is called a (proper) rotation if:
- It is linear;
- It preserves distances and angles between vectors;
- It preserves orientation (right-handedness of bases).
Let \( \{ \mathbf{e}_1,\mathbf{e}_2,\mathbf{e}_3 \} \) be the basis of frame \( \{A\} \) and \( \{ \mathbf{b}_1,\mathbf{b}_2,\mathbf{b}_3 \} \) the basis of frame \( \{B\} \), where each \( \mathbf{b}_i \) is a unit vector and the set is right-handed. The rotation matrix from \( \{B\} \) to \( \{A\} \) is
\[ {}^A R_B = \begin{bmatrix} {}^A \mathbf{b}_1 & {}^A \mathbf{b}_2 & {}^A \mathbf{b}_3 \end{bmatrix} \in \mathbb{R}^{3\times 3}, \]
where \( {}^A \mathbf{b}_i \) is the coordinate column of the unit vector \( \mathbf{b}_i \) expressed in frame \( \{A\} \). For any physical vector \( \mathbf{p} \), its coordinates in frames \( \{A\} \) and \( \{B\} \) are related by
\[ {}^A \mathbf{p} = {}^A R_B \, {}^B \mathbf{p}. \]
Because the axes of \( \{B\} \) are orthonormal, we have \( \mathbf{b}_i \cdot \mathbf{b}_j = \delta_{ij} \), which in coordinates becomes
\[ ({}^A R_B)^T \, {}^A R_B = \begin{bmatrix} \mathbf{b}_1 \cdot \mathbf{b}_1 & \mathbf{b}_1 \cdot \mathbf{b}_2 & \mathbf{b}_1 \cdot \mathbf{b}_3 \\ \mathbf{b}_2 \cdot \mathbf{b}_1 & \mathbf{b}_2 \cdot \mathbf{b}_2 & \mathbf{b}_2 \cdot \mathbf{b}_3 \\ \mathbf{b}_3 \cdot \mathbf{b}_1 & \mathbf{b}_3 \cdot \mathbf{b}_2 & \mathbf{b}_3 \cdot \mathbf{b}_3 \end{bmatrix} = I_3. \]
Thus, the columns of a rotation matrix form an orthonormal basis. The orientation-preserving condition is encoded by the determinant: \( \det({}^A R_B) = 1 \).
flowchart TD
P["Rigid body with body frame {B} and inertial frame {A}"] --> A1["Choose orthonormal axes b1, b2, b3 attached to body"]
A1 --> A2["Express b1, b2, b3 in coordinates of frame {A}"]
A2 --> A3["Form 3x3 matrix R = [b1 b2 b3]"]
A3 --> A4["Check R^T R = I (orthonormality)"]
A4 --> A5["Check det(R) = 1 (right-handed)"]
A5 --> A6["Declare R a valid element of SO(3)"]
Key geometric property: for any vectors \( \mathbf{p}, \mathbf{q} \in \mathbb{R}^3 \) and a rotation matrix \( R \),
\[ \| R\mathbf{p} \| = \| \mathbf{p} \|,\quad (R\mathbf{p}) \cdot (R\mathbf{q}) = \mathbf{p} \cdot \mathbf{q}, \]
i.e., lengths and angles are preserved.
2. The Special Orthogonal Group \( \mathrm{SO}(3) \)
We now give the formal matrix definition of the 3D rotation group. The orthogonal group in dimension 3 is
\[ \mathrm{O}(3) = \{ R \in \mathbb{R}^{3\times 3} \mid R^T R = I_3 \}. \]
Matrices in \( \mathrm{O}(3) \) are either rotations (proper) or reflections (improper). The special orthogonal group is
\[ \mathrm{SO}(3) = \{ R \in \mathbb{R}^{3\times 3} \mid R^T R = I_3,\ \det(R) = 1 \}. \]
Elements of \( \mathrm{SO}(3) \) are exactly the proper rotations about the origin in \( \mathbb{R}^3 \). It is a group under matrix multiplication:
- Identity: \( I_3 \in \mathrm{SO}(3) \).
- Closure: If \( R_1,R_2 \in \mathrm{SO}(3) \) then \( R_1 R_2 \in \mathrm{SO}(3) \):
\[ (R_1 R_2)^T (R_1 R_2) = R_2^T R_1^T R_1 R_2 = R_2^T R_2 = I_3,\quad \det(R_1 R_2) = \det(R_1)\det(R_2) = 1\cdot 1 = 1. \]
- Inverse: If \( R \in \mathrm{SO}(3) \), then \( R^{-1} = R^T \) and \( R^T \in \mathrm{SO}(3) \):
\[ (R^T)^T R^T = R R^T = I_3,\quad \det(R^T) = \det(R) = 1. \]
Associativity follows from matrix multiplication, so \( \mathrm{SO}(3) \) is indeed a group. It is also a smooth 3-dimensional manifold, making it a matrix Lie group, but the differential-geometric aspects will be developed informally in later lessons.
The constraints \( R^T R = I_3 \) and \( \det(R)=1 \) remove redundancy in the \( 3\times 3 \) matrix. A generic \( 3\times 3 \) real matrix has 9 parameters. The orthonormality constraints impose:
- 3 unit-length constraints (one for each column);
- 3 orthogonality constraints between distinct columns.
These 6 constraints are not all independent, but the resulting manifold has dimension 3: rotations in 3D have exactly 3 degrees of freedom.
3. Matrix Properties of Rotation Matrices
3.1 Orthonormal Columns and Rows
For \( R \in \mathrm{SO}(3) \), let its columns be \( \mathbf{r}_1, \mathbf{r}_2, \mathbf{r}_3 \). From \( R^T R = I_3 \), we have
\[ (R^T R)_{ij} = \mathbf{r}_i \cdot \mathbf{r}_j = \begin{cases} 1 & \text{if } i=j, \\ 0 & \text{if } i\neq j. \end{cases} \]
Therefore, the columns of \( R \) form an orthonormal basis. Similarly, from \( R R^T = I_3 \), the rows of \( R \) form an orthonormal basis. This implies:
- \( \| \mathbf{r}_i \| = 1 \) for all \( i \);
- \( \mathbf{r}_i \cdot \mathbf{r}_j = 0 \) for \( i\neq j \);
- the triple \( (\mathbf{r}_1,\mathbf{r}_2,\mathbf{r}_3) \) is right-handed because \( \det(R) = 1 \).
3.2 Norm and Angle Preservation
Let \( R \in \mathrm{SO}(3) \) and \( \mathbf{p},\mathbf{q}\in\mathbb{R}^3 \). Then
\[ \| R\mathbf{p} \|^2 = (R\mathbf{p})^T (R\mathbf{p}) = \mathbf{p}^T R^T R \mathbf{p} = \mathbf{p}^T \mathbf{p} = \| \mathbf{p} \|^2, \]
and similarly,
\[ (R\mathbf{p})^T (R\mathbf{q}) = \mathbf{p}^T R^T R \mathbf{q} = \mathbf{p}^T \mathbf{q}. \]
Thus, both lengths and inner products are preserved, proving that rotation matrices are isometries of \( \mathbb{R}^3 \) that fix the origin.
3.3 Cross Product Compatibility
For a proper rotation \( R \in \mathrm{SO}(3) \), we also have the cross-product compatibility property:
\[ R(\mathbf{p} \times \mathbf{q}) = (R\mathbf{p}) \times (R\mathbf{q}), \quad \mathbf{p},\mathbf{q}\in\mathbb{R}^3. \]
Intuitively, this follows because the cross product is defined via area and orientation, both preserved by rotations. This property will later connect rotation matrices to skew-symmetric matrices that encode angular velocity.
4. Composition, Inverse, and Frame Transformations
4.1 Frame-to-Frame Rotation Matrices
Let \( \{A\}, \{B\}, \{C\} \) be three right-handed frames. We denote by \( {}^A R_B \) the rotation matrix that maps coordinates of a vector from frame \( \{B\} \) to frame \( \{A\} \):
\[ {}^A \mathbf{p} = {}^A R_B \, {}^B \mathbf{p}. \]
The inverse relation is
\[ {}^B \mathbf{p} = {}^B R_A \, {}^A \mathbf{p}, \quad {}^B R_A = ({}^A R_B)^{-1} = ({}^A R_B)^T. \]
Thus, the transpose of a frame-to-frame rotation matrix reverses the direction of the coordinate change.
4.2 Composition of Rotations
Suppose we know the relative orientations \( {}^A R_B \) and \( {}^B R_C \). The rotation from \( \{C\} \) to \( \{A\} \) is their product:
\[ {}^A R_C = {}^A R_B \, {}^B R_C. \]
This is the algebraic statement that the rotation group operation is composition of coordinate changes. In terms of a vector \( \mathbf{p} \),
\[ {}^A \mathbf{p} = {}^A R_C \, {}^C \mathbf{p} = ({}^A R_B \, {}^B R_C) \, {}^C \mathbf{p}. \]
flowchart TD
P_A["Vector coordinates in {A}: p_A"] --> R_AB["Known rotation R_AB (from {B} to {A})"]
R_AB --> P_B["Convert to {B}: p_B = R_BA * p_A"]
P_B --> R_BC["Known rotation R_BC (from {C} to {B})"]
R_BC --> P_C["Convert to {C}: p_C = R_CB * p_B"]
P_C --> G["Combined rotation R_AC = R_AB * R_BC in SO(3)"]
This composition rule underlies serial manipulator kinematics: orientations along a kinematic chain are obtained as products of joint-level rotations, a viewpoint that will be developed in detail in later chapters.
5. Eigenstructure, Rotation Angle, and Degrees of Freedom
5.1 Existence of a Rotation Axis
A fundamental fact about \( \mathrm{SO}(3) \) is that every rotation can be interpreted as a rotation by some angle \( \theta \) around a unit axis \( \mathbf{n} \). At the linear-algebra level, this appears as:
- \( R \in \mathrm{SO}(3) \) has an eigenvalue \( \lambda = 1 \) with a real unit eigenvector \( \mathbf{n} \) (the rotation axis);
- the other two eigenvalues lie on the unit circle and are complex conjugates: \( e^{\pm i\theta} \).
Consequently, the characteristic polynomial of \( R \) factors as
\[ \det(\lambda I_3 - R) = (\lambda - 1)(\lambda^2 - 2\cos\theta\,\lambda + 1). \]
5.2 Trace–Angle Relation
Taking the trace of \( R \), which equals the sum of eigenvalues, we obtain
\[ \operatorname{tr}(R) = 1 + e^{i\theta} + e^{-i\theta} = 1 + 2\cos\theta. \]
Therefore, given a rotation matrix \( R \in \mathrm{SO}(3) \), its rotation angle \( \theta \in [0,\pi] \) can be recovered from
\[ \cos\theta = \frac{\operatorname{tr}(R) - 1}{2}. \]
This relation will be used later when we connect \( \mathrm{SO}(3) \) to axis–angle representations and exponential coordinates.
5.3 Degrees of Freedom via Orthonormal Columns
We can also understand the 3 degrees of freedom of \( \mathrm{SO}(3) \) by a constructive argument:
- Choose the first column \( \mathbf{r}_1 \) as any unit vector on the 2-sphere \( S^2 \) (2 degrees of freedom).
- Choose the second column \( \mathbf{r}_2 \) as any unit vector orthogonal to \( \mathbf{r}_1 \) (1 degree of freedom).
- Set \( \mathbf{r}_3 = \mathbf{r}_1 \times \mathbf{r}_2 \) to enforce right-handedness and orthonormality.
This construction shows there are three independent parameters, consistent with the idea that orientation in 3D is specified by three scalars (e.g., Euler angles) subject to coordinate singularities that we will study later.
6. Python Implementation — Rotation Matrices and SO(3) Checks
In Python, basic matrix operations are conveniently done with
numpy. For robotics-specific tools, typical options
include:
-
scipy.spatial.transform.Rotation(SciPy): rotation objects, conversions; -
spatialmathandroboticstoolbox-python(Corke): SO(3)/SE(3) classes; -
pytransform3d: transformations and Lie group utilities.
Below we implement elementary rotations about the coordinate axes, compose them, and check membership in \( \mathrm{SO}(3) \).
import numpy as np
def rot_x(theta: float) -> np.ndarray:
"""Rotation about x-axis by angle theta (radians)."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[1.0, 0.0, 0.0],
[0.0, c, -s ],
[0.0, s, c ]], dtype=float)
def rot_y(theta: float) -> np.ndarray:
"""Rotation about y-axis by angle theta (radians)."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[ c, 0.0, s ],
[0.0, 1.0, 0.0],
[ -s, 0.0, c ]], dtype=float)
def rot_z(theta: float) -> np.ndarray:
"""Rotation about z-axis by angle theta (radians)."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[ c, -s, 0.0],
[ s, c, 0.0],
[0.0, 0.0, 1.0]], dtype=float)
def is_so3(R: np.ndarray, tol: float = 1e-9) -> bool:
"""Return True if R is in SO(3) within tolerance tol."""
if R.shape != (3, 3):
return False
I = np.eye(3)
should_be_I = R.T @ R
orth_err = np.linalg.norm(should_be_I - I, ord="fro")
det_R = np.linalg.det(R)
return orth_err < tol and abs(det_R - 1.0) < tol
if __name__ == "__main__":
theta_deg = 30.0
theta = np.deg2rad(theta_deg)
# Compose a rotation: first about z, then about y
Rz = rot_z(theta)
Ry = rot_y(theta)
R = Rz @ Ry
print("R =\n", R)
print("R^T R =\n", R.T @ R)
print("det(R) =", np.linalg.det(R))
print("R in SO(3)?", is_so3(R))
# Example: transform a vector between frames
p_B = np.array([1.0, 0.0, 0.0]) # coordinates in frame B
p_A = R @ p_B # coordinates of same vector in frame A
print("p_A =", p_A)
# If spatialmath-python is installed:
try:
from spatialmath import SO3
R_sm = SO3.Rz(theta_deg, unit="deg") * SO3.Ry(theta_deg, unit="deg")
print("SpatialMath R matrix:\n", R_sm.R)
print("SpatialMath R in SO(3)?", R_sm.isSO())
except ImportError:
print("spatialmath not installed; skipping SO3 demo.")
This code illustrates both a from-scratch implementation of axis
rotations and a higher-level interface via SO3 objects when
robotics libraries are available.
7. C++ Implementation — Eigen and Robotics Libraries
In C++, the de facto standard linear algebra library in robotics is
Eigen. Many frameworks (ROS, KDL, MoveIt, pinocchio) expose
rotations either as Eigen::Matrix3d or as thin wrappers
around them.
The example below implements axis rotations, composes them, and checks the \( \mathrm{SO}(3) \) constraints numerically.
#include <iostream>
#include <cmath>
#include <Eigen/Dense>
Eigen::Matrix3d RotX(double theta) {
double c = std::cos(theta);
double s = std::sin(theta);
Eigen::Matrix3d R;
R << 1.0, 0.0, 0.0,
0.0, c, -s,
0.0, s, c;
return R;
}
Eigen::Matrix3d RotY(double theta) {
double c = std::cos(theta);
double s = std::sin(theta);
Eigen::Matrix3d R;
R << c, 0.0, s,
0.0, 1.0, 0.0,
-s, 0.0, c;
return R;
}
Eigen::Matrix3d RotZ(double theta) {
double c = std::cos(theta);
double s = std::sin(theta);
Eigen::Matrix3d R;
R << c, -s, 0.0,
s, c, 0.0,
0.0, 0.0, 1.0;
return R;
}
bool isSO3(const Eigen::Matrix3d& R, double tol = 1e-9) {
Eigen::Matrix3d I = Eigen::Matrix3d::Identity();
Eigen::Matrix3d shouldBeI = R.transpose() * R;
double orthErr = (shouldBeI - I).norm();
double detR = R.determinant();
return orthErr < tol && std::abs(detR - 1.0) < tol;
}
int main() {
double theta = M_PI / 6.0; // 30 degrees
Eigen::Matrix3d Rz = RotZ(theta);
Eigen::Matrix3d Ry = RotY(theta);
Eigen::Matrix3d R = Rz * Ry;
std::cout << "R =\n" << R << std::endl;
std::cout << "R^T R =\n" << R.transpose() * R << std::endl;
std::cout << "det(R) = " << R.determinant() << std::endl;
std::cout << "R in SO(3)? " << std::boolalpha << isSO3(R) << std::endl;
// Example: transform coordinates of a vector
Eigen::Vector3d p_B(1.0, 0.0, 0.0);
Eigen::Vector3d p_A = R * p_B;
std::cout << "p_A =\n" << p_A << std::endl;
return 0;
}
In ROS-based systems, these matrices are often wrapped in
quaternion-based types (Eigen::Quaterniond,
geometry_msgs::Quaternion) and converted internally to/from
Eigen::Matrix3d. Understanding the underlying \(
\mathrm{SO}(3) \) matrix structure is essential before introducing such
higher-level representations.
8. Java Implementation — Basic Matrices and EJML
In Java, robotics projects often use numerical libraries such as EJML or
Apache Commons Math. For clarity, we first implement \( 3\times 3 \)
matrices using plain double[][] and then note how EJML
simplifies the code.
public class SO3Demo {
public static double[][] rotZ(double theta) {
double c = Math.cos(theta);
double s = Math.sin(theta);
return new double[][] {
{ c, -s, 0.0 },
{ s, c, 0.0 },
{ 0.0, 0.0, 1.0 }
};
}
public static double[][] rotY(double theta) {
double c = Math.cos(theta);
double s = Math.sin(theta);
return new double[][] {
{ c, 0.0, s },
{ 0.0, 1.0, 0.0 },
{ -s, 0.0, c }
};
}
public static double[][] matMul(double[][] A, double[][] B) {
double[][] C = new double[3][3];
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
C[i][j] = 0.0;
for (int k = 0; k < 3; ++k) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
return C;
}
public static double[][] transpose(double[][] A) {
double[][] T = new double[3][3];
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
T[i][j] = A[j][i];
}
}
return T;
}
public static double frobNorm(double[][] A) {
double s = 0.0;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
s += A[i][j] * A[i][j];
}
}
return Math.sqrt(s);
}
public static double det3(double[][] R) {
return
R[0][0]*(R[1][1]*R[2][2] - R[1][2]*R[2][1]) -
R[0][1]*(R[1][0]*R[2][2] - R[1][2]*R[2][0]) +
R[0][2]*(R[1][0]*R[2][1] - R[1][1]*R[2][0]);
}
public static boolean isSO3(double[][] R, double tol) {
double[][] I = {
{1.0, 0.0, 0.0},
{0.0, 1.0, 0.0},
{0.0, 0.0, 1.0}
};
double[][] RT = transpose(R);
double[][] RT_R = matMul(RT, R);
double[][] diff = new double[3][3];
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
diff[i][j] = RT_R[i][j] - I[i][j];
}
}
double orthErr = frobNorm(diff);
double detR = det3(R);
return orthErr < tol && Math.abs(detR - 1.0) < tol;
}
public static void main(String[] args) {
double theta = Math.toRadians(30.0);
double[][] Rz = rotZ(theta);
double[][] Ry = rotY(theta);
double[][] R = matMul(Rz, Ry);
System.out.println("det(R) = " + det3(R));
System.out.println("R in SO(3)? " + isSO3(R, 1e-9));
}
}
With EJML, the same code becomes shorter:
SimpleMatrix already supports transposition,
multiplication, determinants, and norms, so the isSO3 check
mirrors the Python implementation using EJML method calls.
9. MATLAB/Simulink Implementation
MATLAB is widely used in robotics, with the Robotics System Toolbox
providing high-level functions such as eul2rotm,
axang2rotm, and rotm2eul. Here we first
implement axis rotations manually, then sketch how to embed them in
Simulink.
function so3_demo
theta = deg2rad(30);
Rz = rotz_local(theta);
Ry = roty_local(theta);
R = Rz * Ry;
I = eye(3);
orthErr = norm(R' * R - I, "fro");
detR = det(R);
fprintf("R =\n");
disp(R);
fprintf("orthErr = %g\n", orthErr);
fprintf("det(R) = %g\n", detR);
if orthErr < 1e-9 && abs(detR - 1.0) < 1e-9
fprintf("R is numerically in SO(3).\n");
else
fprintf("R fails SO(3) test.\n");
end
end
function R = rotx_local(theta)
c = cos(theta); s = sin(theta);
R = [1, 0, 0;
0, c, -s;
0, s, c];
end
function R = roty_local(theta)
c = cos(theta); s = sin(theta);
R = [ c, 0, s;
0, 1, 0;
-s, 0, c];
end
function R = rotz_local(theta)
c = cos(theta); s = sin(theta);
R = [ c, -s, 0;
s, c, 0;
0, 0, 1];
end
Simulink sketch: to construct a block diagram that applies a fixed rotation \( R \in \mathrm{SO}(3) \) to a 3D vector \( \mathbf{p} \):
- Create a new model and add a 3-element Constant block for \( \mathbf{p} \).
- Add a Gain block and set its gain matrix to the \( 3\times 3 \) rotation matrix \( R \).
- Connect the constant block to the gain block; the output is \( R\mathbf{p} \).
-
Optionally, wrap the MATLAB functions
rotx_local,roty_local,rotz_localin a MATLAB Function block to generate time-varying rotations parameterized by a signal \( \theta(t) \).
Later, when we consider time-varying orientations and dynamics, the same rotation matrices will appear inside Simulink models of rigid-body motion.
10. Wolfram Mathematica Implementation
Wolfram Mathematica provides built-in support for rotations via
RotationMatrix, which returns an element of \(
\mathrm{SO}(3) \) as a symbolic or numeric matrix. We can easily verify
the group properties.
(* Rotation about coordinate axes *)
rotX[theta_] := RotationMatrix[theta, {1, 0, 0}];
rotY[theta_] := RotationMatrix[theta, {0, 1, 0}];
rotZ[theta_] := RotationMatrix[theta, {0, 0, 1}];
theta = 30 Degree;
Rz = rotZ[theta];
Ry = rotY[theta];
R = Rz.Ry;
(* Check orthogonality and determinant *)
orthErr = Norm[Transpose[R].R - IdentityMatrix[3], "Frobenius"];
detR = Det[R];
Print["R = "];
Print[MatrixForm[R]];
Print["orthErr = ", orthErr];
Print["det(R) = ", detR];
(* Trace-angle relation *)
traceR = Tr[R];
thetaFromTrace = ArcCos[(traceR - 1)/2];
Print["thetaFromTrace (deg) = ", N[thetaFromTrace/Degree]];
Symbolic capabilities allow exact algebraic manipulation of rotation matrices (for example, verifying identities for arbitrary angles), which will be useful in later chapters when deriving kinematic and dynamic equations.
11. Problems and Solutions
Problem 1 (Norm Preservation): Let \( R \in \mathrm{SO}(3) \) and \( \mathbf{v} \in \mathbb{R}^3 \). Prove that \( \|R\mathbf{v}\| = \|\mathbf{v}\| \).
Solution:
Using the definition \( R^T R = I_3 \), we have
\[ \|R\mathbf{v}\|^2 = (R\mathbf{v})^T (R\mathbf{v}) = \mathbf{v}^T R^T R \mathbf{v} = \mathbf{v}^T I_3 \mathbf{v} = \|\mathbf{v}\|^2. \]
Since norms are nonnegative, taking square roots yields \( \|R\mathbf{v}\| = \|\mathbf{v}\| \). Thus, rotation matrices preserve Euclidean norms.
Problem 2 (Frame Inverse Relation): Let \( {}^A R_B \in \mathrm{SO}(3) \) represent the rotation from frame \( \{B\} \) to frame \( \{A\} \). Show that \( {}^B R_A = ({}^A R_B)^T \) and that coordinate transformations satisfy \( {}^A \mathbf{p} = {}^A R_B {}^B \mathbf{p} \) and \( {}^B \mathbf{p} = {}^B R_A {}^A \mathbf{p} \).
Solution:
By definition, \( {}^A \mathbf{p} = {}^A R_B \, {}^B \mathbf{p} \). Applying \( {}^B R_A \) to both sides gives
\[ {}^B R_A \, {}^A \mathbf{p} = {}^B R_A \, {}^A R_B \, {}^B \mathbf{p}. \]
But the composition must be identity on frame \( \{B\} \), so \( {}^B R_A \, {}^A R_B = I_3 \), hence \( {}^B R_A = ({}^A R_B)^{-1} \). Since \( {}^A R_B \in \mathrm{SO}(3) \), we know \( ({}^A R_B)^{-1} = ({}^A R_B)^T \). Therefore \( {}^B R_A = ({}^A R_B)^T \).
The two coordinate relations, \( {}^A \mathbf{p} = {}^A R_B {}^B \mathbf{p} \) and \( {}^B \mathbf{p} = {}^B R_A {}^A \mathbf{p} \), then follow from the fact that the two rotation matrices are inverses of each other.
Problem 3 (Explicit Rotation Matrix and Trace): Consider a rotation of angle \( \theta = \pi/3 \) about the z-axis. Write the explicit rotation matrix \( R_z(\theta) \), verify that it lies in \( \mathrm{SO}(3) \), and compute its trace. Use the trace–angle relation to recover \( \theta \).
Solution:
The rotation matrix about the z-axis is
\[ R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}. \]
We compute
\[ R_z(\theta)^T R_z(\theta) = \begin{bmatrix} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} = I_3, \]
and \( \det(R_z(\theta)) = 1 \), so \( R_z(\theta) \in \mathrm{SO}(3) \). The trace is
\[ \operatorname{tr}(R_z(\theta)) = \cos\theta + \cos\theta + 1 = 1 + 2\cos\theta. \]
For \( \theta = \pi/3 \), we have \( \cos(\pi/3) = 1/2 \), so \( \operatorname{tr}(R_z(\pi/3)) = 1 + 2\cdot(1/2) = 2 \). Using the trace–angle relation,
\[ \cos\theta = \frac{\operatorname{tr}(R) - 1}{2} = \frac{2 - 1}{2} = \frac{1}{2} \quad\Rightarrow\quad \theta = \frac{\pi}{3}, \]
as expected.
Problem 4 (Degrees of Freedom via Constraints): Let \( R \in \mathbb{R}^{3\times 3} \). Show that the constraints \( R^T R = I_3 \) and \( \det(R) = 1 \) reduce the nine independent entries of \( R \) to three degrees of freedom.
Solution:
The matrix \( R \) has 9 entries. The orthonormality constraint \( R^T R = I_3 \) imposes:
- 3 equations for unit-length columns: \( \|\mathbf{r}_1\|^2 = 1 \), \( \|\mathbf{r}_2\|^2 = 1 \), \( \|\mathbf{r}_3\|^2 = 1 \);
- 3 equations for orthogonality: \( \mathbf{r}_1 \cdot \mathbf{r}_2 = 0 \), \( \mathbf{r}_1 \cdot \mathbf{r}_3 = 0 \), \( \mathbf{r}_2 \cdot \mathbf{r}_3 = 0 \).
Naively this is 6 scalar constraints, but one of them is redundant due to the determinant condition. A more geometric argument is:
- Choose \( \mathbf{r}_1 \) as any unit vector on the sphere \( S^2 \) (2 degrees of freedom).
- Choose \( \mathbf{r}_2 \) as any unit vector orthogonal to \( \mathbf{r}_1 \) (1 degree of freedom: rotate within the plane orthogonal to \( \mathbf{r}_1 \)).
- Set \( \mathbf{r}_3 = \mathbf{r}_1 \times \mathbf{r}_2 \) to enforce orthogonality and right-handed orientation (no extra degrees of freedom).
Thus, \( \mathrm{SO}(3) \) has 3 degrees of freedom. This matches the intuition that arbitrary orientations in 3D can be specified by three independent parameters.
12. Summary
- 3D rotations about the origin are represented by \( 3\times 3 \) matrices whose columns form an orthonormal basis and whose determinant is \( +1 \); these matrices form the special orthogonal group \( \mathrm{SO}(3) \).
- Rotation matrices preserve Euclidean distances, angles, and the cross product, making them the natural objects for describing orientation of rigid-body frames and changes of coordinates between them.
- The group operations in \( \mathrm{SO}(3) \) correspond to composition of rotations and inversion (transpose). Frame-to-frame relations satisfy \( {}^A R_C = {}^A R_B {}^B R_C \) and \( {}^B R_A = ({}^A R_B)^T \).
- The eigenstructure of a rotation matrix reveals a fixed axis and a rotation angle \( \theta \) related to the trace by \( \operatorname{tr}(R) = 1 + 2\cos\theta \), and the manifold \( \mathrm{SO}(3) \) has 3 degrees of freedom.
- Implementations in Python (NumPy/SciPy), C++ (Eigen), Java (arrays/EJML), MATLAB/Simulink, and Wolfram Mathematica all follow the same structure: construct axis rotations, compose them, and numerically verify the defining properties of \( \mathrm{SO}(3) \).
In the next lesson, we will embed these rotation matrices into \( 4\times 4 \) homogeneous transformation matrices to obtain the full rigid-body motion group \( \mathrm{SE}(3) \).
13. References
- Park, F.C. (1995). Distance metrics on the rigid-body motions with applications to mechanism design. Journal of Mechanical Design, 117(1), 48–54.
- Moakher, M. (2002). Means and averaging in the group of rotations. SIAM Journal on Matrix Analysis and Applications, 24(1), 1–16.
- Chirikjian, G.S. (2000). Hyper-Redundant Robot Mechanisms: Applications, Mathematics, and Control. Advanced Robotics, 13(4), 343–379.
- Shuster, M.D. (1993). A survey of attitude representations. Journal of the Astronautical Sciences, 41(4), 439–517.
- Vranceanu, G. (1934). Sur les groupes continus de transformations des espaces euclidiens. Comptes Rendus de l'Académie des Sciences, 198, 1536–1538.
- Smith, R. (1961). The rotation group \( \mathrm{SO}(3) \) and its subgroup structure. Proceedings of the American Mathematical Society, 12(2), 220–227.
- Brockett, R.W. (1972). System theory on group manifolds and coset spaces. SIAM Journal on Control, 10(2), 265–284.
- Murray, R.M., Li, Z., & Sastry, S.S. (1994). A mathematical introduction to robotic manipulation. Various journal and conference excerpts on matrix Lie groups and SO(3).