a b/utils/pose_conversion.py
1
# Two functions
2
# convert (rotation matrix,translation vector) to (global 3d position, euler angles)
3
# convert (global 3d position, euler angles) to (rotation matrix,translation vector)
4
# Used functions from 'Rotation Matrix To Euler Angles' by Satya Mallick
5
# https://learnopencv.com/rotation-matrix-to-euler-angles/
6
import math
7
import numpy as np
8
9
10
# Calculates Rotation Matrix given euler angles.
11
def eulerAnglesToRotationMatrix(theta):
12
    # inputs
13
    # -- theta: tuple/list (theta_x,theta_y,theta_z) euler angles (in radians)
14
15
    R_x = np.array([[1, 0, 0],
16
                    [0, math.cos(theta[0]), -math.sin(theta[0])],
17
                    [0, math.sin(theta[0]), math.cos(theta[0])]
18
                    ])
19
20
    R_y = np.array([[math.cos(theta[1]), 0, math.sin(theta[1])],
21
                    [0, 1, 0],
22
                    [-math.sin(theta[1]), 0, math.cos(theta[1])]
23
                    ])
24
25
    R_z = np.array([[math.cos(theta[2]), -math.sin(theta[2]), 0],
26
                    [math.sin(theta[2]), math.cos(theta[2]), 0],
27
                    [0, 0, 1]
28
                    ])
29
30
    R = np.dot(R_z, np.dot(R_y, R_x))
31
32
    return R
33
34
35
# Checks if a matrix is a valid rotation matrix.
36
def isRotationMatrix(R):
37
    Rt = np.transpose(R)
38
    shouldBeIdentity = np.dot(Rt, R)
39
    I = np.identity(3, dtype=R.dtype)
40
    n = np.linalg.norm(I - shouldBeIdentity)
41
    return n < 1e-6
42
43
44
# Calculates rotation matrix to euler angles
45
# The result is the same as MATLAB except the order
46
# of the euler angles ( x and z are swapped ).
47
def rotationMatrixToEulerAngles(R):
48
    assert (isRotationMatrix(R))
49
50
    sy = math.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
51
52
    singular = sy < 1e-6
53
54
    if not singular:
55
        x = math.atan2(R[2, 1], R[2, 2])
56
        y = math.atan2(-R[2, 0], sy)
57
        z = math.atan2(R[1, 0], R[0, 0])
58
    else:
59
        x = math.atan2(-R[1, 2], R[1, 1])
60
        y = math.atan2(-R[2, 0], sy)
61
        z = 0
62
63
    return np.array([x, y, z])
64
65
66
# convert (rotation matrix,translation vector) to (global 3d position, euler angles)
67
# output is the pose at the center, which is used as the origin of its own frame
68
# e.g. tag center of apriltag
69
def transformation_to_pose(transformation):
70
    # input
71
    # -- transformation: tuple/list (3x3 rotation matrix R, 3x1 translation vector t)
72
    # output
73
    # -- pose: tuple (global position (x,y,z), euler angles (theta_x,theta_y,theta_z))
74
    assert isinstance(transformation, tuple) or isinstance(transformation, list)
75
    assert len(transformation) == 2
76
    R, t = transformation
77
    assert R.shape == (3, 3) and t.size == 3
78
    pos = t.reshape(3, )
79
    angles = rotationMatrixToEulerAngles(R)
80
81
    return (pos, angles)
82
83
84
# convert (global 3d position, euler angles) to (rotation matrix,translation vector)
85
# must be the pose data at the center, which is used as the origin of its own frame
86
def pose_to_transformation(pose):
87
    # input
88
    # -- pose: tuple (global position (x,y,z), euler angles (theta_x,theta_y,theta_z))
89
    # output
90
    # -- transformation: tuple/list (3x3 rotation matrix R, 3x1 translation vector t)
91
    assert isinstance(pose, tuple) or isinstance(pose, list)
92
    assert len(pose) == 2
93
    pos, angles = pose
94
    assert len(pos) == 3 and len(angles) == 3
95
96
    t = (np.array(pos)).reshape(3, )
97
98
    R = eulerAnglesToRotationMatrix(angles)
99
100
    return (R, t)
101
102
103
def rotation_align(z,d):
104
    """
105
    https://iquilezles.org/www/articles/noacos/noacos.htm
106
    compute rotation matrix to align vector z to vector d
107
    :param z:
108
    :param d:
109
    :return: rotation matrix M
110
    """
111
    assert np.isclose(np.linalg.norm(z),1)
112
    assert np.isclose(np.linalg.norm(d), 1)
113
    v = np.cross(z,d)
114
    c = np.sum(z*d)
115
116
    M = v.reshape(-1,1) @ v.reshape(1,-1) / (1+c) + np.array([
117
        [c, -v[2], v[1]],
118
        [v[2], c, -v[0]],
119
        [-v[1], v[0], c]
120
    ])
121
122
    return M