In [55]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline

In [56]:
data = pd.read_csv("logdata.csv") #loading datas
data


Out[56]:
T 加速度X(G) 加速度Y(G) 加速度Z(G) 加速度合計(G) 地磁気X(gauss) 地磁気Y(gauss) 地磁気Z(gauss) ジャイロX(dps) ジャイロY(dps) ジャイロZ(dps) 気圧(hPa) 高度(m) 気温(℃) 飛行状態 Unnamed: 15 Unnamed: 16
0 - 9.99 -0.070 -0.020 1.075 1.08 0.527 -0.648 -0.204 -3.080 3.299 -3.780 980.64 275.06 39.54 0 NaN NaN
1 - 9.86 -0.088 -0.025 1.073 1.08 0.528 -0.648 -0.204 -3.229 3.386 -3.623 980.72 274.41 39.54 0 NaN NaN
2 - 9.73 -0.082 -0.023 1.072 1.08 0.528 -0.648 -0.207 -2.949 3.378 -3.623 980.61 275.29 39.54 0 NaN NaN
3 - 9.60 -0.072 -0.034 1.069 1.07 0.527 -0.648 -0.205 -3.071 3.325 -3.684 980.64 275.02 39.54 0 NaN NaN
4 - 9.47 -0.079 -0.031 1.073 1.08 0.527 -0.648 -0.205 -3.089 3.439 -3.631 980.67 274.85 39.55 0 NaN NaN
5 - 9.34 -0.075 -0.034 1.076 1.08 0.528 -0.647 -0.205 -3.010 3.351 -3.684 980.63 275.18 39.54 0 NaN NaN
6 - 9.21 -0.079 -0.025 1.071 1.07 0.527 -0.647 -0.206 -3.089 3.395 -3.684 980.69 274.60 39.54 0 NaN NaN
7 - 9.09 -0.081 -0.021 1.072 1.07 0.528 -0.647 -0.206 -3.036 3.246 -3.754 980.68 274.72 39.54 0 NaN NaN
8 - 8.96 -0.075 -0.025 1.078 1.08 0.528 -0.647 -0.206 -3.071 3.238 -3.614 980.69 274.68 39.54 0 NaN NaN
9 - 8.83 -0.073 -0.015 1.069 1.07 0.528 -0.647 -0.206 -3.063 3.325 -3.763 980.67 274.78 39.55 0 NaN NaN
10 - 8.70 -0.075 -0.025 1.075 1.08 0.528 -0.648 -0.205 -2.966 3.316 -3.614 980.64 275.08 39.54 0 NaN NaN
11 - 8.56 -0.072 -0.026 1.071 1.07 0.527 -0.647 -0.205 -3.045 3.343 -3.824 980.67 274.80 39.54 0 NaN NaN
12 - 8.43 -0.077 -0.029 1.073 1.08 0.527 -0.648 -0.206 -3.159 3.421 -3.824 980.51 276.19 39.54 0 NaN NaN
13 - 8.31 -0.078 -0.030 1.078 1.08 0.528 -0.647 -0.205 -3.063 3.308 -3.675 980.67 274.80 39.54 0 NaN NaN
14 - 8.18 -0.081 -0.025 1.072 1.08 0.528 -0.647 -0.205 -3.019 3.168 -3.763 980.67 274.79 39.54 0 NaN NaN
15 - 8.05 -0.078 -0.022 1.067 1.07 0.528 -0.647 -0.205 -3.106 3.299 -3.693 980.60 275.38 39.54 0 NaN NaN
16 - 7.92 -0.083 -0.023 1.070 1.07 0.528 -0.647 -0.206 -3.098 3.465 -3.649 980.55 275.85 39.55 0 NaN NaN
17 - 7.79 -0.071 -0.029 1.080 1.08 0.528 -0.648 -0.205 -3.185 3.360 -3.658 980.54 275.88 39.54 0 NaN NaN
18 - 7.66 -0.087 -0.031 1.075 1.08 0.528 -0.647 -0.205 -3.036 3.395 -3.570 980.63 275.12 39.54 0 NaN NaN
19 - 7.53 -0.083 -0.017 1.069 1.07 0.528 -0.647 -0.205 -3.010 3.308 -3.640 980.58 275.57 39.54 0 NaN NaN
20 - 7.40 -0.075 -0.023 1.067 1.07 0.528 -0.648 -0.205 -3.080 3.378 -3.859 980.65 274.99 39.54 0 NaN NaN
21 - 7.27 -0.077 -0.028 1.076 1.08 0.527 -0.647 -0.206 -3.098 3.255 -3.815 980.61 275.34 39.54 0 NaN NaN
22 - 7.14 -0.072 -0.030 1.079 1.08 0.529 -0.645 -0.203 -2.958 3.343 -3.763 980.63 275.17 39.54 0 NaN NaN
23 - 7.01 -0.073 -0.029 1.075 1.08 0.528 -0.647 -0.205 -3.124 3.369 -3.745 980.60 275.37 39.54 0 NaN NaN
24 - 6.88 -0.075 -0.029 1.077 1.08 0.528 -0.647 -0.205 -3.185 3.308 -3.824 980.65 274.96 39.54 0 NaN NaN
25 - 6.75 -0.072 -0.029 1.070 1.07 0.530 -0.645 -0.202 -3.098 3.290 -3.666 980.68 274.69 39.54 0 NaN NaN
26 - 6.62 -0.076 -0.028 1.075 1.08 0.527 -0.647 -0.204 -3.185 3.316 -3.938 980.62 275.21 39.54 0 NaN NaN
27 - 6.49 -0.083 -0.020 1.069 1.07 0.530 -0.645 -0.203 -3.063 3.220 -3.684 980.65 274.98 39.55 0 NaN NaN
28 - 6.36 -0.076 -0.031 1.078 1.08 0.529 -0.647 -0.206 -3.063 3.378 -3.868 980.62 275.25 39.54 0 NaN NaN
29 - 6.23 -0.075 -0.030 1.080 1.08 0.529 -0.647 -0.206 -3.001 3.334 -3.798 980.65 274.95 39.54 0 NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
157 + 10.42 -0.013 -0.036 -0.100 0.11 0.050 -0.528 0.334 65.660 20.799 222.810 962.65 169.72 39.39 4 NaN NaN
158 + 10.54 -0.010 -0.047 -0.081 0.09 -0.073 -0.326 0.364 55.650 -12.933 196.315 963.16 164.87 39.39 4 NaN NaN
159 + 10.67 -0.022 -0.067 -0.026 0.07 -0.073 -0.326 0.364 23.424 -22.654 168.508 963.17 164.76 39.38 4 NaN NaN
160 + 10.81 -0.075 -0.075 0.010 0.11 -0.041 -0.130 0.383 0.201 -2.931 129.159 963.66 160.10 39.38 4 NaN NaN
161 + 10.93 -0.098 -0.044 0.012 0.11 0.026 -0.021 0.385 -6.641 25.489 110.714 963.91 157.73 39.38 4 NaN NaN
162 + 11.06 -0.070 -0.016 -0.015 0.07 0.073 0.039 0.375 -3.903 45.990 103.268 964.54 151.70 39.38 4 NaN NaN
163 + 11.20 -0.048 -0.034 -0.033 0.07 0.114 0.085 0.357 3.955 50.278 93.564 965.22 145.25 39.37 4 NaN NaN
164 + 11.34 -0.005 -0.040 -0.020 0.05 0.114 0.085 0.357 6.991 41.755 86.660 965.52 142.37 39.37 4 NaN NaN
165 + 11.46 0.006 -0.046 0.001 0.05 0.165 0.120 0.337 -3.605 24.063 91.306 966.04 137.40 39.38 4 NaN NaN
166 + 11.59 0.004 -0.061 -0.001 0.06 0.233 0.136 0.334 -22.934 6.536 101.833 966.32 134.79 39.37 4 NaN NaN
167 + 11.72 0.010 -0.056 -0.015 0.06 0.328 0.119 0.349 -40.801 -1.260 106.943 966.92 129.09 39.36 4 NaN NaN
168 + 11.85 -0.007 -0.045 -0.034 0.06 0.416 0.051 0.378 -47.731 -0.674 102.454 967.73 121.37 39.36 4 NaN NaN
169 + 11.98 -0.015 -0.002 -0.026 0.03 0.416 0.051 0.378 -41.545 -0.123 94.343 968.03 118.54 39.36 4 NaN NaN
170 + 12.11 0.004 0.000 -0.006 0.01 0.469 -0.043 0.399 -26.364 -2.013 86.013 968.69 112.35 39.35 4 NaN NaN
171 + 12.24 0.012 0.021 -0.005 0.02 0.488 -0.134 0.404 -7.429 -7.158 79.494 968.99 109.47 39.35 4 NaN NaN
172 + 12.37 -0.028 0.016 -0.028 0.04 0.496 -0.202 0.397 6.178 -13.291 75.215 969.80 101.84 39.34 4 NaN NaN
173 + 12.50 -0.015 -0.008 -0.042 0.05 0.503 -0.256 0.389 10.929 -12.311 67.760 970.66 93.67 39.34 4 NaN NaN
174 + 12.63 -0.037 -0.010 -0.051 0.06 0.503 -0.256 0.389 5.189 -4.891 59.868 971.10 89.55 39.34 4 NaN NaN
175 + 12.76 -0.044 -0.010 -0.045 0.06 0.496 -0.295 0.383 -2.083 9.450 50.908 971.93 81.71 39.35 4 NaN NaN
176 + 12.89 -0.042 -0.013 -0.051 0.07 0.471 -0.338 0.386 -8.549 18.953 45.745 972.31 78.13 39.34 4 NaN NaN
177 + 13.02 -0.009 -0.007 -0.052 0.05 0.428 -0.379 0.392 -11.708 19.740 43.584 973.20 69.77 39.34 4 NaN NaN
178 + 13.15 0.003 0.015 -0.054 0.06 0.388 -0.423 0.397 -11.953 11.751 46.673 974.11 61.23 39.33 4 NaN NaN
179 + 13.28 0.008 0.008 -0.061 0.06 0.349 -0.457 0.393 -10.911 -1.986 47.583 974.50 57.52 39.34 4 NaN NaN
180 + 13.41 0.006 -0.020 -0.060 0.06 0.349 -0.457 0.393 -12.093 -7.420 49.018 975.46 48.49 39.33 4 NaN NaN
181 + 13.54 -0.009 0.008 -0.077 0.08 0.314 -0.488 0.385 -12.163 -6.151 50.015 975.91 44.31 39.33 4 NaN NaN
182 + 13.67 -0.034 0.026 -0.066 0.08 0.272 -0.510 0.377 -7.814 0.831 47.478 976.93 34.74 39.32 4 NaN NaN
183 + 13.80 -0.025 0.009 -0.066 0.07 0.233 -0.523 0.374 -2.511 4.725 46.025 977.89 25.74 39.32 4 NaN NaN
184 + 13.93 -0.025 -0.019 -0.096 0.10 0.182 -0.526 0.375 -0.963 3.903 48.038 978.31 21.83 39.32 4 NaN NaN
185 + 14.06 -0.009 -0.010 -0.086 0.09 0.182 -0.526 0.375 -4.681 1.601 48.869 979.36 12.00 39.32 4 NaN NaN
186 + 14.19 -0.010 -0.004 -0.117 0.12 0.136 -0.523 0.377 -5.320 2.695 50.916 979.83 7.62 39.31 4 NaN NaN

