SRBA: Sparser Relative Bundle Adjustment
/home/travis/build/MRPT/srba/examples/cpp/tutorial-srba-range-bearing-se3.cpp
00001 /* +---------------------------------------------------------------------------+
00002    |                     Mobile Robot Programming Toolkit (MRPT)               |
00003    |                          http://www.mrpt.org/                             |
00004    |                                                                           |
00005    | Copyright (c) 2005-2015, Individual contributors, see AUTHORS file        |
00006    | See: http://www.mrpt.org/Authors - All rights reserved.                   |
00007    | Released under BSD License. See details in http://www.mrpt.org/License    |
00008    +---------------------------------------------------------------------------+ */
00009 
00010 #include <srba.h>
00011 #include <mrpt/gui.h>  // For rendering results as a 3D scene
00012 #include <mrpt/random.h>
00013 
00014 using namespace srba;
00015 using namespace mrpt::random;
00016 using namespace std;
00017 
00018 struct RBA_OPTIONS : public RBA_OPTIONS_DEFAULT
00019 {
00020 //  typedef ecps::local_areas_fixed_size            edge_creation_policy_t;  //!< One of the most important choices: how to construct the relative coordinates graph problem
00021 //  typedef options::sensor_pose_on_robot_none      sensor_pose_on_robot_t;  //!< The sensor pose coincides with the robot pose
00022     typedef options::observation_noise_constant_matrix<observations::RangeBearing_3D>   obs_noise_matrix_t;      // The sensor noise matrix is the same for all observations and equal to some given matrix
00023 //  typedef options::solver_LM_schur_dense_cholesky solver_t;                //!< Solver algorithm (Default: Lev-Marq, with Schur, with dense Cholesky)
00024 };
00025 
00026 typedef RbaEngine<
00027     kf2kf_poses::SE3,               // Parameterization  of KF-to-KF poses
00028     landmarks::Euclidean3D,     // Parameterization of landmark positions
00029     observations::RangeBearing_3D, // Type of observations
00030     RBA_OPTIONS
00031     >  my_srba_t;
00032 
00033 // --------------------------------------------------------------------------------
00034 // A test dataset. Generated with https://github.com/jlblancoc/recursive-world-toolkit 
00035 //  and the script: tutorials_dataset-range-bearing-3d.cfg
00036 // --------------------------------------------------------------------------------
00037 const double SENSOR_NOISE_STD = 1e-4;
00038 struct basic_range_bearing_dataset_entry_t 
00039 {
00040     unsigned int landmark_id;
00041     double range,yaw,pitch;
00042 };
00043 
00044 // Observations for KF#0. Ground truth pose: (xyz,q)= 0.000000 0.000000 0.000000 0.995247 0.003802 -0.043453 0.087073
00045 const mrpt::poses::CPose3DQuat GT_pose0(0,0,0, mrpt::math::CQuaternionDouble(0.995247,0.003802,-0.043453,0.087073));
00046 
00047 basic_range_bearing_dataset_entry_t   observations_0[] = {
00048  {    98,   1.57995661,  -1.11612141,  -0.88057821},
00049  {   124,   1.58750481,   1.33252588,  -0.17632351},
00050  {    61,   1.66239883,  -1.66622477,  -0.53198357},
00051  {    67,   1.72059795,   1.20765968,  -0.15204502},
00052  {   143,   1.92608851,  -0.40410293,  -1.19165567},
00053  {   146,   1.96633957,   0.01419675,   0.34239113},
00054  {    21,   2.09390189,  -1.59218478,   1.21487641},
00055  {    53,   2.11366834,  -1.20428778,  -0.00185802},
00056  {   144,   2.34694432,   0.35807227,   0.55639248},
00057  {   119,   2.47846784,  -0.79955155,  -1.04817877},
00058  {   118,   2.49461941,   0.80510532,   0.30549132},
00059  {    55,   2.58392412,  -0.66488196,   0.39272175},
00060  {    64,   2.63705829,  -1.13584981,   0.26975274},
00061  {    39,   2.69260050,  -0.13730595,   0.18298585},
00062  {   107,   2.76637849,   1.19932152,  -0.19808894},
00063  {    35,   2.89046723,  -0.01671179,   0.22590799},
00064  {   114,   2.94211876,   0.91679581,  -0.77201784},
00065  {    20,   2.94613590,   1.08737805,   0.17648625},
00066  {    58,   2.96345569,  -1.62191147,   0.40354151},
00067  {    63,   2.98730148,   1.08823686,  -0.81591035},
00068  {    23,   3.09687711,  -1.26068054,  -0.83347972},
00069  {    28,   3.21386016,  -0.18630799,   0.24547471},
00070  {    52,   3.26808549,   0.84726362,  -0.31014202},
00071  {   141,   3.27732824,  -0.42493052,   0.36914937},
00072  {    84,   3.37825912,   0.51818394,  -0.03880613},
00073  {    76,   3.40806843,  -0.23702528,  -0.00751110},
00074  {    71,   3.54231093,   0.00461531,  -0.48140070},
00075  {    45,   3.55010435,  -1.21450567,   0.04014509},
00076  {    68,   3.57398713,  -1.42712728,   0.68378257},
00077  {    95,   3.66312088,   0.11439174,   0.90085832},
00078  {    37,   3.68678484,  -0.40365290,  -0.23302471},
00079  {    18,   3.71358208,  -1.06381526,   0.38328004},
00080  {    91,   3.75799153,  -1.65835524,  -0.67656730},
00081  {   100,   3.87873841,   0.87088081,  -0.39445635},
00082  {   127,   3.87992671,   0.49019065,   0.45098085},
00083  {   115,   3.88108895,   0.81696074,  -0.12154930},
00084  {    59,   3.94675883,  -0.43490279,   0.00432028},
00085  };
00086 // Observations for KF#10:(xyz,q)= 1.226070 0.293637 0.110099 0.999103 0.000160 -0.042322 0.001049
00087 const mrpt::poses::CPose3DQuat GT_pose10(1.226071, 0.293637, 0.110099, mrpt::math::CQuaternionDouble(0.999103,0.000160,-0.042322,0.001049));
00088 basic_range_bearing_dataset_entry_t  observations_10[] = {
00089  {   146,   0.88599661,   0.10332223,   0.83802213},
00090  {    39,   1.51019758,  -0.13524493,   0.33114189},
00091  {   144,   1.51242000,   0.98270483,   0.94766117},
00092  {    35,   1.68742684,   0.09858576,   0.39107551},
00093  {   118,   1.84320333,   1.51134950,   0.40445321},
00094  {    55,   1.93814292,  -1.01548620,   0.54728525},
00095  {    28,   2.06846167,  -0.17048903,   0.38844089},
00096  {    53,   2.11104700,  -1.64093228,   0.01291843},
00097  {    76,   2.22915825,  -0.23247995,  -0.00936564},
00098  {    84,   2.31303597,   0.93419094,  -0.06931260},
00099  {   141,   2.34502432,  -0.54109440,   0.53505424},
00100  {    64,   2.49028032,  -1.47875157,   0.30062864},
00101  {    71,   2.49298897,   0.12625740,  -0.72128471},
00102  {    20,   2.54628805,   1.70789406,   0.19016031},
00103  {    52,   2.59242989,   1.40249273,  -0.40965589},
00104  {   114,   2.60044783,   1.73535532,  -0.92388492},
00105  {    37,   2.65977766,  -0.46313367,  -0.32044489},
00106  {    59,   2.89638583,  -0.47213886,   0.01141878},
00107  {   127,   2.93616190,   0.88931571,   0.60152145},
00108  {    95,   3.04488843,   0.39090980,   1.22500067},
00109  {   115,   3.09422585,   1.27633221,  -0.16714667},
00110  {   113,   3.13437020,   0.80859085,   0.20829286},
00111  {    23,   3.14474672,  -1.69442056,  -0.80209768},
00112  {   128,   3.16030734,   0.54822387,  -0.21670432},
00113  {     6,   3.16473119,  -0.46131456,   0.41454190},
00114  {    86,   3.17553418,   0.35002168,  -0.33416683},
00115  {   100,   3.22501637,   1.36825755,  -0.49516815},
00116  {    45,   3.40382071,  -1.40643899,   0.05618251},
00117  {    18,   3.40978425,  -1.26468864,   0.43312935},
00118  {    99,   3.47156965,   1.18299202,   0.27180899},
00119  {    13,   3.48896168,   0.34197139,   0.46908459},
00120  {    68,   3.71363308,  -1.69773527,   0.66827742},
00121  {    26,   3.89957951,  -0.17030487,  -0.07981311},
00122 };
00123 
00124 
00125 int main(int argc, char**argv)
00126 {
00127     my_srba_t rba;     //  Create an empty RBA problem
00128 
00129     // --------------------------------------------------------------------------------
00130     // Set parameters 
00131     // --------------------------------------------------------------------------------
00132     rba.setVerbosityLevel( 1 );   // 0: None; 1:Important only; 2:Verbose
00133 
00134     rba.parameters.srba.use_robust_kernel = false; // true
00135 
00136 //  rba.parameters.obs_noise.std_noise_observations = 0.03; //SENSOR_NOISE_STD;
00137     
00138     rba.parameters.obs_noise.lambda.setZero();
00139     rba.parameters.obs_noise.lambda(0,0) = 100;  // range
00140     rba.parameters.obs_noise.lambda(1,1) = 50;   // yaw 
00141     rba.parameters.obs_noise.lambda(2,2) = 30;   // pitch
00142 
00143     // =========== Topology parameters ===========
00144     rba.parameters.srba.max_tree_depth       = 3;
00145     rba.parameters.srba.max_optimize_depth   = 3;
00146     //rba.parameters.srba.cov_recovery = crpNone;
00147     // ===========================================
00148 
00149     // Set sensors parameters:
00150     // rba.sensor_params has no parameters for the "Cartesian" sensor.
00151     
00152     // Alternatively, parameters can be loaded from an .ini-like config file
00153     // -----------------------------------------------------------------------
00154     // rba.parameters.loadFromConfigFileName("config_file.cfg", "srba");
00155     
00156     // --------------------------------------------------------------------------------
00157     // Dump parameters to console (for checking/debugging only)
00158     // --------------------------------------------------------------------------------
00159     //cout << "RBA parameters:\n-----------------\n";
00160     //rba.parameters.dumpToConsole();
00161 
00162     // --------------------------------------------------------------------------------
00163     // Define observations of KF #0:
00164     // --------------------------------------------------------------------------------
00165     my_srba_t::new_kf_observations_t  list_obs;
00166     my_srba_t::new_kf_observation_t   obs_field;
00167 
00168     obs_field.is_fixed = false;   // Landmarks have unknown relative positions (i.e. treat them as unknowns to be estimated)
00169     obs_field.is_unknown_with_init_val = false; // We don't have any guess on the initial LM position (will invoke the inverse sensor model)
00170 
00171     for (size_t i=0;i<sizeof(observations_0)/sizeof(observations_0[0]);i++)
00172     {
00173         obs_field.obs.feat_id = observations_0[i].landmark_id;
00174         obs_field.obs.obs_data.range = observations_0[i].range + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00175         obs_field.obs.obs_data.yaw = observations_0[i].yaw + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00176         obs_field.obs.obs_data.pitch = observations_0[i].pitch + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00177         list_obs.push_back( obs_field );
00178     }
00179 
00180 
00181     //  Here happens the main stuff: create Key-frames, build structures, run optimization, etc.
00182     //  ============================================================================================
00183     my_srba_t::TNewKeyFrameInfo new_kf_info;
00184     rba.define_new_keyframe(
00185         list_obs,      // Input observations for the new KF
00186         new_kf_info,   // Output info
00187         true           // Also run local optimization?
00188         );
00189 
00190     cout << "Created KF #" << new_kf_info.kf_id 
00191         << " | # kf-to-kf edges created:" <<  new_kf_info.created_edge_ids.size()  << endl
00192         << "Optimization error: " << new_kf_info.optimize_results.total_sqr_error_init << " -> " << new_kf_info.optimize_results.total_sqr_error_final << endl
00193         << "-------------------------------------------------------" << endl;
00194 
00195 
00196     // --------------------------------------------------------------------------------
00197     // Define observations of next KF:
00198     // --------------------------------------------------------------------------------
00199     list_obs.clear();
00200 
00201     for (size_t i=0;i<sizeof(observations_10)/sizeof(observations_10[0]);i++)
00202     {
00203         obs_field.obs.feat_id = observations_10[i].landmark_id;
00204         obs_field.obs.obs_data.range = observations_10[i].range + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00205         obs_field.obs.obs_data.yaw = observations_10[i].yaw + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00206         obs_field.obs.obs_data.pitch = observations_10[i].pitch + randomGenerator.drawGaussian1D(0,SENSOR_NOISE_STD);
00207         list_obs.push_back( obs_field );
00208     }
00209     
00210     //  Here happens the main stuff: create Key-frames, build structures, run optimization, etc.
00211     //  ============================================================================================
00212     rba.define_new_keyframe(
00213         list_obs,      // Input observations for the new KF
00214         new_kf_info,   // Output info
00215         true           // Also run local optimization?
00216         );
00217 
00218     cout << "Created KF #" << new_kf_info.kf_id 
00219         << " | # kf-to-kf edges created:" <<  new_kf_info.created_edge_ids.size() << endl
00220         << "Optimization error: " << new_kf_info.optimize_results.total_sqr_error_init << " -> " << new_kf_info.optimize_results.total_sqr_error_final << endl
00221         << "-------------------------------------------------------" << endl;
00222 
00223 
00224     // Dump the relative pose of KF#0 wrt KF#1:
00225     cout << "inv_pose of KF-to-KF edge #0 (relative pose of KF#0 wrt KF#1):\n" << rba.get_k2k_edges()[0].inv_pose << endl;
00226     cout << "Relative pose of KF#1 wrt KF#0:\n" << (-rba.get_k2k_edges()[0].inv_pose) << endl;
00227 
00228     // Compare to ground truth:
00229     cout << "Ground truth: relative pose of KF#1 wrt KF#0: \n" << mrpt::poses::CPose3D(GT_pose10-GT_pose0) << endl;
00230     
00231 
00232     // --------------------------------------------------------------------------------
00233     // Saving RBA graph as a DOT file:
00234     // --------------------------------------------------------------------------------
00235     const string sFil = "graph.dot";
00236     cout << "Saving final graph of KFs and LMs to: " << sFil << endl;
00237     rba.save_graph_as_dot(sFil, true /* LMs=save */);
00238     cout << "Done.\n";
00239 
00240     // --------------------------------------------------------------------------------
00241     // Show 3D view of the resulting map:
00242     // --------------------------------------------------------------------------------
00243     my_srba_t::TOpenGLRepresentationOptions  opengl_options;
00244     mrpt::opengl::CSetOfObjectsPtr rba_3d = mrpt::opengl::CSetOfObjects::Create();
00245 
00246     rba.build_opengl_representation(
00247         0,  // Root KF,
00248         opengl_options, // Rendering options
00249         rba_3d  // Output scene 
00250         );
00251 
00252     // Display:
00253 #if MRPT_HAS_WXWIDGETS
00254     mrpt::gui::CDisplayWindow3D win("RBA results",640,480);
00255     {
00256         mrpt::opengl::COpenGLScenePtr &scene = win.get3DSceneAndLock();
00257         scene->insert(rba_3d);
00258         win.unlockAccess3DScene();
00259     }
00260     win.setCameraZoom( 4 ); 
00261     win.repaint();
00262 
00263     cout << "Press any key or close window to exit.\n";
00264     win.waitForKey();
00265 #endif
00266         
00267     return 0; // All ok
00268 }
00269 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends