예제 #1
0
def integrate(p0, R0, pdot0, om0, t_out):
    '''
    integrate Newton-Euler equations, then integrate kinematics
    t_out = times at which to return states
    '''

    # setup
    t0 = t_out[0]
    tf_out = t_out[-1]
    n_t_out = t_out.size

    # integration times
    dt = .001
    t = np.arange(t0, tf_out + dt,
                  dt)  # tf_out+dt: make sure tf_out is included
    tf = t[-1]
    n_t = t.size

    # integrate newton-euler (state is pdot & om)
    pdot_om0 = np.concatenate((pdot0, om0))
    sol = sp.integrate.solve_ivp(newton_euler, (t0, tf),
                                 pdot_om0,
                                 method='RK45',
                                 t_eval=t)
    pdot_om = sol.y
    pdot = pdot_om[:3, :]
    om = pdot_om[3:, :]

    # 1st-order forward Euler integration of kinematics to get p and R
    p = np.full((3, n_t), np.nan)
    R = np.full((3, 3, n_t), np.nan)
    p[:, 0] = p0
    R[:, :, 0] = R0
    for i in range(1, n_t):
        p[:, i] = p[:, i - 1] + dt * pdot[:, i - 1]
        R[:, :, i] = R[:, :, i - 1] @ so3.exp(so3.cross(dt * om[:, i - 1]))

    # get tangent space representation of R so we can interpolate
    s = np.full((3, n_t), np.nan)
    for i in range(n_t):
        s[:, i] = so3.skew_elements(so3.log(R[:, :, i]))

    # interpolate t values using state x=(p,s,v,om)
    x = np.concatenate((p, s, pdot, om), axis=0)
    interp_fun = sp.interpolate.interp1d(t, x)
    x_out = interp_fun(t_out)

    # get x values
    p_out = x_out[:3, :]
    s_out = x_out[3:6, :]
    pdot_out = x_out[6:9, :]
    om_out = x_out[9:, :]

    # convert tangent space s back into rotations
    R_out = np.full((3, 3, n_t_out), np.nan)
    for i in range(n_t_out):
        R_out[:, :, i] = so3.exp(so3.cross(s_out[:, i]))

    return p_out, R_out, pdot_out, om_out
예제 #2
0
def gen_and_eval(mode):

    # to be filled
    p = np.full((3, n_ims), np.nan)
    R = np.full((3, 3, n_ims), np.nan)

    if mode == 'trans_x':
        x = np.linspace(-.2, .2, n_ims)
        for i in range(n_ims):
            R[:, :, i] = R_upright
            p[:, i] = np.array([x[i], .8, 0])
        img_dir = dirs.trans_x_dir

    elif mode == 'trans_y':
        y = np.linspace(.4, 1.8, n_ims)
        for i in range(n_ims):
            R[:, :, i] = R_upright
            p[:, i] = np.array([0, y[i], 0])
        img_dir = dirs.trans_y_dir

    elif mode == 'trans_z':
        z = np.linspace(-.15, .15, n_ims)
        for i in range(n_ims):
            R[:, :, i] = R_upright
            p[:, i] = np.array([0, .8, z[i]])
        img_dir = dirs.trans_z_dir

    elif mode == 'rot_x':
        # rotate eps is needed to avoided wrapping of the exp/log maps
        ang = np.linspace(-np.pi + rot_eps, np.pi - rot_eps, n_ims)
        for i in range(n_ims):
            s_i = np.array([ang[i], 0, 0])
            R[:, :, i] = so3.exp(so3.cross(s_i)) @ R_upright
            p[:, i] = np.array([0, .8, 0])
        img_dir = dirs.rot_x_dir

    elif mode == 'rot_y':
        ang = np.linspace(-np.pi + rot_eps, np.pi - rot_eps, n_ims)
        for i in range(n_ims):
            s_i = np.array([0, ang[i], 0])
            R[:, :, i] = so3.exp(so3.cross(s_i)) @ R_upright
            p[:, i] = np.array([0, .8, 0])
        img_dir = dirs.rot_y_dir

    elif mode == 'rot_z':
        ang = np.linspace(-np.pi + rot_eps, np.pi - rot_eps, n_ims)
        for i in range(n_ims):
            s_i = np.array([0, 0, ang[i]])
            R[:, :, i] = so3.exp(so3.cross(s_i)) @ R_upright
            p[:, i] = np.array([0, .8, 0])
        img_dir = dirs.rot_z_dir

    # render and predict
    dt = 1  # this doesn't matter for this simulation
    br.soup_gen(dt, p, R, img_dir, lighting_energy, world_RGB)
    p, R, p_est, R_est = db.get_predictions(img_dir, print_errors=False)
