28 #ifndef GRAPH_SLAM_LEVMARQ_H
29 #define GRAPH_SLAM_LEVMARQ_H
72 template <
class GRAPH_T>
76 const std::set<mrpt::utils::TNodeID> * in_nodes_to_optimize = NULL,
82 using namespace mrpt::poses;
83 using namespace mrpt::graphslam;
84 using namespace mrpt::math;
85 using namespace mrpt::utils;
89 #define VERBOSE_PREFIX "[optimize_graph_spa_levmarq] "
94 typename gst::Array_O array_O_zeros; array_O_zeros.fill(0);
97 static const unsigned int DIMS_POSE = gst::SE_TYPE::VECTOR_SIZE;
100 const bool verbose = 0!=extra_params.getWithDefaultVal(
"verbose",0);
101 const size_t max_iters = extra_params.getWithDefaultVal(
"max_iterations",100);
102 const bool enable_profiler = 0!=extra_params.getWithDefaultVal(
"profiler",0);
104 const double initial_lambda = extra_params.getWithDefaultVal(
"initial_lambda", 0);
105 const double tau = extra_params.getWithDefaultVal(
"tau", 1e-3);
106 const double e1 = extra_params.getWithDefaultVal(
"e1",1e-6);
107 const double e2 = extra_params.getWithDefaultVal(
"e2",1e-6);
109 const double SCALE_HESSIAN = extra_params.getWithDefaultVal(
"scale_hessian",1);
113 profiler.
enter(
"optimize_graph_spa_levmarq (entire)");
116 profiler.
enter(
"optimize_graph_spa_levmarq.list_IDs");
117 const set<TNodeID> * nodes_to_optimize;
118 set<TNodeID> nodes_to_optimize_auxlist;
119 if (in_nodes_to_optimize)
121 nodes_to_optimize = in_nodes_to_optimize;
127 if (it->first!=graph.root)
128 nodes_to_optimize_auxlist.insert(nodes_to_optimize_auxlist.end(), it->first );
129 nodes_to_optimize = &nodes_to_optimize_auxlist;
131 profiler.
leave(
"optimize_graph_spa_levmarq.list_IDs");
134 const size_t nFreeNodes = nodes_to_optimize->size();
142 ostream_iterator<TNodeID> out_it (cout,
", ");
143 std::copy(nodes_to_optimize->begin(), nodes_to_optimize->end(), out_it );
150 typedef typename gst::observation_info_t observation_info_t;
151 vector<observation_info_t> lstObservationData;
156 for (
typename gst::edge_const_iterator it=graph.edges.begin();it!=graph.edges.end();++it)
159 const typename gst::graph_t::edge_t &edge = it->second;
161 if (nodes_to_optimize->find(ids.first)==nodes_to_optimize->end() &&
162 nodes_to_optimize->find(ids.second)==nodes_to_optimize->end())
168 ASSERTDEBMSG_(itP1!=graph.nodes.end(),
"Node1 in an edge does not have a global pose in 'graph.nodes'.")
169 ASSERTDEBMSG_(itP2!=graph.nodes.end(),
"Node2 in an edge does not have a global pose in 'graph.nodes'.")
171 const typename gst::graph_t::constraint_t::type_value &EDGE_POSE = edge.getPoseMean();
174 typename gst::observation_info_t new_entry;
176 new_entry.edge_mean = &EDGE_POSE;
177 new_entry.P1 = &itP1->second;
178 new_entry.P2 = &itP2->second;
180 lstObservationData.push_back(new_entry);
184 const size_t nObservations = lstObservationData.size();
188 std::auto_ptr<CSparseMatrix::CholeskyDecomp> ptrCh;
194 typename gst::map_pairIDs_pairJacobs_t lstJacobians;
201 profiler.
enter(
"optimize_graph_spa_levmarq.Jacobians&err");
202 double total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
203 graph, lstObservationData,
205 profiler.
leave(
"optimize_graph_spa_levmarq.Jacobians&err");
211 vector<pair<size_t,size_t> > observationIndex_to_relatedFreeNodeIndex;
212 observationIndex_to_relatedFreeNodeIndex.reserve(nObservations);
213 ASSERTDEB_(lstJacobians.size()==nObservations)
216 const TNodeID id1 = itJ->first.first;
217 const TNodeID id2 = itJ->first.second;
218 observationIndex_to_relatedFreeNodeIndex.push_back(
227 vector<map_ID2matrix_VxV_t> H_map(nFreeNodes);
229 double lambda = initial_lambda;
231 bool have_to_recompute_H_and_grad =
true;
238 size_t last_iter = 0;
240 for (
size_t iter=0;iter<max_iters;++iter)
244 if (have_to_recompute_H_and_grad)
246 have_to_recompute_H_and_grad =
false;
254 profiler.
enter(
"optimize_graph_spa_levmarq.grad");
264 for (idx_obs=0, itJ=lstJacobians.begin();
265 itJ!=lstJacobians.end();
268 ASSERTDEB_(itJ->first==lstObservationData[idx_obs].edge->first)
275 const size_t idx1 = observationIndex_to_relatedFreeNodeIndex[idx_obs].first;
276 const size_t idx2 = observationIndex_to_relatedFreeNodeIndex[idx_obs].second;
278 if (idx1!=string::npos)
279 detail::AuxErrorEval<typename gst::edge_t,gst>::multiply_Jt_W_err(
281 lstObservationData[idx_obs].edge ,
286 if (idx2!=string::npos)
287 detail::AuxErrorEval<typename gst::edge_t,gst>::multiply_Jt_W_err(
289 lstObservationData[idx_obs].edge ,
297 ::memcpy(&grad[0],&grad_parts[0], nFreeNodes*DIMS_POSE*
sizeof(grad[0]));
298 grad /= SCALE_HESSIAN;
299 profiler.
leave(
"optimize_graph_spa_levmarq.grad");
303 if (grad_norm_inf<=e1)
306 if (verbose) printf(
VERBOSE_PREFIX "End condition #1: math::norm_inf(g)<=e1 :%f<=%f\n",grad_norm_inf,e1);
311 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:build map");
325 for (idxObs=0, itJacobPair=lstJacobians.begin();
326 idxObs<nObservations;
327 ++itJacobPair,++idxObs)
330 const bool edge_straight = itJacobPair->first.first < itJacobPair->first.second;
333 const size_t idx_i = edge_straight ? observationIndex_to_relatedFreeNodeIndex[idxObs].first : observationIndex_to_relatedFreeNodeIndex[idxObs].second;
334 const size_t idx_j = edge_straight ? observationIndex_to_relatedFreeNodeIndex[idxObs].second : observationIndex_to_relatedFreeNodeIndex[idxObs].first;
336 const bool is_i_free_node = idx_i!=string::npos;
337 const bool is_j_free_node = idx_j!=string::npos;
342 const typename gst::matrix_VxV_t &J1 = edge_straight ? itJacobPair->second.first : itJacobPair->second.second;
343 const typename gst::matrix_VxV_t &J2 = edge_straight ? itJacobPair->second.second : itJacobPair->second.first;
349 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJtLambdaJ(J1,JtJ,lstObservationData[idxObs].edge);
350 H_map[idx_i][idx_i] += JtJ;
356 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJtLambdaJ(J2,JtJ,lstObservationData[idxObs].edge);
357 H_map[idx_j][idx_j] += JtJ;
360 if (is_i_free_node && is_j_free_node)
363 detail::AuxErrorEval<typename gst::edge_t,gst>::multiplyJ1tLambdaJ2(J1,J2,JtJ,lstObservationData[idxObs].edge);
364 H_map[idx_j][idx_i] += JtJ;
368 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:build map");
371 if (lambda<=0 && iter==0)
373 profiler.
enter(
"optimize_graph_spa_levmarq.lambda_init");
374 double H_diagonal_max = 0;
375 for (
size_t i=0;i<nFreeNodes;i++)
378 const size_t j = it->first;
381 for (
size_t k=0;k<DIMS_POSE;k++)
385 lambda = tau * H_diagonal_max;
387 profiler.
leave(
"optimize_graph_spa_levmarq.lambda_init");
397 {
CMatrixDouble H; sp_H.get_dense(H); H.saveToTextFile(
"d:\\H.txt"); }
402 cout <<
VERBOSE_PREFIX "Iter: " << iter <<
" ,total sqr. err: " << total_sqr_err <<
", avrg. err per edge: " << std::sqrt(total_sqr_err/nObservations) <<
" lambda: " << lambda << endl;
405 if (functor_feedback)
407 (*functor_feedback)(graph,iter,max_iters,total_sqr_err);
411 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:build");
414 CSparseMatrix sp_H(nFreeNodes*DIMS_POSE,nFreeNodes*DIMS_POSE);
415 for (
size_t i=0;i<nFreeNodes;i++)
417 const size_t i_offset = i*DIMS_POSE;
421 const size_t j = it->first;
422 const size_t j_offset = j*DIMS_POSE;
428 for (
size_t r=0;r<DIMS_POSE;r++)
431 sp_H.
insert_entry_fast(j_offset+r,i_offset+r, it->second.get_unsafe(r,r) + lambda );
433 for (
size_t c=r+1;c<DIMS_POSE;c++)
445 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:build");
454 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:chol");
457 else ptrCh.get()->update(sp_H);
458 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:chol");
460 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:backsub");
461 ptrCh->backsub(grad,delta);
462 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:backsub");
467 if (verbose ) cout <<
VERBOSE_PREFIX "Got non-definite positive matrix, retrying with a larger lambda...\n";
478 profiler.
enter(
"optimize_graph_spa_levmarq.delta_norm");
480 profiler.
leave(
"optimize_graph_spa_levmarq.delta_norm");
483 profiler.
enter(
"optimize_graph_spa_levmarq.x_norm" );
489 const typename gst::graph_t::constraint_t::type_value &P = itP->second;
490 for (
size_t i=0;i<DIMS_POSE;i++)
493 x_norm=std::sqrt(x_norm);
495 profiler.
leave(
"optimize_graph_spa_levmarq.x_norm" );
498 const double thres_norm = e2*(x_norm+e2);
499 if (delta_norm<thres_norm)
502 if (verbose ) cout <<
VERBOSE_PREFIX <<
format(
"End condition #2: %e < %e\n", delta_norm, thres_norm );
511 typename gst::graph_t::global_poses_t old_poses_backup;
514 ASSERTDEB_(delta.size()==nodes_to_optimize->size()*DIMS_POSE)
515 const double *delta_ptr = &delta[0];
520 typename gst::Array_O exp_delta;
521 for (
size_t i=0;i<DIMS_POSE;i++)
522 exp_delta[i]= - *delta_ptr++;
523 gst::SE_TYPE::exp(exp_delta,exp_delta_pose);
527 old_poses_backup[*it] = it_old_value->second;
528 it_old_value->second.composeFrom(exp_delta_pose, it_old_value->second);
535 typename gst::map_pairIDs_pairJacobs_t new_lstJacobians;
538 profiler.
enter(
"optimize_graph_spa_levmarq.Jacobians&err");
539 double new_total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
540 graph, lstObservationData,
541 new_lstJacobians, new_errs);
542 profiler.
leave(
"optimize_graph_spa_levmarq.Jacobians&err");
545 if (new_total_sqr_err < total_sqr_err)
548 new_lstJacobians.swap(lstJacobians);
550 std::swap( new_total_sqr_err, total_sqr_err);
553 have_to_recompute_H_and_grad =
true;
560 graph.nodes[it->first] = it->second;
562 if (verbose ) cout <<
VERBOSE_PREFIX "Got larger error=" << new_total_sqr_err <<
", retrying with a larger lambda...\n";
572 profiler.
leave(
"optimize_graph_spa_levmarq (entire)");