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)