Overview Demos Technical Derivations Validation Next
Attitude Estimation and Control

Simulink GN&C Mini-Stack

A CubeSat-class attitude determination and control simulation in Simulink. Quaternion state, reaction wheels, sampled sensors, PD control, and an MEKF. Built to support GN&C and autonomy roles in Seattle.

MATLAB + Simulink
Linear Control
Kalman Filtering

Project Overview

This project models an end-to-end rigid-body spacecraft attitude control loop in Simulink, using scalar-first quaternions and body rates. The sensor models include sampling and noise, and the reaction wheel actuators include torque saturation plus wheel speed tracking and limits. A PD controller handles detumble and attitude tracking, while an MEKF estimates attitude and gyro bias. The demo videos below show closed-loop stability, tuning tradeoffs, and commanded maneuvers including attitude hold and roll reversal.

State

Quaternion (scalar-first) and body rates.

Sensors

Gyro at 100 Hz, Star tracker at 5 Hz with a valid-data pulse.

Actuators

Three reaction wheels with torque limit, inertia, speed tracking, speed limit logic.

Control

PD detumble and tracking. Gains set from a second-order target response.

Estimation

MEKF (error-state EKF) to handle non-linear quanternion data

Validation

Seeded RNG, repeatable runs, unit-test mindset. A full Monte Carlo test suite planned next.

System Parameters

Timing and sampling

  • Base simulation step: \(T_s = 0.01\) s
  • Gyro rate: \(T_{s,\text{gyro}} = 0.01\) s
  • Star tracker rate: \(T_{s,\text{star}} = 0.2\) s
  • Sim end time: \(t_\text{end} = 120\) s

Dynamics and initial conditions

  • Inertia: \(J = \mathrm{diag}([0.02, 0.02, 0.01])\) kg m\(^2\)
  • Initial attitude: \(q_0 = [1,0,0,0]^T\)
  • Initial rates: \(\omega_0 = [-53, +78, +82]^T\) deg/s
  • Constant disturbance torque: \([10^{-5}, -2\cdot10^{-5}, 10^{-5}]^T\) N m

Reaction wheels

  • Torque limit: \(\tau_{\max} = 0.01\) N m per axis
  • Wheel inertia: \(J_\text{wheel} = 2\cdot10^{-4}\) kg m\(^2\)
  • Speed limit: \(\Omega_{\max} = 6000\) rpm

Noise and bias

  • Gyro noise: \(0.02\) deg/s 1-sigma
  • Gyro bias init: \(0.005\) deg/s 1-sigma
  • Bias random walk: \(0.0002\) deg/\(\sqrt{\text{s}}\)
  • Star tracker noise: \(0.01\) deg 1-sigma

Demo Results

Demo 1

Controller off vs on

This clip shows the same initial tumble for a spacecraft with, and for a spacecraft without a controller. The left side runs without control. The right side runs with the PD loop enabled at t = 5 seconds. The controlled case detumbles quickly and holds attitude under a constant disturbance torque.

Left: controller off. Right: controller on.
Initial rates: \([-53, +78, +82]\) deg/s
Disturbance torque enabled
Wheel torque saturation enforced
Demo 2

Tuning tradeoffs

This clip overlays three responses. Kp and Kd gains were calculated via an analytical derivation. Underdamped settles fast but overshoots. Overdamped avoids overshoot but settles slow. The target is near critical damping as it gives a clean response and a short settling time. The only variation changed between runs was the set damping ratio.

Underdamped, critically damped, and overdamped test cases.
Design knob: \(\zeta\) and \(T_s\)
Goal: fast settle, low overshoot
Gains set from the derivation below
Demo 3

Attitude hold with controlled roll reversal

This clip steps through three fixed attitutes and then returns home. At each attitude, the controller settles first, then commands a constant body-rate roll about a single axis, then reverses the roll direction, then settles again. Attitude commands use scalar-first quaternions, body-rate commands are in rad/s.

Tracking commanded attitudes with settle windows and roll reversal.
\(q_{x90}\)
\([0.70710678,\ 0.70710678,\ 0,\ 0]^T\)
90 deg about X
\(q_{y90}\)
\([0.70710678,\ 0,\ 0.70710678,\ 0]^T\)
90 deg about Y
\(q_{z90}\)
\([0.70710678,\ 0,\ 0,\ 0.70710678]^T\)
90 deg about Z

Technical Architecture

Top-level block diagram

The architecture follows a standard ADCS stack: the sensors drive the estimator, the controller uses the estimated attitude and rate, and the reaction wheels apply torque to the rigid-body plant. A scenario block provides the commanded attitudes and disturbances used in the demos.

Top-level architecture diagram for the Simulink GN&C mini-stack
End-to-end data flow. Sensors, MEKF, control law, reaction wheels, rigid-body dynamics.

Subsystem browser

Use the menu to jump between subsystem layouts. Each panel includes a short intent statement and the diagram screenshot.

Plant

