00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifndef GRAPH_SLAM_LEVMARQ_H
00029 #define GRAPH_SLAM_LEVMARQ_H
00030
00031 #include <mrpt/graphslam/types.h>
00032 #include <mrpt/utils/TParameters.h>
00033
00034 #include <mrpt/graphslam/levmarq_impl.h>
00035
00036 namespace mrpt
00037 {
00038 namespace graphslam
00039 {
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 template <class GRAPH_T>
00073 void optimize_graph_spa_levmarq(
00074 GRAPH_T & graph,
00075 TResultInfoSpaLevMarq & out_info,
00076 const std::set<mrpt::utils::TNodeID> * in_nodes_to_optimize = NULL,
00077 const mrpt::utils::TParametersDouble & extra_params = mrpt::utils::TParametersDouble(),
00078 typename graphslam_traits<GRAPH_T>::TFunctorFeedback functor_feedback = NULL
00079 )
00080 {
00081 using namespace mrpt;
00082 using namespace mrpt::poses;
00083 using namespace mrpt::graphslam;
00084 using namespace mrpt::math;
00085 using namespace mrpt::utils;
00086 using namespace std;
00087
00088 MRPT_START
00089 #define VERBOSE_PREFIX "[optimize_graph_spa_levmarq] "
00090
00091
00092 typedef graphslam_traits<GRAPH_T> gst;
00093
00094 typename gst::Array_O array_O_zeros; array_O_zeros.fill(0);
00095
00096
00097 static const unsigned int DIMS_POSE = gst::SE_TYPE::VECTOR_SIZE;
00098
00099
00100 const bool verbose = 0!=extra_params.getWithDefaultVal("verbose",0);
00101 const size_t max_iters = extra_params.getWithDefaultVal("max_iterations",100);
00102 const bool enable_profiler = 0!=extra_params.getWithDefaultVal("profiler",0);
00103
00104 const double initial_lambda = extra_params.getWithDefaultVal("initial_lambda", 0);
00105 const double tau = extra_params.getWithDefaultVal("tau", 1e-3);
00106 const double e1 = extra_params.getWithDefaultVal("e1",1e-6);
00107 const double e2 = extra_params.getWithDefaultVal("e2",1e-6);
00108
00109 const double MIN_LAMBDA = extra_params.getWithDefaultVal("min_lambda",1e-290);
00110 const double SCALE_HESSIAN = extra_params.getWithDefaultVal("scale_hessian",1);
00111
00112
00113 mrpt::utils::CTimeLogger profiler(enable_profiler);
00114 profiler.enter("optimize_graph_spa_levmarq (entire)");
00115
00116
00117 profiler.enter("optimize_graph_spa_levmarq.list_IDs");
00118 const set<TNodeID> * nodes_to_optimize;
00119 set<TNodeID> nodes_to_optimize_auxlist;
00120 if (in_nodes_to_optimize)
00121 {
00122 nodes_to_optimize = in_nodes_to_optimize;
00123 }
00124 else
00125 {
00126
00127 for (typename gst::graph_t::global_poses_t::const_iterator it=graph.nodes.begin();it!=graph.nodes.end();++it)
00128 if (it->first!=graph.root)
00129 nodes_to_optimize_auxlist.insert(nodes_to_optimize_auxlist.end(), it->first );
00130 nodes_to_optimize = &nodes_to_optimize_auxlist;
00131 }
00132 profiler.leave("optimize_graph_spa_levmarq.list_IDs");
00133
00134
00135 const size_t nFreeNodes = nodes_to_optimize->size();
00136 ASSERT_ABOVE_(nFreeNodes,0)
00137
00138 if (verbose)
00139 {
00140 cout << VERBOSE_PREFIX << nFreeNodes << " nodes to optimize: ";
00141 if (nFreeNodes<14)
00142 {
00143 ostream_iterator<TNodeID> out_it (cout,", ");
00144 std::copy(nodes_to_optimize->begin(), nodes_to_optimize->end(), out_it );
00145 }
00146 cout << endl;
00147 }
00148
00149
00150
00151 typedef typename gst::observation_info_t observation_info_t;
00152 vector<observation_info_t> lstObservationData;
00153
00154
00155
00156
00157 for (typename gst::edge_const_iterator it=graph.edges.begin();it!=graph.edges.end();++it)
00158 {
00159 const TPairNodeIDs &ids = it->first;
00160 const typename gst::graph_t::edge_t &edge = it->second;
00161
00162 if (nodes_to_optimize->find(ids.first)==nodes_to_optimize->end() &&
00163 nodes_to_optimize->find(ids.second)==nodes_to_optimize->end())
00164 continue;
00165
00166
00167 typename gst::graph_t::global_poses_t::iterator itP1 = graph.nodes.find(ids.first);
00168 typename gst::graph_t::global_poses_t::iterator itP2 = graph.nodes.find(ids.second);
00169 ASSERTDEBMSG_(itP1!=graph.nodes.end(),"Node1 in an edge does not have a global pose in 'graph.nodes'.")
00170 ASSERTDEBMSG_(itP2!=graph.nodes.end(),"Node2 in an edge does not have a global pose in 'graph.nodes'.")
00171
00172 const typename gst::graph_t::constraint_t::type_value &EDGE_POSE = edge.getPoseMean();
00173
00174
00175 typename gst::observation_info_t new_entry;
00176 new_entry.edge = it;
00177 new_entry.edge_mean = &EDGE_POSE;
00178 new_entry.P1 = &itP1->second;
00179 new_entry.P2 = &itP2->second;
00180
00181 lstObservationData.push_back(new_entry);
00182 }
00183
00184
00185 const size_t nObservations = lstObservationData.size();
00186 ASSERT_ABOVE_(nObservations,0)
00187
00188
00189 std::auto_ptr<CSparseMatrix::CholeskyDecomp> ptrCh;
00190
00191
00192
00193
00194
00195 typename gst::map_pairIDs_pairJacobs_t lstJacobians;
00196
00197 typename mrpt::aligned_containers<typename gst::Array_O>::vector_t errs;
00198
00199
00200
00201
00202 profiler.enter("optimize_graph_spa_levmarq.Jacobians&err");
00203 double total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
00204 graph, lstObservationData,
00205 lstJacobians, errs);
00206 profiler.leave("optimize_graph_spa_levmarq.Jacobians&err");
00207
00208
00209
00210
00211
00212 vector<pair<size_t,size_t> > observationIndex_to_relatedFreeNodeIndex;
00213 observationIndex_to_relatedFreeNodeIndex.reserve(nObservations);
00214 ASSERTDEB_(lstJacobians.size()==nObservations)
00215 for (typename gst::map_pairIDs_pairJacobs_t::const_iterator itJ=lstJacobians.begin();itJ!=lstJacobians.end();++itJ)
00216 {
00217 const TNodeID id1 = itJ->first.first;
00218 const TNodeID id2 = itJ->first.second;
00219 observationIndex_to_relatedFreeNodeIndex.push_back(
00220 std::make_pair(
00221 mrpt::utils::find_in_vector(id1,*nodes_to_optimize),
00222 mrpt::utils::find_in_vector(id2,*nodes_to_optimize) ));
00223 }
00224
00225
00226 vector_double grad(nFreeNodes*DIMS_POSE);
00227 typedef typename mrpt::aligned_containers<TNodeID,typename gst::matrix_VxV_t>::map_t map_ID2matrix_VxV_t;
00228 vector<map_ID2matrix_VxV_t> H_map(nFreeNodes);
00229
00230 double lambda = initial_lambda;
00231 double rho = 0.5;
00232 double v = 1;
00233 bool have_to_recompute_H_and_grad = true;
00234
00235
00236
00237
00238
00239
00240 size_t last_iter = 0;
00241
00242 for (size_t iter=0;iter<max_iters;++iter)
00243 {
00244 last_iter = iter;
00245
00246 if (have_to_recompute_H_and_grad)
00247 {
00248 have_to_recompute_H_and_grad = false;
00249
00250
00251
00252
00253
00254
00255
00256 profiler.enter("optimize_graph_spa_levmarq.grad");
00257 typename mrpt::aligned_containers<typename gst::Array_O>::vector_t grad_parts(nFreeNodes, array_O_zeros);
00258
00259
00260 ASSERT_EQUAL_(lstJacobians.size(),lstObservationData.size())
00261
00262 {
00263 size_t idx_obs;
00264 typename gst::map_pairIDs_pairJacobs_t::const_iterator itJ;
00265
00266 for (idx_obs=0, itJ=lstJacobians.begin();
00267 itJ!=lstJacobians.end();
00268 ++itJ,++idx_obs)
00269 {
00270 ASSERTDEB_(itJ->first==lstObservationData[idx_obs].edge->first)
00271
00272
00273
00274
00275
00276
00277 const size_t idx1 = observationIndex_to_relatedFreeNodeIndex[idx_obs].first;
00278 const size_t idx2 = observationIndex_to_relatedFreeNodeIndex[idx_obs].second;
00279
00280 if (idx1!=string::npos)
00281 detail::AuxErrorEval<typename gst::edge_t,gst>::multiply_Jt_W_err(
00282 itJ->second.first ,
00283 lstObservationData[idx_obs].edge ,
00284 errs[idx_obs] ,
00285 grad_parts[idx1]
00286 );
00287
00288 if (idx2!=string::npos)
00289 detail::AuxErrorEval<typename gst::edge_t,gst>::multiply_Jt_W_err(
00290 itJ->second.second ,
00291 lstObservationData[idx_obs].edge ,
00292 errs[idx_obs] ,
00293 grad_parts[idx2]
00294 );
00295 }
00296 }
00297
00298
00299 ::memcpy(&grad[0],&grad_parts[0], nFreeNodes*DIMS_POSE*sizeof(grad[0]));
00300 profiler.leave("optimize_graph_spa_levmarq.grad");
00301
00302
00303 const double grad_norm_inf = math::norm_inf(grad);
00304 if (grad_norm_inf<=e1)
00305 {
00306
00307 if (verbose) printf(VERBOSE_PREFIX "End condition #1: math::norm_inf(g)<=e1 :%f<=%f\n",grad_norm_inf,e1);
00308 break;
00309 }
00310
00311
00312 profiler.enter("optimize_graph_spa_levmarq.sp_H:build map");
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322 {
00323 size_t idxObs;
00324 typename gst::map_pairIDs_pairJacobs_t::const_iterator itJacobPair;
00325
00326 for (idxObs=0, itJacobPair=lstJacobians.begin();
00327 idxObs<nObservations;
00328 ++itJacobPair,++idxObs)
00329 {
00330
00331 const bool edge_straight = itJacobPair->first.first < itJacobPair->first.second;
00332
00333
00334 const size_t idx_i = edge_straight ? observationIndex_to_relatedFreeNodeIndex[idxObs].first : observationIndex_to_relatedFreeNodeIndex[idxObs].second;
00335 const size_t idx_j = edge_straight ? observationIndex_to_relatedFreeNodeIndex[idxObs].second : observationIndex_to_relatedFreeNodeIndex[idxObs].first;
00336
00337 const bool is_i_free_node = idx_i!=string::npos;
00338 const bool is_j_free_node = idx_j!=string::npos;
00339
00340
00341
00342
00343 const typename gst::matrix_VxV_t &J1 = edge_straight ? itJacobPair->second.first : itJacobPair->second.second;
00344 const typename gst::matrix_VxV_t &J2 = edge_straight ? itJacobPair->second.second : itJacobPair->second.first;
00345
00346
00347 if (is_i_free_node)
00348 {
00349 typename gst::matrix_VxV_t JtJ(UNINITIALIZED_MATRIX);
00350 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJtLambdaJ(J1,JtJ,lstObservationData[idxObs].edge);
00351 H_map[idx_i][idx_i] += JtJ * SCALE_HESSIAN;
00352 }
00353
00354 if (is_j_free_node)
00355 {
00356 typename gst::matrix_VxV_t JtJ(UNINITIALIZED_MATRIX);
00357 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJtLambdaJ(J2,JtJ,lstObservationData[idxObs].edge);
00358 H_map[idx_j][idx_j] += JtJ * SCALE_HESSIAN;
00359 }
00360
00361 if (is_i_free_node && is_j_free_node)
00362 {
00363 typename gst::matrix_VxV_t JtJ(UNINITIALIZED_MATRIX);
00364 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJ1tLambdaJ2(J1,J2,JtJ,lstObservationData[idxObs].edge);
00365 H_map[idx_j][idx_i] += JtJ * SCALE_HESSIAN;
00366 }
00367 }
00368 }
00369 profiler.leave("optimize_graph_spa_levmarq.sp_H:build map");
00370
00371
00372 if (lambda<=0 && iter==0)
00373 {
00374 profiler.enter("optimize_graph_spa_levmarq.lambda_init");
00375 double H_diagonal_max = 0;
00376 for (size_t i=0;i<nFreeNodes;i++)
00377 for (typename map_ID2matrix_VxV_t::const_iterator it=H_map[i].begin();it!=H_map[i].end();++it)
00378 {
00379 const size_t j = it->first;
00380 if (i==j)
00381 {
00382 for (size_t k=0;k<DIMS_POSE;k++)
00383 mrpt::utils::keep_max(H_diagonal_max, it->second.get_unsafe(k,k) );
00384 }
00385 }
00386 lambda = tau * H_diagonal_max;
00387
00388 profiler.leave("optimize_graph_spa_levmarq.lambda_init");
00389 }
00390 #if 0
00391 { CMatrixDouble H; sp_H.get_dense(H); H.saveToTextFile("d:\\H.txt"); }
00392 #endif
00393
00394
00395
00396 lambda *= std::max(0.333, 1-pow(2*rho-1,3.0) );
00397 utils::keep_max(lambda, 1e-200);
00398 v = 2;
00399
00400
00401 if (lambda<MIN_LAMBDA)
00402 {
00403 if (verbose ) cout << VERBOSE_PREFIX "End condition #3: lambda < " << MIN_LAMBDA << "\n";
00404 break;
00405 }
00406
00407 }
00408
00409 if (verbose )
00410 cout << VERBOSE_PREFIX "Iter: " << iter << " ,total sqr. err: " << total_sqr_err << ", avrg. err per edge: " << std::sqrt(total_sqr_err/nObservations) << " lambda: " << lambda << endl;
00411
00412
00413 if (functor_feedback)
00414 {
00415 (*functor_feedback)(graph,iter,max_iters,total_sqr_err);
00416 }
00417
00418
00419 profiler.enter("optimize_graph_spa_levmarq.sp_H:build");
00420
00421
00422 CSparseMatrix sp_H(nFreeNodes*DIMS_POSE,nFreeNodes*DIMS_POSE);
00423 for (size_t i=0;i<nFreeNodes;i++)
00424 {
00425 const size_t i_offset = i*DIMS_POSE;
00426
00427 for (typename map_ID2matrix_VxV_t::const_iterator it=H_map[i].begin();it!=H_map[i].end();++it)
00428 {
00429 const size_t j = it->first;
00430 const size_t j_offset = j*DIMS_POSE;
00431
00432
00433
00434 if (i==j)
00435 {
00436 for (size_t r=0;r<DIMS_POSE;r++)
00437 {
00438
00439 sp_H.insert_entry_fast(j_offset+r,i_offset+r, it->second.get_unsafe(r,r) + lambda );
00440
00441 for (size_t c=r+1;c<DIMS_POSE;c++)
00442 sp_H.insert_entry_fast(j_offset+r,i_offset+c, it->second.get_unsafe(r,c));
00443 }
00444 }
00445 else
00446 {
00447 sp_H.insert_submatrix(j_offset,i_offset, it->second);
00448 }
00449 }
00450 }
00451
00452 sp_H.compressFromTriplet();
00453 profiler.leave("optimize_graph_spa_levmarq.sp_H:build");
00454
00455
00456
00457
00458
00459 vector_double delta;
00460 try
00461 {
00462 profiler.enter("optimize_graph_spa_levmarq.sp_H:chol");
00463 if (!ptrCh.get())
00464 ptrCh = std::auto_ptr<CSparseMatrix::CholeskyDecomp>(new CSparseMatrix::CholeskyDecomp(sp_H) );
00465 else ptrCh.get()->update(sp_H);
00466 profiler.leave("optimize_graph_spa_levmarq.sp_H:chol");
00467
00468 profiler.enter("optimize_graph_spa_levmarq.sp_H:backsub");
00469 ptrCh->backsub(grad,delta);
00470 profiler.leave("optimize_graph_spa_levmarq.sp_H:backsub");
00471 }
00472 catch (CExceptionNotDefPos &)
00473 {
00474
00475 if (verbose ) cout << VERBOSE_PREFIX "Got non-definite positive matrix, retrying with a larger lambda...\n";
00476 lambda *= v;
00477 v*= 2;
00478 if (lambda>1e9)
00479 {
00480 break;
00481 }
00482 continue;
00483 }
00484
00485
00486 profiler.enter("optimize_graph_spa_levmarq.delta_norm");
00487 const double delta_norm = math::norm(delta);
00488 profiler.leave("optimize_graph_spa_levmarq.delta_norm");
00489
00490
00491 profiler.enter("optimize_graph_spa_levmarq.x_norm" );
00492 double x_norm = 0;
00493 {
00494 for (set<TNodeID>::const_iterator it=nodes_to_optimize->begin();it!=nodes_to_optimize->end();++it)
00495 {
00496 typename gst::graph_t::global_poses_t::const_iterator itP = graph.nodes.find(*it);
00497 const typename gst::graph_t::constraint_t::type_value &P = itP->second;
00498 for (size_t i=0;i<DIMS_POSE;i++)
00499 x_norm+=square(P[i]);
00500 }
00501 x_norm=std::sqrt(x_norm);
00502 }
00503 profiler.leave("optimize_graph_spa_levmarq.x_norm" );
00504
00505
00506 const double thres_norm = e2*(x_norm+e2);
00507 if (delta_norm<thres_norm)
00508 {
00509
00510 if (verbose ) cout << VERBOSE_PREFIX << format("End condition #2: %e < %e\n", delta_norm, thres_norm );
00511 break;
00512 }
00513 else
00514 {
00515
00516
00517
00518
00519 typename gst::graph_t::global_poses_t old_poses_backup;
00520
00521 {
00522 ASSERTDEB_(delta.size()==nodes_to_optimize->size()*DIMS_POSE)
00523 const double *delta_ptr = &delta[0];
00524 for (set<TNodeID>::const_iterator it=nodes_to_optimize->begin();it!=nodes_to_optimize->end();++it)
00525 {
00526
00527 typename gst::graph_t::constraint_t::type_value exp_delta_pose(UNINITIALIZED_POSE);
00528 typename gst::Array_O exp_delta;
00529 for (size_t i=0;i<DIMS_POSE;i++)
00530 exp_delta[i]= - *delta_ptr++;
00531 gst::SE_TYPE::exp(exp_delta,exp_delta_pose);
00532
00533
00534 typename gst::graph_t::global_poses_t::iterator it_old_value = graph.nodes.find(*it);
00535 old_poses_backup[*it] = it_old_value->second;
00536 it_old_value->second.composeFrom(exp_delta_pose, it_old_value->second);
00537 }
00538 }
00539
00540
00541
00542
00543 typename gst::map_pairIDs_pairJacobs_t new_lstJacobians;
00544 typename mrpt::aligned_containers<typename gst::Array_O>::vector_t new_errs;
00545
00546 profiler.enter("optimize_graph_spa_levmarq.Jacobians&err");
00547 double new_total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
00548 graph, lstObservationData,
00549 new_lstJacobians, new_errs);
00550 profiler.leave("optimize_graph_spa_levmarq.Jacobians&err");
00551
00552
00553
00554
00555 double denom;
00556 {
00557 vector_double aux = delta;
00558 aux*=lambda;
00559 aux+=grad;
00560 denom = mrpt::math::dotProduct<vector_double,vector_double>(delta,aux);
00561 }
00562 rho = (total_sqr_err - new_total_sqr_err) / denom;
00563
00564 if (rho>0)
00565 {
00566
00567 new_lstJacobians.swap(lstJacobians);
00568 new_errs.swap(errs);
00569 std::swap( new_total_sqr_err, total_sqr_err);
00570
00571
00572 have_to_recompute_H_and_grad = true;
00573 }
00574 else
00575 {
00576
00577
00578 for (typename gst::graph_t::global_poses_t::const_iterator it=old_poses_backup.begin();it!=old_poses_backup.end();++it)
00579 graph.nodes[it->first] = it->second;
00580
00581 if (verbose ) cout << VERBOSE_PREFIX "Got larger error=" << new_total_sqr_err << ", retrying with a larger lambda...\n";
00582
00583 lambda *= v;
00584 v*= 2;
00585 }
00586
00587 }
00588
00589 }
00590
00591 profiler.leave("optimize_graph_spa_levmarq (entire)");
00592
00593
00594
00595
00596 out_info.num_iters = last_iter;
00597 out_info.final_total_sq_error = total_sqr_err;
00598
00599 MRPT_END
00600 }
00601
00602
00603
00604
00605 }
00606 }
00607
00608 #endif