====== geo.pyの基本的な使い方 ====== ====== geo_basic ====== I wrote a module called [[./geo.py|geo.py]] that performs 3D geometric operations such as vectors, rotation matrices, and coordinate transformations. This ipynb file shows a basic usage of the geo module. This was run under the following conditions, - ProBook 474s, メモリ:8 GB,CPU:Core™ i5-3230M,OS: Ubuntu 20.04 - jupyter notebook,python3 The file is [[./matrix_vs_quaternion.ipynb|matrix_vs_quaternion.ipynb]]. ===== import module ===== Note: geo.py imports the math module in the form “from math import *”. geo.py defines the VECTOR class, MATRIX class, QUATERNION class, and FRAME class. The class names are all capitalized to avoid name collisions with other similar classes. from geo import * ===== VECTOR ===== VECTOR class is defined as a subclass of list. class VECTOR(list) : def __init__(self, x=0.0, y=0.0, z=0.0, vec=[]) : # vec is provided to create a copy of a VECTOR. def __repr__(self) : def dot(self, other) : # inner product def __neg__(self) : # negative vector def __add__(self, other) : # addition def __radd__(self, other) : # addition to avoid list concatenation def __sub__(self, other) : # subtraction def __mul__(self, other) : # cross product def __rmul__(self, other) : # multiplication with scalar and MATRIX(this is defined in MATRIX) def __abs__(self) : # absolute value def normalize(self) : # normalizetion ==== generationg VECTORs ==== v1=VECTOR(1,2,3) v2=VECTOR(y=3) print(v1) print(v2) v:[1.0, 2.0, 3.0] v:[0.0, 3.0, 0.0] ===== addition and subtraction ===== v1+v2 v:[1.0, 5.0, 3.0] v1-v2 v:[1.0, -1.0, 3.0] ==== inner product ==== v1.dot(v2) 6.0 ==== cross product ==== v1 * v2 v:[-9.0, 0.0, 3.0] ==== scalar multiplication ==== 3*v1 v:[3.0, 6.0, 9.0] v1*3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in ----> 1 v1*3 ~/openrtm/Robots/core/geo.py in __mul__(self, other) 24 def __mul__(self, other) : # cross protuct 25 if not isinstance(other, VECTOR) : ---> 26 raise TypeError('error') 27 tmp = VECTOR(); 28 tmp[0] = self[1]*other[2] - self[2]*other[1] TypeError: error ==== negatification ==== -v1 v:[-1.0, -2.0, -3.0] ==== absolute value ==== abs(v1) 3.7416573867739413 ==== nomalization ==== v3=v1.normalize() print("v1: ", v1) print("v3: ", v3) print("abs(v3): ", abs(v3)) v1: v:[1.0, 2.0, 3.0] v3: v:[0.2672612419124244, 0.5345224838248488, 0.8017837257372732] abs(v3): 1.0 ==== exercises ==== === Find the angle between VECTOR(0.866, 0.5, 0) and VECTOR(0.707, 0.707, 0). === a=VECTOR(0.866,0.5,0) b=VECTOR(0.707,0.707,0) angle_rad = acos(a.dot(b)/abs(a)/abs(b)) angle_deg = 180*angle_rad/pi print(angle_deg) 14.999272219172607 === Find the rotation angle around the z-axis of VECTOR(-0.707,-0.707, 0) from VECTOR(1,0,0) . === a wrong answer a=VECTOR(1,0,0) b=VECTOR(-0.707,-0.707,0) angle_rad = acos(a.dot(b)/abs(a)/abs(b)) angle_deg = 180*angle_rad/pi print(angle_deg) 135.0 the right answer x=VECTOR(1,0,0) y=VECTOR(0,1,0) b=VECTOR(-0.707,-0.707,0) co=x.dot(b) si=x.dot(b) angle_rad = atan2(si,co) angle_deg = 180*angle_rad/pi print(angle_deg) -135.0 ===== MATRIX ===== MATRIX class is defined as subclass of list. It is a list of 3 lists(rows of the matrix). MATRIX here is not a general matrix. It is an orthogonal matrix which represents rotation operation (exclude mirror operation) or orientation of a right-hand coordinate system. So it does not have real inverse operation. Here it is just same as transpose operation. class MATRIX(list) : def __init__(self, mat=[], a=None, b=None, c=None, angle=None, axis=None) : # a,b,c are rotations around x,y,z axes def __repr__(self) : def col(self, idx, arg=[]) : # extraction of column VECTOR def row(self, idx, arg=[]) : # extraction of row VECTOR def rot_axis(self) : # exraction of rotation angle and axis def quaternion(self): # quaternion for rotation def abc(self) : # extraction of alpha, beta, gamma, rotation around x,y,z def rpy(self) : # extraction of roll, pitch, yaw, rotation around x,y,z def trans(self) : # transposition def __neg__(self) : # inverse, ie., transposition def __mul__(self, other) : # multiplication with MATRIX or VECTOR ==== generation of MATRIXs (matrices) ==== generation of a rotation matrix which rotate pi/4 around x-axis. R1=MATRIX(a=pi/4) print(R1) m:[[1.0, 0.0, 0.0], [0.0, 0.7071067811865476, -0.7071067811865475], [0.0, 0.7071067811865475, 0.7071067811865476]] and rotate pi/6 around y-axis and rotate pi/3 around z-axis R2=MATRIX(b=pi/6) R3=MATRIX(c=pi/3) ==== product of matrices ==== R123=R1*R2*R3 R123 m:[[0.43301270189221946, -0.75, 0.49999999999999994], [0.7891491309924314, 0.04736717274537666, -0.6123724356957945], [0.4355957403991576, 0.6597396084411711, 0.6123724356957946]] ==== product of a matrix and a vector, ie. , coordinate transformation or rotation of the vector ==== R1*VECTOR(0,1,0) v:[0.0, 0.7071067811865476, 0.7071067811865475] ==== extraction of abc(alph, beta, gamma Euler angles) ==== R123.abc() [0.7853981633974482, 0.5235987755982988, 1.0471975511965976] ==== extraction of rpy(roll, pitch, yaw Euler angles) ==== R123.rpy() [0.8226160330164501, -0.45069999992702, 1.0689453418473993] (R3*R2*R1).rpy() [0.7853981633974482, 0.5235987755982988, 1.0471975511965976] ==== generation of MATRIX with rotation axis and angle (rodrigues) ==== R4=MATRIX(axis=VECTOR(1,1,0),angle=pi/4) R4 m:[[0.8535533905932737, 0.14644660940672619, 0.4999999999999999], [0.14644660940672619, 0.8535533905932737, -0.4999999999999999], [-0.4999999999999999, 0.4999999999999999, 0.7071067811865476]] ==== extraction of rotation axis and angle (rodrigues) ==== R4.rot_axis() [0.7853981633974483, v:[0.7071067811865475, 0.7071067811865475, 0.0]] ==== transpose or inverse of MATRIX ==== print(R1.trans()) R1*R1.trans() m:[[1.0, 0.0, 0.0], [0.0, 0.7071067811865476, 0.7071067811865475], [0.0, -0.7071067811865475, 0.7071067811865476]] m:[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] print(-R1) R1*(-R1) m:[[1.0, 0.0, 0.0], [0.0, 0.7071067811865476, 0.7071067811865475], [0.0, -0.7071067811865475, 0.7071067811865476]] m:[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] R1.trans().rot_axis() [0.7853981633974483, v:[-0.9999999999999999, 0.0, 0.0]] ==== extraction of column and row as a VECTOR ==== R2.col(0) v:[0.8660254037844387, 0.0, -0.49999999999999994] R2.row(0) v:[0.8660254037844387, 0.0, 0.49999999999999994] ==== MATRIX to QUATERNION ==== R1.quaternion() q:(0.9238795325112867, v:[0.3826834323650897, 0.0, 0.0]) ===== QUATERNION ===== QUATERNION class has a real part and an imaginary part. The imaginary part is defined as a VECTOR. Quaternion itself is not only for these operations, it is a kind of number. But QUATERNION here is mainly used for representing rotation operation or orientation of a coordinate system. So only the methods necessary for them are implemented. class QUATERNION(object) : # quaternion mainly for rotation def __init__(self,w_v=None, a=None, b=None, c=None, angle=None, axis=None) : w_v is a list [w,v] def __repr__(self) : def __mul__(self, other) : def __neg__(self) : def __abs__(self) : def normalize(self) : def inv(self) : def conjugate(self) : def matrix(self) : ==== generation of QUATERNION ==== q1 = QUATERNION(a=pi/4) q1 q:(0.9238795325112867, v:[0.3826834323650898, 0.0, 0.0]) q2 = QUATERNION(b=pi/6) q3 = QUATERNION(c=pi/3) === generation of QUATERNION with [w, v] === you shouod give arguments as [cos(th/2), sin(th/2)*axis.normalize()] co=cos((pi/4)/2) si=sin((pi/4)/2) q4=QUATERNION([co, si*VECTOR(1,0,0)]) q4 q:(0.9238795325112867, v:[0.3826834323650898, 0.0, 0.0]) === generation of QUATERNION with rotation angle and axis === axis does not have to be normalized. q5=QUATERNION(angle=pi/4, axis=VECTOR(2,0,0)) q5 q:(0.9238795325112867, v:[0.3826834323650898, 0.0, 0.0]) ==== product of QUATERNION ==== q123=q1*q2*q3 ==== QUATERNION to MATRIX ==== q123.matrix() m:[[0.43301270189221946, -0.7500000000000001, 0.5], [0.7891491309924316, 0.04736717274537666, -0.6123724356957946], [0.4355957403991576, 0.6597396084411712, 0.6123724356957947]] R123 m:[[0.43301270189221946, -0.75, 0.49999999999999994], [0.7891491309924314, 0.04736717274537666, -0.6123724356957945], [0.4355957403991576, 0.6597396084411711, 0.6123724356957946]] ==== conjugate of QUATERNION, which is inverse of rotational quaternion. ==== q1*q1.conjugate() q:(1.0, v:[0.0, 0.0, 0.0]) ==== inverse of QUATERNION ==== inverse for any QUATERNION q4=QUATERNION([2,VECTOR(1,2,3)]) q4 q:(2, v:[1.0, 2.0, 3.0]) q4*q4.inv() q:(4.242640687119286, v:[0.0, 0.0, 0.0]) ==== absolute value and nomalization ==== abs(q4) 4.242640687119285 q5=q4.normalize() q4 q:(2, v:[1.0, 2.0, 3.0]) q5 q:(0.47140452079103173, v:[0.23570226039551587, 0.47140452079103173, 0.7071067811865476]) ==== rotattion of vector ==== q1*QUATERNION([0,VECTOR(0,1,0)])*q1.conjugate() q:(0.0, v:[0.0, 0.7071067811865475, 0.7071067811865476]) ===== FRAME ===== FRAME has a MATRIX and a VECTOR which represents orientation and position of coordinate system. It also represets transformation of coordinate system. class FRAME(object) : def __init__(self, frm=[], mat=[], vec=[], xyzabc=[], xyzrpy=[]) : def __repr__(self) : def xyzabc(self) : def xyzrpy(self) : def __neg__(self) : # inverse def __mul__(self, other) : # multiplication with frame or positional vector ==== generation of FRAME ==== F1 = FRAME(mat=R123,vec=v1) F1 f:(m:[[0.43301270189221946, -0.75, 0.49999999999999994], [0.7891491309924314, 0.04736717274537666, -0.6123724356957945], [0.4355957403991576, 0.6597396084411711, 0.6123724356957946]],v:[1.0, 2.0, 3.0]) F2=FRAME(xyzabc=[1,2,3,pi/4,pi/6,pi/3]) F2 f:(m:[[0.43301270189221946, -0.75, 0.49999999999999994], [0.7891491309924314, 0.04736717274537666, -0.6123724356957945], [0.4355957403991576, 0.6597396084411711, 0.6123724356957946]],v:[1.0, 2.0, 3.0]) F3=FRAME(xyzrpy=[1,2,4,pi/4,pi/6,pi/3]) F3 f:(m:[[0.43301270189221946, -0.4355957403991577, 0.7891491309924313], [0.75, 0.6597396084411711, -0.04736717274537655], [-0.49999999999999994, 0.6123724356957945, 0.6123724356957946]],v:[1.0, 2.0, 4.0]) ==== extraction of xyzabc ==== F1.xyzabc() [1.0, 2.0, 3.0, 0.7853981633974482, 0.5235987755982988, 1.0471975511965976] F3.xyzabc() [1.0, 2.0, 4.0, 0.07719655674229194, 0.9094224366744116, 0.7883719209238913] ==== extraction of xyzrpy ==== F1.xyzrpy() [1.0, 2.0, 3.0, 0.8226160330164501, -0.45069999992702, 1.0689453418473993] F3.xyzrpy() [1.0, 2.0, 4.0, 0.7853981633974482, 0.5235987755982988, 1.0471975511965976] ==== product of frames ==== product of frames represents the coordinate transformation. F1*F2 f:(m:[[-0.18656397804474475, -0.03041510175761164, 0.9819718955658528], [0.11234453608956696, -0.9936245501365428, -0.009431785449894092], [0.9759982516925634, 0.10855954564674164, 0.18879151925346918]],v:[1.4330127018922192, 1.0467661693958012, 6.592192264368883]) ==== product of a frame and a vector ==== product of a frame and a vector represents the coordinate transformation of the position vector. F1*v2 = R123*v2+v1 F1*v2 v:[-1.25, 2.14210151823613, 4.979218825323513] R123*v2 v:[-2.25, 0.14210151823613, 1.9792188253235132] R123*v2+v1 v:[-1.25, 2.14210151823613, 4.979218825323513]