This subsystem is the attitude “physics” and supports rigid-body rotational dynamics with scalar-first quaternion kinematics. It integrates the spacecraft attitude and body rates while applying wheel torque and any external disturbance torque.

Simulink subsystem diagram for Plant_Attitude
Rigid-body dynamics: \(J\dot{\omega} + \omega \times (J\omega) = \tau\). Quaternion kinematics use a scalar-first convention.

Actuator RW

Reaction wheel torque commands are converted into wheel acceleration, with per-axis torque saturation. Wheel speed is integrated using the rotor inertia and clamped to a maximum speed so the model behaves like a "real" actuator.

Simulink subsystem diagram for Actuator_RW
Actuation limits follow the initialization: \(\tau_{\max}=0.01\) N m and \(\Omega_{\max}=6000\) rpm.

Sensors (gyro and star tracker)

The gyro runs at 100 Hz and includes white noise, an initial bias, and a bias random walk. The star tracker runs at 5 Hz and produces a “valid” pulse, which the estimator uses to decide when to accept star tracker measurement updates.

Simulink subsystem diagram for Sensors
Sampling rates follow the code: \(T_{s,\text{gyro}}=0.01\) s and \(T_{s,\text{star}}=0.2\) s.

Estimator (MEKF)

The MEKF is an error-state EKF that estimates attitude and gyro bias. Propagation runs at the base rate using the gyro, and corrections are applied only when a star-tracker measurement is marked valid.

Simulink subsystem diagram for Estimator (MEKF)
MEKF structure: propagate with gyro, correct on star-tracker pulses, and estimate bias with a random-walk process model.

Controller (PD detumble and tracking)

The controller uses a PD law that drives the log-mapped attitude error toward zero while damping body rates. Gains are set from a second-order target model, which ties \(K_p\) and \(K_d\) to the chosen settling time and damping ratio \(\zeta\).

Simulink subsystem diagram for Controller
The control law combines attitude error (via the log map) and rate error to produce wheel torque commands.

Scenario_Command

This block determines the desired attitude and body rate throughout the simulation. It schedules the commanded attitude targets, and applies the disturbance torque used in the demos.

Simulink subsystem diagram for Scenario_Command
Scenario sequencing: settle at a command, switch to the next command, settle again, and repeat.

Orbit_Propagator

This subsystem propagates position and velocity for visualization and context, but it does not feed back into the attitude loop. The run initializes with \(p_0=[0,6878137,0]^T\) and \(v_0=[-7612.608,0,0]^T\).

Simulink subsystem diagram for Orbit_Propagator
Orbit propagation for scene motion; attitude control remains a separate rigid-body loop.

PD Gain Derivation

The goal of this controller was to have an easy to adjust damping ratio set the performance and response characteristics. You can first set a settling time target and a damping ratio target, which can then be used to compute \(K_p\) and \(K_d\) gains for the system.

1) Start from rigid-body rotational dynamics

Euler’s rotational equation gives the relationship between torque and angular acceleration. The plant uses spacecraft inertia \(J\), body rate \(\omega\), and total applied torque \(\tau\).

\[ J\dot{\omega} + \omega \times (J\omega) = \tau \]

For a reaction-wheel controlled spacecraft, the commanded wheel torque drives the body torque with opposite sign. The controller output is \(u\) and the plant torque is \(\tau = -u\).

\[ J\dot{\omega} + \omega \times (J\omega) = -u \]

2) Define the quaternion convention and attitude error

The state uses a scalar-first quaternion \(q = [q_0,\ q_v^T]^T\), with \(q_v \in \mathbb{R}^3\). Quaternion kinematics relate rate to quaternion derivative.

\[ q = \begin{bmatrix} q_0 \\ q_v \end{bmatrix}, \quad \dot{q}_0 = -\frac{1}{2}q_v^T\omega, \quad \dot{q}_v = \frac{1}{2}\left(q_0 I_3 + [q_v]_\times\right)\omega \]

The attitude error quaternion uses the standard composition rule.

\[ q_e = q_d^{-1} \otimes q = \begin{bmatrix} q_{e0}\\ q_{ev} \end{bmatrix} \]

As quaternions have a double cover, \(q\) and \(-q\) represent the same attitude. To enforce the short rotation, I flip the error quaternion when the scalar term is negative. Otherwise, instead of commanding a 45° rotation, the system could command a -315° rotation, which would be significantly less efficient.

\[ q_{e0} < 0 \Rightarrow q_e \leftarrow -q_e \]

3) Convert quaternion error to a vector error using the log map

A PD law needs an error vector in \(\mathbb{R}^3\). The log map converts the error quaternion into an axis-angle rotation and then into a 3D error vector.

\[ s = q_{e0}, \quad v = q_{ev}, \quad r = \lVert v \rVert \] \[ \theta = 2\ \mathrm{atan2}(r, s) \] \[ \hat{a} = \begin{cases} v/r, & r > \epsilon \\ \text{any unit vector}, & r \le \epsilon \end{cases} \] \[ e = \theta \hat{a} \]

