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.]])