예제 #3
0
def h(p_prev, R_prev, u, v):
    '''
    measurement function
    '''

    p_new = p_prev + v[:3]
    R_noise = so3.exp(so3.cross(v[3:]))
    R_new = R_prev @ R_noise

    return p_new, R_new
예제 #4
0
def newton_euler(t, pdot_om):
    '''
    Newton-Euler equations for a rigid body
    assumes velocity (pdot_A) is in inertial frame (A) coordinates,
    and omega (om_B) is in body-frame (B) coordinates
    ref: eq. 4.16 (p. 167) in A Mathematical Introduction to Robotic
    Manipulation
    '''

    pdot_A = pdot_om[:3]
    om_B = pdot_om[3:]
    om_col_B = om_B[:, np.newaxis]  # omega as column vector

    # translational dynamics: all expressed in inertial frame A
    F_A = np.array([[0, 0, -m * g]]).T  # inertial frame force of gravity
    pdot_dot_A = np.linalg.inv(m * np.eye(3)) @ F_A

    # rotational dynamics: all expressed in body frame B
    tau_B = np.array([[0, 0, 0]]).T  # inertial frame torque
    omdot_B = np.linalg.inv(J) @ (tau_B - so3.cross(om_B) @ J @ om_col_B)
    pdot_omdot = np.concatenate((pdot_dot_A, omdot_B)).ravel()

    return pdot_omdot
예제 #5
0
def filter(p0_hat, R0_hat, pdot0_hat, om0_hat, COV_xx_0_hat, COV_ww, COV_vv, U,
           P_MEAS, R_MEAS, dt):
    '''
    Unscented Filter from Section 3.7 of Optimal Estimation of Dynamic Systems
    (2nd ed.) by Crassidis & Junkins
    '''

    # system constants
    alpha = 1e-2
    beta = 2
    kappa = 2
    n = 12
    n_w = COV_ww.shape[0]
    n_v = COV_vv.shape[0]
    n_t = P_MEAS.shape[1]  # number of time points
    n_y = 6

    # constants
    L = n + n_w + n_v  # size of augmented state
    lam = (alpha**2) * (L + kappa) - L  # eq. 3.256
    gam = np.sqrt(L + lam)  # eq. 3.255

    # weights
    W_mean = np.full(2 * L + 1, np.nan)
    W_cov = np.full(2 * L + 1, np.nan)
    W_mean[0] = lam / (L + lam)
    W_cov[0] = W_mean[0] + (1 - alpha**2 + beta)
    W_mean[1:] = 1 / (2 * (L + lam))
    W_cov[1:] = 1 / (2 * (L + lam))

    # create arrays for saving values in the for loop
    P_HAT = np.full((3, n_t), np.nan)
    R_HAT = np.full((3, 3, n_t), np.nan)
    PDOT_HAT = np.full((3, n_t), np.nan)
    OM_HAT = np.full((3, n_t), np.nan)
    COV_XX_ALL = np.full((n, n, n_t), np.nan)
    Y_HAT = np.full((n_y, 2 * L + 1), np.nan)  # y_hat^(i) for i=0,...,2L

    # fill arraysw with initial estimates
    P_HAT[:, 0] = p0_hat
    R_HAT[:, :, 0] = R0_hat
    PDOT_HAT[:, 0] = pdot0_hat
    OM_HAT[:, 0] = om0_hat
    s0_hat = np.array([0, 0, 0])

    # initialize variables to be used in for loop
    x_hat = np.block([p0_hat, s0_hat, pdot0_hat, om0_hat])
    x_hat = x_hat[:, np.newaxis]
    COV_xx = COV_xx_0_hat
    R_hat = R0_hat

    # iterate over all time points
    for i in range(1, n_t):
        # load control and measurement at time i
        u = U[:, i]
        s_meas = so3.skew_elements(so3.log(R_hat.T @ R_MEAS[:, :, i]))  # eq. 5
        y = np.block([P_MEAS[:, i], s_meas])
        y = y[:, np.newaxis]

        # cross-correlations (usually zero, see Crassidis after eq. 3.252)
        COV_xw = np.full((n, n_w), 0.0)
        COV_xv = np.full((n, n_v), 0.0)
        COV_wv = np.full((n_w, n_v), 0.0)

        # sigma points
        COV_chi = np.block([[COV_xx, COV_xw, COV_xv],
                            [COV_xw.T, COV_ww, COV_wv],
                            [COV_xv.T, COV_wv.T, COV_vv]])
        M = gam * np.linalg.cholesky(
            COV_chi)  # returns L such that COV_chi = L @ L.T
        sig = np.block([M, -M])
        chi_hat_pls = np.block([[x_hat], [np.zeros((n_w + n_v, 1))]])
        CHI_HAT = np.block([chi_hat_pls, chi_hat_pls + sig
                            ])  # all chi_hat^(i) for i=0,...,2L
        X_HAT = CHI_HAT[:n, :]  # all x_hat^(i) for i=0,...,2L
        W_HAT = CHI_HAT[n:n + n_w, :]  # all w_hat^(i) for i=0,...,2L
        V_HAT = CHI_HAT[n + n_w:, :]  # all v_hat^(i) for i=0,...,2L

        # iteration (over all sigma points)
        for k in range(2 * L + 1):
            # dynamics
            om_k = X_HAT[9:, k]
            R_k = R_hat @ so3.exp(so3.cross(X_HAT[3:6, k]))
            p_new, R_new, v_new, om_new = f(X_HAT[:3, k], R_k, X_HAT[6:9, k],
                                            om_k, W_HAT[:, k], u, dt)
            s_new = so3.skew_elements(so3.log(R_hat.T @ R_new))
            X_HAT[:, k] = np.block([p_new, s_new, v_new, om_new])

            # measurement
            p_meas, R_meas = h(p_new, R_new, u, V_HAT[:, k])
            s_meas = so3.skew_elements(so3.log(R_hat.T @ R_meas))
            Y_HAT[:, k] = np.block([p_meas, s_meas])

        # predictions
        x_hat = np.sum(W_mean * X_HAT, axis=1)
        x_hat = x_hat[:, np.newaxis]
        COV_xx = W_cov * (X_HAT - x_hat) @ (X_HAT - x_hat).T  # COV_xx minus
        y_hat = np.sum(W_mean * Y_HAT, axis=1)  # eq. 2.262
        y_hat = y_hat[:, np.newaxis]

        # covariances
        COV_yy = W_cov * (Y_HAT - y_hat) @ (Y_HAT - y_hat).T
        COV_xy = W_cov * (X_HAT - x_hat) @ (Y_HAT - y_hat).T

        # update
        e = y - y_hat  # eq. 3.250
        K = COV_xy @ np.linalg.inv(COV_yy)  # eq. 3.251
        x_hat = x_hat + K @ e  # eq. 3.249a
        COV_xx = COV_xx - K @ COV_yy @ K.T  # eq. 3.249b

        # rotation
        R_hat = R_hat @ so3.exp(so3.cross(x_hat[3:6].ravel()))
        x_hat[3:6] = 0

        # save values
        P_HAT[:, i] = x_hat[:3].ravel()
        R_HAT[:, :, i] = R_hat
        PDOT_HAT[:, i] = x_hat[6:9].ravel()
        OM_HAT[:, i] = x_hat[9:].ravel()
        COV_XX_ALL[:, :, i] = COV_xx

    return P_HAT, R_HAT, PDOT_HAT, OM_HAT, COV_XX_ALL