187 rows × 17 columns


In [57]:
# pandas -> ndarray
cols = data.columns
times = np.array(map(lambda x: float(x.replace(' ','')),data[cols[0]]))
accelX = np.array(data[cols[1]])
accelY = np.array(data[cols[2]])
accelZ = np.array(data[cols[3]])
magX = np.array(data[cols[5]])
magY = np.array(data[cols[6]])
magZ = np.array(data[cols[7]])
gyroX = np.array(data[cols[8]])
gyroY = np.array(data[cols[9]])
gyroZ = np.array(data[cols[10]])
press = np.array(data[cols[11]] * 100.0) # [hPa]->[Pa]
height = np.array(data[cols[12]] )
temperature = np.array(data[cols[13]])
flightmode = np.array(data[cols[14]],np.int)

In [58]:
def normalize(vec):
    vec = np.array(vec)
    norm = np.linalg.norm(vec)
    if norm == 0.0:
        return vec
    else:
        return (vec/norm)

In [59]:
# make the height before launching zero
height[ flightmode < 2] = 0.0

In [60]:
# The graph of height
plt.plot(times,height)
plt.xlabel("time[s]")
plt.ylabel("height[m]")
plt.title("The height of rocket")


Out[60]:
<matplotlib.text.Text at 0x84af390>

In [61]:
# The graph of atmospheric pressure
plt.plot(times,press)
plt.xlabel("time[s]")
plt.ylabel("atmospheric pressure[Pa]")
plt.title("The atmospheric pressure")


Out[61]:
<matplotlib.text.Text at 0x8696b30>

In [62]:
# The graph of temperature
plt.plot(times,temperature)
plt.xlabel("time[s]")
plt.ylabel("temperature[C^o]")
plt.title("The temperature of atmosphpere")


Out[62]:
<matplotlib.text.Text at 0x879eb10>

In [63]:
# The graph of acceleration
accelMag = map( lambda x: np.linalg.norm(x) , zip(accelX,accelY,accelZ) )
plt.plot(times,accelMag,label="accel mag")
plt.plot(times,accelX,label="accelX")
plt.plot(times,accelY,label="accelY")
plt.plot(times,accelZ,label="accelZ")
plt.legend()
plt.title("The acceleration of rocket")
plt.xlabel("time[s]")
plt.ylabel("accel[G]")


Out[63]:
<matplotlib.text.Text at 0x88f6810>

In [64]:
# detect bias of the gyro sensor and eliminate
detect_bias = lambda xs: xs - np.average(xs[flightmode == 0])
gyroX = detect_bias(gyroX)
gyroY = detect_bias(gyroY)
gyroZ = detect_bias(gyroZ)