This avoids the small-angle issues you hit with raw quaternion vector parts when errors get larger and is able to produces an error direction and magnitude in one vector.

4) Apply the PD control law and linearize for tuning

We define the rate error \(\tilde{\omega} = \omega - \omega_d\). For detumble and hold, typically \(\omega_d = 0\), although this can be set within the scenario command. For a general PD control law:

\[ u = K_p e + K_d \tilde{\omega} \]

For gain selection, I chose to use a small-angle approximation and ignore the cross-coupling term to reduce complexity and be able to form a spring-dampener system.

\[ J\dot{\omega} \approx -u \]

With small attitude errors, the error kinematics reduce to \(\dot{e} \approx -\omega\) for a zero command rate. We can then substitute \(\omega \approx -\dot{e}\) and \(\dot{\omega} \approx -\ddot{e}\) into the dynamics.

\[ J(-\ddot{e}) = -K_p e - K_d(-\dot{e}) \] \[ J\ddot{e} + K_d \dot{e} + K_p e = 0 \]

5) Match to a standard second-order system

If \(J\), \(K_p\), and \(K_d\) are diagonal (which they are in this system), each axis behaves like an independent second-order system. We can then compare the closed-loop form to the standard template.

\[ \ddot{e}_i + 2\zeta_i \omega_{n,i}\dot{e}_i + \omega_{n,i}^2 e_i = 0 \] \[ \omega_{n,i}^2 = \frac{K_{p,i}}{J_i}, \quad 2\zeta_i\omega_{n,i} = \frac{K_{d,i}}{J_i} \]

With these terms broken out, we can solve for the response gains in terms of inertia, natural frequency, and damping ratio.

\[ K_{p,i} = J_i \omega_{n,i}^2, \quad K_{d,i} = 2\zeta_i J_i \omega_{n,i} \]

6) Use the 2 percent settling-time approximation

A common design rule for a second-order system is the 2 percent settling-time approximation:

\[ T_{s,i} \approx \frac{4}{\zeta_i \omega_{n,i}} \]

If we substitute \(\omega_{n,i} = 4/(\zeta_i T_{s,i})\) into the gain expressions, this produces our direct tuning knobs: pick \(T_s\) and \(\zeta\), we can compute \(K_p\) and \(K_d\).

\[ K_{p,i} = J_i\left(\frac{4}{\zeta_i T_{s,i}}\right)^2, \quad K_{d,i} = \frac{8J_i}{T_{s,i}} \]

In matrix form with diagonal \(J\), this matches the MATLAB initialization:

\[ K_p = J\left(\frac{4}{\zeta T_s}\right)^2, \quad K_d = J\left(\frac{8}{T_s}\right) \]
Benefits

With this relationship established, you can get predictable tuning behavior. Smaller \(T_s\) increases \(K_p\) and \(K_d\). Lower \(\zeta\) increases overshoot. This equation setup allows for easy response adjustments as can be seen in the Demo 2 visualization.

Validation Approach

Reproducibility

The simulation seeds the RNG and uses fixed sampling periods. This keeps runs repeatable while I iterate on estimator and control logic.

  • Seeded RNG using twister, seed = 1
  • Fixed step size and sensor sample rates
  • Centralized parameter initialization script

Metrics tracked during runs

These plots are the first line of defense to catch sign errors, estimator divergences, and response errors. The script tracks and plots:

  • \(\omega\) and \(\tilde{\omega}\) per axis
  • Wheel torque saturation flags
  • Wheel speed and speed-limit flags
  • MEKF innovations on star tracker updates
  • Bias estimate vs injected bias drift
  • Truth and estimate of attitude and angular velocity
  • Commanded position and angular velocity

Pass and fail checks

These checks support fast iteration and ensure that initial variables and responses stay within expected bounds.

  • Quaternion norm stays near 1
  • Control torque respects \(\tau_{\max}\)
  • Wheel speeds stay below \(\Omega_{\max}\)
  • MEKF covariance stays positive definite
  • Innovation magnitude stays bounded after each update

A full Monte Carlo validation suite is in progress. The next section outlines the plan and the exact metrics it will report.

Roadmap and Next Steps

Estimator hardening

  • Lock down quaternion sign conventions across plant, sensors, and MEKF.
  • Add explicit frame annotations at every interface.
  • Track estimator covariance and ensure convergence.
  • NEES/NIS consistency statistics.

Monte Carlo and consistency

  • Batch runs across initial rates, bias draws, and star tracker dropout patterns.
  • Add NEES and NIS checks with chi-square bounds.
  • Report pass rates with seeded reproducibility.

Disturbances and environment

  • Add gravity-gradient torque and a simple aerodynamic torque model.
  • Extend wheel friction and motor dynamics.
  • Add sensor misalignment and scale factor errors.

Let’s Connect

If you are hiring for GN&C, autonomy, or modeling and simulation in Seattle, I would like to talk.

Copied to Clipboard