3D Transformation
Tutorial on Handling SO(3) and SE(3) Transformations for Simulation
This tutorial assumes the numpy and transforms3d Python packages have been installed.
Inverting Transformation
For an SO(3) rotation numpy matrix R, you can get the inverse simply as R.T. For quaternion q=(w,x,y,z), the inverse
is (-w,x,y,z).
For an SE(3) transformation matrix T, np.linalg.inv(T) can be used to compute the inverse transformation. However,
given its complexity of O(n^3), this isn't efficient. A more efficient method to compute the inverse of the
transformation is:
def inverse_pose(pose: np.ndarray):
inv_pose = np.eye(4, dtype=pose.dtype)
inv_pose[:3, :3] = pose[:3, :3].T
inv_pose[:3, 3] = -pose[:3, :3].T @ pose[:3, 3]
return inv_pose
SO(3) Rotation Linear Interpolation
def interpolate_rotation(mat1: np.ndarray, mat2: np.ndarray, mat1_weight: float):
if mat1_weight < 0 or mat1_weight > 1:
raise ValueError(f"Weight of rotation matrix should be 0-1, but given {mat1_weight}")
relative_rot = mat1.T @ mat2
# For numerical stability, first convert to quaternion and then to axis-angle for a not-perfect rotation matrix
axis, angle = transforms3d.quaternions.quat2axangle(transforms3d.quaternions.mat2quat(relative_rot))
inter_angle = (1 - mat1_weight) * angle
inter_rot = transforms3d.axangles.axangle2mat(axis, inter_angle)
SE(3) Transformation Linear Interpolation
def interpolate_transformation(mat1: np.ndarray, mat2: np.ndarray, mat1_weight: float):
if mat1_weight < 0 or mat1_weight > 1:
raise ValueError(f"Weight of rotation matrix should be 0-1, but given {mat1_weight}")
result_pose = np.eye(4)
rot1 = mat1[:3, :3]
rot2 = mat2[:3, :3]
inter_rot = interpolate_rotation(rot1, rot2, mat1_weight)
inter_pos = mat1[:3, 3] * mat1_weight + mat2[:3, 3] * (1 - mat1_weight)
result_pose[:3, :3] = inter_rot
result_pose[:3, 3] = inter_pos
return result_pose
Creating a Skew Matrix from a Vector
def skew_matrix(vec):
return np.array([[0, -vec[2], vec[1]],
[vec[2], 0, -vec[0]],
[-vec[1], vec[0], 0]])
Converting SE(3) Pose to Screw Motion
def pose2se3(pose: np.ndarray):
axis, theta = transforms3d.axangles.mat2axangle(pose[:3, :3])
skew = skew_matrix(axis)
inv_left_jacobian = np.eye(3, dtype=np.float) / theta - 0.5 * skew + (
1.0 / theta - 0.5 / np.tan(theta / 2)) * skew @ skew
v = inv_left_jacobian @ pose[:3, 3]
return np.concatenate([v, axis]), theta