def test_simulate_continuous(self): m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) q = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 1e-6])) dyn = ReentryVehicle2DTransition(x0, q, dt=0.05) x = dyn.simulate_continuous(200, dt=0.05) plt.figure() plt.plot(x[0, ...], x[1, ...], color='r') plt.show()
def test_cho_dot_ein(self): # attempt to compute the transformed covariance using cholesky decomposition # integrand # input moments mean_in = np.array([6500.4, 349.14, 1.8093, 6.7967, 0.6932]) cov_in = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) f = ReentryVehicle2DTransition(GaussRV(5, mean_in, cov_in), GaussRV(3)).dyn_eval dim_in, dim_out = ReentryVehicle2DTransition.dim_state, 1 # transform ker_par_mo = np.hstack((np.ones((dim_out, 1)), 25 * np.ones((dim_out, dim_in)))) tf_so = GaussianProcessTransform(dim_in, dim_out, ker_par_mo, point_str='sr') # Monte-Carlo for ground truth # tf_ut = UnscentedTransform(dim_in) # tf_ut.apply(f, mean_in, cov_in, np.atleast_1d(1), None) tf_mc = MonteCarloTransform(dim_in, 1000) mean_mc, cov_mc, ccov_mc = tf_mc.apply(f, mean_in, cov_in, np.atleast_1d(1)) C_MC = cov_mc + np.outer(mean_mc, mean_mc.T) # evaluate integrand x = mean_in[:, None] + la.cholesky(cov_in).dot(tf_so.model.points) Y = np.apply_along_axis(f, 0, x, 1.0, None) # covariance via np.dot iK, Q = tf_so.model.iK, tf_so.model.Q C1 = iK.dot(Q).dot(iK) C1 = Y.dot(C1).dot(Y.T) # covariance via np.einsum C2 = np.einsum('ab, bc, cd', iK, Q, iK) C2 = np.einsum('ab,bc,cd', Y, C2, Y.T) # covariance via np.dot and cholesky K = tf_so.model.kernel.eval(tf_so.model.kernel.par, tf_so.model.points) L_lower = la.cholesky(K) Lq = la.cholesky(Q) phi = la.solve(L_lower, Lq) psi = la.solve(L_lower, Y.T) bet = psi.T.dot(phi) C3_dot = bet.dot(bet.T) C3_ein = np.einsum('ij, jk', bet, bet.T) logging.debug("MAX DIFF: {:.4e}".format(np.abs(C1 - C2).max())) logging.debug("MAX DIFF: {:.4e}".format(np.abs(C3_dot - C3_ein).max())) self.assertTrue(np.allclose(C1, C2), "MAX DIFF: {:.4e}".format(np.abs(C1 - C2).max())) self.assertTrue(np.allclose(C3_dot, C3_ein), "MAX DIFF: {:.4e}".format(np.abs(C3_dot - C3_ein).max())) self.assertTrue(np.allclose(C1, C3_dot), "MAX DIFF: {:.4e}".format(np.abs(C1 - C3_dot).max()))
def test_single_vs_multi_output(self): # results of the GPQ and GPQMO should be same if parameters properly chosen, GPQ is a special case of GPQMO m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) dyn = ReentryVehicle2DTransition(x0, GaussRV(5)) f = dyn.dyn_eval dim_in, dim_out = dyn.dim_in, dyn.dim_state # input mean and covariance mean_in, cov_in = m0, P0 # single-output GPQ ker_par_so = np.hstack((np.ones((1, 1)), 25 * np.ones((1, dim_in)))) tf_so = GaussianProcessTransform(dim_in, dim_out, ker_par_so) # multi-output GPQ ker_par_mo = np.hstack((np.ones((dim_out, 1)), 25 * np.ones( (dim_out, dim_in)))) tf_mo = MultiOutputGaussianProcessTransform(dim_in, dim_out, ker_par_mo) # transformed moments # FIXME: transformed covariances different mean_so, cov_so, ccov_so = tf_so.apply(f, mean_in, cov_in, np.atleast_1d(0)) mean_mo, cov_mo, ccov_mo = tf_mo.apply(f, mean_in, cov_in, np.atleast_1d(0)) print('mean delta: {}'.format(np.abs(mean_so - mean_mo).max())) print('cov delta: {}'.format(np.abs(cov_so - cov_mo).max())) print('ccov delta: {}'.format(np.abs(ccov_so - ccov_mo).max())) # results of GPQ and GPQMO should be the same self.assertTrue(np.array_equal(mean_so, mean_mo)) self.assertTrue(np.array_equal(cov_so, cov_mo)) self.assertTrue(np.array_equal(ccov_so, ccov_mo))
def setUpClass(cls): cls.ssm = {} # setup UNGM x0 = GaussRV(1) q = GaussRV(1, cov=np.array([[10.0]])) r = GaussRV(1) dyn = UNGMTransition(x0, q) obs = UNGMMeasurement(r, 1) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'ungm': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}}) # setup UNGM with non-additive noise x0 = GaussRV(1) q = GaussRV(1, cov=np.array([[10.0]])) r = GaussRV(1) dyn = UNGMNATransition(x0, q) obs = UNGMNAMeasurement(r, 1) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'ungmna': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}}) # setup 2D pendulum x0 = GaussRV(2, mean=np.array([1.5, 0]), cov=0.01 * np.eye(2)) dt = 0.01 q = GaussRV(2, cov=0.01 * np.array([[(dt**3) / 3, (dt**2) / 2], [(dt**2) / 2, dt]])) r = GaussRV(1, cov=np.array([[0.1]])) dyn = Pendulum2DTransition(x0, q, dt=dt) obs = Pendulum2DMeasurement(r, dyn.dim_state) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'pend': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}}) # setup reentry vehicle radar tracking m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) q = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 1e-6])) r = GaussRV(2, cov=np.diag([1e-6, 0.17e-6])) dyn = ReentryVehicle2DTransition(x0, q) obs = Radar2DMeasurement(r, 5) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'rer': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}}) # setup coordinated turn bearing only tracking m0 = np.array([1000, 300, 1000, 0, np.deg2rad(-3.0)]) P0 = np.diag([100, 10, 100, 10, 0.1]) x0 = GaussRV(5, m0, P0) dt = 0.1 rho_1, rho_2 = 0.1, 1.75e-4 A = np.array([[dt**3 / 3, dt**2 / 2], [dt**2 / 2, dt]]) Q = np.zeros((5, 5)) Q[:2, :2], Q[2:4, 2:4], Q[4, 4] = rho_1 * A, rho_1 * A, rho_2 * dt q = GaussRV(5, cov=Q) r = GaussRV(4, cov=10e-3 * np.eye(4)) sen = np.vstack((1000 * np.eye(2), -1000 * np.eye(2))).astype(np.float) dyn = CoordinatedTurnTransition(x0, q) obs = BearingMeasurement(r, 5, state_index=[0, 2], sensor_pos=sen) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'ctb': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}}) # setup CTRS with radar measurements x0 = GaussRV(5, cov=0.1 * np.eye(5)) q = GaussRV(2, cov=np.diag([0.1, 0.1 * np.pi])) r = GaussRV(2, cov=np.diag([0.3, 0.03])) dyn = ConstantTurnRateSpeed(x0, q) obs = Radar2DMeasurement(r, 5) x = dyn.simulate_discrete(100) y = obs.simulate_measurements(x) cls.ssm.update({'ctrs': {'dyn': dyn, 'obs': obs, 'x': x, 'y': y}})
def reentry_gpq_demo(): mc_sims = 20 disc_tau = 0.5 # discretization period # ground-truth data generator (true system) m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 0]) x0 = GaussRV(5, m0, P0) Q = np.diag([2.4064e-5, 2.4064e-5, 0]) q = GaussRV(3, cov=Q) sys = ReentryVehicle2DTransition(x0, q, dt=disc_tau) # radar measurement model r = GaussRV(2, cov=np.diag([1e-6, 0.17e-6])) radar_x, radar_y = sys.R0, 0 obs = Radar2DMeasurement(r, 5, radar_loc=np.array([radar_x, radar_y])) # Generate reference trajectory by Euler-Maruyama integration x = sys.simulate_continuous(duration=200, dt=disc_tau, mc_sims=mc_sims) x_ref = x.mean(axis=2) # generate radar measurements y = obs.simulate_measurements(x) # setup SSM for the filter m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) q = GaussRV(3, cov=disc_tau*Q) dyn = ReentryVehicle2DTransition(x0, q, dt=disc_tau) # Initialize filters hdyn = np.array([[1.0, 25, 25, 25, 25, 25]]) hobs = np.array([[1.0, 25, 25, 1e4, 1e4, 1e4]]) alg = ( GaussianProcessKalman(dyn, obs, hdyn, hobs, kernel='rbf', points='ut'), UnscentedKalman(dyn, obs), ) # Are both filters using the same sigma-points? # assert np.array_equal(alg[0].tf_dyn.model.points, alg[1].tf_dyn.unit_sp) num_alg = len(alg) d, steps, mc_sims = x.shape mean, cov = np.zeros((d, steps, mc_sims, num_alg)), np.zeros((d, d, steps, mc_sims, num_alg)) for imc in range(mc_sims): for ia, a in enumerate(alg): mean[..., imc, ia], cov[..., imc, ia] = a.forward_pass(y[..., imc]) a.reset() # Plots plt.figure() g = GridSpec(2, 4) plt.subplot(g[:, :2]) # Earth surface w/ radar position t = 0.02 * np.arange(-1, 4, 0.1) plt.plot(sys.R0 * np.cos(t), sys.R0 * np.sin(t), color='darkblue', lw=2) plt.plot(radar_x, radar_y, 'ko') plt.plot(x_ref[0, :], x_ref[1, :], color='r', ls='--') # Convert from polar to cartesian meas = np.stack((radar_x + y[0, ...] * np.cos(y[1, ...]), radar_y + y[0, ...] * np.sin(y[1, ...])), axis=0) for i in range(mc_sims): # Vehicle trajectory # plt.plot(x[0, :, i], x[1, :, i], alpha=0.35, color='r', ls='--') # Plot measurements plt.plot(meas[0, :, i], meas[1, :, i], 'k.', alpha=0.3) # Filtered position estimate plt.plot(mean[0, 1:, i, 0], mean[1, 1:, i, 0], color='g', alpha=0.3) plt.plot(mean[0, 1:, i, 1], mean[1, 1:, i, 1], color='orange', alpha=0.3) # Performance score plots error2 = mean.copy() lcr = np.zeros((steps, mc_sims, num_alg)) for a in range(num_alg): for k in range(steps): mse = mse_matrix(x[:4, k, :], mean[:4, k, :, a]) for imc in range(mc_sims): error2[:, k, imc, a] = squared_error(x[:, k, imc], mean[:, k, imc, a]) lcr[k, imc, a] = log_cred_ratio(x[:4, k, imc], mean[:4, k, imc, a], cov[:4, :4, k, imc, a], mse) # Averaged RMSE and Inclination Indicator in time pos_rmse_vs_time = np.sqrt((error2[:2, ...]).sum(axis=0)).mean(axis=1) inc_ind_vs_time = lcr.mean(axis=1) # Plots plt.subplot(g[0, 2:]) plt.title('RMSE') plt.plot(pos_rmse_vs_time[:, 0], label='GPQKF', color='g') plt.plot(pos_rmse_vs_time[:, 1], label='UKF', color='r') plt.legend() plt.subplot(g[1, 2:]) plt.title('Inclination Indicator $I^2$') plt.plot(inc_ind_vs_time[:, 0], label='GPQKF', color='g') plt.plot(inc_ind_vs_time[:, 1], label='UKF', color='r') plt.legend() plt.show() print('Average RMSE: {}'.format(pos_rmse_vs_time.mean(axis=0))) print('Average I2: {}'.format(inc_ind_vs_time.mean(axis=0)))
def reentry_demo(dur=200, mc_sims=100, outfile=None): # use default filename if unspecified if outfile is None: outfile = 'reentry_demo_results.dat' outfile = os.path.join('..', outfile) if not os.path.exists(outfile) or True: tau = 0.05 disc_tau = 0.1 # initial condition statistics m0 = np.array([6500, 350, -1.8, -6.8, 0.7]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 0]) init_rv = GaussRV(5, m0, P0) # process noise statistics noise_rv = GaussRV(3, cov=np.diag([2.4e-5, 2.4e-5, 0])) sys = ReentryVehicle2DTransition(init_rv, noise_rv) # measurement noise statistics meas_noise_rv = GaussRV(2, cov=np.diag([1e-6, 0.17e-6])) obs = Radar2DMeasurement(meas_noise_rv, 5, radar_loc=np.array([6374, 0.0])) np.random.seed(0) # Generate reference trajectory by SDE simulation x = sys.simulate_continuous(duration=dur, dt=tau, mc_sims=mc_sims) y = obs.simulate_measurements(x) # subsample data for the filter and performance score calculation x = x[:, ::2, ...] # take every second point on the trajectory y = y[:, ::2, ...] # and corresponding measurement # Initialize state-space model with mis-specified initial mean # m0 = np.array([6500.4, 349.14, -1.1093, -6.1967, 0.6932]) m0 = np.array([6500, 350, -1.1, -6.1, 0.7]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) init_rv = GaussRV(5, m0, P0) noise_rv = GaussRV(3, cov=np.diag([2.4e-5, 2.4e-5, 1e-6])) dyn = ReentryVehicle2DTransition(init_rv, noise_rv, dt=disc_tau) # NOTE: higher tau causes higher spread of trajectories at the ends # Initialize filters par_dyn = np.array([[1.0, 1, 1, 1, 1, 1]]) par_obs = np.array([[1.0, 0.9, 0.9, 1e4, 1e4, 1e4]]) # np.atleast_2d(np.ones(6)) mul_ut = np.hstack((np.zeros((dyn.dim_in, 1)), np.eye(dyn.dim_in), 2 * np.eye(dyn.dim_in))).astype(np.int) alg = OrderedDict({ # GaussianProcessKalman(ssm, par_dyn, par_obs, kernel='rbf', points='ut'), 'bsqkf': BayesSardKalman(dyn, obs, par_dyn, par_obs, mul_ut, mul_ut, points='ut'), 'bsqkf_2e-6': BayesSardKalman(dyn, obs, par_dyn, par_obs, mul_ut, mul_ut, points='ut'), 'bsqkf_2e-7': BayesSardKalman(dyn, obs, par_dyn, par_obs, mul_ut, mul_ut, points='ut'), 'ukf': UnscentedKalman(dyn, obs, beta=0.0), }) alg['bsqkf'].tf_dyn.model.model_var = np.diag( [0.0002, 0.0002, 0.0002, 0.0002, 0.0002]) alg['bsqkf'].tf_obs.model.model_var = 0 * np.eye(2) alg['bsqkf_2e-6'].tf_dyn.model.model_var = 2e-6 * np.eye(5) alg['bsqkf_2e-6'].tf_obs.model.model_var = 0 * np.eye(2) alg['bsqkf_2e-7'].tf_dyn.model.model_var = 2e-7 * np.eye(5) alg['bsqkf_2e-7'].tf_obs.model.model_var = 0 * np.eye(2) # print experiment setup print('System (reality)') print('{:10s}: {}'.format('x0_mean', sys.init_rv.mean)) print('{:10s}: {}'.format('x0_cov', sys.init_rv.cov.diagonal())) print() print('State-Space Model') print('{:10s}: {}'.format('x0_mean', dyn.init_rv.mean)) print('{:10s}: {}'.format('x0_cov', dyn.init_rv.cov.diagonal())) print() print('BSQ Expected Model Variance') print('{:10s}: {}'.format( 'dyn_func', alg['bsqkf'].tf_dyn.model.model_var.diagonal())) print('{:10s}: {}'.format( 'obs_func', alg['bsqkf'].tf_obs.model.model_var.diagonal())) print() num_alg = len(alg) d, steps, mc_sims = x.shape mean, cov = np.zeros((d, steps, mc_sims, num_alg)), np.zeros( (d, d, steps, mc_sims, num_alg)) for ia, a in enumerate(alg): print('Running {:<5} ... '.format(a.upper()), end='', flush=True) t0 = time.time() for imc in range(mc_sims): mean[..., imc, ia], cov[..., imc, ia] = alg[a].forward_pass(y[..., imc]) alg[a].reset() print('{:>30}'.format('Done in {:.2f} [sec]'.format(time.time() - t0))) # Performance score plots print() print('Computing performance scores ...') error2 = mean.copy() lcr_x = np.zeros((steps, mc_sims, num_alg)) lcr_pos = lcr_x.copy() lcr_vel = lcr_x.copy() lcr_theta = lcr_x.copy() for a in range(num_alg): for k in range(steps): mse = mse_matrix(x[:, k, :], mean[:, k, :, a]) for imc in range(mc_sims): error2[:, k, imc, a] = squared_error(x[:, k, imc], mean[:, k, imc, a]) lcr_x[k, imc, a] = log_cred_ratio(x[:, k, imc], mean[:, k, imc, a], cov[:, :, k, imc, a], mse) lcr_pos[k, imc, a] = log_cred_ratio(x[:2, k, imc], mean[:2, k, imc, a], cov[:2, :2, k, imc, a], mse[:2, :2]) lcr_vel[k, imc, a] = log_cred_ratio(x[2:4, k, imc], mean[2:4, k, imc, a], cov[2:4, 2:4, k, imc, a], mse[2:4, 2:4]) lcr_theta[k, imc, a] = log_cred_ratio(x[4, k, imc], mean[4, k, imc, a], cov[4, 4, k, imc, a], mse[4, 4]) # Averaged RMSE and Inclination Indicator in time x_rmse_vs_time = np.sqrt(error2.sum(axis=0)).mean(axis=1) pos_rmse_vs_time = np.sqrt((error2[:2, ...]).sum(axis=0)).mean(axis=1) vel_rmse_vs_time = np.sqrt((error2[2:4, ...]).sum(axis=0)).mean(axis=1) theta_rmse_vs_time = np.sqrt((error2[4, ...])).mean(axis=1) x_inc_vs_time = lcr_x.mean(axis=1) pos_inc_vs_time = lcr_pos.mean(axis=1) vel_inc_vs_time = lcr_vel.mean(axis=1) theta_inc_vs_time = lcr_theta.mean(axis=1) print('{:10s}: {}'.format('RMSE', x_rmse_vs_time.mean(axis=0))) print('{:10s}: {}'.format('INC', x_inc_vs_time.mean(axis=0))) # Save the simulation results into outfile data_dict = { 'duration': dur, 'disc_tau': disc_tau, 'alg_str': list(alg.keys()), 'x': x, 'mean': mean, 'cov': cov, 'state': { 'rmse': x_rmse_vs_time, 'inc': x_inc_vs_time }, 'position': { 'rmse': pos_rmse_vs_time, 'inc': pos_inc_vs_time }, 'velocity': { 'rmse': vel_rmse_vs_time, 'inc': vel_inc_vs_time }, 'parameter': { 'rmse': theta_rmse_vs_time, 'inc': theta_inc_vs_time }, } joblib.dump(data_dict, outfile) # Visualize simulation results, plots, tables reentry_demo_results(data_dict) else: reentry_demo_results(joblib.load(outfile))
def reentry_analysis_demo(dur=50, mc_sims=10): tau = 0.05 disc_tau = 0.1 # initial condition statistics m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 0]) init_rv = GaussRV(5, m0, P0) # process noise statistics noise_rv = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 0])) sys = ReentryVehicle2DTransition(init_rv, noise_rv) # measurement noise statistics dim_obs = 2 meas_noise_rv = GaussRV(dim_obs, cov=np.diag([1e-6, 0.17e-6])) obs = Radar2DMeasurement(meas_noise_rv, 5, radar_loc=np.array([6374, 0.0])) np.random.seed(0) # Generate reference trajectory by SDE simulation x = sys.simulate_continuous(duration=dur, dt=tau, mc_sims=mc_sims) y = obs.simulate_measurements(x) # subsample data for the filter and performance score calculation x = x[:, ::2, ...] # take every second point on the trajectory y = y[:, ::2, ...] # and corresponding measurement # Initialize state-space model with mis-specified initial mean m0 = np.array([6500.4, 349.14, -1.1093, -6.1967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) init_rv = GaussRV(5, m0, P0) noise_rv = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 1e-6])) dyn = ReentryVehicle2DTransition(init_rv, noise_rv, dt=disc_tau) # NOTE: higher tau causes higher spread of trajectories at the ends # Initialize filters par_dyn = np.array([[1.0, 1, 1, 1, 1, 1]]) par_obs = np.array([[1.0, 0.9, 0.9, 1e4, 1e4, 1e4]]) # np.atleast_2d(np.ones(6)) mul_ut = np.hstack((np.zeros((dyn.dim_in, 1)), np.eye(dyn.dim_in), 2 * np.eye(dyn.dim_in))).astype(np.int) alg = OrderedDict({ # GaussianProcessKalman(ssm, par_dyn, par_obs, kernel='rbf', points='ut'), 'bsqkf': BayesSardKalman(dyn, obs, par_dyn, par_obs, mul_ut, mul_ut, points='ut'), 'ukf': UnscentedKalman(dyn, obs, kappa=0.0, beta=2.0), }) alg['bsqkf'].tf_dyn.model.model_var = np.diag([0.1, 0.1, 0.1, 0.1, 0.015]) alg['bsqkf'].tf_obs.model.model_var = 0 * np.eye(2) print('BSQ EMV\ndyn: {} \nobs: {}'.format( alg['bsqkf'].tf_dyn.model.model_var.diagonal(), alg['bsqkf'].tf_obs.model.model_var.diagonal())) # FILTERING num_alg = len(alg) d, steps, mc_sims = x.shape mean, cov = np.zeros((d, steps, mc_sims, num_alg)), np.zeros( (d, d, steps, mc_sims, num_alg)) mean_x, cov_x = mean.copy(), cov.copy() mean_y, cov_y = np.zeros((dim_obs, steps, mc_sims, num_alg)), np.zeros( (dim_obs, dim_obs, steps, mc_sims, num_alg)) cov_yx = np.zeros((dim_obs, d, steps, mc_sims, num_alg)) # y = np.hstack((np.zeros((dim_obs, 1, mc_sims)), y)) for ia, a in enumerate(alg): print('Running {:<5} ... '.format(a.upper()), end='', flush=True) t0 = time.time() for imc in range(mc_sims): for k in range(steps): # TIME UPDATE alg[a]._time_update(k) # record predictive moments mean_x[:, k, imc, ia], cov_x[..., k, imc, ia] = alg[a].x_mean_pr, alg[a].x_cov_pr mean_y[:, k, imc, ia], cov_y[..., k, imc, ia] = alg[a].y_mean_pr, alg[a].y_cov_pr cov_yx[..., k, imc, ia] = alg[a].xy_cov # MEASUREMENT UPDATE alg[a]._measurement_update(y[:, k, imc]) # record filtered moments mean[:, k, imc, ia], cov[..., k, imc, ia] = alg[a].x_mean_fi, alg[a].x_cov_fi # reset filter storage alg[a].reset() print('{:>30}'.format('Done in {:.2f} [sec]'.format(time.time() - t0)))
def ukf_trunc_demo(mc_sims=50): disc_tau = 0.5 # discretization period in seconds duration = 200 # define system m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 0]) x0 = GaussRV(5, m0, P0) q = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 0])) sys = ReentryVehicle2DTransition(x0, q, dt=disc_tau) # define radar measurement model r = GaussRV(2, cov=np.diag([1e-6, 0.17e-6])) obs = Radar2DMeasurement(r, sys.dim_state) # simulate reference state trajectory by SDE integration x = sys.simulate_continuous(duration, disc_tau, mc_sims) x_ref = x.mean(axis=2) # simulate corresponding radar measurements y = obs.simulate_measurements(x) # initialize state-space model; uses cartesian2polar as measurement (not polar2cartesian) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) q = GaussRV(3, cov=np.diag([2.4064e-5, 2.4064e-5, 1e-6])) dyn = ReentryVehicle2DTransition(x0, q, dt=disc_tau) # initialize UKF and UKF in truncated version alg = ( UnscentedKalman(dyn, obs), TruncatedUnscentedKalman(dyn, obs), ) num_alg = len(alg) # space for filtered mean and covariance steps = x.shape[1] x_mean = np.zeros((dyn.dim_in, steps, mc_sims, num_alg)) x_cov = np.zeros((dyn.dim_in, dyn.dim_in, steps, mc_sims, num_alg)) # filtering estimate of the state trajectory based on provided measurements from tqdm import trange for i_est, estimator in enumerate(alg): for i_mc in trange(mc_sims): x_mean[..., i_mc, i_est], x_cov[..., i_mc, i_est] = estimator.forward_pass(y[..., i_mc]) estimator.reset() # Plots plt.figure() g = GridSpec(2, 4) plt.subplot(g[:, :2]) # Earth surface w/ radar position radar_x, radar_y = dyn.R0, 0 t = 0.02 * np.arange(-1, 4, 0.1) plt.plot(dyn.R0 * np.cos(t), dyn.R0 * np.sin(t), color='darkblue', lw=2) plt.plot(radar_x, radar_y, 'ko') plt.plot(x_ref[0, :], x_ref[1, :], color='r', ls='--') # Convert from polar to cartesian meas = np.stack(( + y[0, ...] * np.cos(y[1, ...]), radar_y + y[0, ...] * np.sin(y[1, ...])), axis=0) for i in range(mc_sims): # Vehicle trajectory # plt.plot(x[0, :, i], x[1, :, i], alpha=0.35, color='r', ls='--') # Plot measurements plt.plot(meas[0, :, i], meas[1, :, i], 'k.', alpha=0.3) # Filtered position estimate plt.plot(x_mean[0, 1:, i, 0], x_mean[1, 1:, i, 0], color='g', alpha=0.3) plt.plot(x_mean[0, 1:, i, 1], x_mean[1, 1:, i, 1], color='orange', alpha=0.3) # Performance score plots error2 = x_mean.copy() lcr = np.zeros((steps, mc_sims, num_alg)) for a in range(num_alg): for k in range(steps): mse = mse_matrix(x[:4, k, :], x_mean[:4, k, :, a]) for imc in range(mc_sims): error2[:, k, imc, a] = squared_error(x[:, k, imc], x_mean[:, k, imc, a]) lcr[k, imc, a] = log_cred_ratio(x[:4, k, imc], x_mean[:4, k, imc, a], x_cov[:4, :4, k, imc, a], mse) # Averaged RMSE and Inclination Indicator in time pos_rmse_vs_time = np.sqrt((error2[:2, ...]).sum(axis=0)).mean(axis=1) inc_ind_vs_time = lcr.mean(axis=1) # Plots plt.subplot(g[0, 2:]) plt.title('RMSE') plt.plot(pos_rmse_vs_time[:, 0], label='UKF', color='g') plt.plot(pos_rmse_vs_time[:, 1], label='UKF-trunc', color='r') plt.legend() plt.subplot(g[1, 2:]) plt.title('Inclination Indicator $I^2$') plt.plot(inc_ind_vs_time[:, 0], label='UKF', color='g') plt.plot(inc_ind_vs_time[:, 1], label='UKF-trunc', color='r') plt.legend() plt.show() print('Average RMSE: {}'.format(pos_rmse_vs_time.mean(axis=0))) print('Average I2: {}'.format(inc_ind_vs_time.mean(axis=0)))