The solver is not the intelligence
In GNC simulation, a smooth plot can be dangerously convincing. A numerical solver such as ODE45 does not know whether the equations describe real physics. It only integrates the equations supplied to it.
If the model is wrong, the result can still look clean, stable, and professional. That is the danger. A trajectory can be visually smooth and still be physically false. This page is built around that idea: simulation is not validation.
This problem walks through a progressive GNC learning path: first a missing state variable, then a correct second-order model, then PD control, then a gain-sweep stability map, then high-gain numerical sensitivity, and finally a chaser-target control extension.
One problem, six connected lessons
A small modeling omission turns second-order rotational dynamics into the wrong first-order system.
The wrong and correct systems are integrated with the same solver to isolate model error.
The rotational system is rewritten in state-space form so a feedback controller can be simulated correctly.
A sweep over \(K_p\) and \(K_d\) reveals stable, oscillatory, unstable, and numerically sensitive regions.
Aggressive control can make the physical response faster, but also make the numerical problem harder.
The same second-order control idea is extended to relative motion and compared using Euler-style and ODE45-style integration.
Missing ω: wrong vs correct second-order dynamics
Rotational motion is naturally second-order. Angular acceleration changes angular velocity, and angular velocity changes angle. The correct model therefore keeps both states.
The common mistake is to directly integrate angular acceleration as if it were angular rate:
Only one state is used: angle. Angular velocity is missing, so the system has lost its true dynamic memory.
Two states are used: angle and angular velocity. The angle changes because velocity exists, not because acceleration directly changes angle.
ODE45 trajectory comparison: same solver, different physics
Use this interactive comparison to see how the wrong and correct models separate over time. Both curves are smooth. That is the important lesson: smoothness is not proof of correctness.
PD controller in state-space form
A PD controller applies correction based on position error and velocity damping. For rotational motion, the controlled equation is:
ODE45 does not directly integrate a second derivative equation in this written form. It needs a first-order state-space system. Define:
Then the correct state model becomes:
Run the rotational PD controller
Change \(K_p\) and \(K_d\) to see how proportional action and damping shape the response. Low damping can oscillate. Too little proportional action can be slow. Large gains can become numerically demanding.
Stability map for Kp and Kd
Instead of randomly trying gains, sweep the controller space. The plot below classifies the response for many \((K_p,K_d)\) combinations. This turns tuning into a structured engineering task.
High gains: physical vs numerical instability
Higher gains often reduce response time, but they also create faster state changes. A physical system may be stable in theory while the numerical simulation becomes difficult because the solver must resolve rapid dynamics.
| Instability type | Meaning | What to check |
|---|---|---|
| Physical instability | The modeled dynamics genuinely diverge or fail to settle. | Controller structure, sign convention, damping, actuator limits. |
| Numerical instability | The equations may be stable, but the solver/timestep/tolerance cannot resolve them well. | Step size, tolerances, stiffness, scaling, saturation modeling. |
Chaser-target control extension
Now apply the same idea to a mission-style relative-motion problem. A chaser begins with position error \(x\) and relative velocity \(\dot{x}\). A PD-like controller tries to bring the chaser to the target.
This comparison shows why manual integration can exaggerate error when the timestep is too coarse, while ODE45-style adaptive integration usually gives a smoother response. But the lesson remains: even a better solver cannot repair poor gains or incorrect dynamics.
Code translation: from page logic to engineering tools
Use these snippets as teaching blocks in the page. Keep the black-pill code style because it visually separates implementation from theory.
# Python version using scipy.solve_ivp as an ODE45-style adaptive RK solver
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
alpha = 0.4
omega0 = 0.0
theta0 = 0.0
t_final = 10.0
# Wrong: theta_dot = alpha
def wrong_model(t, y):
theta = y[0]
return [alpha]
# Correct: omega_dot = alpha, theta_dot = omega
def correct_model(t, y):
theta, omega = y
return [omega, alpha]
t_eval = np.linspace(0, t_final, 400)
wrong = solve_ivp(wrong_model, [0, t_final], [theta0], t_eval=t_eval)
correct = solve_ivp(correct_model, [0, t_final], [theta0, omega0], t_eval=t_eval)
plt.plot(wrong.t, wrong.y[0], label="Wrong: theta_dot = alpha")
plt.plot(correct.t, correct.y[0], label="Correct: theta_dot = omega")
plt.xlabel("Time [s]")
plt.ylabel("Angle theta")
plt.legend()
plt.grid(True)
plt.show()
% MATLAB ODE45 version for PD-controlled rotational dynamics
Kp = 12;
Kd = 6;
theta_ref = 1;
x0 = [0; 0]; % x(1) = theta, x(2) = theta_dot
tspan = [0 10];
f = @(t,x) [
x(2);
Kp*(theta_ref - x(1)) - Kd*x(2)
];
[t,x] = ode45(f, tspan, x0);
figure;
plot(t, x(:,1), 'LineWidth', 1.8); hold on;
yline(theta_ref, '--', 'Reference');
xlabel('Time [s]');
ylabel('Angle theta');
grid on;
title('PD-Controlled Rotational Response using ODE45');
# Simple response classification idea
# This can be used after simulating theta(t)
final_error = abs(theta[-1] - theta_ref)
overshoot = max(theta) - theta_ref
peak_abs = max(abs(theta))
zero_crossings = np.sum(np.diff(np.sign(theta - theta_ref)) != 0)
if peak_abs > 10 * max(1, abs(theta_ref)):
label = "Unstable"
elif final_error > 0.2 * max(1, abs(theta_ref)):
label = "Unstable or too slow"
elif zero_crossings >= 3 or overshoot > 0.15 * max(1, abs(theta_ref)):
label = "Oscillatory"
else:
label = "Stable"
print(label)
ode45. Python does not call it ODE45, but solve_ivp with RK45 plays the same role conceptually: adaptive Runge-Kutta integration.
A simulation can look smooth and still be wrong
If a state such as \(\omega\) is missing, the simulation no longer represents the physical system.
ODE45 improves numerical integration, but it does not check whether your equations are correct.
Gain choices create regions of stable, oscillatory, unstable, and numerically sensitive behaviour.