C++ Module: reactionWheelStateEffector

Executive Summary

This state effector models reaction wheels attached to a spacecraft hub, including wheel-speed states, motor torques, friction, and the resulting spacecraft coupling. It supports balanced wheels, simple jitter, and fully coupled jitter. The jitter models also maintain wheel-angle states.

The module PDF Description contains further information on this module’s function, how to run it, as well as testing.

Module Assumptions and Limitations

The wheel model and includeWheelMassProperties option determine how mass properties are accounted for. The option defaults to False to preserve existing simulations: balanced and simple-jitter wheels then require their constant mass properties to be included in the hub.

Default wheel mass-property accounting (includeWheelMassProperties = False)

RWModel

Effector contribution

Required hub configuration

BalancedWheels

No mass, first mass moment, or constant inertia contribution.

Include the wheel’s constant mass properties in the hub.

JitterSimple

No mass, first mass moment, or constant inertia contribution. Imbalance is approximated through applied forces and torques.

Include the wheel’s constant mass properties in the hub, as for balanced wheels.

JitterFullyCoupled

Adds the configured wheel mass, center-of-mass contribution, inertia, and their applicable time derivatives, including imbalance effects.

Exclude the mass properties already represented by this wheel effector from the hub.

With the default option, setting a nonzero mass on a BalancedWheels or JitterSimple wheel does not increase the spacecraft mass. Likewise, setting rWB_B, Jt, or Jg does not add the wheel’s constant center-of-mass or inertia contribution in these two models. The spin-axis inertia Js is still used in wheel acceleration, spacecraft rotational coupling, angular momentum, and energy; it must be configured even when the wheel’s constant inertia is included in the hub.

With includeWheelMassProperties = True, balanced and simple-jitter wheels instead contribute their nominal mass, center-of-mass offset, and axisymmetric inertia through the effector. The option does not change the accounting for fully coupled wheels, which always contribute their own properties.

See Configuring Hub and Wheel Mass Properties for the hub inputs and model-switching guidance.

Message Connection Descriptions

The following table lists all the module input and output messages. The module msg connection 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.

reactionWheelStateEffector module input and output messages

Module I/O Messages

Msg Variable Name

Msg Type

Description

rwMotorCmdInMsg

ArrayMotorTorqueMsgPayload

(optional) RW motor torque array cmd input message. If not connected the motor torques are set to zero.

rwSpeedOutMsg

RWSpeedMsgPayload

RW speed array output message.

rwOutMsgs

RWConfigLogMsgPayload

vector of RW log output messages.

User Guide

The reaction wheel state effector module provides functionality for simulating reaction wheels in a spacecraft. It includes safety mechanisms to prevent numerical instability that can occur with excessive wheel acceleration or when using unlimited torque with small spacecraft inertia.

Configuring Hub and Wheel Mass Properties

By default, for BalancedWheels and JitterSimple, configure the hub to include the wheels as if they were locked relative to the body. The effector separately models their relative spin. Update all three hub inputs consistently:

  • hub.mHub: combined mass of the rigid hub and the wheels accounted for in the hub, in kg.

  • hub.r_BcB_B: location of that combined center of mass relative to body-frame origin \(B\), expressed in body-frame components, in m.

  • hub.IHubPntBc_B: combined inertia about that combined center of mass \(B_c\), expressed in body-frame components, in kg m2. Include each wheel’s full constant inertia, including its spin-axis inertia, and the parallel-axis contributions from its location.

For example, let \(m_0\), \(\mathbf r_0\), and \(I_0\) describe the rigid hub before adding the wheels, with \(I_0\) taken about its own center of mass. For each wheel to be included in the hub, let \(m_i\), \(\mathbf r_i\), and \(I_i\) be its mass, nominal center-of-mass location, and constant inertia about its own center of mass. All position vectors are measured from \(B\), and all vectors and inertia tensors are expressed in body-frame components. Then set:

\[m_h = m_0 + \sum_i m_i, \qquad \mathbf r_h = \frac{m_0\mathbf r_0 + \sum_i m_i\mathbf r_i}{m_h}\]
\[\begin{split}\begin{aligned} I_h &= I_0 + m_0 P(\mathbf r_0 - \mathbf r_h) + \sum_i \left[I_i + m_i P(\mathbf r_i - \mathbf r_h)\right], \\ P(\mathbf a) &= (\mathbf a^T\mathbf a)\mathbf 1_3 - \mathbf a\mathbf a^T \end{aligned}\end{split}\]

