Skip to content

Gyroscope in a gimbal

A gyroscope in a gimbal: a rotor in an inner ring that turns in an outer ring about a fixed axis.

  • Name: gimballed_gyroscope
  • Field: physics/mechanics
  • Formalism: ordinary differential equations
  • References: Ryzhov et al. 2021

Factory

@register_model(
    field='physics/mechanics',
    tags=('mechanics', 'ode', 'second order', 'rotation', 'gyroscope', 'lagrangian'),
    references=('Ryzhov et al. 2021',),
    defaults={'until': 10.0, 'dt': 0.01},
)
def gimballed_gyroscope(
    spin: float = 50.0,
    beta: float = 0.3,
    *,
    A: float = 1.0,
    C: float = 2.0,
    A1: float = 0.5,
    B1: float = 0.5,
    C1: float = 0.5,
    A2: float = 1.0,
    torque: float = 1.0,
) -> System:
    """Return a gyroscope in a gimbal: a rotor in an inner ring that turns in an outer ring about a fixed axis.

    The outer ring turns by `alpha` about the fixed axis, the inner ring by `beta` in the outer ring, and the rotor
    spins by `gamma` in the inner ring. The equations are the Lagrange equations of the kinetic energy
    `[A2 + (A + A1) cos² beta + (C + C1) sin² beta] alpha'² / 2 + (A + B1) beta'² / 2 + C alpha' gamma' sin beta
    + C gamma'² / 2`, with a torque on the axis of the inner ring, which makes the fast rotor precess about the fixed
    axis. The momenta conjugate to `alpha` and `gamma` are conserved.

    Args:
        spin:
            The initial angular velocity of the rotor in the inner ring.

        beta:
            The initial angle of the inner ring.

        A:
            The equatorial moment of inertia of the rotor.

        C:
            The polar moment of inertia of the rotor.

        A1:
            The first principal moment of inertia of the inner ring.

        B1:
            The second principal moment of inertia of the inner ring.

        C1:
            The third principal moment of inertia of the inner ring.

        A2:
            The moment of inertia of the outer ring about its axis.

        torque:
            The torque on the axis of the inner ring.

    Returns:
        system:
            The model, with the angles `gyroscope.alpha`, `gyroscope.beta`, and `gyroscope.gamma`, all of order 2.

    Examples:
        >>> from skcomplex.models.physics.mechanics import gimballed_gyroscope
        >>> gimballed_gyroscope(spin=10.0).elements[0].gamma_d1
        10.0
    """
    return System(
        elements=[
            GyroscopeElement(
                'gyroscope',
                beta=beta,
                gamma_d1=spin,
                A=A,
                C=C,
                A1=A1,
                B1=B1,
                C1=C1,
                A2=A2,
                torque=torque,
            ),
        ],
        interactions=[GyroscopeInteraction('motion', target='gyroscope')],
    )

Components

