C++ Module: spinningBodyNDOFStateEffector

Executive Summary

The N-degree-of-freedom spinning body class is an instantiation of the state effector abstract class. The integrated test is validating the interaction between the N-DoF spinning body module and the rigid body hub that it is attached to. In this case, an N-DoF spinning body system has four masses and four inertia tensors. The lower axis is attached to the hub and the upper axis is attached to the lower body. This module can represent multiple different effectors, with any number of degrees of freedom. A spring and damper can be included in each axis, and an optional motor torque can be applied on each spinning axis.

Nominally, each degree of freedom corresponds to an additional rigid body link. However, by setting the mass and the inertia of to 0, the module can simulate multiple degrees of freedom for the same spinning body instead.

Message Connection Descriptions

The following table lists all the module input and output messages. The module msg variable name is set by the user from python. The msg type contains a link to the message structure definition, while the description provides information on what this message is used for.

spinningBodyNDOFStateEffector module input and output messages

Module I/O Messages

Msg Variable Name

Msg Type

Description

spinningBodyOutMsgs

HingedRigidBodyMsgPayload

Output vector of messages containing the spinning body state angle and angle rate.

motorTorqueInMsg

ArrayMotorTorqueMsgPayload

(Optional) Motor torques for the axes in the order bodies were added with addSpinningBody().

motorLockInMsg

ArrayEffectorLockMsgPayload

(Optional) Lock commands for the axes in the order bodies were added with addSpinningBody().

spinningBodyConfigLogOutMsgs

SCStatesMsgPayload

Output vector of messages containing the spinning body inertial position and attitude states.

Detailed Module Description

An N-DoF spinning body has 2N states: theta, thetaDot for each degree of freedom.

Command Array Indexing

One lock message controls the degrees of freedom within this module. Element effectorLockFlag[i] controls the axis of the body added by the i-th call to addSpinningBody(), counting from zero. Use 0 for a free axis and 1 for a locked axis. For a three-body chain, [1, 0, 1] locks the first and third axes and leaves the second free to rotate. Motor torque commands use the same ordering in motorTorque[i]. Set one entry for every body in the chain.

These indexes identify degrees of freedom within a single module, independently of effectorID and the order in which module instances are added to the spacecraft. Connect the messages to spinningBodyEffector, which owns the chain, rather than to an individual SpinningBody. Use separate lock and torque messages for module instances that need independent commands. Instances sharing a message read the same array starting at element [0]. See C++ Module: spinningBodyOneDOFStateEffector for an example of independent lock messages.

The lock and motor torque payload arrays each contain MAX_EFF_CNT entries. When using either input message, limit the chain to that many degrees of freedom. Spacecraft initialization and Reset() raise a BasiliskError if either input is linked and the chain exceeds this limit, even if the message has not been written. Input processing repeats the check to catch messages connected after initialization. Larger chains are allowed when neither array input is linked.

Mathematical Modeling

See the following conference paper for a detailed description of this model.

Note

J. Vaz Carneiro, C. Allard and H. Schaub, “Effector Dynamics For Sequentially Rotating Rigid Body Spacecraft Components”, AAS Astrodynamics Specialist Conference, Bog Sky, MT, Aug. 13-17, 2023

User Guide

This section is to outline the steps needed to setup a Spinning Body N DoF State Effector in Python using Basilisk.

  1. Import the spinningBodyNDOFStateEffector class:

    from Basilisk.simulation import spinningBodyNDOFStateEffector
    
  2. Create an instantiation of a Spinning body:

    spinningBodyEffector = spinningBodyNDOFStateEffector.SpinningBodyNDOFStateEffector()
    
  3. Define all physical parameters for each spinning body. For example:

    spinningBody = spinningBodyNDOFStateEffector.SpinningBody()
    spinningBody.setMass(25.0)
    spinningBody.setISPntSc_S([[50, 0.0, 0.0],
                                [0.0, 40, 0.0],
                                [0.0, 0.0, 30]])
    spinningBody.setDCM_S0P([[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])
    spinningBody.setR_ScS_S([[0.2],
                              [-0.3],
                              [0.1]])
    spinningBody.setR_SP_P([[-0.05],
                             [0.0],
                             [0.1]])
    spinningBody.setSHat_S([[0], [0], [1]])
    spinningBodyEffector.addSpinningBody(spinningBody)
    
  4. (Optional) Define initial conditions of the effector. Default values are zero states:

    spinningBody.setThetaInit(10.0 * macros.D2R)
    spinningBody.setThetaDotInit(-1.0 * macros.D2R)
    
  5. (Optional) Define spring and damper coefficients. Default values are zero states:

    spinningBody.setK(100)
    spinningBody.setC(20)
    
  6. (Optional) Define a unique name for each state. If you have multiple spinning bodies, they each must have a unique name. If these names are not specified, then the default names are used which are incremented by the effector number:

    spinningBodyEffector.setNameOfThetaState("spinningBodyTheta")
    spinningBodyEffector.setNameOfThetaDotState("spinningBodyThetaDot")
    
  7. (Optional) Connect a command torque message. This example commands the single body added above:

    cmdArray = messaging.ArrayMotorTorqueMsgPayload()
    cmdArray.motorTorque = [cmdTorque]  # [Nm]
    cmdMsg = messaging.ArrayMotorTorqueMsg().write(cmdArray)
    spinningBodyEffector.motorTorqueInMsg.subscribeTo(cmdMsg)
    
  8. (Optional) Connect an axis-locking message. This example locks the single body added above:

    lockArray = messaging.ArrayEffectorLockMsgPayload()
    lockArray.effectorLockFlag = [1]
    lockMsg = messaging.ArrayEffectorLockMsg().write(lockArray)
    spinningBodyEffector.motorLockInMsg.subscribeTo(lockMsg)
    
  9. The angular states of the body are created using an output vector of messages spinningBodyOutMsgs.

  10. The spinning body config log state output messages is spinningBodyConfigLogOutMsgs.

  11. Add the effector to your spacecraft:

    scObject.addStateEffector(spinningBodyEffector)
    

    See C++ Module: spacecraft documentation on how to set up a spacecraft object.

  12. Add the module to the task list:

    unitTestSim.AddModelToTask(unitTaskName, spinningBodyEffector)
    

Initialization and Reset

Attaching the effector to a spacecraft causes its configuration to be validated when the spacecraft registers the effector states. Initialization requires at least one body and a positive mass for the final body. It also rejects an invalid initial rotation matrix or an invalid inertia tensor for any body with positive mass. Spin axes are normalized and validated by setSHat_S() when configured.

The spacecraft drives the effector dynamics. Add the effector to a task to process its optional torque, lock, and reference inputs and to update its output messages through UpdateState().

When the effector is scheduled, its Reset() repeats the chain validation. Reset() does not reset the integrated angle or angle-rate states; those states receive their configured initial values when they are registered with the spacecraft dynamics.

Hosting a Dynamic Effector

This effector supports the branching described in Advanced: Effector Module Branching, so a compatible dynamic effector can be carried by one of the spinning bodies rather than by the hub:

spinningBodyEffector.addDynamicEffector(childEffector, segment)

Here segment is the one-based spinning body number, counting outward from the hub, so 1 is the spinning body attached to the hub.

This effector then makes its inertial position, velocity, attitude, and angular velocity available in place of the hub’s, and the child reads whichever of the four its model needs. Any geometry given to the child is expressed in that spinning body’s frame rather than the hub body frame. Both this effector and the child are still added to the task in the usual way.

Finite Configuration Values

The combined body mass must remain finite. All configured masses, spring and damping coefficients, initial angles and angular rates, position offsets, and inertia entries must be finite. This requirement also applies to inertia entries on massless bodies; the existing rules for inertia realizability remain unchanged. The frame rotation checks reject non-finite DCMs as well as improper rotations.

Spin axes must contain only finite components and have a norm strictly greater than 0.01. Normalization scales the components before computing their norm, so very large finite axes retain the correct direction. Repeated resets preserve integrated states and commands.

setMass(), setK(), setC(), and setSHat_S() reject invalid values before replacing the previous setting. Other configured values are checked before state registration and from Reset(). Massless intermediate bodies remain supported; the final body still requires a strictly positive mass. Invalid values raise BasiliskError.


struct SpinningBody
#include <spinningBodyNDOFStateEffector.h>

Struct containing all the spinning bodies variables.

Public Functions

void setMass(double mass)

setter for mass property

inline void setR_SP_P(Eigen::Vector3d r_SP_P)

setter for r_SP_P property

inline void setR_ScS_S(Eigen::Vector3d r_ScS_S)

setter for r_ScS_S property

inline void setISPntSc_S(const Eigen::Matrix3d &ISPntSc_S)

setter for ISPntSc_S property

void setSHat_S(Eigen::Vector3d sHat_S)

setter for sHat_S property

inline void setDCM_S0P(const Eigen::Matrix3d &dcm_S0P)

setter for dcm_S0P property

void setK(double k)

setter for k property

void setC(double c)

setter for c property

inline void setThetaInit(double thetaInit)

setter for thetaInit property

inline void setThetaDotInit(double thetaDotInit)

setter for thetaDotInit property

inline double getMass() const

getter for mass property

inline Eigen::Vector3d getR_SP_P() const

getter for r_SP_P property

inline Eigen::Vector3d getR_ScS_S() const

getter for r_ScS_S property

inline Eigen::Matrix3d getISPntSc_S() const

getter for ISPntSc_S property

inline Eigen::Vector3d getSHat_S() const

getter for sHat_S property

inline Eigen::Matrix3d getDCM_S0P() const

getter for dcm_S0P property

inline double getK() const

getter for k property

inline double getC() const

getter for c property

inline double getThetaInit() const

getter for thetaInit property

inline double getThetaDotInit() const

getter for thetaDotInit property

Private Functions

template<typename Type>
inline void assignStateParamNames(Type effector)

Assign the state engine parameter names

Private Members

double thetaInit = 0.0

[rad] initial spinning body angle

double thetaDotInit = 0.0

[rad/s] initial spinning body angle rate

double k = 0.0

[N-m/rad] torsional spring constant

double c = 0.0

[N-m-s/rad] rotational damping coefficient

double mass = 1.0

[kg] spinning body mass

Eigen::Vector3d r_SP_P = Eigen::Vector3d::Zero()

[m] vector pointing from parent frame P origin to spinning frame S origin in P frame components

Eigen::Vector3d r_ScS_S = Eigen::Vector3d::Zero()

[m] vector pointing from spinning frame S origin to point Sc (center of mass of the spinner) in S frame components

Eigen::Vector3d sHat_S = {1.0, 0.0, 0.0}

spinning axis in S frame components

Eigen::Matrix3d dcm_S0S = Eigen::Matrix3d::Identity()

DCM from the S0 frame to S frame (rotated by theta).

Eigen::Matrix3d dcm_S0P = Eigen::Matrix3d::Identity()

DCM from the parent frame to the S0 frame (S frame for theta=0).

Eigen::Matrix3d ISPntSc_S = Eigen::Matrix3d::Identity()

[kg-m^2] Inertia of spinning body about point Sc in S frame components

double theta = 0.0

[rad] current spinning body angle

double thetaDot = 0.0

[rad/s] current spinning body angle rate

double thetaRef = 0.0

[rad] reference spinning body angle

double thetaDotRef = 0.0

[rad/s] reference spinning body angle rate

double u = 0.0

[N-m] initial spinning body angle

bool isAxisLocked = false

axis lock flag

Eigen::Vector3d sHat_B = {1.0, 0.0, 0.0}

spinning axis in B frame components

Eigen::Vector3d r_SP_B = Eigen::Vector3d::Zero()

[m] vector pointing from parent frame P origin to spinning frame S origin in B frame components

Eigen::Vector3d r_SB_B = Eigen::Vector3d::Zero()

[m] vector pointing from body frame B origin to spinning frame S origin in B frame components

Eigen::Vector3d r_ScS_B = Eigen::Vector3d::Zero()

[m] vector pointing from spinning frame S origin to point Sc (center of mass of the spinner) in B frame components

Eigen::Vector3d r_ScB_B = Eigen::Vector3d::Zero()

[m] vector pointing from body frame B origin to point Sc (center of mass of the spinner) in B frame components

Eigen::Vector3d rPrime_SP_B = Eigen::Vector3d::Zero()

[m/s] body frame time derivative of r_SP_B in B frame components

Eigen::Vector3d rPrime_SB_B = Eigen::Vector3d::Zero()

[m/s] body frame time derivative of r_SB_B in B frame components

Eigen::Vector3d rPrime_ScS_B = Eigen::Vector3d::Zero()

[m/s] body frame time derivative of r_ScS_B in B frame components

Eigen::Vector3d rPrime_ScB_B = Eigen::Vector3d::Zero()

[m/s] body frame time derivative of r_ScB_B in B frame components

Eigen::Vector3d rDot_ScB_B = Eigen::Vector3d::Zero()

[m/s] inertial time derivative of r_ScB_B in B frame components

Eigen::Vector3d omega_SP_B = Eigen::Vector3d::Zero()

[rad/s] angular velocity of the S frame wrt the P frame in B frame components

Eigen::Vector3d omega_SB_B = Eigen::Vector3d::Zero()

[rad/s] angular velocity of the S frame wrt the B frame in B frame components

Eigen::Vector3d omega_SN_B = Eigen::Vector3d::Zero()

[rad/s] angular velocity of the S frame wrt the N frame in B frame components

Eigen::Vector3d extForce_S = Eigen::Vector3d::Zero()

[N] external force acting on the spinning body in S frame components

Eigen::Vector3d extTorquePntS_S = Eigen::Vector3d::Zero()

[N-m] external torque acting on the spinning body about point Sc in S frame components

Eigen::Matrix3d ISPntSc_B = Eigen::Matrix3d::Identity()

[kg-m^2] inertia of spinning body about point Sc in S frame components

Eigen::Matrix3d IPrimeSPntSc_B = Eigen::Matrix3d::Zero()

[kg-m^2] body frame derivative of the inertia of spinning body about point Sc in S frame components

Eigen::Matrix3d dcm_BS = Eigen::Matrix3d::Identity()

DCM from spinner frame to body frame.

Eigen::Matrix3d rTilde_ScB_B = Eigen::Matrix3d::Zero()

[m] tilde matrix of r_ScB_B

Eigen::Matrix3d omegaTilde_SB_B = Eigen::Matrix3d::Zero()

[rad/s] tilde matrix of omega_SB_B

std::vector<DynamicEffector*> dynEffectors

Vector of dynamic effectors attached.

std::string nameOfInertialPositionProperty

identifier for the inertial position property

std::string nameOfInertialVelocityProperty

identifier for the inertial velocity property

std::string nameOfInertialAttitudeProperty

identifier for the inertial attitude property

std::string nameOfInertialAngVelocityProperty

identifier for the inertial angular velocity property

Eigen::Vector3d r_ScN_N

[m] position vector of the spinning body center of mass Sc relative to the inertial frame origin N

Eigen::Vector3d v_ScN_N

[m/s] inertial velocity vector of Sc relative to inertial frame

Eigen::MatrixXd *r_SN_N

[m] position vector of the spinning body frame origin S relative to the inertial frame origin N

Eigen::MatrixXd *v_SN_N

[m/s] inertial velocity vector of S relative to inertial frame

Eigen::MatrixXd *sigma_SN

MRP attitude of frame S relative to inertial frame.

Eigen::MatrixXd *omega_SN_S

[rad/s] inertial spinning body frame angular velocity vector

BSKLogger bskLogger

Friends

friend class SpinningBodyNDOFStateEffector
class SpinningBodyNDOFStateEffector : public StateEffector, public SysModel
#include <spinningBodyNDOFStateEffector.h>

spinning rigid body state effector class

Public Functions

SpinningBodyNDOFStateEffector()

Constructor.

~SpinningBodyNDOFStateEffector() override

Destructor.

void addSpinningBody(const std::shared_ptr<SpinningBody> newBody)

Add a spinning body.

method for adding a new spinning body

Parameters:

newBody – [in] Spinning-body configuration to add.

std::shared_ptr<SpinningBody> getSpinningBody(uint64_t index)

Get a spinning-body configuration.

method for getting an indexed spinning body

Parameters:

index – [in] Zero-based body index.

inline void setNameOfThetaState(const std::string &nameOfThetaState)

setter for nameOfThetaState property

inline void setNameOfThetaDotState(const std::string &nameOfThetaDotState)

setter for nameOfThetaDotState property

inline std::string getNameOfThetaState() const

getter for nameOfThetaState property

inline std::string getNameOfThetaDotState() const

getter for nameOfThetaDotState property

Public Members

std::vector<Message<HingedRigidBodyMsgPayload>*> spinningBodyOutMsgs

state output message

std::vector<Message<SCStatesMsgPayload>*> spinningBodyConfigLogOutMsgs

spinning body state config log message

ReadFunctor<ArrayMotorTorqueMsgPayload> motorTorqueInMsg

(optional) motor torque input message

ReadFunctor<ArrayEffectorLockMsgPayload> motorLockInMsg

(optional) lock flag input message

std::vector<ReadFunctor<HingedRigidBodyMsgPayload>> spinningBodyRefInMsgs

(optional) reference state input message

Private Functions

void validateConfiguration()

Validate the user-supplied spinning-body chain configuration.

void validateCommandCapacity()

Reject body chains that exceed the capacity of a linked command array.

void Reset(uint64_t CurrentClock) override

Validate the module configuration when the scheduler resets the model.

Parameters:

CurrentClock – [ns] Time at which the reset occurs

void writeOutputStateMessages(uint64_t CurrentClock) override

Write the effector state output messages.

Parameters:

CurrentClock – [in] [ns] Current simulation time.

void UpdateState(uint64_t CurrentSimNanos) override

Update the scheduled effector state.

Parameters:

CurrentSimNanos – [in] [ns] Current simulation time.

void registerStates(DynParamManager &statesIn) override

Register the effector dynamics states.

Parameters:

statesIn – [inout] Dynamic parameter manager used to register states or properties.

void linkInStates(DynParamManager &states) override

Link the required dynamics states.

Parameters:

states – [in] Dynamic parameter manager containing the required states.

void addDynamicEffector(DynamicEffector *newDynamicEffector, int segment) override

Attach a dynamic effector to a body segment.

Parameters:
  • newDynamicEffector – [in] Dynamic effector to attach.

  • segment – [in] One-based body segment receiving the attached effector.

void registerProperties(DynParamManager &states) override

Register the effector dynamics properties.

Parameters:

states – [inout] Dynamic parameter manager used to register states or properties.

void computeDependentEffectors(BackSubMatrices &backSubContr, double integTime)

Compute loads from attached dynamic effectors.

Parameters:
  • backSubContr – [inout] Backsubstitution contributions.

  • integTime – [in] [s] Current integration time.

void updateContributions(double integTime, BackSubMatrices &backSubContr, Eigen::MRPd sigma_BN, Eigen::Vector3d omega_BN_B, Eigen::Vector3d g_N) override

Update the effector Backsubstitution contributions.

Parameters:
  • integTime – [in] [s] Current integration time.

  • backSubContr – [inout] Backsubstitution contributions.

  • sigma_BN – [in] Hub attitude relative to the inertial frame.

  • omega_BN_B – [in] [rad/s] Hub angular velocity expressed in body-frame components.

  • g_N – [in] [m/s^2] Gravitational acceleration expressed in inertial-frame components.

void computeDerivatives(double integTime, Eigen::Vector3d rDDot_BN_N, Eigen::Vector3d omegaDot_BN_B, Eigen::MRPd sigma_BN) override

Compute the effector state derivatives.

Parameters:
  • integTime – [in] [s] Current integration time.

  • rDDot_BN_N – [in] [m/s^2] Hub translational acceleration expressed in inertial-frame components.

  • omegaDot_BN_B – [in] [rad/s^2] Hub angular acceleration expressed in body-frame components.

  • sigma_BN – [in] Hub attitude relative to the inertial frame.

void updateEffectorMassProps(double integTime) override

Update the effector mass properties.

Parameters:

integTime – [in] [s] Current integration time.

void updateEnergyMomContributions(double integTime, Eigen::Vector3d &rotAngMomPntCContr_B, double &rotEnergyContr, Eigen::Vector3d omega_BN_B) override

Update the effector energy and momentum contributions.

Parameters:
  • integTime – [in] [s] Current integration time.

  • rotAngMomPntCContr_B – [inout] [kg*m^2/s] Rotational angular momentum contribution.

  • rotEnergyContr – [inout] [J] Rotational energy contribution.

  • omega_BN_B – [in] [rad/s] Hub angular velocity expressed in body-frame components.

void prependSpacecraftNameToStates() override
void readInputMessages()
void computeSpinningBodyInertialStates()
void computeAttitudeProperties(std::shared_ptr<SpinningBody> spinningBody, size_t spinningBodyIndex) const

Compute spinning-body attitude properties.

Parameters:
  • spinningBody – [inout] Spinning-body configuration being evaluated.

  • spinningBodyIndex – [in] Zero-based spinning-body index.

void computeAngularVelocityProperties(std::shared_ptr<SpinningBody> spinningBody, size_t spinningBodyIndex) const

Compute spinning-body angular velocity properties.

Parameters:
  • spinningBody – [inout] Spinning-body configuration being evaluated.

  • spinningBodyIndex – [in] Zero-based spinning-body index.

void computePositionProperties(std::shared_ptr<SpinningBody> spinningBody, size_t spinningBodyIndex) const

Compute spinning-body position properties.

Parameters:
  • spinningBody – [inout] Spinning-body configuration being evaluated.

  • spinningBodyIndex – [in] Zero-based spinning-body index.

void computeVelocityProperties(std::shared_ptr<SpinningBody> spinningBody, size_t spinningBodyIndex) const

Compute spinning-body velocity properties.

Parameters:
  • spinningBody – [inout] Spinning-body configuration being evaluated.

  • spinningBodyIndex – [in] Zero-based spinning-body index.

void computeInertiaProperties(std::shared_ptr<SpinningBody> spinningBody) const

Compute spinning-body inertia properties.

Parameters:

spinningBody – [inout] Spinning-body configuration being evaluated.

void computeMTheta(Eigen::MatrixXd &MTheta)

Compute the spinning-body joint mass matrix.

Parameters:

MTheta – [out] Spinning-body joint mass matrix.

void computeAThetaStar(Eigen::MatrixX3d &AThetaStar)

Compute the A-theta-star Backsubstitution matrix.

Parameters:

AThetaStar – [out] A-theta-star Backsubstitution matrix.

void computeBThetaStar(Eigen::MatrixX3d &BThetaStar)

Compute the B-theta-star Backsubstitution matrix.

Parameters:

BThetaStar – [out] B-theta-star Backsubstitution matrix.

void computeCThetaStar(Eigen::VectorXd &CThetaStar, const Eigen::Vector3d &g_N)

Compute the C-theta-star Backsubstitution vector.

Parameters:
  • CThetaStar – [out] C-theta-star Backsubstitution vector.

  • g_N – [in] [m/s^2] Gravitational acceleration expressed in inertial-frame components.

void computeBackSubMatrices(BackSubMatrices &backSubContr) const

Compute the Backsubstitution matrices.

Parameters:

backSubContr – [inout] Backsubstitution contributions.

void computeBackSubVectors(BackSubMatrices &backSubContr) const

Compute the Backsubstitution vectors.

Parameters:

backSubContr – [inout] Backsubstitution contributions.

Private Members

int numberOfDegreesOfFreedom = 0
std::vector<std::shared_ptr<SpinningBody>> spinningBodyVec
Eigen::MatrixX3d ATheta
Eigen::MatrixX3d BTheta
Eigen::VectorXd CTheta
Eigen::Vector3d omega_BN_B = Eigen::Vector3d::Zero()
Eigen::MRPd sigma_BN
StateData *hubSigmaState = nullptr

hub attitude state, read live for the published kinematics

Eigen::Matrix3d dcm_BN = Eigen::Matrix3d::Zero()
Eigen::MatrixXd *inertialPositionProperty = nullptr
Eigen::MatrixXd *inertialVelocityProperty = nullptr
StateData *thetaState = nullptr
StateData *thetaDotState = nullptr
std::string nameOfThetaState = {}
std::string nameOfThetaDotState = {}
std::string propertyNameIndex = {}

Private Static Attributes

static uint64_t effectorID = 1