Exemplo n.º 1
0
def regular_update(regular, meas, cov_meas, gt, step_id, steps, plot_cond, save_path, use_mmgw):
    """
    Fuse estimate and measurement in original state space in Kalman fashion.
    :param regular:     Current estimate (also stores error); will be modified as a result
    :param meas:        Measurement in original state space
    :param cov_meas:    Covariance of measurement in original state space
    :param gt:          Ground truth
    :param step_id:     Current measurement step
    :param steps:       Total measurement steps
    :param plot_cond:   Boolean determining whether to plot the current estimate
    :param save_path:   Path to save the plots
    :param use_mmgw:    Use the MMGW instead of the ordinary estimate
    """
    # predict
    regular['x'] = np.dot(F, regular['x'])
    error_mat = np.array([
        [0.5 * T ** 2, 0.0],
        [0.0, 0.5 * T ** 2],
        [0.0, 0.0],
        [0.0, 0.0],
        [0.0, 0.0],
        [T, 0.0],
        [0.0, T],
    ])
    error_cov = np.dot(np.dot(error_mat, np.diag([SIGMA_V1, SIGMA_V2]) ** 2), error_mat.T)
    error_cov[SR, SR] = SIGMA_SHAPE ** 2
    regular['cov'] = np.dot(np.dot(F, regular['cov']), F.T) + error_cov

    # store prior for plotting
    all_prior = regular['est'].copy()

    # Kalman fusion
    innov = meas - np.dot(H, regular['x'])
    innov[AL] = (innov[AL] + np.pi) % (2*np.pi) - np.pi
    innov_cov = np.dot(np.dot(H, regular['cov']), H.T) + cov_meas
    gain = np.dot(np.dot(regular['cov'], H.T), np.linalg.inv(innov_cov))
    regular['x'] = regular['x'] + np.dot(gain, innov)
    regular['cov'] = regular['cov'] - np.dot(np.dot(gain, innov_cov), gain.T)
    if use_mmgw:
        particles = sample_m(regular['x'], regular['cov'], False, N_PARTICLES_MMGW)
        regular['est'] = mmgw_estimate_from_particles(particles)
    else:
        regular['est'] = regular['x'].copy()

    # save error and plot estimate
    regular['error'][step_id::steps] += error_and_plotting(regular['est'][M], regular['est'][L], regular['est'][W],
                                                           regular['est'][AL], all_prior[M], all_prior[L], all_prior[W],
                                                           all_prior[AL], meas[M], meas[L], meas[W], meas[AL], gt[M],
                                                           gt[L], gt[W], gt[AL], plot_cond, regular['name'],
                                                           save_path + 'example' + regular['name'] + '%i.svg' % step_id)