class GyroscopeElement(Element):
    """Gyroscope in a gimbal: a rotor in an inner ring that turns in an outer ring about a fixed axis.

    The angle `alpha` of the outer ring, `beta` of the inner ring, and `gamma` of the rotor have order 2. The motion
    comes from `GyroscopeInteraction`.

    Args:
        label:
            The label of the gyroscope.

        visual:
            How the gyroscope is drawn, or `None` for none.

        alpha:
            The initial angle of the outer ring.

        alpha_d1:
            The initial angular velocity of the outer ring.

        beta:
            The initial angle of the inner ring.

        beta_d1:
            The initial angular velocity of the inner ring.

        gamma:
            The initial angle of the rotor.

        gamma_d1:
            The initial angular velocity of the rotor.

        A:
            The equatorial moment of inertia of the rotor.

        C:
            The polar moment of inertia of the rotor.

        A1:
            The first principal moment of inertia of the inner ring.

        B1:
            The second principal moment of inertia of the inner ring.

        C1:
            The third principal moment of inertia of the inner ring.

        A2:
            The moment of inertia of the outer ring about its axis.

        torque:
            The torque on the axis of the inner ring.

    Examples:
        >>> from skcomplex.models.physics.mechanics import GyroscopeElement
        >>> GyroscopeElement('gyroscope', gamma_d1=10.0).gamma_d1
        10.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        alpha: Annotated[float, StateSpec(period=2 * pi, order=2)] = 0.0,
        alpha_d1: float = 0.0,
        beta: Annotated[float, StateSpec(order=2)] = 0.3,
        beta_d1: float = 0.0,
        gamma: Annotated[float, StateSpec(period=2 * pi, order=2)] = 0.0,
        gamma_d1: float = 0.0,
        A: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
        C: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 2.0,
        A1: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        B1: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        C1: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        A2: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
        torque: Annotated[float, ParameterSpec()] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.alpha = alpha
        self.alpha_d1 = alpha_d1
        self.beta = beta
        self.beta_d1 = beta_d1
        self.gamma = gamma
        self.gamma_d1 = gamma_d1
        self.A = A
        self.C = C
        self.A1 = A1
        self.B1 = B1
        self.C1 = C1
        self.A2 = A2
        self.torque = torque

class GyroscopeInteraction(EulerLagrangeInteraction):
    """Motion of a gimballed gyroscope, the Euler-Lagrange equations of its kinetic energy with a torque on `beta`.

    The Lagrangian is the kinetic energy `[A2 + (A + A1) cos² β + (C + C1) sin² β] alpha'² / 2 + (A + B1) β'² / 2
    + C alpha' gamma' sin β + C gamma'² / 2`, and the torque of the target acts on the axis of the inner ring.

    Args:
        label:
            The label of the interaction.

        target:
            The path of the gyroscope.

    Examples:
        >>> from skcomplex.models.physics.mechanics import GyroscopeInteraction
        >>> GyroscopeInteraction('motion', target='gyroscope').states
        ('alpha', 'beta', 'gamma')
    """

    def __init__(self, label: str, *, target: Annotated[str, RoleSpec()]) -> None:
        super().__init__(label, target=target, states=('alpha', 'beta', 'gamma'), forcing=self._get_torques)

    @staticmethod
    def _get_torques(namespace: SimpleNamespace) -> list[Expression]:
        """Return the generalized forces, the torque on the inner ring and none on the outer ring and the rotor."""
        return [0, namespace.target.torque, 0]

    def _get_lagrangian(self, namespace: SimpleNamespace) -> Expression:
        """Return the kinetic energy of the rings and the rotor.

        Args:
            namespace:
                The namespace of the symbols the Lagrangian can use.

        Returns:
            lagrangian:
                The Lagrangian.
        """
        p = namespace.target
        da, db, dc = derivative(p.alpha, 1), derivative(p.beta, 1), derivative(p.gamma, 1)
        outer = p.A2 + (p.A + p.A1) * cos(p.beta) ** 2 + (p.C + p.C1) * sin(p.beta) ** 2
        return outer * da**2 / 2 + (p.A + p.B1) * db**2 / 2 + p.C * da * dc * sin(p.beta) + p.C * dc**2 / 2

Equations

gyroscope.alpha: diff(alpha_gyroscope(t), t, t) == -(C_gyroscope*cos(beta_gyroscope(t))*diff(alpha_gyroscope(t), t)*diff(beta_gyroscope(t), t) - (C_gyroscope*cos(beta_gyroscope(t))*diff(gamma_gyroscope(t), t) - 2*((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)) - (C1_gyroscope + C_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)))*diff(alpha_gyroscope(t), t))*C_gyroscope*sin(beta_gyroscope(t))*diff(beta_gyroscope(t), t)/((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope))*C_gyroscope*sin(beta_gyroscope(t))/(((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope)*(C_gyroscope^2*sin(beta_gyroscope(t))^2/((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope) - C_gyroscope)) - (C_gyroscope*cos(beta_gyroscope(t))*diff(gamma_gyroscope(t), t) - 2*((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)) - (C1_gyroscope + C_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)))*diff(alpha_gyroscope(t), t))*diff(beta_gyroscope(t), t)/((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope)
gyroscope.beta: diff(beta_gyroscope(t), t, t) == (C_gyroscope*cos(beta_gyroscope(t))*diff(alpha_gyroscope(t), t)*diff(gamma_gyroscope(t), t) - ((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)) - (C1_gyroscope + C_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)))*diff(alpha_gyroscope(t), t)^2 + torque_gyroscope)/(A_gyroscope + B1_gyroscope)
gyroscope.gamma: diff(gamma_gyroscope(t), t, t) == (C_gyroscope*cos(beta_gyroscope(t))*diff(alpha_gyroscope(t), t)*diff(beta_gyroscope(t), t) - (C_gyroscope*cos(beta_gyroscope(t))*diff(gamma_gyroscope(t), t) - 2*((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)) - (C1_gyroscope + C_gyroscope)*cos(beta_gyroscope(t))*sin(beta_gyroscope(t)))*diff(alpha_gyroscope(t), t))*C_gyroscope*sin(beta_gyroscope(t))*diff(beta_gyroscope(t), t)/((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope))/(C_gyroscope^2*sin(beta_gyroscope(t))^2/((A1_gyroscope + A_gyroscope)*cos(beta_gyroscope(t))^2 + (C1_gyroscope + C_gyroscope)*sin(beta_gyroscope(t))^2 + A2_gyroscope) - C_gyroscope)