In [65]:
# The graph of angular velocity
gyroMag = map( lambda x: np.linalg.norm(x) , zip(gyroX,gyroY,gyroZ) )
plt.plot(times,gyroMag,label="gyro mag")
plt.plot(times,gyroX,label="gyroX")
plt.plot(times,gyroY,label="gyroY")
plt.plot(times,gyroZ,label="gyroZ")
plt.legend(loc='upper left')
plt.title("The angular velocity of rocket")
plt.xlabel("time[s]")
plt.ylabel("angular velocity[deg/s]")


Out[65]:
<matplotlib.text.Text at 0x88eafb0>

In [66]:
# detect bias of the magnetic sensor and eliminate
def calc_bias_mag(magXs,magYs,magZs):
    
    # Least-square method
    # make the norm of mag. constant
    A = np.array([magXs,magYs,magZs,np.ones_like(magXs)]).T
    b = map( lambda x: x[0]**2+x[1]**2+x[2]**2 , zip(magXs,magYs,magZs) )
    a,b,c,d = np.linalg.solve(2*A.T.dot(A),A.T.dot(b))
    
    return (a,b,c)

a,b,c = calc_bias_mag(magX,magY,magZ)
magX = magX[:] - a
magY = magY[:] - b
magZ = magZ[:] - c

