Add a quaternion dtype to NumPy with comprehensive quaternion arithmetic and mathematical operations
—
Advanced functionality for working with quaternion time series including spherical interpolation methods and angular velocity integration. These functions are essential for smooth animation, physics simulation, and trajectory planning.
Smooth interpolation between quaternion time series and individual quaternions.
def slerp(R1, R2, t1, t2, t_out):
"""
Spherical linear interpolation between quaternion time series.
Args:
R1 (quaternion array): First quaternion time series
R2 (quaternion array): Second quaternion time series
t1 (array_like): Time values for R1
t2 (array_like): Time values for R2
t_out (array_like): Output time values for interpolation
Returns:
quaternion array: Interpolated quaternions at t_out times
Notes:
Provides smooth interpolation between two quaternion time series.
Handles time alignment and produces constant angular velocity
interpolation between corresponding points.
"""
def slerp_evaluate(q1, q2, t):
"""
Low-level slerp evaluation between two quaternions.
Args:
q1 (quaternion): Start quaternion
q2 (quaternion): End quaternion
t (float or array): Interpolation parameter (0=q1, 1=q2)
Returns:
quaternion or quaternion array: Interpolated quaternion(s)
Notes:
Core slerp function. For t in [0,1], produces shortest-path
interpolation on quaternion sphere with constant angular velocity.
"""Higher-order interpolation for C1-continuous quaternion sequences.
def squad(R_in, t_in, t_out, unflip_input_rotors=False):
"""
Spherical quadratic interpolation for smooth quaternion time series.
Args:
R_in (quaternion array): Input quaternion time series
t_in (array_like): Input time values
t_out (array_like): Output time values for interpolation
unflip_input_rotors (bool): Automatically ensure input continuity (default: False)
Returns:
quaternion array: Smoothly interpolated quaternions at t_out times
Notes:
Provides C1-continuous interpolation (continuous first derivative).
Superior to slerp for smooth animation and requires at least 4 input points.
Automatically handles tangent computation for smooth transitions.
"""
def squad_evaluate(q1, q2, a1, a2, t):
"""
Low-level squad evaluation between quaternions with control points.
Args:
q1 (quaternion): Start quaternion
q2 (quaternion): End quaternion
a1 (quaternion): First control quaternion
a2 (quaternion): Second control quaternion
t (float or array): Interpolation parameter (0=q1, 1=q2)
Returns:
quaternion or quaternion array: Interpolated quaternion(s)
Notes:
Core squad function using Bezier-like control points for smooth
interpolation with continuous first derivatives.
"""Integrate angular velocity to produce quaternion time series for physics simulations.
def integrate_angular_velocity(Omega, t0, t1, R0=None, tolerance=1e-12):
"""
Integrate angular velocity to get quaternion time series.
Args:
Omega (array_like): Angular velocity time series with shape (..., 3)
t0 (array_like): Time values for angular velocity data
t1 (array_like): Output time values for integration
R0 (quaternion, optional): Initial quaternion (default: identity)
tolerance (float): Integration tolerance (default: 1e-12)
Returns:
quaternion array: Quaternion time series at t1 times
Notes:
Numerically integrates angular velocity ω(t) to produce rotation
quaternions R(t). Essential for physics simulations where angular
velocity is known but orientations must be computed.
"""Additional utilities for working with quaternion time series.
def unflip_rotors(q, axis=-1, inplace=False):
"""
Ensure quaternion time series continuity by eliminating sign flips.
Args:
q (quaternion array): Input quaternion time series
axis (int): Time axis along which to enforce continuity (default: -1)
inplace (bool): Modify input array in place (default: False)
Returns:
quaternion array: Quaternion series with consistent sign choices
Notes:
Quaternions q and -q represent the same rotation. This function
ensures the quaternion time series uses consistent sign choices
to avoid sudden jumps that complicate interpolation and differentiation.
"""
def angular_velocity(R, t):
"""
Compute angular velocity from quaternion time series.
Args:
R (quaternion array): Quaternion time series representing rotations
t (array_like): Time values corresponding to quaternions
Returns:
ndarray: Angular velocity time series with shape (..., 3)
Notes:
Differentiates quaternion time series to extract angular velocity
vector ω(t). Uses numerical differentiation with appropriate
quaternion calculus formulas.
"""
def minimal_rotation(R, t, iterations=2):
"""
Find minimal rotation representation of quaternion time series.
Args:
R (quaternion array): Input quaternion time series
t (array_like): Time values corresponding to quaternions
iterations (int): Number of optimization iterations (default: 2)
Returns:
quaternion array: Optimized quaternion time series
Notes:
Minimizes the total rotation magnitude while preserving the
relative rotations in the time series. Useful for generating
smooth animation paths and reducing gimbal lock effects.
"""import quaternion
import numpy as np
# Create sample quaternion time series
t = np.linspace(0, 2*np.pi, 20)
# Rotation around z-axis with varying speed
angles = 0.5 * t**2
R_input = np.array([quaternion.from_euler_angles(0, 0, angle) for angle in angles])
# Interpolate with slerp for higher resolution
t_fine = np.linspace(0, 2*np.pi, 100)
R_slerp = quaternion.slerp(R_input, t, t_fine)
# Use squad for smoother interpolation
R_squad = quaternion.squad(R_input, t, t_fine)
# Direct slerp between two quaternions
q1 = quaternion.quaternion(1, 0, 0, 0) # Identity
q2 = quaternion.from_euler_angles(0, 0, np.pi/2) # 90° z-rotation
t_interp = np.linspace(0, 1, 10)
q_interpolated = quaternion.slerp_evaluate(q1, q2, t_interp)
# Squad evaluation with control points
# (Control points would typically be computed automatically by squad function)
a1 = quaternion.quaternion(0.9, 0.1, 0, 0).normalized()
a2 = quaternion.quaternion(0.9, 0, 0.1, 0).normalized()
q_squad_direct = quaternion.squad_evaluate(q1, q2, a1, a2, t_interp)
# Integrate angular velocity back to quaternions
t_integrate = np.linspace(0, 2*np.pi, 50)
# Use constant angular velocity for simple example
omega_const = np.zeros((len(t_integrate), 3))
omega_const[:, 2] = 1.0 # 1 rad/s around z-axis
R_integrated = quaternion.integrate_angular_velocity(
omega_const, t_integrate, t_integrate, R0=quaternion.one
)
print(f"Integrated quaternion sequence length: {len(R_integrated)}")
# Physics simulation example with varying angular velocity
dt = 0.01
time_steps = np.arange(0, 2, dt)
# Sinusoidal angular velocity
omega_physics = np.zeros((len(time_steps), 3))
omega_physics[:, 0] = 2 * np.sin(time_steps) # x-axis
omega_physics[:, 1] = 1 * np.cos(time_steps) # y-axis
# Integrate to get orientation time series
R_physics = quaternion.integrate_angular_velocity(
omega_physics, time_steps, time_steps
)
print(f"Physics simulation: {len(R_physics)} quaternions over {time_steps[-1]} seconds")Install with Tessl CLI
npx tessl i tessl/pypi-numpy-quaternion