Basic frames usage#
Illustrates frames layer usage
This notebook demonstrates how to use the Frames layer in kdFlex for working with reference frames.

import Karana.Math as km
import Karana.Frame as kf
Frame container#
The reference frames we create will all be managed by a FrameContainer. Here, the "root_fr" argument is the name of an implicitly created root Frame.
frames = kf.FrameContainer("root_fr")
Defining a helper function to visualize the relationship between reference frames. We’ll use this throughout the notebook to see what is going on:
def visualizeFrames(frames: kf.FrameContainer):
"""Visualize a set of frames.
Parameters
----------
frames : kf.FrameContainer
Container with all frames to show.
"""
from IPython.display import display, IFrame
display_server = frames.showFramesGraph(port=0, chains=True, print_connection_info=False)
display(IFrame(src=display_server.getUrl(), width="100%", height="400px"))
As you can see, a Frame with the name, "root_fr" has automatically been created. This frame forms the root of the Frame tree we will build up.
visualizeFrames(frames)
root_fr = frames.root()
root_fr.name()
'root_fr'
Adding a reference frame#
Here we create another Frame, ch1_fr, whose properties will be defined relative to root_fr:
ch1_fr = kf.Frame("ch1_fr", frames)
visualizeFrames(frames)
At this point it is unspecified how ch1_fr relates to the rest of the frame tree. To do anything useful, we must first attach ch1_fr to another Frame. This can be done by creating a PrescribedFrameToFrame, to define the pose of ch1_fr relative to its parent frame in the tree:
root_to_ch1 = kf.PrescribedFrameToFrame(root_fr, ch1_fr)
Now we can see that there is an edge from root_fr to ch1_fr, that defines their relative transform, velocity, and acceleration:
visualizeFrames(frames)
print(root_to_ch1.relTransform())
print(root_to_ch1.relSpVel())
print(root_to_ch1.relSpAccel())
Karana.Math.HomTran(
Karana.Math.UnitQuaternion(0, 0, 0, 1),
array([0., 0., 0.])
)
Karana.Math.SpatialVelocity(
<Quantity([0. 0. 0.], '1 / second')>,
<Quantity([0. 0. 0.], 'meter / second')>
)
Karana.Math.SpatialAcceleration(
<Quantity([0. 0. 0.], '1 / second ** 2')>,
<Quantity([0. 0. 0.], 'meter / second ** 2')>
)
In practice, a FrameToFrame will usually be updated by system dynamics, or even based on the ephemerides of celestial bodies via SPICE data. Here, for the sake of example, we’ll set the transform directly:
root_to_ch1.setRelTransform(km.HomTran(vec=[1, 2, 3]))
root_to_ch1.relTransform()
Karana.Math.HomTran(
Karana.Math.UnitQuaternion(0, 0, 0, 1),
array([1., 2., 3.])
)
Similarly, we can assign a relative spatial velocity:
omega_x = km.SpatialVelocity([10, 0, 0], [0, 0, 0])
root_to_ch1.setRelSpVel(omega_x)
root_to_ch1.relSpVel()
Karana.Math.SpatialVelocity(
<Quantity([10. 0. 0.], '1 / second')>,
<Quantity([0. 0. 0.], 'meter / second')>
)
As well as a relative spatial acceleration:
alpha_x = km.SpatialAcceleration([0.1, 0, 0], [0, 0, 0])
root_to_ch1.setRelSpAccel(alpha_x)
root_to_ch1.relSpAccel()
Karana.Math.SpatialAcceleration(
<Quantity([0.1 0. 0. ], '1 / second ** 2')>,
<Quantity([0. 0. 0.], 'meter / second ** 2')>
)
For any FrameToFrame, we refer to the first, typically more inboard frame as the oframe, and the second frame as the pframe:
root_to_ch1.oframe() == root_fr
True
root_to_ch1.pframe() == ch1_fr
True
Chained frame to frames#
First we create another child frame in a similar manner:
ch2_fr = kf.Frame("ch2_fr", frames)
root_to_ch2 = kf.PrescribedFrameToFrame(root_fr, ch2_fr)
root_to_ch2.setRelTransform(km.HomTran(vec=[1, 2, 5]))
visualizeFrames(frames)
We often need to compute the relative spatial properties of an arbitrary pair of frames. Rather than manually walking and composing transforms along the tree of frames, we can get a so-called ChainedFrameToFrame to query relative properties between any pair of frames. In our visualization, this is represented as a dotted line. Note that they have a relative translation of \(2\) in the \(z\)-direction:
ch1_to_ch2 = ch1_fr.frameToFrame(ch2_fr)
visualizeFrames(frames)
print(ch1_to_ch2.relTransform())
Karana.Math.HomTran(
Karana.Math.UnitQuaternion(0, 0, 0, 1),
array([0., 0., 2.])
)
As seen earlier, we can also query the relative spatial velocity and acceleration of ch1 relative to ch2. Since ch2 has an offset relative to ch1, and ch1 is rotating, ch2 has a linear velocity relative to ch1:
print(ch1_to_ch2.relSpVel())
print(ch1_to_ch2.relSpAccel())
Karana.Math.SpatialVelocity(
<Quantity([-10. 0. 0.], '1 / second')>,
<Quantity([ 0. 20. 0.], 'meter / second')>
)
Karana.Math.SpatialAcceleration(
<Quantity([-0.1 0. 0. ], '1 / second ** 2')>,
<Quantity([ 0.e+00 2.e-01 -2.e+02], 'meter / second ** 2')>
)
import atexit
def cleanup():
"""Clean up the simulation."""
global root_fr, ch1_fr, ch2_fr, root_to_ch1, root_to_ch2, ch1_to_ch2
del root_fr, ch1_fr, ch2_fr, root_to_ch1, root_to_ch2, ch1_to_ch2
import Karana.Core as kc
kc.discard(frames)
atexit.register(cleanup)
<function __main__.cleanup()>