Here \(\mathbf 1_3\) is the identity matrix. Assign \(m_h\), \(\mathbf r_h\), and \(I_h\) to mHub, r_BcB_B, and IHubPntBc_B, respectively. Rotate wheel inertia tensors into the body frame before combining them. For an axisymmetric balanced wheel, the constant tensor about its center of mass is \(I_i = J_t\mathbf 1_3 + (J_s-J_t)\hat{\mathbf g}_s\hat{\mathbf g}_s^T\), where \(J_g=J_t\) and \(\hat{\mathbf g}_s\) is gsHat_B. Other state effectors continue to contribute their own mass properties separately.

For JitterFullyCoupled, the effector performs the wheel mass-property accounting. Its wheel center of mass is rWB_B + d * w2Hat_B, with d = U_s / mass. It rotates the wheel inertia defined by Js, Jt, Jg, and J13 = U_d into the body frame and includes the parallel-axis contribution about \(B\). Include only the remaining rigid structure in the hub; any stationary wheel housing not represented by the configured wheel mass properties still belongs in the hub. Check the selected factory’s mass and inertia definitions when partitioning the hardware.

Warning

Count each wheel’s mass properties exactly once. With the default option, if the hub inputs already describe the assembled spacecraft with balanced or simple-jitter wheels, do not add those wheels again. When switching to JitterFullyCoupled, remove the mass properties represented by those wheels from the hub and recompute its center of mass and inertia about that center. When switching back, include them again. Changing RWModel does not adjust the hub inputs automatically. For mixed wheel models, apply this convention to each wheel individually.

Automatic Inclusion of Wheel Mass Properties

To let the effector account for the constant properties of its balanced and simple-jitter wheels, set the option before InitializeSimulation():

rwStateEffector = reactionWheelStateEffector.ReactionWheelStateEffector()
rwStateEffector.includeWheelMassProperties = True
rwFactory.addToSpacecraft("ReactionWheels", rwStateEffector, scObject)

Configure scObject.hub with only the remaining rigid structure, excluding the mass properties represented by these wheels. The option applies to all balanced and simple-jitter wheels in this effector. For mixed wheel models, fully coupled wheels still contribute exactly once. Leave the option unchanged during a simulation run.

For each balanced or simple-jitter wheel, the effector uses mass, rWB_B, gsHat_B, Js, and Jt to compute the nominal mass properties. Its constant inertia is the axisymmetric tensor given above, shifted from rWB_B to \(B\) using the parallel-axis theorem. The effector also includes the corresponding rigid-body kinetic energy and angular momentum, in addition to the existing relative-spin terms. The derivatives of these constant mass properties are zero.

This option requires finite, positive mass, Js, Jt, and Jg, with Jt = Jg and Js <= 2*Jt for a physically valid axisymmetric inertia. The inertia comparisons allow a relative tolerance of \(10^{-12}\). rWB_B must be finite, and gsHat_B must be a finite unit vector (squared norm within \(10^{-12}\) of unity). Invalid configurations raise BSK_ERROR during state registration or Reset(). The axisymmetry restriction applies to balanced and simple-jitter wheels when the option is enabled.

The option does not turn a simplified wheel model into a fully coupled imbalance model. Balanced wheels continue to ignore imbalance parameters; simple-jitter wheels retain their existing applied imbalance forces and torques, while their mass properties use nominal centered geometry. Use JitterFullyCoupled when the moving center of mass and coupled imbalance dynamics are needed.

When enabling the option on an existing simulation, remove the wheels’ constant mass properties from the hub and recompute its center of mass and inertia about that center. When disabling it, add the balanced and simple-jitter wheel properties back into the hub. With the option enabled, switching between balanced wheels and fully coupled wheels with zero imbalance and axisymmetric inertia does not require repartitioning the hub mass properties.

Configuration Validation

Every wheel model requires finite, positive Js, since it is used in the spin dynamics regardless of the mass-accounting option. Balanced and simple-jitter wheels with the default option do not require mass, Jt, or Jg to be populated: those constant properties are accounted for in the hub instead.

JitterFullyCoupled always requires finite, positive mass, Jt, and Jg, as well as the finite position and unit spin axis described above. Its U_s and U_d must be finite, the derived offset U_s / mass must remain finite, and the full rotor inertia tensor with J13 = U_d must be positive definite. With positive diagonal moments, this last check requires U_d**2 < Js*Jg. These checks apply even when includeWheelMassProperties is False, and occur before the derived wheel configuration is used. They run during state registration and Reset().

