Low-Cost IMU Attitude Estimation

This project focused on improving orientation estimates from a low-cost IMU using an Extended Kalman Filter (EKF). I worked with Kyle Frith to connect a BNO055 9-axis IMU to an Arduino UNO, bring the data into MATLAB, and use a quaternion-based filter to estimate roll and pitch more cleanly than raw sensor measurements alone.

The hardware was intentionally simple and inexpensive, which made the real question more interesting: how much performance can you get out of modest hardware if the estimation logic is thoughtful? The result was a practical attitude-estimation setup that handled low-frequency motion well, while also making the limits of the sampling rate and sensor responsiveness very clear during faster maneuvers.

9-axis BNO055 IMU with onboard sensing and fusion support
4-state quaternion state used in the EKF implementation
2 tests low-frequency 10 s runs and faster 2 s runs
MATLAB used for acquisition, covariance tuning, and plotting

Hardware Setup

The physical setup was intentionally lightweight: an Arduino UNO Rev3, a BNO055 9-axis IMU, a mini breadboard, and four jumper wires. That small footprint made it easy to prototype, but it also meant the filter had to do real work if the final orientation estimates were going to look stable and useful.

The I2C wiring was simple and reliable:

Arduino UNO BNO055 IMU Purpose
A5SCLI2C clock line
A4SDAI2C data line
GNDGNDCommon electrical ground
5VVinPower input to the IMU board

Why this platform worked well

The Arduino UNO gave the project an easy serial and power interface, while the BNO055 packaged a gyroscope, accelerometer, and magnetometer into one accessible sensor board. It was a practical testbed for attitude-estimation work without turning the build into a larger embedded systems project.

What the setup exposed

Because the hardware was low-cost, noise and responsiveness limits were visible right away. That was actually useful: it made it easier to see what the EKF improved, and where sensor bandwidth still became the limiting factor.

Estimation Approach

The filter was built as a quaternion-based EKF rather than a direct Euler-angle filter. That choice mattered because quaternions avoid the singularities and awkward edge cases that can come with naive roll-pitch-yaw integration.

k- = f( x̂k-1, uk-1, 0 )
Pk- = Ak Pk-1 AkT + Q
Prediction used the prior state, gyro input, and a simplified process-noise model with a constant Q term.
Kk = Pk- ( Pk- + Rk )-1
k = x̂k- + Kk ( zk - h( x̂k-, 0 ) )
After prediction, the filter blended the state estimate with measured attitude information derived from the accelerometers.

The measurement side of the filter used accelerometer-based attitude estimates for roll and pitch, while yaw was held at zero because the magnetometer behavior was too sensitive to rely on cleanly in this setup:

θ = sin-1( ax / g ),  φ = tan-1( ay / az ),  ψ = 0
That tradeoff kept the project focused on stable roll and pitch estimation without over-claiming yaw accuracy from noisy magnetic data.

The sensor covariance values used for the measurement model were gathered by leaving the IMU stationary for roughly three minutes and computing the covariance in MATLAB:

Sensor channel Measured covariance
Accel x1.732 × 10-7
Accel y1.327 × 10-7
Accel z4.473 × 10-7
Gyro x4.125 × 10-7
Gyro y6.884 × 10-7
Gyro z4.011 × 10-7

Bring-Up Workflow

Before the filtering work could be tested, the MATLAB support package for Arduino had to be configured so the board and IMU could be read reliably from the desktop toolchain. That step was simple, but it mattered because the whole data path depended on a clean hardware-software handshake.

Experimental Results

The project tested the EKF under two broad motion regimes:

  1. Low-frequency roll, pitch, and combined maneuvers over 10 seconds.
  2. Higher-frequency roll, pitch, and combined maneuvers over 2 seconds.

That split made the performance differences easy to see. The filter looked strong when the motion changed gradually, but the faster tests showed where the IMU and overall sampling chain started to struggle.

Low-Frequency Maneuvers

The 10-second tests produced the cleanest results. Roll and pitch traces stayed smooth, and the combined maneuver behaved consistently with the single-axis cases.

Higher-Frequency Maneuvers

When the same general test was compressed into a 2-second window, the outputs became much sharper and more angular. The roll and pitch estimates still tracked the commanded motion directionally, but the response was visibly less smooth and less settled.

What worked well

The EKF was effective for gradual attitude changes. In the slower 10-second tests, the filtered roll and pitch estimates looked smooth, stable, and well-behaved for both single-axis and combined motions.

What broke down first

High-frequency motion exposed the system's limits. The filter did not fail outright, but the curves became visibly more angular, which points back to hardware sampling constraints and the need for a faster, more robust IMU for aggressive maneuvers.

Outcome and Next Steps

This project was a good example of how much value you can get from a modest hardware stack when the estimation logic is solid. With an Arduino, a BNO055, and MATLAB, we built a working attitude-estimation pipeline that could clean up pitch and roll measurements and clearly demonstrate the strengths and limits of a low-cost EKF-based solution.

The next steps are clear from the test results:

  1. Add yaw estimation back in once the magnetic measurement path is more trustworthy.
  2. Tune the EKF more aggressively for dynamic maneuvers.
  3. Move to a higher-rate IMU if fast attitude changes are a real requirement.
  4. Keep the same estimation framework, but apply it to a more flight-like embedded setup.

If you want the full derivation, the report and editable writeup linked above go into the equations, implementation details, and experimental discussion in much more depth.

Explore Next

Search & Reacquisition of Resident Space Object

Other Projects