Source code for opengnc.actuators.reaction_wheel

"""
Reaction Wheel (RW) actuator model with friction and momentum limits.
"""

from __future__ import annotations

from typing import Any

import numpy as np

from opengnc.actuators.actuator import Actuator


[docs] class ReactionWheel(Actuator): """ Reaction Wheel Actuator. Models torque generation with saturation, max speed limits, and friction. Parameters ---------- max_torque : float, optional Maximum torque (Nm). Default is None (no saturation). max_momentum : float, optional Maximum angular momentum (Nms). inertia : float, optional Moment of inertia about spin axis (kg*m^2). name : str, optional Actuator name. Default is "RW". static_friction : float, optional Static friction torque (Nm). Default is 0.0. viscous_friction : float, optional Viscous friction coefficient (Nm/(rad/s)). Default is 0.0. coulomb_friction : float, optional Coulomb friction torque (Nm). Default is 0.0. """ def __init__( self, max_torque: float | None = None, max_momentum: float | None = None, inertia: float | None = None, name: str = "RW", static_friction: float = 0.0, viscous_friction: float = 0.0, coulomb_friction: float = 0.0, ) -> None: r""" Initialize reaction wheel parameters. Parameters ---------- max_torque : float | None, optional Saturation torque (Nm). max_momentum : float | None, optional Momentum storage limit (Nms). inertia : float | None, optional Wheel inertia ($kg \cdot m^2$). name : str, optional Actuator name. Default "RW". static_friction : float, optional Static friction torque (Nm). viscous_friction : float, optional Viscous coefficient ($Nm / (rad/s)$). coulomb_friction : float, optional Coulomb friction torque (Nm). """ super().__init__(name=name, saturation=max_torque) self.max_momentum = max_momentum self.inertia = inertia self.static_friction = static_friction self.viscous_friction = viscous_friction self.coulomb_friction = coulomb_friction
[docs] def command( self, torque_cmd: float | None = None, current_speed: float = 0.0, *args: Any, **kwargs: Any ) -> float: r""" Calculate delivered motor torque. Friction Model: $\tau_f = f_v \omega + f_c \text{sgn}(\omega)$ Parameters ---------- torque_cmd : float Commanded motor torque (Nm). current_speed : float, optional Current wheel spin rate (rad/s). Default 0.0. Returns ------- float Actual torque delivered to the spacecraft (Nm). """ if torque_cmd is None: if not args: raise ValueError("torque_cmd is required.") torque_cmd = float(args[0]) torque = self.apply_deadband(torque_cmd) torque = float(self.apply_saturation(torque)) if abs(current_speed) < 1e-4: if abs(torque) < self.static_friction: torque = 0.0 else: torque -= float(np.sign(torque) * self.static_friction) else: friction_torque = ( self.viscous_friction * current_speed + self.coulomb_friction * np.sign(current_speed) ) torque -= float(friction_torque) if self.inertia is not None and self.max_momentum is not None: current_momentum = float(self.inertia * current_speed) if (current_momentum >= self.max_momentum and torque > 0) or ( current_momentum <= -self.max_momentum and torque < 0 ): torque = 0.0 return float(torque)