Vector Algebra#
As a basic example, consider two orthonormal frames in Euclidean space. The first one is the standard frame defined with the unit matrix \(\mathbf{I}\), the second is obtained by rotating \(\mathbf{I}\) around the \(z\) axis with \(90\) degrees. A vector is defined in \(\mathbf{I}\) (the source) and we want to know it’s components in the rotated frame (the target).
[15]:
import numpy as np
# v is a vector in the old system
# in xy plane, (1, 0, 0) rotated 30° from x towards y
arr_source = np.array([3 ** 0.5 / 2, 0.5, 0])
arr_source
[15]:
array([0.8660254, 0.5 , 0. ])
NumPy#
We can build up the DCM matrix manually and carry out the operations using NumPy.
[16]:
from numpy import vstack
# old base vectors in old frame
e1 = np.array([1., 0., 0.])
e2 = np.array([0., 1., 0.])
e3 = np.array([0., 0., 1.])
# new base vectors in old frame
E1 = np.array([0., 1., 0.])
E2 = np.array([-1., 0., 0.])
E3 = np.array([0, 0., 1.])
# direction cosine matrix from old to new
DCM = vstack([E1, E2, E3])
DCM
[16]:
array([[ 0., 1., 0.],
[-1., 0., 0.],
[ 0., 0., 1.]])
From the definition of the DCM matrix, cooridnates of our vector in the target frame can be calculated as:
[17]:
arr_target = DCM @ arr_source
arr_target
[17]:
array([ 0.5 , -0.8660254, 0. ])
SymPy#
SymPy provides a mechanism for obtaiing the DCM matrix between two frames:
[18]:
from sympy.physics.vector import ReferenceFrame
source = ReferenceFrame('source')
target = source.orientnew('target', 'Body', [0, 0, 90*np.pi/180], 'XYZ')
DCM = np.array(target.dcm(source).evalf()).astype(float)
DCM[np.abs(DCM) < 1e-12] = 0.
DCM
[18]:
array([[ 0., 1., 0.],
[-1., 0., 0.],
[ 0., 0., 1.]])
Neumann#
In Neumann, the ReferenceFrame provides the machinery to establish a connection between two frames. First, we create the frames as:
[19]:
from neumann.linalg import ReferenceFrame
source = ReferenceFrame(dim=3) # this is a 3 by 3 identity matrix
target = source.orient_new('Body', [0, 0, 90*np.pi/180], 'XYZ')
The DCM matrix for transformation from the source to the target can be calculated like this:
[20]:
DCM = source.dcm(target=target)
DCM[np.abs(DCM) < 1e-12] = 0.
DCM
[20]:
ArrayBase([[ 0., 1., 0.],
[-1., 0., 0.],
[ 0., 0., 1.]])
or this
[21]:
DCM = target.dcm(source=source)
DCM[np.abs(DCM) < 1e-12] = 0.
DCM
[21]:
ArrayBase([[ 0., 1., 0.],
[-1., 0., 0.],
[ 0., 0., 1.]])
or even this, since in this case the source frame is also the top level frame:
[22]:
DCM = target.dcm()
DCM[np.abs(DCM) < 1e-12] = 0.
DCM
[22]:
ArrayBase([[ 0., 1., 0.],
[-1., 0., 0.],
[ 0., 0., 1.]])
To transform the vector into the target frame, we can use the Vector object directly, and it handles everything in the background.
[23]:
from neumann.linalg import Vector
Vector(arr_source, frame=source).show(target)
[23]:
array([ 0.5 , -0.8660254, 0. ])
Remarks#
The following calls all return the identity matrix:
[24]:
target.dcm(target=target)
target.dcm(source=target)
source.dcm(target=source)
source.dcm(source=source)
[24]:
ArrayBase([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])