0
# Interpolation and Animation
1
2
Smooth interpolation between quaternion orientations for animation, trajectory planning, and quaternion integration over time.
3
4
## Capabilities
5
6
### Spherical Linear Interpolation (SLERP)
7
8
Smooth interpolation between two quaternions along the shortest path on the 4D unit hypersphere.
9
10
```python { .api }
11
@classmethod
12
def slerp(cls, q0, q1, amount=0.5):
13
"""
14
Spherical Linear Interpolation between quaternions.
15
16
Finds a valid quaternion rotation at a specified distance along the
17
minor arc of a great circle passing through two quaternion endpoints
18
on the unit radius hypersphere.
19
20
Parameters:
21
q0: Quaternion, first endpoint rotation
22
q1: Quaternion, second endpoint rotation
23
amount: float, interpolation parameter [0, 1]
24
0.0 returns q0, 1.0 returns q1, 0.5 returns midpoint
25
26
Returns:
27
Quaternion: Interpolated unit quaternion
28
29
Note:
30
Input quaternions are automatically normalized to unit length.
31
Takes the shorter rotation path by flipping quaternion sign if needed.
32
"""
33
```
34
35
**Usage Examples:**
36
37
```python
38
from pyquaternion import Quaternion
39
import numpy as np
40
41
# Create start and end orientations
42
q_start = Quaternion(axis=[0, 0, 1], degrees=0) # No rotation
43
q_end = Quaternion(axis=[0, 0, 1], degrees=90) # 90° about Z
44
45
# Interpolate at various points
46
q_quarter = Quaternion.slerp(q_start, q_end, 0.25) # 25% along path
47
q_half = Quaternion.slerp(q_start, q_end, 0.5) # 50% along path
48
q_three_quarter = Quaternion.slerp(q_start, q_end, 0.75) # 75% along path
49
50
print(f"Start: {q_start.degrees:.1f}°") # 0.0°
51
print(f"Quarter: {q_quarter.degrees:.1f}°") # ~22.5°
52
print(f"Half: {q_half.degrees:.1f}°") # ~45.0°
53
print(f"Three-quarter: {q_three_quarter.degrees:.1f}°") # ~67.5°
54
print(f"End: {q_end.degrees:.1f}°") # 90.0°
55
56
# Animate rotation by applying to vector
57
vector = [1, 0, 0]
58
for t in np.linspace(0, 1, 11):
59
q_t = Quaternion.slerp(q_start, q_end, t)
60
rotated = q_t.rotate(vector)
61
print(f"t={t:.1f}: {rotated}")
62
```
63
64
### Intermediate Quaternion Generation
65
66
Generate sequences of evenly spaced quaternions for smooth animation.
67
68
```python { .api }
69
@classmethod
70
def intermediates(cls, q0, q1, n, include_endpoints=False):
71
"""
72
Generate evenly spaced quaternion rotations between two endpoints.
73
74
Convenience method based on slerp() for creating animation sequences.
75
76
Parameters:
77
q0: Quaternion, initial endpoint rotation
78
q1: Quaternion, final endpoint rotation
79
n: int, number of intermediate quaternions within the interval
80
include_endpoints: bool, if True, includes q0 and q1 in sequence
81
resulting in n+2 total quaternions
82
if False, excludes endpoints (default)
83
84
Yields:
85
Generator[Quaternion]: Sequence of intermediate quaternions
86
87
Note:
88
Input quaternions are automatically normalized to unit length.
89
"""
90
```
91
92
**Usage Examples:**
93
94
```python
95
# Create smooth rotation sequence
96
q_start = Quaternion() # Identity
97
q_end = Quaternion(axis=[1, 1, 1], degrees=120)
98
99
# Generate 5 intermediate steps (excluding endpoints)
100
intermediates = list(Quaternion.intermediates(q_start, q_end, 5))
101
print(f"Generated {len(intermediates)} intermediate quaternions")
102
103
# Generate sequence including endpoints
104
full_sequence = list(Quaternion.intermediates(q_start, q_end, 5, include_endpoints=True))
105
print(f"Full sequence has {len(full_sequence)} quaternions")
106
107
# Use in animation loop
108
vector = [1, 0, 0]
109
print("Animation frames:")
110
for i, q in enumerate(full_sequence):
111
rotated = q.rotate(vector)
112
print(f"Frame {i}: {rotated}")
113
114
# Create continuous rotation animation
115
for q in Quaternion.intermediates(q_start, q_end, 20):
116
angle_deg = q.degrees if q.degrees is not None else 0.0
117
print(f"Rotation: {angle_deg:.1f}° about {q.axis}")
118
```
119
120
### Quaternion Derivatives
121
122
Compute instantaneous quaternion derivatives for angular velocity integration.
123
124
```python { .api }
125
def derivative(self, rate):
126
"""
127
Get instantaneous quaternion derivative for a quaternion rotating at 3D rate.
128
129
Computes the time derivative of the quaternion given angular velocity,
130
useful for integrating rotational motion.
131
132
Parameters:
133
rate: array-like, 3-element angular velocity vector [ωx, ωy, ωz]
134
Units should be consistent (e.g., rad/s)
135
Represents rotation rates about global x, y, z axes
136
137
Returns:
138
Quaternion: Quaternion derivative representing rotation rate
139
Formula: 0.5 * self * Quaternion(vector=rate)
140
"""
141
```
142
143
### Quaternion Integration
144
145
Integrate quaternion rotations over time with constant angular velocity.
146
147
```python { .api }
148
def integrate(self, rate, timestep):
149
"""
150
Advance quaternion to its value after time interval with constant angular velocity.
151
152
Modifies the quaternion object in-place using closed-form integration.
153
The quaternion remains normalized throughout the process.
154
155
Parameters:
156
rate: array-like, 3-element angular velocity [ωx, ωy, ωz]
157
Rotation rates about global x, y, z axes (e.g., rad/s)
158
timestep: float, time interval for integration
159
Must have consistent units with rate (e.g., seconds)
160
161
Note:
162
- Modifies the quaternion object directly
163
- Assumes constant angular velocity over the timestep
164
- Smaller timesteps give more accurate results for varying rates
165
- Automatically maintains unit quaternion property
166
"""
167
```
168
169
**Usage Examples:**
170
171
```python
172
import numpy as np
173
174
# Initial orientation
175
q = Quaternion() # Identity quaternion
176
177
# Angular velocity (rad/s)
178
angular_velocity = [0, 0, 1.0] # 1 rad/s about Z-axis
179
dt = 0.1 # 100ms timestep
180
181
# Simulate rotation over time
182
print("Quaternion integration simulation:")
183
print(f"Time: 0.0s, Angle: {q.degrees:.1f}°")
184
185
for step in range(10):
186
# Get derivative
187
q_dot = q.derivative(angular_velocity)
188
189
# Integrate one timestep
190
q.integrate(angular_velocity, dt)
191
192
time = (step + 1) * dt
193
angle = q.degrees if q.degrees is not None else 0.0
194
print(f"Time: {time:.1f}s, Angle: {angle:.1f}°")
195
196
# Verify final result
197
expected_angle = np.degrees(1.0) # 1 rad/s * 1.0s total
198
print(f"Expected final angle: {expected_angle:.1f}°")
199
200
# Integration with varying angular velocity
201
q = Quaternion() # Reset
202
time_points = np.linspace(0, 2*np.pi, 100)
203
dt = time_points[1] - time_points[0]
204
205
trajectory = []
206
for t in time_points:
207
# Time-varying angular velocity (sinusoidal)
208
omega = [np.sin(t), np.cos(t), 0.5]
209
q.integrate(omega, dt)
210
trajectory.append((t, q.degrees, q.axis.copy()))
211
212
# Analyze trajectory
213
print(f"\nTrajectory analysis:")
214
print(f"Final time: {time_points[-1]:.2f}s")
215
print(f"Final angle: {trajectory[-1][1]:.1f}°")
216
print(f"Final axis: {trajectory[-1][2]}")
217
```
218
219
### Advanced Integration Examples
220
221
```python
222
# Spacecraft attitude control simulation
223
class SpacecraftAttitude:
224
def __init__(self):
225
self.orientation = Quaternion() # Initial orientation
226
self.angular_velocity = np.zeros(3) # rad/s
227
228
def apply_torque(self, torque, dt):
229
"""Apply torque for time dt (simplified dynamics)."""
230
# Simplified: assume torque directly changes angular velocity
231
self.angular_velocity += torque * dt
232
233
# Integrate orientation
234
self.orientation.integrate(self.angular_velocity, dt)
235
236
def get_attitude(self):
237
"""Get current attitude as rotation matrix."""
238
return self.orientation.rotation_matrix
239
240
# Usage
241
spacecraft = SpacecraftAttitude()
242
243
# Apply control torques
244
control_torque = [0.1, 0, 0] # Torque about x-axis
245
dt = 0.01 # 10ms control timestep
246
247
for i in range(1000): # 10 seconds of simulation
248
spacecraft.apply_torque(control_torque, dt)
249
250
if i % 100 == 0: # Print every second
251
time = i * dt
252
angle = spacecraft.orientation.degrees
253
print(f"t={time:.1f}s: attitude angle={angle:.1f}°")
254
```