Filtering out low-frequency bias I am trying to extrapolate the bearing of a vehicle using GPS and a gyroscope.

To this end, I first obtain the yaw component using gyroscope readings and the (known) yaw axis of the vehicle, then integrate the result over time and add it to the previous bearing.

I have implemented a real-time visualization of:


the direction of the vehicle yaw axis
the direction of the rotation vector reported by the gyroscope
raw GPS bearing
extrapolated bearing


About half of the time this works very well: the extrapolated bearing smoothly moves as the vehicle turns, and the GPS (which updates a second later) aligns with it almost perfectly. The other half of the time the gyroscope seems to suffer from bias, causing the extrapolated bearing to drift even when the vehicle is stationary.

The visualization shows me that the gyroscope rotation vector changes direction at random as long as I'm not suffering from the bias problem and the vehicle is not turning (indicating I'm getting something close to white noise from the gyroscope, which is what I'd expect). When the bias problem occurs, the gyroscope direction remains constant, corroborating my suspicion that I'm suffering from bias.

Based on the assumption that a vehicle will never turn in the same direction for more than about 1–4 minutes, and on the observation that bias comes and goes at longer intervals (several minutes), I tried using a highpass filter to eliminate bias. I used a 5th-order Butterworth filter.

With an initial cutoff frequency of $\frac{1}{240} \text{Hz}$ (corresponding to a 4-minute period), when I rotate the device by 90° at roughly $\frac{\pi}{2} \frac{\text {rad}}{\text s}$, as soon as the rotation stops, the bearing drifts in the opposite direction. I then started working downward from there: at $\frac{1}{1800} \text{Hz}$ (a half-an-hour period) the effect was still visible but much weaker, and at $\frac{1}{3600} \text{Hz}$ (one hour) it was virtually gone.

A real-world test at $\frac{1}{3600} \text{Hz}$, however, still resulted in random bias, of an even greater magnitude than what I'd experienced with raw values.

I then tried increasing the frequency to $\frac{1}{15} \text{Hz}$ and noticed I am likely suffering from overshoot, as the bearing now bounces back and oscillates around a certain value somewhere between the old and the new bearing.

I then went back to the original $\frac{1}{240} \text{Hz}$ and experimented with the filter order and characteristics: First I tried a 1st-order Butterworth filter, then a 5th-order Bessel filter – to no avail.

Where's the error here, and how can I elimiate bias without introducing any other, even less desirable artifacts?
I have decided to take a different approach: Rather than passing the raw accelerometer data through a filter and choosing between overshoot and ringing versus bias, I am now detecting bias by periodically comparing extrapolated $\Delta \psi _{gyro}$ to $\Delta \psi _{GPS}$:


Initially assume zero bias.
For each extrapolation step, subtract bias from measured $\omega _\psi$
On each new GPS bearing, calculate the change in bias as $\frac{\Delta \psi _{gyro} - \Delta \psi _{GPS}}{t}$
Add this change to the previous bias estimate
Determine the accuracy of the new bias estimate, considering the accuracy of $\psi _{GPS}$, approximate standard deviation of gyroscope output at rest and time elapsed
If we have a previous bias estimate (other than the initial assumption of zero), run the old and new bias estimate through a Kálmán filter; else simply continue with the new bias estimate
Return to step 2.


Results look good so far – this visibly reduces bias without introducing any new artifacts. Responsiveness still needs to be improved, which I can probably tackle by tweaking my accuracy model.