Rotation¶
Euler angles¶
Extrinsic Rotation¶
Right multiply matrix
In [1]:
import sympy
from sympy.vector import AxisOrienter, CoordSys3D
x, y, theta = sympy.symbols("x y theta")
N = CoordSys3D("N")
M = AxisOrienter(theta, N.k).rotation_matrix(N)
M
Out[1]:
$\displaystyle \left[\begin{matrix}\cos{\left(\theta \right)} & \sin{\left(\theta \right)} & 0\\- \sin{\left(\theta \right)} & \cos{\left(\theta \right)} & 0\\0 & 0 & 1\end{matrix}\right]$
In [2]:
M.inv()
Out[2]:
$\displaystyle \left[\begin{matrix}- \frac{\sin^{2}{\left(\theta \right)}}{\cos{\left(\theta \right)}} + \frac{1}{\cos{\left(\theta \right)}} & - \sin{\left(\theta \right)} & 0\\\sin{\left(\theta \right)} & \cos{\left(\theta \right)} & 0\\0 & 0 & 1\end{matrix}\right]$
In [3]:
v = sympy.Matrix([x, y, 0]).T * M.subs(theta, sympy.pi/2)
v
Out[3]:
$\displaystyle \left[\begin{matrix}- y & x & 0\end{matrix}\right]$
In [4]:
import py3d
py3d.Transform.from_euler("xyz", [0.4, -0.2, 0])
Out[4]:
Transform([[ 0.98006658, 0. , 0.19866933, 0. ], [-0.07736548, 0.92106099, 0.3816559 , 0. ], [-0.18298657, -0.38941834, 0.9027011 , 0. ], [ 0. , 0. , 0. , 1. ]])
In [5]:
import py3d
axis=py3d.axis(dashed=True)
rotated_axis=py3d.axis()@py3d.Transform.from_euler("xyz", [1,2,3])
py3d.render(axis,rotated_axis)
Out[5]:
Intrinsic Rotation¶
In [6]:
import py3d
py3d.Transform.from_euler("XYZ", [0.4, -0.2, 0])
Out[6]:
Transform([[ 0.98006658, -0.07736548, 0.18298657, 0. ], [ 0. , 0.92106099, 0.38941834, 0. ], [-0.19866933, -0.3816559 , 0.9027011 , 0. ], [ 0. , 0. , 0. , 1. ]])
In [7]:
import py3d
py3d.Transform.from_rpy([1.3, 1.0, 0]) @ py3d.Transform.from_rpy([0.9, .0, 0])
Out[7]:
Transform([[ 0.54030231, 0.68032627, 0.49520661, 0. ], [ 0. , -0.58850112, 0.8084964 , 0. ], [ 0.84147098, -0.43683247, -0.31796851, 0. ], [ 0. , 0. , 0. , 1. ]])
Apply a intrinsic rotation to a point
In [9]:
import py3d
p = py3d.Vector3([1, 3, 4])
py3d.label("P", p.tolist())
py3d.render(p.as_point().paint(py3d.Color(g=1)))
t = py3d.Transform.from_euler("XYZ", [0, 0, 1])
p1 = (p @ t)
a = py3d.axis()
a.xyz @= t
py3d.label("P1", p1.tolist())
py3d.render(p1.as_point().paint(py3d.Color(r=1)), a)
Out[9]:
In [10]:
p
Out[10]:
Vector3([1., 3., 4.])
Difference between extrinsic rotation and intrinsic rotation¶
In [11]:
import py3d
e_car = py3d.car()
e_car.color = py3d.Color(g=1)
e_car.xyz @= py3d.Transform.from_euler("xyz", [0.4, -0.2, 0])
py3d.label("Extrincs", [4, 1, 3], color="green")
i_car = py3d.car()
i_car.color = py3d.Color(r=1)
i_car.xyz @= py3d.Transform.from_euler("XYZ", [0.4, -0.2, 0])
py3d.label("Intrincs", [4, -1, 2], color="red")
py3d.render(e_car, i_car)
Out[11]:
RPY¶
In py3d, rpy represents roll, pitch and yaw, it is kind of intrinsic rotation
In [12]:
import py3d, numpy
data = py3d.read_img("20220917214012.jpg")
data.as_image() @ py3d.Transform.from_rpy(p=numpy.linspace(0, py3d.pi/3, 3)) @ py3d.Transform.from_translation(z=numpy.linspace(0, 300, 3))
Out[12]:
Axis angle¶
Axis angle rotation matrix¶
In [13]:
from sympy.vector import AxisOrienter, CoordSys3D
import sympy
x, y, z, theta = sympy.symbols("x y z theta")
N = CoordSys3D('N')
expr = AxisOrienter(theta, x*N.i+y*N.j+z*N.k).rotation_matrix(N)
expr.subs(x**2+y**2+z**2, 1)
Out[13]:
$\displaystyle \left[\begin{matrix}x^{2} + \left(1 - x^{2}\right) \cos{\left(\theta \right)} & - x y \cos{\left(\theta \right)} + x y + z \sin{\left(\theta \right)} & - x z \cos{\left(\theta \right)} + x z - y \sin{\left(\theta \right)}\\- x y \cos{\left(\theta \right)} + x y - z \sin{\left(\theta \right)} & y^{2} + \left(1 - y^{2}\right) \cos{\left(\theta \right)} & x \sin{\left(\theta \right)} - y z \cos{\left(\theta \right)} + y z\\- x z \cos{\left(\theta \right)} + x z + y \sin{\left(\theta \right)} & - x \sin{\left(\theta \right)} - y z \cos{\left(\theta \right)} + y z & z^{2} + \left(1 - z^{2}\right) \cos{\left(\theta \right)}\end{matrix}\right]$
In [14]:
import py3d
py3d.Transform.from_axis_angle(axis=[[0, 0, 1], [0, 0, 1]], angle=[0.6, 1])
Out[14]:
Transform([[[ 0.82533561, 0.56464247, 0. , 0. ], [-0.56464247, 0.82533561, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[ 0.54030231, 0.84147098, 0. , 0. ], [-0.84147098, 0.54030231, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]]])
Order of rotation and translation
In [15]:
import sympy
r = sympy.symbols("r:9")
dr = sympy.symbols("dr:9")
t = sympy.symbols("t:3")
R = sympy.Matrix([
[r[0], r[1], r[2], 0],
[r[3], r[4], r[5], 0],
[r[6], r[7], r[8], 0],
[0, 0, 0, 1]
])
T = sympy.Matrix([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[t[0], t[1], t[2], 1]
])
R*T
Out[15]:
$\displaystyle \left[\begin{matrix}r_{0} & r_{1} & r_{2} & 0\\r_{3} & r_{4} & r_{5} & 0\\r_{6} & r_{7} & r_{8} & 0\\t_{0} & t_{1} & t_{2} & 1\end{matrix}\right]$
Get rotation matrix from two vectors
In [16]:
import py3d
py3d.Transform.from_two_vectors([1, 0, 0], [0, 0, 1]).as_rpy()
Out[16]:
Vector3([ 0. , -1.57079633, 0. ])
Rotation Vector¶
In [17]:
import py3d
r = py3d.Transform.from_rotation_vector([[1, 2, 3],[-1,0,7]])
r
Out[17]:
Transform([[[-0.69492056, -0.19200697, 0.69297817, 0. ], [ 0.71352099, -0.30378504, 0.6313497 , 0. ], [ 0.08929286, 0.93319235, 0.34810748, 0. ], [ 0. , 0. , 0. , 1. ]], [[ 0.71124095, 0.70173688, -0.04125129, 0. ], [-0.70173688, 0.70534791, -0.10024813, 0. ], [-0.04125129, 0.10024813, 0.99410696, 0. ], [ 0. , 0. , 0. , 1. ]]])
In [18]:
r.as_rotation_vector()
Out[18]:
Vector3([[-0.67925191, -1.35850382, -2.03775573], [-0.11142341, 0. , 0.77996389]])
Quaternion¶
In py3d, quaternion is in scalar-last format. Normaly, Vector4 is used to represent it.
In [20]:
import py3d
py3d.Vector4(x=0, y=0, z=0, w=1)
Out[20]:
Vector4([0., 0., 0., 1.])
In [21]:
import py3d
py3d.Transform.from_quaternion([0, 0, 0, 1])
Out[21]:
Transform([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]])
Get a quaternion from an axis angle pair, and get its rotation matrix
In [22]:
import py3d
angle_axis = py3d.Vector4(x=1, y=1, z=1, w=py3d.pi/2)
print(angle_axis)
q = angle_axis.from_axis_angle_to_quaternion()
print(q)
py3d.Transform.from_quaternion(q)
[1. 1. 1. 1.57079633] [0.40824829 0.40824829 0.40824829 0.70710678]
Out[22]:
Transform([[ 0.33333333, 0.9106836 , -0.24401694, 0. ], [-0.24401694, 0.33333333, 0.9106836 , 0. ], [ 0.9106836 , -0.24401694, 0.33333333, 0. ], [ 0. , 0. , 0. , 1. ]])
In [23]:
import py3d
q = [
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 0]
]
py3d.Transform.from_quaternion(q)
Out[23]:
Transform([[[ 1., 0., 0., 0.], [ 0., 1., 0., 0.], [ 0., 0., 1., 0.], [ 0., 0., 0., 1.]], [[-1., 0., 0., 0.], [ 0., -1., 0., 0.], [ 0., 0., 1., 0.], [ 0., 0., 0., 1.]], [[-1., 0., 0., 0.], [ 0., 1., 0., 0.], [ 0., 0., -1., 0.], [ 0., 0., 0., 1.]], [[ 1., 0., 0., 0.], [ 0., -1., 0., 0.], [ 0., 0., -1., 0.], [ 0., 0., 0., 1.]]])
Convert quaternion to angle axis
In [25]:
import py3d
quat = py3d.Transform.from_quaternion(
[0.18257419, 0.36514837, 0.54772256, 0.73029674])
quat.as_axis_angle()
Out[25]:
Vector4([0.26726125, 0.53452248, 0.80178373, 1.50408018])
Convert RPY to quaternion¶
Return quaternion with sequence "xyzw"
In [27]:
import py3d
rpy = [
[0, 0, -py3d.pi],
[0, 0, -py3d.pi-0.01],
[0, 0, -py3d.pi/3],
[-1, 0, 0],
[-py3d.pi, 0, 1],
[0, -py3d.pi, 0]]
py3d.Transform.from_rpy(rpy).as_quaternion()
Out[27]:
Vector4([[ 0. , 0. , -1. , 0. ], [ 0. , 0. , 0.9999875 , 0.00499998], [ 0. , 0. , -0.5 , 0.8660254 ], [-0.47942554, 0. , 0. , 0.87758256], [-0.87758256, 0.47942554, 0. , 0. ], [ 0. , -1. , 0. , 0. ]])
In [29]:
import py3d
# positions
p = [[3.24544068, -1.4610586, 1.21331756],
[3.66378017, -1.32072563, 1.89712674],
[-5.0884622, -2.48626808, 1.68773464],
[-5.47338134, -2.65634697, 2.02463642],
[1.830746, -0.8155359, 1.90245186],
[-0.7094184, -0.84719837, 1.4467056],
[-1.72178753, -0.681502, 1.17706321],
[-3.88463547, -1.20610078, 1.14303617],
[-4.527405, -3.12150274, 0.94426914],
[4.13260871, -1.71061771, 1.49295544],
[3.25896384, -1.46451182, 0.9032174],
[-3.63891521, -1.03317465, 1.11405222]]
# quaternions
q = [[0.00307048, -0.27852711, -0.24115858, 0.92965357],
[0.00955487, -0.328664, -0.25972646, 0.90798174],
[0.05519327, 0.22308439, 0.22751421, 0.94626864],
[0.05064761, 0.24596963, 0.23314524, 0.93945572],
[-0.01006987, -0.17448035, -0.11591101, 0.97776267],
[0.01230182, -0.03710485, 0.00123949, 0.99923489],
[0.02991609, 0.0383105, 0.0729396, 0.99615117],
[0.05252438, 0.12527874, 0.1242716, 0.98290538],
[-0.09333274, 0.14651227, 0.2808575, 0.94389735],
[0.00967634, -0.29085732, -0.28211318, 0.91417752],
[0.00214324, -0.25691119, -0.23230781, 0.93809655],
[0.04813863, 0.1177435, 0.11584668, 0.98508816]]
vertice = py3d.Vector3(
z=[0, 1]) @ py3d.Transform.from_quaternion(q) @ py3d.Transform.from_translation(p)
directions = vertice.as_linesegment()
directions.start.color = py3d.Color(b=1)
directions.end.color = py3d.Color(r=1)
py3d.render(directions)
Out[29]: