Spacecraft Attitude Problem
The spacecraft is represented by a simple rotational model. Its attitude is described using orientation angle \( \theta \), angular velocity \( \omega \), and control torque \( \tau \).
The goal is to rotate the spacecraft toward a target attitude without excessive overshoot or oscillation.
In this simplified model, the moment of inertia is taken as unity so that the torque command directly produces angular acceleration. This keeps the focus on the simulation timing problem rather than spacecraft inertia modelling.
PD Attitude Controller
The controller uses attitude error and angular velocity to calculate torque. The proportional term pulls the spacecraft toward the target. The derivative term provides damping.
In the fixed-step version, the response looks stable because both the controller and the state update at the same known timestep.
\(K_p\) decides how strongly the spacecraft reacts to attitude error. \(K_d\) decides how strongly the controller resists angular velocity.
In a fixed-step simulation, this controller is evaluated at a regular time interval. That means the damping behaviour is predictable. The hidden assumption is that the controller has a known update rate.
What This Problem Investigates
This problem compares three ways of simulating the same spacecraft attitude controller. The gains are unchanged. The spacecraft dynamics are unchanged. Only the timing assumption changes.
- Case 1 — Fixed-step Euler: the state and controller update every fixed \( \Delta t \).
- Case 2 — Adaptive solver style: the timestep changes, as if the solver is choosing internal steps. The controller is evaluated at irregular intervals.
- Case 3 — Sample-and-hold hybrid: the plant may move with smaller internal steps, but the controller updates only at a fixed clock rate. Between updates, the torque command is held constant.
The third case represents the engineering fix. Real flight software does not update whenever the numerical solver feels like stepping. It updates on a clock.
Where the Timing Assumption Breaks
The controller implicitly assumes a known control update rate:
With adaptive stepping, \( \Delta t \) is no longer constant.
That means control may be applied too late, too frequently, or at irregular intervals. The derivative term no longer behaves with the same damping effect.
Run the Same Controller Three Ways
Select a simulation mode, adjust the gains, and run the response. The purpose is to observe how timing affects attitude response, angular velocity, torque command, and effective timestep.
Fixed Step vs Adaptive Solver Behaviour
| Behaviour | Fixed Step | Adaptive Solver |
|---|---|---|
| Rise time | Smooth | Irregular |
| Damping | Consistent | Varies |
| Stability | Stable | May oscillate |
| Control effort | Predictable | Spiky |
Why Adaptive Stepping Can Mislead the Controller
An adaptive solver such as ODE45 changes its timestep to solve the differential equation efficiently and accurately. If the motion changes quickly, the solver may take smaller steps. If the motion is smooth, it may take larger steps.
That behaviour is good for solving physics. The problem appears when a discrete controller is placed inside the solver without modelling its update timing.
A digital controller normally assumes a fixed update rate. It reads the state, calculates a command, sends the command, waits for the next control cycle, and repeats. That is a sampled-data system.
If the controller is evaluated at irregular solver-selected times, then the controller is no longer behaving like real flight software. The damping term can become inconsistent, the response may overshoot more, and the torque command may become irregular.
What the Adaptive Solver Is Actually Doing
An adaptive solver does not step randomly. It adjusts its timestep based on how fast the solution is changing.
When dynamics are fast → small timestep When dynamics are smooth → large timestep
Notice how the timestep shrinks during rapid motion and expands when the system settles. This behaviour is ideal for solving physics — but it breaks assumptions of a fixed-rate controller.
The Fix Is Not Simply “Change the Gains”
Poor behaviour does not always mean the controller gains are wrong. Sometimes the simulation architecture is wrong.
Option A — Fixed-step simulation
Use a fixed simulation timestep when the controller is intended to run at a fixed update rate.
Option B — Sample-and-hold inside continuous propagation
Let the plant evolve continuously, but update the controller only at fixed clock intervals. Between controller updates, hold the previous torque command constant.
Option C — Fully continuous controller
Treat the controller as a continuous-time law:
This is mathematically valid, but it is not the same as real digital flight software unless the real controller is also continuous.
Why This Controller Still Fails in Real Systems
Even after fixing the simulation timing, the controller is still based on a simplified assumption: that torque can be applied instantly and without limits.
In a real spacecraft, control is implemented through actuators such as reaction wheels or control moment gyros. These introduce constraints that fundamentally change how the controller behaves.
Actuator Limits
The commanded torque cannot exceed actuator capability:
If the controller demands more torque than available, the system saturates. This removes damping and can lead to overshoot or oscillation.
Momentum Buildup
Reaction wheels accumulate angular momentum over time:
Without momentum dumping, the actuator eventually reaches its limit and can no longer produce control torque.
Sensor and Estimation Delay
The controller does not receive the true state instantly. There is always delay due to sensing and estimation:
This delay reduces effective damping and can destabilize otherwise stable gains.
Key Engineering Insight
This is why controller design must always be validated against physical constraints, not just numerical behaviour.
Reproducing This Behaviour in Python
The same mismatch appears when using adaptive solvers like
solve_ivp in Python.
import numpy as np
from scipy.integrate import solve_ivp
Kp = 5
Kd = 2
theta_target = 1
def dynamics(t, state):
theta, omega = state
tau = Kp*(theta_target - theta) - Kd*omega
return [omega, tau]
t_span = (0, 20)
y0 = [0, 0]
sol = solve_ivp(dynamics, t_span, y0, method='RK45')
This produces behaviour similar to the adaptive simulation shown earlier. The controller is evaluated at solver-selected time points, not at a fixed rate.
What This Problem Shows
A controller that works in simulation is not automatically a good controller. It only works under the assumptions used by that simulation.
Same \(K_p\), same \(K_d\), same target attitude.
Same attitude dynamics: \( \dot{\theta} = \omega \), \( \dot{\omega} = \tau \).
The timing assumption changes.
The engineering lesson is simple but important: