SRBA: Sparser Relative Bundle Adjustment
|
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