예제 #6
0
R0 = t3d.quaternions.quat2mat(q0)
pdot0 = np.array([0.9, 1.1, 2.3])
om0 = np.array([5, 8, 4])

# rigid body dynamics
p, R, pdot, om  = rb.integrate(p0, R0, pdot0, om0, t)
save_dir = os.path.join(dirs.results_dir, 'filter_test/')

# simulate measurements
noise_p = .04
noise_R = .17
p_meas = p + noise_p*np.random.uniform(-1,1,(3,n_ims))
R_meas = np.full((3,3,n_ims), np.nan)
for i in range(n_ims):
    s_noise = noise_R*np.random.uniform(.5,-.5,3)
    R_noise = so3.exp(so3.cross(s_noise))
    R_meas[:,:,i] = R_noise @ R[:,:,i]

# filter initial estimates
p0_hat = p_meas[:,0] # use the true value to make it a fair comparison
R0_hat = R_meas[:,:,0] # use the true value to make it a fair comparison
pdot0_hat = pdot0 + .2*np.array([-1.1, 1.2, 1.1])
om0_hat = om0 + .6*np.array([-1.0, 1.1, 1.0])
cov_xx_0_hat = np.ones(12)
COV_xx_0_hat = np.diag(cov_xx_0_hat)

# run filter
U = np.full((1, n_ims), 0.0) # there is no control in this problem
COV_ww, COV_vv = df.get_noise_covariances()
p_filt, R_filt, pdot_filt, om_filt, COV_XX_ALL = uf.filter(p0_hat, R0_hat, pdot0_hat, om0_hat, COV_xx_0_hat, COV_ww, COV_vv, U, p_meas, R_meas, dt)