2023-08-17 09:36:40 +02:00
|
|
|
import numpy as np
|
|
|
|
from numpy.linalg import inv
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
from math import cos, sin, tan, asin, pi
|
|
|
|
import pandas as pd
|
2023-08-18 11:51:44 +02:00
|
|
|
from scipy.signal import butter, filtfilt
|
2023-08-17 09:36:40 +02:00
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
def get_data(i, data):
|
2023-08-17 09:36:40 +02:00
|
|
|
x = data[0][i] # (41500, 1)
|
|
|
|
y = data[1][i] # (41500, 1)
|
|
|
|
z = data[2][i] # (41500, 1)
|
|
|
|
return x, y, z
|
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
def Euler_accel(ax, ay, az):
|
|
|
|
g = 9.8 # 9.8
|
2023-08-17 09:36:40 +02:00
|
|
|
theta = asin(ax / g)
|
|
|
|
phi = asin(-ay / (g * cos(theta)))
|
|
|
|
return phi, theta
|
|
|
|
|
|
|
|
def sec(theta):
|
|
|
|
return 1/cos(theta)
|
|
|
|
|
|
|
|
def Ajacob(xhat, rates, dt):
|
|
|
|
'''
|
|
|
|
:param xhat: State Variables(phi, theta, psi)
|
|
|
|
:param rates: angel speed(p,q,r)
|
|
|
|
:param dt: variable to make discrete form
|
|
|
|
'''
|
|
|
|
A = np.zeros([3,3])
|
|
|
|
phi = xhat[0]
|
|
|
|
theta = xhat[1]
|
|
|
|
|
|
|
|
p,q,r = rates[0], rates[1], rates[2]
|
|
|
|
|
|
|
|
A[0][0] = q * cos(phi)*tan(theta) - r*sin(phi)*tan(theta)
|
|
|
|
A[0][1] = q * sin(phi)*(sec(theta)**2) + r*cos(phi)*(sec(theta)**2)
|
|
|
|
A[0][2] = 0
|
|
|
|
|
|
|
|
A[1][0] = -q * sin(phi) - r * cos(phi)
|
|
|
|
A[1][1] = 0
|
|
|
|
A[1][2] = 0
|
|
|
|
|
|
|
|
A[2][0] = q * cos(phi) * sec(theta) - r * sin(phi) * sec(theta)
|
|
|
|
A[2][1] = q * sin(phi) * sec(theta)*tan(theta) + r*cos(phi)*sec(theta)*tan(theta)
|
|
|
|
A[2][2] = 0
|
|
|
|
|
|
|
|
A = np.eye(3) + A*dt
|
|
|
|
|
|
|
|
return A
|
|
|
|
|
|
|
|
def fx(xhat, rates, dt):
|
|
|
|
phi = xhat[0]
|
|
|
|
theta = xhat[1]
|
|
|
|
|
|
|
|
p,q,r = rates[0], rates[1], rates[2]
|
|
|
|
|
|
|
|
xdot = np.zeros([3,1])
|
|
|
|
xdot[0] = p + q * sin(phi) * tan(theta) + r * cos(phi)*tan(theta)
|
|
|
|
xdot[1] = q * cos(phi) - r * sin(phi)
|
|
|
|
xdot[2] = q * sin(phi)*sec(theta) + r * cos(phi) * sec(theta)
|
|
|
|
|
|
|
|
xp = xhat.reshape(-1,1) + xdot*dt # xhat : (3,) --> (3,1)
|
|
|
|
return xp
|
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
def Euler_EKF(z, rates, dt):
|
2023-08-17 09:36:40 +02:00
|
|
|
global firstRun
|
|
|
|
global Q, H, R
|
|
|
|
global x, P
|
|
|
|
if firstRun:
|
|
|
|
H = np.array([[1,0,0],[0,1,0]])
|
|
|
|
Q = np.array([[0.0001,0,0],[0,0.0001,0],[0,0,0.1]])
|
|
|
|
R = 10 * np.eye(2)
|
|
|
|
x = np.array([0, 0, 0]).transpose()
|
|
|
|
P = 10 * np.eye(3)
|
|
|
|
firstRun = False
|
|
|
|
else:
|
|
|
|
A = Ajacob(x, rates, dt)
|
|
|
|
Xp = fx(x, rates, dt) # Xp : State Variable Prediction
|
|
|
|
Pp = A @ P @ A.T + Q # Error Covariance Prediction
|
|
|
|
|
|
|
|
K = (Pp @ H.T) @ inv(H@Pp@H.T + R) # K : Kalman Gain
|
|
|
|
|
|
|
|
x = Xp + K@(z.reshape(-1,1) - H@Xp) # Update State Variable Estimation
|
|
|
|
P = Pp - K@H@Pp # Update Error Covariance Estimation
|
|
|
|
|
|
|
|
phi = x[0]
|
|
|
|
theta = x[1]
|
|
|
|
psi = x[2]
|
|
|
|
return phi, theta, psi
|
|
|
|
|
2023-08-18 11:51:44 +02:00
|
|
|
def butter_lowpass_filter(data, cutoff, fs, order):
|
|
|
|
nyq = (0.5 * fs) # Nyquist Frequency
|
|
|
|
normal_cutoff = cutoff / nyq
|
|
|
|
# Get the filter coefficients
|
|
|
|
b, a = butter(order, normal_cutoff, btype='low', analog=False)
|
|
|
|
y = filtfilt(b, a, data)
|
|
|
|
return y
|
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
def normalize_data_vector(data, division):
|
|
|
|
return [i/division for i in list(data)]
|
|
|
|
|
2023-08-17 09:36:40 +02:00
|
|
|
H, Q, R = None, None, None
|
|
|
|
x, P = None, None
|
|
|
|
firstRun = True
|
2023-08-18 11:51:44 +02:00
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
division_param = 32768
|
2023-08-17 09:36:40 +02:00
|
|
|
|
2023-08-18 11:51:44 +02:00
|
|
|
# Filter requirements.
|
|
|
|
T = 5.0 # Sample Period
|
|
|
|
fs = 30.0 # sample rate, Hz
|
|
|
|
cutoff = 2 # desired cutoff frequency of the filter, Hz, slightly higher than actual 1.2 Hz
|
|
|
|
order = 2 # sin wave can be approx represented as quadratic
|
|
|
|
|
2023-08-17 09:36:40 +02:00
|
|
|
df = pd.read_csv('raw_data_6d.xls', sep='\t')
|
|
|
|
|
2023-08-18 11:51:44 +02:00
|
|
|
gyroX = df['%GyroX'].values
|
|
|
|
gyroY = df['GyroY'].values
|
|
|
|
gyroZ = df['GyroZ'].values
|
|
|
|
|
|
|
|
acceX = df['AcceX'].values
|
|
|
|
acceY = df['AcceY'].values
|
|
|
|
acceZ = df['AcceZ'].values
|
|
|
|
|
|
|
|
gyroX = normalize_data_vector(gyroX, division_param)
|
|
|
|
gyroY = normalize_data_vector(gyroY, division_param)
|
|
|
|
gyroZ = normalize_data_vector(gyroZ, division_param)
|
2023-08-18 11:21:57 +02:00
|
|
|
|
2023-08-18 11:51:44 +02:00
|
|
|
acceX = normalize_data_vector(acceX, division_param)
|
|
|
|
acceY = normalize_data_vector(acceY, division_param)
|
|
|
|
acceZ = normalize_data_vector(acceZ, division_param)
|
|
|
|
|
2023-08-18 11:54:47 +02:00
|
|
|
gyroX = butter_lowpass_filter(gyroX, cutoff, fs, order)
|
|
|
|
gyroY = butter_lowpass_filter(gyroY, cutoff, fs, order)
|
|
|
|
gyroZ = butter_lowpass_filter(gyroZ, cutoff, fs, order)
|
2023-08-18 11:51:44 +02:00
|
|
|
|
2023-08-18 11:54:47 +02:00
|
|
|
acceX = butter_lowpass_filter(acceX, cutoff, fs, order)
|
|
|
|
acceY = butter_lowpass_filter(acceY, cutoff, fs, order)
|
|
|
|
acceZ = butter_lowpass_filter(acceZ, cutoff, fs, order)
|
2023-08-18 11:21:57 +02:00
|
|
|
|
|
|
|
gyro_data = [gyroX, gyroY, gyroZ]
|
|
|
|
acce_data = [acceX, acceY, acceZ]
|
2023-08-17 09:36:40 +02:00
|
|
|
|
|
|
|
Nsamples = len(df)
|
|
|
|
EulerSaved = np.zeros([Nsamples,3])
|
|
|
|
dt = 0.01
|
|
|
|
|
|
|
|
for k in range(Nsamples):
|
2023-08-18 11:21:57 +02:00
|
|
|
p, q, r = get_data(k, gyro_data)
|
|
|
|
ax, ay, az = get_data(k, acce_data)
|
|
|
|
phi_a, theta_a = Euler_accel(ax, ay, az)
|
2023-08-17 09:36:40 +02:00
|
|
|
|
2023-08-18 11:21:57 +02:00
|
|
|
phi, theta, psi = Euler_EKF(np.array([phi_a, theta_a]).T, [p,q,r], dt)
|
2023-08-17 09:36:40 +02:00
|
|
|
if type(phi) == type(np.array([])):
|
|
|
|
EulerSaved[k] = [phi[0], theta[0], psi[0]]
|
|
|
|
else:
|
|
|
|
EulerSaved[k] = [phi, theta, psi]
|
|
|
|
|
|
|
|
|
|
|
|
t = np.arange(0, Nsamples * dt ,dt)
|
|
|
|
PhiSaved = EulerSaved[:,0] * 180/pi
|
|
|
|
ThetaSaved = EulerSaved[:,1] * 180/pi
|
|
|
|
PsiSaved = EulerSaved[:,2] * 180/pi
|
|
|
|
|
|
|
|
plt.figure()
|
|
|
|
plt.plot(t, PhiSaved)
|
|
|
|
plt.xlabel('Time [Sec]')
|
|
|
|
plt.ylabel('Roll angle [deg]')
|
|
|
|
plt.savefig('12_EulerEKF_roll.png')
|
|
|
|
|
|
|
|
plt.figure()
|
|
|
|
plt.plot(t, ThetaSaved)
|
|
|
|
plt.xlabel('Time [Sec]')
|
|
|
|
plt.ylabel('Pitch angle [deg]')
|
|
|
|
plt.savefig('12_EulerEKF_pitch.png')
|
|
|
|
plt.show()
|
|
|
|
'''
|
|
|
|
plt.subplot(133)
|
|
|
|
plt.plot(t, PsiSaved)
|
|
|
|
plt.xlabel('Time [Sec]')
|
|
|
|
plt.ylabel('Psi angle [deg]')
|
2023-08-18 12:03:39 +02:00
|
|
|
'''
|