/// <summary> /// Simple overall prediction /// </summary> /// <param name="scene"></param> /// <param name="u"></param> /// <param name="delta_t"></param> public void predict_filter_slow (Scene_Single scene, Vector u, float delta_t) { Debug.WriteLine("*** SLOW PREDICTION ***"); // What we need to do for the prediction: // Calculate f and grad_f_x // Calculate Q // Form x(k+1|k) and P(k+1|k) int size = (int)scene.get_total_state_size(); // First form original total state and covariance Vector x = new Vector(size); MatrixFixed P = new MatrixFixed(size, size); scene.construct_total_state_and_covariance(ref x, ref P); // Make model calculations: store results in RES matrices Vector xv = scene.get_xv(); //Vector xv = new Vector(scene.get_xv()); scene.get_motion_model().func_fv_and_dfv_by_dxv(xv, u, delta_t); scene.get_motion_model().func_Q(scene.get_xv(), u, delta_t); // Find new state f Vector f = new Vector(size); // Feature elements of f are the same as x f.Update(x); f.Update(scene.get_motion_model().get_fvRES(), 0); // Find new P // Since most elements of df_by_dx are zero... MatrixFixed df_by_dx = new MatrixFixed(size, size); df_by_dx.Fill(0.0f); // Fill the rest of the elements of df_by_dx: 1 on diagonal for features for (int i = (int)scene.get_motion_model().STATE_SIZE; i < df_by_dx.Rows; i++) df_by_dx[i,i] = 1.0f; df_by_dx.Update(scene.get_motion_model().get_dfv_by_dxvRES(), 0, 0); // Calculate the process noise MatrixFixed Q = new MatrixFixed(size, size); Q.Fill(0.0f); Q.Update(scene.get_motion_model().get_QxRES(), 0, 0); P.Update(df_by_dx * P * df_by_dx.Transpose()); P += Q; scene.fill_state_and_covariance(f, P); }
public void construct_total_internal_measurement_stuff( Vector nu_tot, MatrixFixed dh_by_dx_tot, MatrixFixed R_tot, uint total_state_size) { //uint size = internal_measurement_model.MEASUREMENT_SIZE; //assert (nu_tot.Size() == size && //dh_by_dx_tot.Rows() == size && //dh_by_dx_tot.Cols() == total_state_size && //R_tot.Rows() == size && R_tot.Cols() == size); //assert(successful_internal_measurement_flag); nu_tot.Fill(0.0f); dh_by_dx_tot.Fill(0.0f); R_tot.Fill(0.0f); nu_tot.Update(nuv, 0); dh_by_dx_tot.Update(dhv_by_dxv, 0, 0); R_tot.Update(Rv, 0, 0); }
//returns matrix of dimensions (3,4) public static MatrixFixed dRq_times_a_by_dq (Quaternion q, Vector3D a) { MatrixFixed aMat = new MatrixFixed(3,1); aMat[0, 0] = a.GetX(); aMat[1, 0] = a.GetY(); aMat[2, 0] = a.GetZ(); MatrixFixed TempR = new MatrixFixed(3,3); MatrixFixed Temp31 = new MatrixFixed(3,1); MatrixFixed dRq_times_a_by_dq = new MatrixFixed(3,4); // Make Jacobian by stacking four vectors together side by side TempR = dR_by_dq0(q); Temp31 = TempR * aMat; dRq_times_a_by_dq.Update(Temp31, 0, 0); TempR = dR_by_dqx(q); Temp31 = TempR * aMat; dRq_times_a_by_dq.Update(Temp31, 0, 1); TempR = dR_by_dqy(q); Temp31 = TempR * aMat; dRq_times_a_by_dq.Update(Temp31, 0, 2); TempR = dR_by_dqz(q); Temp31 = TempR * aMat; dRq_times_a_by_dq.Update(Temp31, 0, 3); return dRq_times_a_by_dq; }
public void construct_total_measurement_stuff(Vector nu_tot, MatrixFixed dh_by_dx_tot, MatrixFixed R_tot) { uint size = successful_measurement_vector_size; //assert (nu_tot.Size() == size && //dh_by_dx_tot.Rows() == size && //dh_by_dx_tot.Cols() == total_state_size && //R_tot.Rows() == size && R_tot.Cols() == size); nu_tot.Fill(0.0f); dh_by_dx_tot.Fill(0.0f); R_tot.Fill(0.0f); uint vector_position = 0; Feature it; for (int i=0;i<selected_feature_list.Count;i++) { it = (Feature)selected_feature_list[i]; if (it.get_successful_measurement_flag()) { nu_tot.Update(it.get_nu(), (int)vector_position); dh_by_dx_tot.Update(it.get_dh_by_dxv(), (int)vector_position, 0); dh_by_dx_tot.Update(it.get_dh_by_dy(), (int)vector_position, (int)it.get_position_in_total_state_vector()); R_tot.Update(it.get_R(), (int)vector_position, (int)vector_position); vector_position += it.get_feature_measurement_model().MEASUREMENT_SIZE; } } }
/// <summary> /// Create the overall covariance matrix by concatenating the robot /// covariance P_{xx} and all the feature covariances P_{xy_i}, P_{y_iy_i} /// and P_{y_iy_j}. /// </summary> /// <param name="M">The matrix to fill with the state</param> public void construct_total_covariance(ref MatrixFixed M) { if ((M.Rows != total_state_size) || (M.Columns != total_state_size)) { Debug.WriteLine("Error: Matrix is the wrong size"); } //test uint test_size = get_tot_state_size(); if (test_size != total_state_size) { Debug.WriteLine("State size discrepancy"); } M.Update(Pxx, 0, 0); uint x_position = motion_model.STATE_SIZE; uint x_feature_no = 0; Feature it; MatrixFixed itmat; uint y_feature_no, y_position; for (int i = 0; i < feature_list.Count; i++) { it = (Feature)feature_list[i]; y_feature_no = 0; y_position = 0; M.Update(it.get_Pxy(), (int)y_position, (int)x_position); M.Update(it.get_Pxy().Transpose(), (int)x_position, (int)y_position); y_position += motion_model.STATE_SIZE; for (int j = 0; j < it.matrix_block_list.Count; j++) { itmat = (MatrixFixed)(it.matrix_block_list[j]); //if ((M.Rows > itmat.Rows + y_position) && // (M.Columns > itmat.Columns + x_position)) //if (y_feature_no < x_feature_no) //if (itmat.Rows == itmat.Columns) { M.Update(itmat, (int)y_position, (int)x_position); M.Update(itmat.Transpose(), (int)x_position, (int)y_position); y_position += (uint)itmat.Rows; y_feature_no++; } /* else { Debug.WriteLine("Error: construct_total_covariance y_feature_no >= x_feature_no"); } */ } M.Update(it.get_Pyy(), (int)y_position, (int)x_position); x_feature_no++; uint fstate_size = it.get_feature_measurement_model().FEATURE_STATE_SIZE; x_position += fstate_size; } //assert (x_position == total_state_size); if (x_position != total_state_size) { Debug.WriteLine("Error: construct_total_covariance 2"); } }
//*************************Some important operations*************************** public void zero_axes() { uint size = get_total_state_size(); Vector x = new Vector(size); x.Fill(0.0f); MatrixFixed dxnew_by_dxold = new MatrixFixed(size, size); dxnew_by_dxold.Fill(0.0f); // We form the new state and Jacobian of the new state w.r.t. the old state uint state_position = 0; // Robot part motion_model.func_zeroedxv_and_dzeroedxv_by_dxv(xv); x.Update(motion_model.get_zeroedxvRES(), 0); dxnew_by_dxold.Update(motion_model.get_dzeroedxv_by_dxvRES(), 0, 0); state_position += motion_model.STATE_SIZE; // Each feature in turn uint feature_no = 0; // Copy these to be on the safe side motion_model.func_xp(xv); Vector local_xp = new Vector(motion_model.get_xpRES()); //Vector local_xp = motion_model.get_xpRES(); motion_model.func_dxp_by_dxv(xv); //MatrixFixed local_dxp_by_dxv = motion_model.get_dxp_by_dxvRES(); MatrixFixed local_dxp_by_dxv = new MatrixFixed(motion_model.get_dxp_by_dxvRES()); Feature it; MatrixFixed Temp_FS1; for (int i=0; i < feature_list.Count; i++) { it = (Feature)feature_list[i]; it.get_feature_measurement_model().func_zeroedyi_and_dzeroedyi_by_dxp_and_dzeroedyi_by_dyi(it.get_y(), local_xp); x.Update(it.get_feature_measurement_model().get_zeroedyiRES(), (int)state_position); // Calculate dzeroedyi_by_dxv in Temp_FS1 Temp_FS1 = it.get_feature_measurement_model().get_dzeroedyi_by_dxpRES() * local_dxp_by_dxv; dxnew_by_dxold.Update(Temp_FS1, (int)state_position, 0); dxnew_by_dxold.Update(it.get_feature_measurement_model().get_dzeroedyi_by_dyiRES(), (int)state_position, (int)state_position); // We also want to redefine xp_orig for this feature: the robot // position at which is was initialised into the map (used by // functions for checking visibility) motion_model.func_xpredef_and_dxpredef_by_dxp_and_dxpredef_by_dxpdef( it.get_xp_orig(), local_xp); it.set_xp_orig(it.get_feature_measurement_model().get_motion_model().get_xpredefRES()); feature_no++; state_position += it.get_feature_measurement_model().FEATURE_STATE_SIZE; } // Check we've counted properly //assert (feature_no == feature_list.size() && // state_position == total_state_size); // Do a Jacobian transform to get the new covariance MatrixFixed P = new MatrixFixed(size, size); construct_total_covariance(ref P); P = dxnew_by_dxold * P * dxnew_by_dxold.Transpose(); // Finally load the scene data structures with the new state and covariance fill_states(x); fill_covariances(P); }
/// <summary> /// Solve the matrix equation M X = B, returning X. /// </summary> /// <param name="B"></param> /// <returns></returns> public MatrixFixed Solve(MatrixFixed B) { MatrixFixed x; // solution matrix if (U_.Rows < U_.Columns) { // augment y with extra rows of MatrixFixed yy = new MatrixFixed(U_.Rows, B.Columns); // zeros, so that it matches yy.Fill(0); yy.Update(B); // cols of u.transpose. ??? x = U_.ConjugateTranspose() * yy; } else x = U_.ConjugateTranspose() * B; int i, j; for (i = 0; i < x.Rows; i++) { // multiply with diagonal 1/W float weight = W_[i, i]; if (weight != 0) //vnl_numeric_traits<T>::zero) weight = 1.0f / weight; for (j = 0; j < x.Columns; j++) x[i, j] *= weight; } x = V_ * x; // premultiply with v. return x; }
/// <summary> /// Step the MonoSLAM application on by one frame. This should be called every time /// a new frame is captured (and care should be taken to avoid skipping frames). /// Before calling this function, Scene_Single::load_new_image() should be called /// (e.g. using /// <code>monoslaminterface.GetRobotNoConst()->load_new_image()</code>), since the /// first parameter to GoOneStep() is currently ignored. /// /// GoOneStep() performs the following processing steps: /// - Kalman filter prediction step (by calling Kalman::predict_filter_fast(), /// with a zero control vector) /// - Select a set of features to make measurements from (set by /// SetNumberOfFeaturesToSelect()) /// - Predict the locations and and make measurements of those features /// - Kalman filter update step /// - Delete any bad features (those that have repeatedly failed to be matched) /// - If we are not currently initialising a enough new features, and the camera is /// translating, and <code>currently_mapping_flag</code> is set, initialise a new /// feature somewhere sensible /// - Update the partially-initialised features /// </summary> /// <param name="img">camera image</param> /// <param name="delta_t">The time between frames in seconds</param> /// <param name="currently_mapping_flag">Set to be true if new features should be detected and added to the map.</param> /// <returns></returns> public bool GoOneStep(float delta_t, bool currently_mapping_flag) { if (delta_t > 0) { // update the integral image for use in feature detection //img.updateIntegralImage(); // nullify image selection robot.nullify_image_selection(); init_feature_search_region_defined_flag = false; // Control vector of accelerations Vector u = new Vector(3); u.Fill(0.0f); sim_or_rob.set_control(u, delta_t); // Record the current position so that I can estimate velocity // (We can guarantee that the state vector has position; we can't // guarantee that it has velocity.) Vector xv = scene.get_xv(); scene.get_motion_model().func_xp(xv); Vector prev_xp_pos = (scene.get_motion_model().get_xpRES()).Extract(3); // Prediction step if (currently_mapping_flag) kalman.predict_filter_fast(scene, u, delta_t); // if features are not seen the first time try a few more times int tries = 0; number_of_visible_features = 0; while (((tries == 0) || (scene.get_no_selected() < 2)) && (tries < 5)) { number_of_visible_features = scene.auto_select_n_features(NUMBER_OF_FEATURES_TO_SELECT); if (scene.get_no_selected() > 0) { //scene.predict_measurements(); // commented out in original code number_of_matched_features = (uint)SceneLib.make_measurements(scene, sim_or_rob, rnd); if (scene.get_successful_measurement_vector_size() != 0) { // this function is the slowest part of the algorithm kalman.total_update_filter_slow(scene); scene.normalise_state(); } } tries++; } if (currently_mapping_flag) scene.delete_bad_features(); // Let's enforce symmetry of covariance matrix... // Add to transpose and divide by 2 uint tot_state_size = scene.get_total_state_size(); MatrixFixed Pxx = new MatrixFixed(tot_state_size, tot_state_size); scene.construct_total_covariance(ref Pxx); MatrixFixed PxxT = Pxx.Transpose(); Pxx.Update(Pxx * 0.5f + PxxT * 0.5f); scene.fill_covariances(Pxx); // Look at camera speed estimate // Get the current position and estimate the speed from it xv = scene.get_xv(); scene.get_motion_model().func_xp(xv); Vector xp_pos = scene.get_motion_model().get_xpRES().Extract(3); velocity = (xp_pos - prev_xp_pos) / delta_t; speed = (float)Math.Sqrt(velocity.SquaredMagnitude()); // This section of code is a workaround for a situation where // the camera suddenly shoots off at high speed, which must be // a bug perhaps in the state update. If the camera suddenly accelerates // at a high rate then this just sets the state back to the previous one. if (prev_speed == 0) prev_speed = speed; else { float speed_change = speed / prev_speed; if ((speed > 1) && (speed_change > 1.2) && (prev_xv != null)) { xv.Update(prev_xv); } } prev_speed = speed; if (prev_xv != null) // TODO: minor bug here with vector != operator prev_xv.Update(xv); else prev_xv = new Vector(xv); if (currently_mapping_flag) { if (speed > 0.2) { if ((number_of_visible_features < NUMBER_OF_FEATURES_TO_KEEP_VISIBLE) && (scene.get_feature_init_info_vector().Count < (uint)(MAX_FEATURES_TO_INIT_AT_ONCE))) { // if the number of features is low make more attempts // to initialise new ones tries = 1; if (number_of_visible_features < 8) tries = 2; if (number_of_visible_features < 5) tries = 3; for (int i = 0; i < tries; i++) AutoInitialiseFeature(u, delta_t); } } } MatchPartiallyInitialisedFeatures(); recordCameraHistory(); } return true; }
/// <summary> /// Fill noise covariance matrix Pnn: this is the covariance of /// the noise vector (V) /// (Omega) /// that gets added to the state. /// Form of this could change later, but for now assume that /// V and Omega are independent, and that each of their components is /// independent... /// </summary> /// <param name="xv"></param> /// <param name="v"></param> /// <param name="delta_t"></param> public override void func_Q(Vector xv, Vector v, float delta_t) { float linear_velocity_noise_variance = SD_A_component_filter * SD_A_component_filter * delta_t * delta_t; float angular_velocity_noise_variance = SD_alpha_component_filter * SD_alpha_component_filter * delta_t * delta_t; // Independence means that the matrix is diagonal MatrixFixed Pnn = new MatrixFixed(6,6); Pnn.Fill(0.0f); Pnn.Put(0, 0, linear_velocity_noise_variance); Pnn.Put(1, 1, linear_velocity_noise_variance); Pnn.Put(2, 2, linear_velocity_noise_variance); Pnn.Put(3, 3, angular_velocity_noise_variance); Pnn.Put(4, 4, angular_velocity_noise_variance); Pnn.Put(5, 5, angular_velocity_noise_variance); // Form Jacobian dxnew_by_dn // Is like this: // I * delta_t 0 // 0 dqnew_by_dOmega // I 0 // 0 I // Start by zeroing MatrixFixed dxnew_by_dn = new MatrixFixed(13,6); dxnew_by_dn.Fill(0.0f); // Fill in easy bits first MatrixFixed Temp33A = new MatrixFixed(3,3); Temp33A.SetIdentity(); dxnew_by_dn.Update(Temp33A, 7, 0); dxnew_by_dn.Update(Temp33A, 10, 3); Temp33A *= delta_t; dxnew_by_dn.Update(Temp33A, 0, 0); // Tricky bit is dqnew_by_dOmega // Is actually the same calculation as in func_fv... // Since omega and Omega are additive...? Vector3D rold = new Vector3D(0, 0, 0); Vector3D vold = new Vector3D(0, 0, 0); Vector3D omegaold = new Vector3D(0, 0, 0); Quaternion qold=new Quaternion(); extract_r_q_v_omega(xv, rold, qold, vold, omegaold); // overkill but easy // Fill in dqnew_by_domega = d(q x qwt)_by_dqwt . dqwt_by_domega // Temp44A is d(q x qwt) by dqwt MatrixFixed Temp44A = MatrixFixed.dq3_by_dq1(qold); // Use function below for dqwt_by_domega MatrixFixed Temp43A = new MatrixFixed(4,3); dqomegadt_by_domega(omegaold, delta_t, Temp43A); // Multiply them together MatrixFixed Temp43B = Temp44A * Temp43A; // And then plug into Jacobian dxnew_by_dn.Update(Temp43B, 3, 3); // Finally do Q = dxnew_by_dn . Pnn . dxnew_by_dnT QxRES.Update( dxnew_by_dn * Pnn * dxnew_by_dn.Transpose() ); // cout << "QxRES" << QxRES; }
public void update_filter_internal_measurement_slow(Scene_Single scene, uint i) { // Size of measurement vector uint size = ((Internal_Measurement)(scene.internal_measurement_vector[(int)i])).get_internal_measurement_model().MEASUREMENT_SIZE; uint size2 = scene.get_total_state_size(); // Size of state vector Vector x = new Vector(size2); MatrixFixed P = new MatrixFixed(size2, size2); scene.construct_total_state_and_covariance(ref x, ref P); // cout << "x:" << x; // cout << "P:" << P; // 1. Form nu and dh_by_dx Vector nu_tot = new Vector(size); MatrixFixed dh_by_dx_tot = new MatrixFixed(size, size2); MatrixFixed R_tot = new MatrixFixed(size, size); scene.construct_total_internal_measurement_stuff(nu_tot, dh_by_dx_tot, R_tot, i); // 2. Calculate S(k+1) MatrixFixed S = new MatrixFixed(size, size); //MatrixFixed Tempss2 = new MatrixFixed(size, size2); //MatrixFixed Temps2s = new MatrixFixed(size2, size); MatrixFixed dh_by_dx_totT = dh_by_dx_tot.Transpose(); S.Update(dh_by_dx_tot * P * dh_by_dx_totT); S += R_tot; // cout << "R_tot:" << R_tot; // cout << "S:" << S; // cout << "dh_by_dx_tot:" << dh_by_dx_tot; // cout << "dh_by_dx_totT:" << dh_by_dx_totT; // 3. Calculate W(k+1) Cholesky S_cholesky = new Cholesky(S); MatrixFixed W = P * dh_by_dx_totT * S_cholesky.Inverse(); // cout << "W:" << W; // 4. Calculate x(k+1|k+1) x += W * nu_tot; // 5. Calculate P(k+1|k+1) P -= W * S * W.Transpose(); scene.fill_state_and_covariance(x, P); // cout << "x after update:" << x; // cout << "P after update:" << P; }
/// <summary> /// Form the covariance matrix Q of the process noise associated with x_v . /// </summary> /// <param name="xv"></param> /// <param name="v"></param> /// <param name="delta_t"></param> public override void func_Q(Vector xv, Vector v, float delta_t) { // Fill noise covariance matrix Pnn: this is the covariance of // the noise vector (V) // (Omega) // that gets added to the state. // Form of this could change later, but for now assume that // V and Omega are independent, and that each of their components is // independent... float linear_velocity_noise_variance = SD_A_component_filter * SD_A_component_filter * delta_t * delta_t; float angular_velocity_noise_variance = SD_alpha_component_filter * SD_alpha_component_filter * delta_t * delta_t; // Independence means that the matrix is diagonal MatrixFixed Pnn = new MatrixFixed(6, 6); Pnn.Fill(0.0f); Pnn.Put(0, 0, linear_velocity_noise_variance); Pnn.Put(1, 1, linear_velocity_noise_variance); Pnn.Put(2, 2, linear_velocity_noise_variance); Pnn.Put(3, 3, angular_velocity_noise_variance); Pnn.Put(4, 4, angular_velocity_noise_variance); Pnn.Put(5, 5, angular_velocity_noise_variance); // Form Jacobian dxnew_by_dn // Is like this: // I * delta_t 0 // 0 dqnew_by_dOmega // Start by zeroing MatrixFixed dxnew_by_dn = new MatrixFixed(7, 6); dxnew_by_dn.Fill(0.0f); // The translation part is just I \Delta t MatrixFixed Temp33A = new MatrixFixed(3, 3); Temp33A.SetIdentity(); Temp33A *= delta_t; dxnew_by_dn.Update(Temp33A, 0, 0); // qnew = q x \Omega \Deltat // dqnew_by_d\Omega = dqnew_by_d\Omega\Delta t . d\Omega\Delta t_by_d\Omega // Get the first part Vector qRXYZ = xv.Extract(4, 3); Quaternion qold = new Quaternion(); qold.SetRXYZ(qRXYZ); MatrixFixed Temp44A = MatrixFixed.dq3_by_dq1(qold); // Use function below for dqwt_by_dOmega Vector Omega = new Vector(3); Omega.Fill(SD_alpha_component_filter); MatrixFixed Temp43A = new MatrixFixed(4, 3); dqomegadt_by_domega(new Vector3D(Omega), delta_t, Temp43A); // Multiply them together MatrixFixed Temp43B = Temp44A * Temp43A; // And then plug into Jacobian dxnew_by_dn.Update(Temp43B, 3, 3); // Finally do Q = dxnew_by_dn . Pnn . dxnew_by_dnT QxRES.Update(dxnew_by_dn * Pnn * dxnew_by_dn.Transpose()); }