Loading...

Filtering the Signal

The MPU-6050 accelerometer produces an accurate measure of the acceleration but is prone to noise. Trying to control the T-Bot using this signal alone will result in a very agitated robot. On the other hand, the integrated gyroscope signal is very smooth but drifts as errors accumulate. The T-Bot will simply fall over if you try this alone. We need to combine the signals. There are some very sophisticated ways of doing this. The most common way is to use a linear quadratic estimater or Kalman Filter. This is a very elegant filter and was used in the navigation system for the Apollo mission to the Moon.

Apollo 11 flight path

An explanation of the Kalman filter can be found here and an implementation of the Kalman filter by Kristian Lauszus for the Arduino can be found here. However, the T-Bot uses a much simpler filter which, in this case, is just as effective. You can use the python code below the compare the two.

We will assume you already know how to run a python script. If not, there are numerous YouTube tutorials available. If you would like to use an integrated development environment (IDE), you can try Spyder.

Combination Filter

Setup Python Code

Import libraries. You will need to have Numpy and Matplotlib installed on your system.

import numpy as np
import matplotlib.pyplot as plt
plt.ion()

Load the data extracted from the T-bot (See Extracting Data From T-Bot tutorial) You can skip extracting the data if you want to use the T-Bot_FilteredData.dat file included with the downloaded software.

v1 = np.loadtxt('T-Bot_FilteredData.dat')
v1[np.where(v1[:,0]<0.001),0]=0.013

The Simple Combination Filter only has one parameter to set.

################   Simple Combination Filter    ################
'''
This code serves as a guide to show how the filters work. 
A filter_weighting of 0 zero will be equivalent to using
the gyro only. A value of 1 will be equivalent to using the accelerometer  
only. You can use serial_GetData.py to collect the data from the T-BOT
to see how effective your filter is.
'''

angle = 0
filter_weighting = 0.04

def getAngleCFilter(pitch, gyro_rate, dt):
    global angle
    angle += gyro_rate * dt
    angle += filter_weighting * (pitch - angle)
    return angle

The Kalman has five parameters to set.

####################   Kalman Filter    ########################
''' 
Based on Arduino code written by Kristian Lauszus 2012
                 TKJ Electronics.
'''

# User parameters
bias = 0
R_measure = 0.15 # measurement noise
Q_angle = 0.1 # process noise 
Q_bias = 0.3 # 
R_measure = 1 # measurement noise


# create arrays

P = np.zeros((2,2))
K = np.zeros(2)


def getAngle(pitch, gyrorate, dt):
    global bias
    global angle
    global P
    rate = gyrorate - bias;
    angle += dt * rate;
    # Update estimation error covariance - Project the error covariance ahead
    P[0,0] += dt * (dt*P[1,1] - P[0,1] - P[1,0] + Q_angle)
    P[0,1] -= dt * P[1,1]
    P[1,0] -= dt * P[1,1]
    P[1,1] += Q_bias * dt

    # Calculate Kalman gain - Compute the Kalman gain
    S = P[0,0] + R_measure
    K[0] = P[0,0] / S
    K[1] = P[1,0] / S
    y = pitch - angle

    #Calculate angle and bias - Update estimate with measurement zk (newAngle)
    angle += K[0] * y
    bias += K[1] * y

    #Calculate estimation error covariance - Update the error covariance
    P[0,0] -= K[0] * P[0,0]
    P[0,1] -= K[0] * P[0,1]
    P[1,0] -= K[1] * P[0,0]
    P[1,1] -= K[1] * P[0,1]
    return angle

Now we can calculate the filtered angles using the Simple Combination Filter and the Kalman Filter.

################## Get filterd angle  ##########################

# Simple combination filter
angleCF = np.array([getAngleCFilter(v1[x,1],v1[x,2],v1[x,0]) for x in range(v1.shape[0])])

# Kalman Filter
angle = 0 # reset global value back to 0
angleKF = np.array([getAngle(v1[x,1],v1[x,2],v1[x,0]) for x in range(v1.shape[0])])

# Integrated Gyro Signal (Not Filtered)
gyroangle = np.cumsum(v1[:,2]*v1[:,0])

# integrated dt to give t for the x axis
t = np.cumsum(v1[:,0])

Now we can plot the data!

###############   Plot the data  ########################

plt.figure(figsize=(10, 8))

ax = plt.subplot(211)
plt.title('On-board Processing')
ax.plot(t, v1[:,1], c=(91/255.,111/255.,189/255.),label = 'Measured Pitch')
ax.plot(t, v1[:,4], c=(56/255.,192/255.,255/255.),label = 'Unfiltered Gyro Angle from T-Bot')
ax.plot(t, v1[:,3], c=(255/255.,0/255.,0/255.),label = 'Filtered Angle by T-Bot')
ax.legend(loc = 'best',prop={ 'size': 8})
plt.xlabel('t (s)')
plt.ylabel('angle (deg)')
plt.axis('tight')
ax = plt.subplot(212)
plt.title('From Python Code')
ax.plot(t, v1[:,1],  c=(91/255.,111/255.,189/255.),label = 'Measured Pitch')
ax.plot(t, gyroangle, c=(56/255.,192/255.,255/255.),label = 'Unfiltered Gyro Angle')
ax.plot(t, angleKF, 'g',label = 'Kalman Filter',linewidth=2)
ax.plot(t, angleCF, 'r--',label = 'Combination Filter',linewidth=2)

ax.legend(loc = 'best',prop={ 'size': 8})
plt.xlabel('t (s)')
plt.ylabel('angle (deg)')
plt.axis('tight')
plt.subplots_adjust(hspace=0.3)
plt.show()