The fully coupled checks do not enforce the triangle inequalities on the principal moments of inertia. Passing validation therefore does not guarantee a physically realizable mass distribution. This limitation preserves compatibility with existing factory inertia approximations, which combine Jt = Jg = Js/2 with nonzero U_d. Stricter realizability checks would require revisiting those factory assumptions. When U_d is nonzero, the principal moments are the eigenvalues of the full rotor inertia tensor; checking its diagonal entries alone is insufficient.

Validation

test_reactionWheelMassProperties.py compares the enabled option with zero-imbalance, axisymmetric fully coupled wheels and with the default model using manually combined hub properties. It checks spacecraft mass, center of mass, inertia, translational and attitude histories, wheel speeds, energy, and angular momentum for balanced, simple-jitter, and mixed configurations. The tests use offset wheels and a nonzero hub center of mass. Free-motion cases verify energy and momentum conservation; motor-driven cases verify momentum conservation and matching energy histories. Additional cases check unchanged default accounting, invalid simplified and fully coupled configuration handling, and spin-inertia validation across all models.

Initialization and Reset

When the attached spacecraft registers the effector states, the module initializes the wheel-speed and applicable wheel-angle states. For each fully coupled jitter wheel, it also derives the center-of-mass offset d from U_s / mass and sets the off-diagonal inertia J13 to U_d. This mandatory attachment path initializes the dynamics configuration even when the reaction wheel state effector is not added to a simulation task.

The Reset() method refreshes these derived configuration values, initializes every wheel’s command entry to zero, reports a warning when a Stribeck coefficient is zero, and clears the wheel-speed output buffer.

The wheel count cannot exceed MAX_EFF_CNT, because the effector publishes a fixed-size wheel-speed array even when its motor-command input is unlinked. State registration, Reset(), command reading, and wheel-speed publication reject excessive counts with BasiliskError before accessing the arrays. Command storage is also sized during state registration and input reading, so it does not depend on a scheduled Reset(). Direct calls to ConfigureRWRequests() may supply partial NewRWCmds vectors, but the vector cannot exceed the wheel count.

Configure all wheels and their models before InitializeSimulation() registers their states. After registration, addReactionWheel() raises BasiliskError without adding a device. The wheel count and the allocation of a jitter-angle state to each wheel must remain unchanged. Changes through ReactionWheelData are rejected before reset, command processing, output publication, or dynamics access, even when the effector is absent from the task. The derived numRW and numRWJitter counts must not be edited. Reset() preserves the registered states; use a new effector and simulation when changing the state layout. Calling Reset() before state registration does not freeze the layout.

Threshold Parameters

The module includes two configurable threshold parameters:

  • maxWheelAcceleration: Maximum allowed wheel acceleration to prevent numerical instability. Default value is 1.0e6 rad/s^2.

  • largeTorqueThreshold: Threshold for warning about large torque with unlimited torque setting. Default value is 10.0 Nm.

These parameters can be accessed and modified using the following getter and setter methods:

# Get the current maximum wheel acceleration threshold
current_max_accel = reactionWheelStateEffector.getMaxWheelAcceleration()

# Set a new maximum wheel acceleration threshold
reactionWheelStateEffector.setMaxWheelAcceleration(2.0e6)  # rad/s^2

# Get the current large torque threshold
current_torque_threshold = reactionWheelStateEffector.getLargeTorqueThreshold()

# Set a new large torque threshold
reactionWheelStateEffector.setLargeTorqueThreshold(15.0)  # Nm

class ReactionWheelStateEffector : public SysModel, public StateEffector
#include <reactionWheelStateEffector.h>

reaction wheel state effector class

Public Functions

ReactionWheelStateEffector()
~ReactionWheelStateEffector()
void registerStates(DynParamManager &states)

Initialize the wheel configuration and register the effector dynamics states.

For fully coupled jitter wheels, this method derives the center-of-mass offset and off-diagonal inertia from the configured static and dynamic imbalance parameters. It then registers and initializes the wheel-speed states and, when required, the wheel-angle states.

Parameters:

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

void linkInStates(DynParamManager &states)

Link the required dynamics states.

Parameters:

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

void writeOutputStateMessages(uint64_t integTimeNanos)

This method is here to write the output message structure into the specified message.

Parameters:

integTimeNanos – The current time used for time-stamping the message

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

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)

Method for stateEffector to give mass contributions.

Update the effector mass properties.

Parameters:

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)

Back-sub contributions.

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 updateEnergyMomContributions(double integTime, Eigen::Vector3d &rotAngMomPntCContr_B, double &rotEnergyContr, Eigen::Vector3d omega_BN_B)

