9 #ifndef GRAPH_SLAM_LEVMARQ_H
10 #define GRAPH_SLAM_LEVMARQ_H
79 template <
class GRAPH_T>
82 const std::set<mrpt::graphs::TNodeID>* in_nodes_to_optimize =
nullptr,
100 typename gst::Array_O array_O_zeros;
101 array_O_zeros.fill(0);
104 static const unsigned int DIMS_POSE = gst::SE_TYPE::VECTOR_SIZE;
107 const bool verbose = 0 != extra_params.getWithDefaultVal(
"verbose", 0);
108 const size_t max_iters =
109 extra_params.getWithDefaultVal(
"max_iterations", 100);
110 const bool enable_profiler =
111 0 != extra_params.getWithDefaultVal(
"profiler", 0);
113 const double initial_lambda = extra_params.getWithDefaultVal(
114 "initial_lambda", 0);
115 const double tau = extra_params.getWithDefaultVal(
"tau", 1e-3);
116 const double e1 = extra_params.getWithDefaultVal(
"e1", 1e-6);
117 const double e2 = extra_params.getWithDefaultVal(
"e2", 1e-6);
119 const double SCALE_HESSIAN =
120 extra_params.getWithDefaultVal(
"scale_hessian", 1);
123 profiler.
enter(
"optimize_graph_spa_levmarq (entire)");
127 profiler.
enter(
"optimize_graph_spa_levmarq.list_IDs");
128 const set<TNodeID>* nodes_to_optimize;
129 set<TNodeID> nodes_to_optimize_auxlist;
131 if (in_nodes_to_optimize)
133 nodes_to_optimize = in_nodes_to_optimize;
140 it != graph.nodes.end(); ++it)
141 if (it->first != graph.root)
142 nodes_to_optimize_auxlist.insert(
143 nodes_to_optimize_auxlist.end(),
146 nodes_to_optimize = &nodes_to_optimize_auxlist;
148 profiler.
leave(
"optimize_graph_spa_levmarq.list_IDs");
151 const size_t nFreeNodes = nodes_to_optimize->size();
156 <<
" nodes to optimize: ";
159 ostream_iterator<TNodeID> out_it(cout,
", ");
161 nodes_to_optimize->begin(), nodes_to_optimize->end(), out_it);
169 using observation_info_t =
typename gst::observation_info_t;
170 vector<observation_info_t> lstObservationData;
175 for (
typename gst::edge_const_iterator it = graph.edges.begin();
176 it != graph.edges.end(); ++it)
178 const auto&
ids = it->first;
179 const auto& edge = it->second;
181 if (nodes_to_optimize->find(
ids.first) == nodes_to_optimize->end() &&
182 nodes_to_optimize->find(
ids.second) == nodes_to_optimize->end())
186 auto itP1 = graph.nodes.find(
ids.first);
187 auto itP2 = graph.nodes.find(
ids.second);
189 itP1 != graph.nodes.end(),
190 "Node1 in an edge does not have a global pose in 'graph.nodes'.");
192 itP2 != graph.nodes.end(),
193 "Node2 in an edge does not have a global pose in 'graph.nodes'.");
195 const auto& EDGE_POSE = edge.getPoseMean();
198 typename gst::observation_info_t new_entry;
200 new_entry.edge_mean = &EDGE_POSE;
201 new_entry.P1 = &itP1->second;
202 new_entry.P2 = &itP2->second;
204 lstObservationData.push_back(new_entry);
209 const size_t nObservations = lstObservationData.size();
213 using SparseCholeskyDecompPtr =
214 std::unique_ptr<CSparseMatrix::CholeskyDecomp>;
215 SparseCholeskyDecompPtr ptrCh;
221 typename gst::map_pairIDs_pairJacobs_t lstJacobians;
231 profiler.
enter(
"optimize_graph_spa_levmarq.Jacobians&err");
232 double total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
233 graph, lstObservationData, lstJacobians, errs);
234 profiler.
leave(
"optimize_graph_spa_levmarq.Jacobians&err");
241 vector<pair<size_t, size_t>>
242 observationIndex_to_relatedFreeNodeIndex;
248 observationIndex_to_relatedFreeNodeIndex.reserve(nObservations);
249 ASSERTDEB_(lstJacobians.size() == nObservations);
251 lstJacobians.begin();
252 itJ != lstJacobians.end(); ++itJ)
254 const TNodeID id1 = itJ->first.first;
255 const TNodeID id2 = itJ->first.second;
256 observationIndex_to_relatedFreeNodeIndex.push_back(std::make_pair(
264 using map_ID2matrix_VxV_t =
266 vector<map_ID2matrix_VxV_t> H_map(nFreeNodes);
268 double lambda = initial_lambda;
270 bool have_to_recompute_H_and_grad =
true;
277 size_t last_iter = 0;
279 for (
size_t iter = 0; iter < max_iters; ++iter)
283 if (have_to_recompute_H_and_grad)
288 have_to_recompute_H_and_grad =
false;
298 profiler.
enter(
"optimize_graph_spa_levmarq.grad");
300 nFreeNodes, array_O_zeros);
309 for (idx_obs = 0, itJ = lstJacobians.begin();
310 itJ != lstJacobians.end(); ++itJ, ++idx_obs)
314 itJ->first == lstObservationData[idx_obs].edge->first);
323 observationIndex_to_relatedFreeNodeIndex[idx_obs].first;
325 observationIndex_to_relatedFreeNodeIndex[idx_obs]
328 if (idx1 != string::npos)
332 lstObservationData[idx_obs].edge ,
337 if (idx2 != string::npos)
341 lstObservationData[idx_obs].edge ,
350 &grad[0], &grad_parts[0],
351 nFreeNodes * DIMS_POSE *
sizeof(grad[0]));
352 grad /= SCALE_HESSIAN;
353 profiler.
leave(
"optimize_graph_spa_levmarq.grad");
358 if (grad_norm_inf <= e1)
364 "End condition #1: math::norm_inf(g)<=e1 "
370 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:build map");
387 for (idxObs = 0, itJacobPair = lstJacobians.begin();
388 idxObs < nObservations; ++itJacobPair, ++idxObs)
392 const bool edge_straight =
393 itJacobPair->first.first < itJacobPair->first.second;
398 ? observationIndex_to_relatedFreeNodeIndex[idxObs]
400 : observationIndex_to_relatedFreeNodeIndex[idxObs]
404 ? observationIndex_to_relatedFreeNodeIndex[idxObs]
406 : observationIndex_to_relatedFreeNodeIndex[idxObs]
409 const bool is_i_free_node = idx_i != string::npos;
410 const bool is_j_free_node = idx_j != string::npos;
416 const typename gst::matrix_VxV_t& J1 =
417 edge_straight ? itJacobPair->second.first
418 : itJacobPair->second.second;
419 const typename gst::matrix_VxV_t& J2 =
420 edge_straight ? itJacobPair->second.second
421 : itJacobPair->second.first;
426 typename gst::matrix_VxV_t JtJ(
430 J1, JtJ, lstObservationData[idxObs].edge);
431 H_map[idx_i][idx_i] += JtJ;
436 typename gst::matrix_VxV_t JtJ(
440 J2, JtJ, lstObservationData[idxObs].edge);
441 H_map[idx_j][idx_j] += JtJ;
444 if (is_i_free_node && is_j_free_node)
446 typename gst::matrix_VxV_t JtJ(
450 J1, J2, JtJ, lstObservationData[idxObs].edge);
451 H_map[idx_j][idx_i] += JtJ;
455 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:build map");
459 if (lambda <= 0 && iter == 0)
462 "optimize_graph_spa_levmarq.lambda_init");
463 double H_diagonal_max = 0;
464 for (
size_t i = 0; i < nFreeNodes; i++)
467 it != H_map[i].end(); ++it)
473 for (
size_t k = 0; k < DIMS_POSE; k++)
476 it->second.get_unsafe(k, k));
479 lambda = tau * H_diagonal_max;
482 "optimize_graph_spa_levmarq.lambda_init");
498 <<
" ,total sqr. err: " << total_sqr_err
499 <<
", avrg. err per edge: "
500 << std::sqrt(total_sqr_err / nObservations)
501 <<
" lambda: " << lambda << endl;
504 if (functor_feedback)
506 functor_feedback(graph, iter, max_iters, total_sqr_err);
509 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:build");
513 CSparseMatrix sp_H(nFreeNodes * DIMS_POSE, nFreeNodes * DIMS_POSE);
514 for (
size_t i = 0; i < nFreeNodes; i++)
516 const size_t i_offset = i * DIMS_POSE;
520 it != H_map[i].end(); ++it)
522 const size_t j = it->first;
523 const size_t j_offset = j * DIMS_POSE;
531 for (
size_t r = 0;
r < DIMS_POSE;
r++)
535 j_offset +
r, i_offset +
r,
536 it->second.get_unsafe(
r,
r) + lambda);
538 for (
size_t c =
r + 1;
c < DIMS_POSE;
c++)
540 j_offset +
r, i_offset +
c,
541 it->second.get_unsafe(
r,
c));
552 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:build");
562 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:chol");
564 ptrCh = SparseCholeskyDecompPtr(
567 ptrCh.get()->update(sp_H);
568 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:chol");
570 profiler.
enter(
"optimize_graph_spa_levmarq.sp_H:backsub");
571 ptrCh->backsub(grad, delta);
572 profiler.
leave(
"optimize_graph_spa_levmarq.sp_H:backsub");
579 <<
"] Got non-definite positive matrix, retrying with a "
580 "larger lambda...\n";
591 profiler.
enter(
"optimize_graph_spa_levmarq.delta_norm");
593 profiler.
leave(
"optimize_graph_spa_levmarq.delta_norm");
596 profiler.
enter(
"optimize_graph_spa_levmarq.x_norm");
600 it != nodes_to_optimize->end(); ++it)
603 graph.nodes.find(*it);
604 const typename gst::graph_t::constraint_t::type_value& P =
606 for (
size_t i = 0; i < DIMS_POSE; i++) x_norm +=
square(P[i]);
608 x_norm = std::sqrt(x_norm);
610 profiler.
leave(
"optimize_graph_spa_levmarq.x_norm");
613 const double thres_norm = e2 * (x_norm + e2);
614 if (delta_norm < thres_norm)
620 "End condition #2: %e < %e\n", delta_norm,
632 typename gst::graph_t::global_poses_t old_poses_backup;
636 delta.size() ==
int(nodes_to_optimize->size() * DIMS_POSE));
637 const double* delta_ptr = &delta[0];
639 nodes_to_optimize->begin();
640 it != nodes_to_optimize->end(); ++it)
643 typename gst::graph_t::constraint_t::type_value
645 typename gst::Array_O exp_delta;
646 for (
size_t i = 0; i < DIMS_POSE; i++)
647 exp_delta[i] = -*delta_ptr++;
651 gst::SE_TYPE::exp(exp_delta, exp_delta_pose);
655 it_old_value = graph.nodes.find(*it);
656 old_poses_backup[*it] =
657 it_old_value->second;
658 it_old_value->second.composeFrom(
659 exp_delta_pose, it_old_value->second);
666 typename gst::map_pairIDs_pairJacobs_t new_lstJacobians;
669 profiler.
enter(
"optimize_graph_spa_levmarq.Jacobians&err");
670 double new_total_sqr_err = computeJacobiansAndErrors<GRAPH_T>(
671 graph, lstObservationData, new_lstJacobians, new_errs);
672 profiler.
leave(
"optimize_graph_spa_levmarq.Jacobians&err");
675 if (new_total_sqr_err < total_sqr_err)
678 new_lstJacobians.swap(lstJacobians);
680 std::swap(new_total_sqr_err, total_sqr_err);
683 have_to_recompute_H_and_grad =
true;
690 old_poses_backup.begin();
691 it != old_poses_backup.end(); ++it)
692 graph.nodes[it->first] = it->second;
696 <<
"] Got larger error=" << new_total_sqr_err
697 <<
", retrying with a larger lambda...\n";
707 profiler.
leave(
"optimize_graph_spa_levmarq (entire)");