In [67]:
# The graph of magnetic sensor
magMag = map( lambda x: np.linalg.norm(x) , zip(magX,magY,magZ) )
plt.plot(times,magMag,label="mag norm")
plt.plot(times,magX,label="magX")
plt.plot(times,magY,label="magY")
plt.plot(times,magZ,label="magZ")
plt.legend(loc='upper left')
plt.title("The geomagnetism ")
plt.xlabel("time[s]")
plt.ylabel("geomagnetism [gauss]")


Out[67]:
<matplotlib.text.Text at 0x877edf0>

In [68]:
# The quaternion class from: http://d.hatena.ne.jp/yatt/20090920/1253452790
class Quaternion(object):
    def __init__(self, t, v):
        self.t = float(t)
        self.v = np.array(map(float,v))
    def __pos__(p): # +p
        return p
    def __neg__(p): # -p
        return Quaternion(-p.t, -p.v)
    def __add__(p, q): # p+q
        return Quaternion(p.t + q.t, p.v + q.v)
    def __sub__(p, q): # p-q
        return Quaternion(p.t - q.t, p.v - q.v)
    def __mul__(p, q): # p*q
        return Quaternion(p.t*q.t - np.dot(p.v, q.v),
                          p.t*q.v + q.t*p.v + np.cross(p.v, q.v)
                         )
    def inverse(p): # p*p_ = 1
        # 逆4元数
        n = p.abs2()
        p_= p.conj()
        return Quaternion(p_.t / n, p_.v / n)
    def abs2(p):
        return p.t**2 + sum(p.v**2)
    
    def to_arr(p):
        return [q.t,q.x,q.y,q.z]
    
    def __abs__(p):
        return p.abs2()**.5
    def conj(p):
        # 共役4元数
        return Quaternion(p.t, -p.v)
    def __repr__(p):
        return "(%f; %f, %f, %f)" % (p.t, p.x, p.y, p.z)
    @property
    def x(self): return self.v[0]
    @property
    def y(self): return self.v[1]
    @property
    def z(self): return self.v[2]

In [69]:
def rotation(v,q_attitude):
    v = Quaternion(0.0,v)
    v = q_attitude * v * q_attitude.conj()
    v = v.v
    return np.array(v)

In [70]:
def gyro_to_quat(gx,gy,gz,dt,q_attitude,is_deg = True):
    """ make the small rotation quaternion """
    if is_deg:
        gx = np.deg2rad(gx)
        gy = np.deg2rad(gy)
        gz = np.deg2rad(gz)
    
    v = rotation([gx,gy,gz],q_attitude)
    norm = np.linalg.norm(v)
    v = normalize(v)
        
    theta = norm * 0.5 * dt
    return Quaternion(np.cos(theta),np.sin(theta)*v)

In [71]:
def to_euler(q):
    """ quaternion -> euler angles """
    q_arr = np.array([q.t,q.x,q.y,q.z])
    norm = np.linalg.norm(q_arr)
    if np.abs(norm) < 1e-8:
        q_arr = [1,0,0,0]
    else:
        q_arr = q_arr / norm
    
    q1,q2,q3,q4 = q_arr
    # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles  
    # https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_Math/quaternion.cpp
    
    pitch = np.arcsin(2.0*(q1*q3 - q4*q2))
    roll = (np.arctan2(2.0*(q1*q2 + q3*q4), 1.0 - 2.0*(q2*q2 + q3*q3)))
    yaw = np.arctan2(2.0*(q1*q4 + q2*q3), 1.0 - 2.0*(q3*q3 + q4*q4))
            
    return (roll,pitch,yaw)

In [72]:
def to_quat(euler):
    """ euler angles -> quaternion """
    # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
    # https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_Math/quaternion.cpp
    
    roll,pitch,yaw = euler
    
    cr2 = np.cos(roll*0.5)
    cp2 = np.cos(pitch*0.5)
    cy2 = np.cos(yaw*0.5)
    sr2 = np.sin(roll*0.5)
    sp2 = np.sin(pitch*0.5)
    sy2 = np.sin(yaw*0.5)

    q1 = cr2*cp2*cy2 + sr2*sp2*sy2
    q2 = sr2*cp2*cy2 - cr2*sp2*sy2
    q3 = cr2*sp2*cy2 + sr2*cp2*sy2
    q4 = cr2*cp2*sy2 - sr2*sp2*cy2
    
    return Quaternion(q1,[q2,q3,q4])