Exemplo n.º 2
0
def test_convergence(steps, runs, prior, cov_prior, cov_meas, random_param,
                     save_path):
    """
    Test convergence of error for different fusion methods. Creates plot of root mean square error convergence and
    errors at first and last measurement step.
    :param steps:           Number of measurements
    :param runs:            Number of MC runs
    :param prior:           Prior prediction mean (ground truth will be drawn from it each run)
    :param cov_prior:       Prior prediction covariance (ground truth will be drawn from it each run)
    :param cov_meas:        Noise of sensor
    :param random_param:    use random parameter switch to simulate ambiguous parameterization
    :param save_path:       Path for saving figures
    """
    error = np.zeros(steps * 2)

    # setup state for various ellipse fusion methods
    mmgw_mc = np.zeros(1, dtype=state_dtype)
    mmgw_mc[0]['error'] = error.copy()
    mmgw_mc[0]['name'] = 'MC-MMGW'
    mmgw_mc[0]['color'] = 'cyan'
    regular = np.zeros(1, dtype=state_dtype)
    regular[0]['error'] = error.copy()
    regular[0]['name'] = 'Regular'
    regular[0]['color'] = 'red'
    regular_mmgw = np.zeros(1, dtype=state_dtype)
    regular_mmgw[0]['error'] = error.copy()
    regular_mmgw[0]['name'] = 'Regular-MMGW'
    regular_mmgw[0]['color'] = 'orange'
    red_mmgw = np.zeros(1, dtype=state_dtype)
    red_mmgw[0]['error'] = error.copy()
    red_mmgw[0]['name'] = 'RED-MMGW'
    red_mmgw[0]['color'] = 'green'
    # red_mmgw_r = np.zeros(1, dtype=state_dtype)
    # red_mmgw_r[0]['error'] = error.copy()
    # red_mmgw_r[0]['name'] = 'RED-MMGW-r'
    # red_mmgw_r[0]['color'] = 'lightgreen'
    # red_mmgw_s = np.zeros(1, dtype=state_dtype)
    # red_mmgw_s[0]['error'] = error.copy()
    # red_mmgw_s[0]['name'] = 'RED-MMGW-s'
    # red_mmgw_s[0]['color'] = 'turquoise'
    shape_mean = np.zeros(1, dtype=state_dtype)
    shape_mean[0]['error'] = error.copy()
    shape_mean[0]['name'] = 'Shape-Mean'
    shape_mean[0]['color'] = 'magenta'

    # rt_red = 0.0
    # rt_red_r = 0.0
    # rt_red_s = 0.0

    for r in range(runs):
        print('Run %i of %i' % (r + 1, runs))
        # initialize ===================================================================================================
        # create gt from prior
        gt = sample_m(prior, cov_prior, False, 1)

        # ellipse orientation should be velocity orientation
        vel = np.linalg.norm(gt[V])
        gt[V] = np.array(np.cos(gt[AL]), np.sin(gt[AL])) * vel

        # get prior in square root space
        mmgw_mc[0]['x'], mmgw_mc[0][
            'cov'], particles_mc = single_particle_approx_gaussian(
                prior, cov_prior, N_PARTICLES_MMGW)
        mmgw_mc[0]['est'] = mmgw_mc[0]['x'].copy()
        mmgw_mc[0]['est'][SR] = get_ellipse_params_from_sr(mmgw_mc[0]['x'][SR])
        mmgw_mc[0]['figure'], mmgw_mc[0]['axes'] = plt.subplots(1, 1)

        # get prior for regular state
        regular[0]['x'] = prior.copy()
        regular[0]['cov'] = cov_prior.copy()
        regular[0]['est'] = prior.copy()
        regular[0]['figure'], regular[0]['axes'] = plt.subplots(1, 1)
        regular_mmgw[0]['x'] = prior.copy()
        regular_mmgw[0]['cov'] = cov_prior.copy()
        particles = sample_m(regular_mmgw[0]['x'], regular_mmgw[0]['cov'],
                             False, N_PARTICLES_MMGW)
        regular_mmgw[0]['est'] = mmgw_estimate_from_particles(particles)
        regular_mmgw[0]['figure'], regular_mmgw[0]['axes'] = plt.subplots(1, 1)

        # get prior for red
        red_mmgw[0]['x'], red_mmgw[0]['cov'], red_mmgw[0][
            'comp_weights'] = turn_mult(prior.copy(), cov_prior.copy())
        particles = sample_mult(red_mmgw[0]['x'], red_mmgw[0]['cov'],
                                red_mmgw[0]['comp_weights'], N_PARTICLES_MMGW)
        red_mmgw[0]['est'] = mmgw_estimate_from_particles(particles)
        red_mmgw[0]['figure'], red_mmgw[0]['axes'] = plt.subplots(1, 1)

        # red_mmgw_r[0]['x'], red_mmgw_r[0]['cov'], red_mmgw_r[0]['comp_weights'] = turn_mult(prior.copy(), cov_prior.copy())
        # particles = sample_mult(red_mmgw_r[0]['x'], red_mmgw_r[0]['cov'], red_mmgw_r[0]['comp_weights'], N_PARTICLES_MMGW)
        # red_mmgw_r[0]['est'] = mmgw_estimate_from_particles(particles)
        # red_mmgw_r[0]['figure'], red_mmgw_r[0]['axes'] = plt.subplots(1, 1)
        #
        # red_mmgw_s[0]['x'], red_mmgw_s[0]['cov'], red_mmgw_s[0]['comp_weights'] = turn_mult(prior.copy(),
        #                                                                                     cov_prior.copy())
        # particles = sample_mult(red_mmgw_s[0]['x'], red_mmgw_s[0]['cov'], red_mmgw_s[0]['comp_weights'],
        #                         N_PARTICLES_MMGW)
        # red_mmgw_s[0]['est'] = mmgw_estimate_from_particles(particles)
        # red_mmgw_s[0]['figure'], red_mmgw_s[0]['axes'] = plt.subplots(1, 1)

        # get prior for RM mean
        shape_mean[0]['x'] = prior[KIN]
        shape_mean[0]['shape'] = to_matrix(prior[AL], prior[L], prior[W],
                                           False)
        shape_mean[0]['cov'] = cov_prior[KIN][:, KIN]
        shape_mean[0]['gamma'] = 6.0
        shape_mean[0]['figure'], shape_mean[0]['axes'] = plt.subplots(1, 1)

        # test different methods
        for i in range(steps):
            if i % 10 == 0:
                print('Step %i of %i' % (i + 1, steps))
            plot_cond = (r + 1 == runs) & ((i % 2) == 1)  # & (i + 1 == steps)

            # move ground truth
            gt = np.dot(F, gt)
            error_mat = np.array([
                [0.5 * T**2, 0.0],
                [0.0, 0.5 * T**2],
                [T, 0.0],
                [0.0, T],
            ])
            kin_cov = np.dot(
                np.dot(error_mat,
                       np.diag([SIGMA_V1, SIGMA_V2])**2), error_mat.T)
            gt[KIN] += mvn(np.zeros(len(KIN)), kin_cov)
            gt[AL] = np.arctan2(gt[V2], gt[V1])

            # create measurement from gt (using alternating sensors) ===================================================
            k = np.random.randint(0, 4) if random_param else 0
            gt_mean = gt.copy()
            if k % 2 == 1:
                l_save = gt_mean[L]
                gt_mean[L] = gt_mean[W]
                gt_mean[W] = l_save
            gt_mean[AL] = (gt_mean[AL] + 0.5 * np.pi * k +
                           np.pi) % (2 * np.pi) - np.pi
            meas = sample_m(np.dot(H, gt_mean), cov_meas, False, 1)

            # fusion methods ===========================================================================================
            mmgw_mc_update(mmgw_mc[0], meas.copy(), cov_meas.copy(),
                           N_PARTICLES_MMGW, gt, i, steps, plot_cond,
                           save_path)

            regular_update(regular[0], meas.copy(), cov_meas.copy(), gt, i,
                           steps, plot_cond, save_path, False)

            regular_update(regular_mmgw[0], meas.copy(), cov_meas.copy(), gt,
                           i, steps, plot_cond, save_path, True)

            # tic = time.time()
            red_update(red_mmgw[0],
                       meas.copy(),
                       cov_meas.copy(),
                       gt,
                       i,
                       steps,
                       plot_cond,
                       save_path,
                       True,
                       mixture_reduction='salmond')
            # toc = time.time()
            # rt_red += toc - tic

            # tic = time.time()
            # red_update(red_mmgw_r[0], meas.copy(), cov_meas.copy(), gt, i, steps, plot_cond, save_path, True,
            #            mixture_reduction='salmond', pruning=False)
            # toc = time.time()
            # rt_red_r += toc - tic

            # tic = time.time()
            # red_update(red_mmgw_s[0], meas.copy(), cov_meas.copy(), gt, i, steps, plot_cond, save_path, True,
            #            mixture_reduction='salmond')
            # toc = time.time()
            # rt_red_s += toc - tic

            shape_mean_update(
                shape_mean[0], meas.copy(), cov_meas.copy(), gt, i, steps,
                plot_cond, save_path, 0.2 if cov_meas[AL, AL] < 0.1 * np.pi
                else 5.0 if cov_meas[AL, AL] < 0.4 * np.pi else 10.0)

        plt.close(mmgw_mc[0]['figure'])
        plt.close(regular[0]['figure'])
        plt.close(regular_mmgw[0]['figure'])
        plt.close(red_mmgw[0]['figure'])
        # plt.close(red_mmgw_r[0]['figure'])
        # plt.close(red_mmgw_s[0]['figure'])
        plt.close(shape_mean[0]['figure'])

    mmgw_mc[0]['error'] = np.sqrt(mmgw_mc[0]['error'] / runs)
    regular[0]['error'] = np.sqrt(regular[0]['error'] / runs)
    regular_mmgw[0]['error'] = np.sqrt(regular_mmgw[0]['error'] / runs)
    red_mmgw[0]['error'] = np.sqrt(red_mmgw[0]['error'] / runs)
    # red_mmgw_r[0]['error'] = np.sqrt(red_mmgw_r[0]['error'] / runs)
    # red_mmgw_s[0]['error'] = np.sqrt(red_mmgw_s[0]['error'] / runs)
    shape_mean[0]['error'] = np.sqrt(shape_mean[0]['error'] / runs)

    # print('Runtime RED:')
    # print(rt_red / (runs*steps))
    # print('Runtime RED no pruning:')
    # print(rt_red_r / (runs * steps))
    # print('Runtime RED-S:')
    # print(rt_red_s / (runs * steps))

    # error plotting ===================================================================================================
    plot_error_bars(
        np.block([regular, regular_mmgw, shape_mean, mmgw_mc, red_mmgw]),
        steps)
    plot_convergence(
        np.block([regular, regular_mmgw, shape_mean, mmgw_mc, red_mmgw]),
        steps, save_path)
Exemplo n.º 3
0
def test_convergence_pos(steps, runs, prior, cov_prior, cov_a, cov_b,
                         n_particles, n_particles_pf, save_path):
    """
    Test convergence of error for different fusion methods. Creates plot of root mean square error convergence and
    errors at first and last measurement step. If a uniform prior is given for alpha, ength, and width, the particle
    based methods use it while the others use the Gaussian prior (assumed to be a Gaussian approximation of the uniform
    prior). In either case, the position is still Gaussian.
    :param steps:           Number of measurements
    :param runs:            Number of MC runs
    :param prior:           Prior prediction mean (ground truth will be drawn from it each run)
    :param cov_prior:       Prior prediction covariance (ground truth will be drawn from it each run)
    :param cov_a:           Noise of sensor A
    :param cov_b:           Noise of sensor B
    :param n_particles:     Number of particles for MMGW-MC
    :param n_particles_pf:  Number of particles for MMGW-PF
    :param save_path:       Path for saving figures
    """
    error = np.zeros(steps * 2)

    # setup state for various ellipse fusion methods
    mmsr_mc = np.zeros(1, dtype=state_dtype)
    mmsr_mc[0]['error'] = error.copy()
    mmsr_mc[0]['name'] = 'MMSR-MC'
    mmsr_mc[0]['color'] = 'lightgreen'
    mmsr_pf = np.zeros(1, dtype=state_dtype)
    mmsr_pf[0]['error'] = error.copy()
    mmsr_pf[0]['name'] = 'MMSR-PF'
    mmsr_pf[0]['color'] = 'darkgreen'
    regular = np.zeros(1, dtype=state_dtype)
    regular[0]['error'] = error.copy()
    regular[0]['name'] = 'Regular'
    regular[0]['color'] = 'red'
    mwdp = np.zeros(1, dtype=state_dtype)
    mwdp[0]['error'] = error.copy()
    mwdp[0]['name'] = 'MWDP'
    mwdp[0]['color'] = 'darkcyan'
    rm_mean = np.zeros(1, dtype=state_dtype)
    rm_mean[0]['error'] = error.copy()
    rm_mean[0]['name'] = 'RM Mean'
    rm_mean[0]['color'] = 'orange'

    for r in range(runs):
        print('Run %i of %i' % (r + 1, runs))
        # initialize ===================================================================================================
        # create gt from prior
        gt = sample_m(prior, cov_prior, False, 1)

        # get prior in square root space
        mmsr_mc[0]['x'], mmsr_mc[0][
            'cov'], particles_mc = single_particle_approx_gaussian(
                prior, cov_prior, n_particles, False)

        # get prior for regular state
        regular[0]['x'] = prior.copy()
        regular[0]['cov'] = cov_prior.copy()

        # get prior for MWDP
        mwdp[0]['x'] = prior.copy()
        mwdp[0]['cov'] = cov_prior.copy()

        # get prior for RM mean
        rm_mean[0]['x'] = prior.copy()
        rm_mean[0]['shape'] = to_matrix(prior[AL], prior[L], prior[W], False)
        rm_mean[0]['cov'] = cov_prior.copy()
        rm_mean[0]['gamma'] = 1

        # get prior for particle filter
        mmsr_pf[0]['x'], mmsr_pf[0][
            'cov'], particles_pf = single_particle_approx_gaussian(
                prior, cov_prior, n_particles_pf, False)
        mmsr_pf[0]['weights'] = np.ones(n_particles_pf) / n_particles_pf

        # test different methods
        for i in range(steps):
            if i % 10 == 0:
                print('Step %i of %i' % (i + 1, steps))
            plot_cond = (r + 1 == runs) & (i + 1 == steps)

            # create measurement from gt (using alternating sensors) ===================================================
            if (i % 2) == 0:
                meas = sample_m(gt, cov_b, True, 1)
                cov_meas = cov_b.copy()
            else:
                meas = sample_m(gt, cov_a, False, 1)
                cov_meas = cov_a.copy()

            # fusion methods ===========================================================================================
            mmsr_mc_update(mmsr_mc[0], meas, cov_meas, n_particles, gt, i,
                           steps, plot_cond, save_path, False)

            regular_update(regular[0], meas, cov_meas, gt, i, steps, plot_cond,
                           save_path)

            mwdp_update(mwdp[0], meas, cov_meas, gt, i, steps, plot_cond,
                        save_path)

            rm_mean_update(rm_mean[0], meas, cov_meas, gt, i, steps, plot_cond,
                           save_path)

            mmsr_pf_update(mmsr_pf[0], meas, cov_meas, particles_pf,
                           n_particles_pf, gt, i, steps, plot_cond, save_path,
                           False)

    mmsr_mc[0]['error'] = np.sqrt(mmsr_mc[0]['error'] / runs)
    mmsr_pf[0]['error'] = np.sqrt(mmsr_pf[0]['error'] / runs)
    regular[0]['error'] = np.sqrt(regular[0]['error'] / runs)
    mwdp[0]['error'] = np.sqrt(mwdp[0]['error'] / runs)
    rm_mean[0]['error'] = np.sqrt(rm_mean[0]['error'] / runs)

    print(mmsr_pf['error'])
    print(rm_mean['error'])

    # error plotting ===================================================================================================
    plot_error_bars(np.block([regular, rm_mean, mmsr_mc, mwdp, mmsr_pf]),
                    steps)
    plot_convergence(np.block([regular, rm_mean, mmsr_mc, mwdp, mmsr_pf]),
                     steps, save_path)
Exemplo n.º 4
0
def test_convergence_pos(steps, runs, prior, cov_prior, cov_a, cov_b,
                         n_particles, n_particles_pf, save_path):
    """
    Test convergence of error for different fusion methods. Creates plot of root mean square error convergence and
    errors at first and last measurement step. If a uniform prior is given for alpha, ength, and width, the particle
    based methods use it while the others use the Gaussian prior (assumed to be a Gaussian approximation of the uniform
    prior). In either case, the position is still Gaussian.
    :param steps:           Number of measurements
    :param runs:            Number of MC runs
    :param prior:           Prior prediction mean (ground truth will be drawn from it each run)
    :param cov_prior:       Prior prediction covariance (ground truth will be drawn from it each run)
    :param cov_a:           Noise of sensor A
    :param cov_b:           Noise of sensor B
    :param n_particles:     Number of particles for MMGW-MC
    :param n_particles_pf:  Number of particles for MMGW-PF
    :param save_path:       Path for saving figures
    """
    error = np.zeros(steps * 2)

    # setup state for various ellipse fusion methods
    mmsr_mc = np.zeros(1, dtype=state_dtype)
    mmsr_mc[0]['error'] = error.copy()
    mmsr_mc[0]['name'] = 'MMSR-MC'
    mmsr_mc[0]['color'] = 'greenyellow'
    mmsr_lin2 = np.zeros(1, dtype=state_dtype)
    mmsr_lin2[0]['error'] = error.copy()
    mmsr_lin2[0]['name'] = 'MMSR-Lin'
    mmsr_lin2[0]['color'] = 'm'
    mmsr_pf = np.zeros(1, dtype=state_dtype)
    mmsr_pf[0]['error'] = error.copy()
    mmsr_pf[0]['name'] = 'MMSR-PF'
    mmsr_pf[0]['color'] = 'darkgreen'
    regular = np.zeros(1, dtype=state_dtype)
    regular[0]['error'] = error.copy()
    regular[0]['name'] = 'Regular'
    regular[0]['color'] = 'red'
    mwdp = np.zeros(1, dtype=state_dtype)
    mwdp[0]['error'] = error.copy()
    mwdp[0]['name'] = 'MWDP'
    mwdp[0]['color'] = 'deepskyblue'
    rm_mean = np.zeros(1, dtype=state_dtype)
    rm_mean[0]['error'] = error.copy()
    rm_mean[0]['name'] = 'RM Mean'
    rm_mean[0]['color'] = 'orange'

    for r in range(runs):
        print('Run %i of %i' % (r + 1, runs))
        # initialize ===================================================================================================
        # create gt from prior
        gt = sample_m(prior, cov_prior, False, 1)

        # get prior in square root space
        mmsr_mc[0]['x'], mmsr_mc[0][
            'cov'], particles_mc = single_particle_approx_gaussian(
                prior, cov_prior, n_particles, False)

        # get prior for regular state
        regular[0]['x'] = prior.copy()
        regular[0]['cov'] = cov_prior.copy()

        # get prior for MWDP
        mwdp[0]['x'] = prior.copy()
        mwdp[0]['cov'] = cov_prior.copy()

        # get prior for RM mean
        rm_mean[0]['x'] = prior.copy()
        rm_mean[0]['shape'] = to_matrix(prior[AL], prior[L], prior[W], False)
        rm_mean[0]['cov'] = cov_prior.copy()
        rm_mean[0]['gamma'] = 1

        # get prior for linearization
        # precalculate values
        prior_shape_sqrt = to_matrix(prior[AL], prior[L], prior[W], True)
        cossin = np.cos(prior[AL]) * np.sin(prior[AL])
        cos2 = np.cos(prior[AL])**2
        sin2 = np.sin(prior[AL])**2
        # transform per element
        mmsr_lin2[0]['x'][M] = prior[M]
        hess = np.zeros((3, 5, 5))
        hess[0] = np.array([
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [
                0, 0, 2 * (prior[W] - prior[L]) * (cos2 - sin2), -2 * cossin,
                2 * cossin
            ],
            [0, 0, -2 * cossin, 0, 0],
            [0, 0, 2 * cossin, 0, 0],
        ])
        mmsr_lin2[0]['x'][2] = prior_shape_sqrt[0, 0] + 0.5 * np.trace(
            np.dot(hess[0], cov_prior))
        hess[1] = np.array([
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [
                0, 0, -4 * (prior[W] - prior[L]) * cossin, cos2 - sin2,
                sin2 - cos2
            ],
            [0, 0, cos2 - sin2, 0, 0],
            [0, 0, sin2 - cos2, 0, 0],
        ])
        mmsr_lin2[0]['x'][3] = prior_shape_sqrt[0, 1] + 0.5 * np.trace(
            np.dot(hess[1], cov_prior))
        hess[2] = np.array([
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [
                0, 0, 2 * (prior[L] - prior[W]) * (cos2 - sin2), 2 * cossin,
                -2 * cossin
            ],
            [0, 0, 2 * cossin, 0, 0],
            [0, 0, -2 * cossin, 0, 0],
        ])
        mmsr_lin2[0]['x'][4] = prior_shape_sqrt[1, 1] + 0.5 * np.trace(
            np.dot(hess[2], cov_prior))

        # transform covariance per element
        jac = get_jacobian(prior[L], prior[W], prior[AL])
        mmsr_lin2[0]['cov'] = np.dot(np.dot(jac, cov_prior), jac.T)
        # add Hessian part where Hessian not 0
        for i in range(3):
            for j in range(3):
                mmsr_lin2[0]['cov'][i + 2, j + 2] += 0.5 * np.trace(
                    np.dot(np.dot(np.dot(hess[i], cov_prior), hess[j]),
                           cov_prior))

        # get prior for particle filter
        mmsr_pf[0]['x'], mmsr_pf[0][
            'cov'], particles_pf = single_particle_approx_gaussian(
                prior, cov_prior, n_particles_pf, False)
        mmsr_pf[0]['weights'] = np.ones(n_particles_pf) / n_particles_pf

        # test different methods
        for i in range(steps):
            if i % 10 == 0:
                print('Step %i of %i' % (i + 1, steps))
            plot_cond = (r + 1 == runs) & (i + 1 == steps)

            # create measurement from gt (using alternating sensors) ===================================================
            if (i % 2) == 0:
                meas = sample_m(gt, cov_b, True, 1)
                cov_meas = cov_b.copy()
            else:
                meas = sample_m(gt, cov_a, False, 1)
                cov_meas = cov_a.copy()

            # fusion methods ===========================================================================================
            mmsr_mc_update(mmsr_mc[0], meas, cov_meas, n_particles, gt, i,
                           steps, plot_cond, save_path, False)

            regular_update(regular[0], meas, cov_meas, gt, i, steps, plot_cond,
                           save_path)

            mwdp_update(mwdp[0], meas, cov_meas, gt, i, steps, plot_cond,
                        save_path)

            rm_mean_update(rm_mean[0], meas, cov_meas, gt, i, steps, plot_cond,
                           save_path)

            mmsr_lin2_update(mmsr_lin2[0], meas, cov_meas, gt, i, steps,
                             plot_cond, save_path)

            mmsr_pf_update(mmsr_pf[0], meas, cov_meas, particles_pf,
                           n_particles_pf, gt, i, steps, plot_cond, save_path,
                           False)

    mmsr_mc[0]['error'] = np.sqrt(mmsr_mc[0]['error'] / runs)
    mmsr_lin2[0]['error'] = np.sqrt(mmsr_lin2[0]['error'] / runs)
    mmsr_pf[0]['error'] = np.sqrt(mmsr_pf[0]['error'] / runs)
    regular[0]['error'] = np.sqrt(regular[0]['error'] / runs)
    mwdp[0]['error'] = np.sqrt(mwdp[0]['error'] / runs)
    rm_mean[0]['error'] = np.sqrt(rm_mean[0]['error'] / runs)

    # error plotting ===================================================================================================
    plot_error_bars(
        np.block([mmsr_mc, mmsr_pf, regular, mwdp, rm_mean, mmsr_lin2]), steps)
    plot_convergence(
        np.block([mmsr_mc, mmsr_pf, regular, mwdp, rm_mean, mmsr_lin2]), steps,
        save_path)