Energy and momentum calculations.

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 Reset(uint64_t CurrentSimNanos)

Reset the reaction-wheel command and output buffers.

This method refreshes model-dependent derived wheel configuration values, initializes the command entry for each configured wheel, reports a zero Stribeck coefficient, and clears the wheel-speed output buffer.

Parameters:

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

void addReactionWheel(std::shared_ptr<RWConfigPayload> NewRW)

Add a reaction-wheel configuration and its output message.

Note

Wheels can only be added before state registration.

Parameters:

NewRW – [in] Reaction-wheel configuration to add.

void UpdateState(uint64_t CurrentSimNanos)

This method is the main cyclical call for the scheduled part of the RW dynamics model. It reads the current commands array and sets the RW configuration data based on that incoming command set. Note that the main dynamical method (ComputeDynamics()) is not called here and is intended to be called from the dynamics plant in the system

Parameters:

CurrentSimNanos – The current simulation time in nanoseconds

void WriteOutputMessages(uint64_t CurrentClock)

This method is here to write the output message structure into the specified message.

Parameters:

CurrentClock – The current time used for time-stamping the message

void ReadInputs()

This method is used to read the incoming command message and set the associated command structure for operating the RWs.

void ConfigureRWRequests(double CurrentTime)

This method is used to read the new commands vector and set the RW firings appropriately. It assumes that the ReadInputs method has already been run successfully.

Parameters:

CurrentTime – The current simulation time converted to a double

inline double getMaxWheelAcceleration() const

Get the maximum wheel acceleration threshold.

Returns:

Maximum wheel acceleration in rad/s^2

inline void setMaxWheelAcceleration(double val)

Set the maximum wheel acceleration threshold.

Parameters:

val – New maximum wheel acceleration value in rad/s^2

inline double getLargeTorqueThreshold() const

Get the large torque threshold for unlimited torque warning.

Returns:

Large torque threshold in Nm

inline void setLargeTorqueThreshold(double val)

Set the large torque threshold for unlimited torque warning.

Parameters:

val – New large torque threshold value in Nm

Public Members

bool includeWheelMassProperties = false

Include constant mass properties for balanced and simple-jitter wheels.

Note

Defaults to false, preserving the convention that these properties are included in the hub. Set before simulation initialization and leave unchanged during a run. When true, exclude these wheel properties from the hub and provide positive mass and axisymmetric inertia (Jt = Jg). Fully coupled jitter wheels always contribute their own mass properties, regardless of this option.

std::vector<std::shared_ptr<RWConfigPayload>> ReactionWheelData

RW information.

ReadFunctor<ArrayMotorTorqueMsgPayload> rwMotorCmdInMsg

RW motor torque array cmd input message.

Message<RWSpeedMsgPayload> rwSpeedOutMsg

RW speed array output message.

std::vector<Message<RWConfigLogMsgPayload>*> rwOutMsgs

vector of RW log output messages

std::vector<RWCmdMsgPayload> NewRWCmds

Incoming attitude commands.

RWSpeedMsgPayload rwSpeedMsgBuffer = {}

(-) Output data from the reaction wheels

std::string nameOfReactionWheelOmegasState

class variable

std::string nameOfReactionWheelThetasState

class variable

size_t numRW

number of reaction wheels

size_t numRWJitter

number of RW with jitter

BSKLogger bskLogger

BSK Logging.

Private Functions

void validateDimensions()

Validate the wheel count against command and speed message capacities.

Reject wheel arrays that cannot fit the command and speed payloads.

void validateRegisteredLayout()

Reject changes to the registered speed and angle state layout.

Reject state-layout changes without relying on live parent state pointers.

void initializeWheelConfiguration(RWConfigPayload &rw)

Initialize model-dependent derived reaction-wheel configuration values.

Parameters:

rw – [inout] Reaction-wheel configuration to initialize.

Private Members

std::optional<std::vector<bool>> registeredWheelLayout

Per-wheel jitter-state allocation after registration.

ArrayMotorTorqueMsgPayload incomingCmdBuffer = {}

One-time allocation for savings.

uint64_t prevCommandTime

Time for previous valid thruster firing.

StateData *OmegasState

class variable

StateData *thetasState

class variable

Eigen::MatrixXd *g_N

[m/s^2] Gravitational acceleration in N frame components

double maxWheelAcceleration = 1.0e6

[rad/s^2] Maximum allowed wheel acceleration to prevent numerical instability

double largeTorqueThreshold = 10.0

[Nm] Threshold for warning about large torque with unlimited torque setting