In [73]:
# calculate the attitude of rocket from gyrosensor and convert quaternion and euler angles
q = Quaternion(1,[0,0,0])
quats = []
mag0 = np.array([magX[0],magY[0],magZ[0]])
mag0 = normalize(mag0)
for dt,gx,gy,gz,mx,my,mz in zip(np.diff(times),gyroX[1:],gyroY[1:],gyroZ[1:],magX[1:],magY[1:],magZ[1:]):
    q_small = gyro_to_quat(gx,gy,gz,dt,q,True)
    q = q_small * q
    """
    # correction by magnetic sensor?????????
    mag_v = rotation([mx,my,mz],q)
    mag_v = normalize(mag_v)
    theta = np.arccos(mag_v.dot(mag0))
    
    v_rot = np.cross(mag_v,mag0)
    v_rot = normalize(v_rot)
    
    if theta <= 1e-8:
        pass
    else:
        corr_ratio = 0.01
        theta = theta * 0.5 * corr_ratio
        q_cor = Quaternion(np.cos(theta),np.sin(theta)*v_rot)
        q = q_cor * q
    """
    quats.append(q)
angle_xyz = map(to_euler,quats)
quats = [q.to_arr() for q in quats]
attitudes = np.c_[times[1:],quats,angle_xyz,height[1:]]
attitudes


Out[73]:
array([[ -9.86000000e+00,   9.99999961e-01,  -2.22281277e-04, ...,
          2.05442069e-04,   2.69396912e-04,   0.00000000e+00],
       [ -9.73000000e+00,   9.99999936e-01,  -1.26911411e-04, ...,
          3.92766890e-04,   5.38774110e-04,   0.00000000e+00],
       [ -9.60000000e+00,   9.99999903e-01,  -1.69943532e-04, ...,
          4.59783346e-04,   6.69792202e-04,   0.00000000e+00],
       ..., 
       [  1.39300000e+01,   7.06555944e-02,   8.80027626e-02, ...,
          1.22840469e-01,   2.95117786e+00,   2.18300000e+01],
       [  1.40600000e+01,   6.67657095e-02,   1.46882630e-01, ...,
          1.00845610e-01,   2.83304742e+00,   1.20000000e+01],
       [  1.41900000e+01,   6.12774145e-02,   2.07315950e-01, ...,
          7.34353882e-02,   2.71162733e+00,   7.62000000e+00]])

In [74]:
np.savetxt("attitudes.csv",attitudes,delimiter=",",header="time,q0,q1,q2,q3,angle_x,angle_y,angle_z,height")

In [75]:
magXYZ = zip( magX[1:],magY[1:],magZ[1:])
for i in range(len(magXYZ)):
    mag_vec = magXYZ[i]
    q = quats[i]
    q = Quaternion(q[0],q[1:])
    magXYZ[i] = rotation(magXYZ[i],q)
magXYZ = np.array(magXYZ)

In [76]:
# The graph of magnetic sensor (r-frame)
magMagXYZ = map( lambda x: np.linalg.norm(x) , magXYZ )
plt.plot(times[1:],magMagXYZ,label="mag norm")
plt.plot(times[1:],magXYZ[:,0],label="magX")
plt.plot(times[1:],magXYZ[:,1],label="magY")
plt.plot(times[1:],magXYZ[:,2],label="magZ")
plt.legend(loc='upper left')
plt.title("The geomagnetism ")
plt.xlabel("time[s]")
plt.ylabel("geomagnetism [gauss]")


Out[76]:
<matplotlib.text.Text at 0x8c3aed0>