In [1]:
from __future__ import print_function
import sympy
import sympy.physics.mechanics as mech
sympy.init_printing()
mech.init_vprinting()
In [2]:
t = sympy.symbols('t')
rot_N, rot_E, rot_D, vel_N, vel_E, vel_D, \
gyro_bias_N, gyro_bias_E, gyro_bias_D, \
accel_bias_N, accel_bias_E, accel_bias_D, \
pos_N, pos_E, asl, terrain_asl, baro_bias, \
wind_N, wind_E, wind_D, d, agl, phi, theta, psi = mech.dynamicsymbols(
'rot_N, rot_E, rot_D, vel_N, vel_E, vel_D, ' \
'gyro_bias_N, gyro_bias_E, gyro_bias_D, ' \
'accel_bias_N, accel_bias_E, accel_bias_D, ' \
'pos_N, pos_E, asl, terrain_asl, baro_bias, ' \
'wind_N, wind_E, wind_D, d, agl, phi, theta, psi')
frame_i = mech.ReferenceFrame('i')
frame_n = frame_i.orientnew('n', 'Quaternion', (1, rot_N, rot_E, rot_D))
#frame_b = frame_n.orientnew('b', 'Quaternion', (q_0, q_1, q_2, q_3))
# easier to see where we get divide by zeros if we express dcm in euler angles
frame_b = frame_n.orientnew('b', 'Body', (psi, theta, phi), '321')
C_nb = frame_n.dcm(frame_b)
assert C_nb[0, 1] == frame_n.x.dot(frame_b.y)
sub_C_nb = {}
for i in range(3):
for j in range(3):
sub_C_nb[C_nb[i, j]] = sympy.Symbol('C_nb({:d}, {:d})'.format(i, j))(t)
sub_C_nb[-C_nb[i, j]] = -sympy.Symbol('C_nb({:d}, {:d})'.format(i, j))(t)
sub_C_nb_rev = { sub_C_nb[key]: key for key in sub_C_nb.keys() }
sub_lin = {
rot_N: 0,
rot_E: 0,
rot_D: 0,
gyro_bias_N: 0,
gyro_bias_E: 0,
gyro_bias_D: 0
}
sub_agl = {
asl - terrain_asl: agl
}
omega_bx, omega_by, omega_bz = mech.dynamicsymbols('omega_bx, omega_by, omega_bz')
flowX, flowY = mech.dynamicsymbols('flowX, flowY')
omega_ib_b = omega_bx * frame_b.x \
+ omega_by * frame_b.y \
+ omega_bz * frame_b.z
gyro_bias_i = gyro_bias_N * frame_i.x \
+ gyro_bias_E * frame_i.y \
+ gyro_bias_D * frame_i.z
omega_nx, omega_ny, omega_nz = mech.dynamicsymbols('omega_nx, omega_ny, omega_nz')
omega_in_n = -gyro_bias_N * frame_n.x \
- gyro_bias_E * frame_n.y \
- gyro_bias_D * frame_n.z
a_N, a_E, a_D = mech.dynamicsymbols('a_N, a_E, a_D')
a_n = a_N*frame_n.x + a_E*frame_n.y + a_D*frame_n.z
a_bias_n = accel_bias_N*frame_n.x + accel_bias_E*frame_n.y + accel_bias_D*frame_n.z
a_n_correct = a_n - a_bias_n
v_i = vel_N*frame_i.x + vel_E*frame_i.y + vel_D*frame_i.z
p_i = pos_N*frame_i.x + pos_E*frame_i.y - asl*frame_i.z
I_wx, I_wy, I_wz = mech.dynamicsymbols('I_wx, I_wy, I_wz')
I_w_n = I_wx*frame_n.x + I_wy*frame_n.y + I_wz*frame_n.z
In [3]:
xe = sympy.Matrix([rot_N, rot_E, rot_D, vel_N, vel_E, vel_D, gyro_bias_N, gyro_bias_E, gyro_bias_D,
accel_bias_N, accel_bias_E, accel_bias_D,
pos_N, pos_E, asl, terrain_asl, baro_bias, wind_N, wind_E, wind_D])
xe.T
Out[3]:
In [4]:
def print_terms(terms):
for t in terms:
s = 'float {:s} = {:s};'.format(
str(t[0]), str(t[1]))
print(s.replace('(t)', ''))
def matrix_to_code(name, mat, i_name, i_syms, j_name, j_syms):
print('Matrix<float, {:s}n, {:s}n> {:s};'.format(i_name, j_name, name))
mat.simplify()
terms, mat = sympy.cse(mat)
print_terms(terms)
mat = mat[0]
for i in range(mat.shape[0]):
for j in range(mat.shape[1]):
if str(mat[i, j]) == "0":
continue
s = '{:s}({:s}{:s}, {:s}{:s}) = {:s};'.format(
str(name), i_name, str(i_syms[i]),
j_name, str(j_syms[j]), str(mat[i, j]))
print(s.replace('(t)', ''))
This is just to check the other derivaiton in IEKF Derivation notebook, doesn't match yet, needes further work.
In [5]:
trans_kin_eqs = list((a_n_correct.express(frame_i) - v_i.diff(t, frame_i)).to_matrix(frame_i))
trans_kin_eqs
Out[5]:
In [6]:
nav_eqs = list((p_i.diff(t, frame_i) - v_i).to_matrix(frame_i))
nav_eqs
Out[6]:
In [7]:
sub_q = {
(1 + rot_N**2 + rot_E**2 + rot_D**2): 1,
2*(1 + rot_N**2 + rot_E**2 + rot_D**2): 2
}
In [8]:
rot_kin_eqs = list((frame_n.ang_vel_in(frame_i) - omega_in_n).to_matrix(frame_n))
rot_kin_eqs
Out[8]:
In [9]:
static_eqs = [
terrain_asl.diff(t),
baro_bias.diff(t),
wind_N.diff(t),
wind_E.diff(t),
wind_D.diff(t),
accel_bias_N.diff(t),
accel_bias_E.diff(t),
accel_bias_D.diff(t),
]
static_eqs
Out[9]:
In [10]:
static_eqs
Out[10]:
In [11]:
gyro_eqs = list((omega_in_n.diff(t, frame_n) - frame_i.ang_vel_in(frame_n).cross(I_w_n)).to_matrix(frame_n))
gyro_eqs
Out[11]:
In [12]:
sol = sympy.solve(rot_kin_eqs + trans_kin_eqs + static_eqs + nav_eqs + gyro_eqs, xe.diff(t))
sol = { key:sol[key].subs(sub_q) for key in sol.keys() }
xe_dot = sympy.Matrix([ sol[var] for var in xe.diff(t) ]).applyfunc(lambda x: x.subs(sub_q))
#xe_dot
In [13]:
A = xe_dot.jacobian(xe).subs(sub_lin)
#A
In [14]:
matrix_to_code('A', A, 'Xe::', xe, 'Xe::', xe)
In [15]:
wind_i = wind_N*frame_i.x + wind_E*frame_i.y + wind_D*frame_i.z
vel_i = vel_N*frame_i.x + vel_E*frame_i.y + vel_D*frame_i.z
In [16]:
rel_wind = wind_i - vel_i
y_airspeed = sympy.Matrix([rel_wind.dot(-frame_b.x)]).subs(sub_C_nb)
y_airspeed
Out[16]:
In [17]:
H_airspeed = y_airspeed.jacobian(xe).subs(sub_lin)
H_airspeed.T
Out[17]:
In [18]:
matrix_to_code('H', H_airspeed,
'Y_airspeed::', [sympy.Symbol('airspeed')],
'Xe::', xe)
In [19]:
d_eq = sympy.solve((d*frame_b.z).dot(frame_i.z).subs(sub_C_nb) - (asl - terrain_asl), d)[0]
d_eq.subs(sub_agl)
Out[19]:
In [20]:
y_dist = sympy.Matrix([d_eq]).subs(sub_C_nb)
y_dist[0].subs(sub_lin).subs(sub_agl)
Out[20]:
In [21]:
H_distance = y_dist.jacobian(xe).subs(sub_lin).subs(sub_agl)
H_distance.T
matrix_to_code('H', H_distance, 'Y_distance_down::',
[sympy.symbols('d')], 'Xe::', xe)
In [22]:
#omega_nx, omega_ny, omega_nz = sympy.symbols('\omega_{nx}, \omega_{ny}, \omega_{nz}')
#omega_ib_n = omega_nx*frame_i.x + omega_ny*frame_i.y + omega_nz*frame_i.z
#omega_ib_n
In [23]:
y_flow_sym = [flowX, flowY]
omega_n = (omega_ib_b - gyro_bias_i)
vel_f_b = -vel_i - omega_n.cross(d_eq*frame_b.z)
vel_f_b.subs(sub_lin).subs(sub_agl)
Out[23]:
In [24]:
y_flow = sympy.Matrix([
-vel_f_b.dot(frame_b.x).subs(sub_C_nb),
-vel_f_b.dot(frame_b.y).subs(sub_C_nb)
]).subs(sub_C_nb)
In [25]:
def sym2latex(s):
return sympy.latex(s).replace(r'{\left (t \right )}', '')
In [26]:
y_flow_lin = y_flow.subs(sub_lin).subs(sub_agl).subs(sub_C_nb)
y_flow_lin.simplify()
matrix_to_code('y_flow_lin', y_flow_lin, 'Y_flow::', y_flow_sym, '', [0])
In [27]:
H_flow = y_flow.jacobian(xe).subs(sub_lin).subs(sub_agl)
H_flow
for i in range(H_flow.shape[0]):
for j in range(H_flow.shape[1]):
if H_flow[i, j] != 0:
s_mat = sym2latex(H_flow[i, j])
print('H[{:s}, {:s}] =& {:s} \\\\'.format(
sym2latex(y_flow_sym[i]),
sym2latex(xe[j]),
sym2latex(H_flow[i, j])))
In [28]:
H_flow.shape[0]
Out[28]:
In [29]:
H_flow.subs(sub_C_nb_rev).subs({phi: 0, theta:0, psi:0})
Out[29]:
In [30]:
P = sympy.diag(*[sympy.Symbol('var_' + str(xi)) for xi in xe])
R = sympy.diag(*[sympy.Symbol('var_flowY'), sympy.Symbol('var_flowX')])
#P = sympy.MatrixSymbol('P', len(xe), len(xe))
#R = sympy.MatrixSymbol('R', 2, 2)
S = H_flow * P * H_flow.T + R
S.simplify()
In [31]:
S[0, 0].subs(sub_agl)
Out[31]:
In [32]:
S[1, 1].subs(sub_agl)
Out[32]:
In [33]:
S[0, 0].subs(sub_agl).subs(sub_C_nb_rev).subs({phi: 0, theta: 0})
Out[33]:
In [34]:
S[1, 1].subs(sub_agl).subs(sub_C_nb_rev).subs({phi: 0, theta: 0, psi:0})
Out[34]:
In [35]:
S.subs(sub_agl).subs(sub_C_nb_rev).subs({phi: 0, theta: 0, psi:0, omega_bx:0, omega_by: 0})
Out[35]:
In [36]:
H_flow.subs(sub_agl).subs(sub_C_nb_rev).subs({phi: 0, theta: 0, psi:0, omega_bx:0, omega_by: 0})
Out[36]:
In [37]:
matrix_to_code('S', sympy.diag(S[0,0]), 'Y_flow::', y_flow_sym, 'Y_flow::', y_flow_sym,)
In [38]:
matrix_to_code('H', H_flow, 'Y_flow::', y_flow_sym, 'Xe::', xe)
In [39]:
y_attitude = sympy.Matrix([
rot_N, rot_E, rot_D
])
H_attitude = y_attitude.jacobian(xe).subs(sub_lin).subs(sub_agl)
H_attitude
Out[39]:
In [40]:
g = sympy.symbols('g')
g_i = -g*frame_i.z + accel_bias_N*frame_i.x + accel_bias_E*frame_i.y + accel_bias_D*frame_i.z
y_accel = sympy.Matrix(g_i.express(frame_b).subs(sub_C_nb).to_matrix(frame_b))
H_accel = y_accel.jacobian(xe).subs(sub_lin)
H_accel
Out[40]:
In [41]:
H_accel.subs(sub_C_nb_rev).subs({phi: 0, theta: 0, psi: 0})
Out[41]:
In [42]:
B_N, B_E, B_D = sympy.symbols('B_N, B_E, B_D')
b_i = B_N*frame_i.x + B_E*frame_i.y + B_D*frame_i.z
y_mag = sympy.Matrix(b_i.express(frame_b).subs(sub_C_nb).to_matrix(frame_b))
H_mag = y_mag.jacobian(xe).subs(sub_lin).subs(sub_agl)
H_mag.simplify()
H_mag
Out[42]:
In [43]:
def find_observable_states(H, x, n_max=3):
O = sympy.Matrix(H)
for n in range(n_max):
O = O.col_join(H*A**n)
return [x[i] for i in O.rref()[1]]
In [44]:
find_observable_states(H_mag, xe)
Out[44]:
In [45]:
find_observable_states(H_accel, xe)
Out[45]:
In [46]:
find_observable_states(H_mag.col_join(H_accel), xe)
Out[46]: