10 #ifndef CLOOPCLOSERERD_IMPL_H
11 #define CLOOPCLOSERERD_IMPL_H
26 template <
class GRAPH_T>
28 : m_visualize_curr_node_covariance(false),
29 m_curr_node_covariance_color(160, 160, 160, 255),
30 m_partitions_full_update(false),
31 m_is_first_time_node_reg(true),
32 m_dijkstra_node_count_thresh(3)
34 this->initializeLoggers(
"CLoopCloserERD");
40 template <
class GRAPH_T>
47 for (
auto it = m_node_optimal_paths.begin();
48 it != m_node_optimal_paths.end(); ++it)
57 template <
class GRAPH_T>
65 this->m_time_logger.enter(
"updateState");
77 getObservation<CObservation2DRangeScan>(observations, observation);
80 m_last_laser_scan2D = scan;
84 if (!m_first_laser_scan)
86 m_first_laser_scan = m_last_laser_scan2D;
90 size_t num_registered =
91 absDiff(this->m_last_total_num_nodes, this->m_graph->nodeCount());
92 bool registered_new_node = num_registered > 0;
94 if (registered_new_node)
97 registered_new_node =
true;
101 if (!this->m_override_registered_nodes_check)
103 if (!((num_registered == 1) ^
104 (num_registered == 2 && m_is_first_time_node_reg)))
107 "Invalid number of registered nodes since last call to "
110 << num_registered <<
"\" new nodes.");
121 if (m_is_first_time_node_reg)
124 "Assigning first laserScan to the graph root node.");
125 this->m_nodes_to_laser_scans2D[this->m_graph->root] =
127 m_is_first_time_node_reg =
false;
131 this->m_nodes_to_laser_scans2D[this->m_graph->nodeCount() - 1] =
134 if (m_laser_params.use_scan_matching)
137 this->addScanMatchingEdges(this->m_graph->nodeCount() - 1);
141 m_partitions_full_update =
142 ((this->m_graph->nodeCount() %
143 m_lc_params.full_partition_per_nodes) == 0 ||
144 this->m_just_inserted_lc)
147 this->updateMapPartitions(
148 m_partitions_full_update,
149 num_registered == 2);
153 this->checkPartitionsForLC(&partitions_for_LC);
154 this->evaluatePartitionsForLC(partitions_for_LC);
156 if (m_visualize_curr_node_covariance)
158 this->execDijkstraProjection();
161 this->m_last_total_num_nodes = this->m_graph->nodeCount();
164 this->m_time_logger.leave(
"updateState");
169 template <
class GRAPH_T>
172 std::set<mrpt::graphs::TNodeID>* nodes_set)
178 int fetched_nodeIDs = 0;
179 for (
int nodeID_i =
static_cast<int>(curr_nodeID) - 1;
180 ((fetched_nodeIDs <= this->m_laser_params.prev_nodes_for_ICP) &&
185 nodes_set->insert(nodeID_i);
190 template <
class GRAPH_T>
196 using namespace mrpt;
204 std::set<TNodeID> nodes_set;
205 this->fetchNodeIDsForScanMatching(curr_nodeID, &nodes_set);
209 node_it != nodes_set.end(); ++node_it)
215 "Fetching laser scan for nodes: " << *node_it <<
"==> "
219 this->getICPEdge(*node_it, curr_nodeID, &rel_edge, &icp_info);
220 if (!found_edge)
continue;
227 m_laser_params.goodness_threshold_win.addNewMeasurement(
230 double goodness_thresh =
231 m_laser_params.goodness_threshold_win.getMedian() *
232 m_consec_icp_constraint_factor;
233 bool accept_goodness = icp_info.
goodness > goodness_thresh;
235 "Curr. Goodness: " << icp_info.
goodness
236 <<
"|\t Threshold: " << goodness_thresh <<
" => "
237 << (accept_goodness ?
"ACCEPT" :
"REJECT"));
241 bool accept_mahal_distance = this->mahalanobisDistanceOdometryToICPEdge(
242 *node_it, curr_nodeID, rel_edge);
245 if (accept_goodness && accept_mahal_distance)
247 this->registerNewEdge(*node_it, curr_nodeID, rel_edge);
253 template <
class GRAPH_T>
261 this->m_time_logger.enter(
"getICPEdge");
278 "TGetICPEdgeAdParams:\n"
285 bool from_success = this->getPropsOfNodeID(
286 from, &from_pose, from_scan, from_params);
289 bool to_success = this->getPropsOfNodeID(to, &to_pose, to_scan, to_params);
291 if (!from_success || !to_success)
295 << from <<
" or node #" << to
296 <<
" doesn't contain a valid LaserScan. Ignoring this...");
309 initial_estim = to_pose - from_pose;
313 "from_pose: " << from_pose <<
"| to_pose: " << to_pose
314 <<
"| init_estim: " << initial_estim);
316 range_ops_t::getICPEdge(
317 *from_scan, *to_scan, rel_edge, &initial_estim, icp_info);
320 this->m_time_logger.leave(
"getICPEdge");
325 template <
class GRAPH_T>
328 const std::map<mrpt::graphs::TNodeID, node_props_t>& group_params,
340 search = group_params.find(nodeID);
342 if (search == group_params.end())
348 *node_props = search->second;
358 template <
class GRAPH_T>
367 bool filled_pose =
false;
368 bool filled_scan =
false;
375 *pose = node_props->
pose;
379 if (node_props->
scan)
381 scan = node_props->
scan;
388 !(filled_pose ^ filled_scan),
390 "Either BOTH or NONE of the filled_pose, filled_scan should be set."
392 static_cast<unsigned long>(nodeID)));
401 this->m_graph->nodes.find(nodeID);
402 if (search != this->m_graph->nodes.end())
404 *pose = search->second;
415 this->m_nodes_to_laser_scans2D.find(nodeID);
416 if (search != this->m_nodes_to_laser_scans2D.end())
418 scan = search->second;
423 return filled_pose && filled_scan;
426 template <
class GRAPH_T>
431 this->m_time_logger.enter(
"LoopClosureEvaluation");
434 using namespace mrpt;
437 partitions_for_LC->clear();
441 map<int, std::vector<uint32_t>>
::iterator finder;
443 if (m_partitions_full_update)
445 m_partitionID_to_prev_nodes_list.clear();
451 partitions_it != m_curr_partitions.end();
452 ++partitions_it, ++partitionID)
456 if (m_lc_params.LC_check_curr_partition_only)
458 bool curr_node_in_curr_partition =
460 partitions_it->begin(), partitions_it->end(),
461 this->m_graph->nodeCount() - 1)) != partitions_it->end());
462 if (!curr_node_in_curr_partition)
469 finder = m_partitionID_to_prev_nodes_list.find(partitionID);
470 if (finder == m_partitionID_to_prev_nodes_list.end())
473 m_partitionID_to_prev_nodes_list.insert(
474 make_pair(partitionID, *partitions_it));
478 if (*partitions_it == finder->second)
488 finder->second = *partitions_it;
493 int curr_node_index = 1;
494 size_t prev_nodeID = *(partitions_it->begin());
496 it = partitions_it->begin() + 1;
497 it != partitions_it->end(); ++it, ++curr_node_index)
499 size_t curr_nodeID = *it;
503 if ((curr_nodeID - prev_nodeID) > m_lc_params.LC_min_nodeid_diff)
507 int num_after_nodes = partitions_it->size() - curr_node_index;
508 int num_before_nodes = partitions_it->size() - num_after_nodes;
509 if (num_after_nodes >= m_lc_params.LC_min_remote_nodes &&
510 num_before_nodes >= m_lc_params.LC_min_remote_nodes)
513 "Found potential loop closures:"
515 <<
"\tPartitionID: " << partitionID << endl
521 <<
"\t" << prev_nodeID <<
" ==> " << curr_nodeID << endl
522 <<
"\tNumber of LC nodes: " << num_after_nodes);
523 partitions_for_LC->push_back(*partitions_it);
530 prev_nodeID = curr_nodeID;
533 "Successfully checked partition: " << partitionID);
536 this->m_time_logger.leave(
"LoopClosureEvaluation");
540 template <
class GRAPH_T>
545 using namespace mrpt;
549 this->m_time_logger.enter(
"LoopClosureEvaluation");
551 if (partitions.size() == 0)
return;
554 "Evaluating partitions for loop closures...\n%s\n",
555 this->header_sep.c_str());
559 p_it != partitions.end(); ++p_it)
561 std::vector<uint32_t> partition(*p_it);
564 std::vector<uint32_t> groupA, groupB;
565 this->splitPartitionToGroups(
566 partition, &groupA, &groupB,
571 this->generateHypotsPool(groupA, groupB, &hypots_pool);
574 CMatrixDouble consist_matrix(hypots_pool.size(), hypots_pool.size());
575 this->generatePWConsistenciesMatrix(
576 groupA, groupB, hypots_pool, &consist_matrix);
580 this->evalPWConsistenciesMatrix(
581 consist_matrix, hypots_pool, &valid_hypots);
584 if (valid_hypots.size())
588 it != valid_hypots.end(); ++it)
590 this->registerHypothesis(**it);
596 it != hypots_pool.end(); ++it)
603 this->m_time_logger.leave(
"LoopClosureEvaluation");
608 template <
class GRAPH_T>
618 valid_hypots->clear();
623 bool valid_lambda_ratio = this->computeDominantEigenVector(
626 if (!valid_lambda_ratio)
return;
634 double dot_product = 0;
635 for (
int i = 0; i !=
w.size(); ++i)
642 double potential_dot_product =
643 ((
w.transpose() * u) /
w.squaredNorm()).
value();
645 "current: %f | potential_dot_product: %f", dot_product,
646 potential_dot_product);
647 if (potential_dot_product > dot_product)
650 dot_product = potential_dot_product;
660 cout <<
"Outcome of discretization: " <<
w.transpose() << endl;
666 for (
int wi = 0; wi !=
w.size(); ++wi)
673 valid_hypots->push_back(this->findHypotByID(hypots_pool, wi));
682 template <
class GRAPH_T>
684 std::vector<uint32_t>& partition, std::vector<uint32_t>* groupA,
685 std::vector<uint32_t>* groupB,
int max_nodes_in_group )
689 using namespace mrpt;
697 max_nodes_in_group == -1 || max_nodes_in_group > 0,
699 "Value %d not permitted for max_nodes_in_group"
700 "Either use a positive integer, "
701 "or -1 for non-restrictive partition size",
702 max_nodes_in_group));
707 size_t index_to_split = 1;
709 it != partition.end(); ++it, ++index_to_split)
713 if ((curr_nodeID - prev_nodeID) > m_lc_params.LC_min_nodeid_diff)
718 prev_nodeID = curr_nodeID;
720 ASSERTDEB_(partition.size() > index_to_split);
723 *groupA = std::vector<uint32_t>(
724 partition.begin(), partition.begin() + index_to_split);
725 *groupB = std::vector<uint32_t>(
726 partition.begin() + index_to_split, partition.end());
728 if (max_nodes_in_group != -1)
731 if (groupA->size() >
static_cast<size_t>(max_nodes_in_group))
733 *groupA = std::vector<uint32_t>(
734 groupA->begin(), groupA->begin() + max_nodes_in_group);
737 if (groupB->size() >
static_cast<size_t>(max_nodes_in_group))
739 *groupB = std::vector<uint32_t>(
740 groupB->end() - max_nodes_in_group, groupB->end());
748 template <
class GRAPH_T>
750 const std::vector<uint32_t>& groupA,
const std::vector<uint32_t>& groupB,
755 using namespace mrpt;
760 "generateHypotsPool: Given hypotsp_t pointer is invalid.");
761 generated_hypots->clear();
766 <<
" - size: " << groupA.size() << endl);
769 <<
" - size: " << groupB.size() << endl);
783 size_t nodes_count = groupA.size();
787 nodes_count ==
params.size(),
789 "Size mismatch between nodeIDs in group [%lu]"
790 " and corresponding properties map [%lu]",
791 nodes_count,
params.size()));
797 int hypot_counter = 0;
798 int invalid_hypots = 0;
802 b_it = groupB.begin();
803 b_it != groupB.end(); ++b_it)
806 a_it = groupA.begin();
807 a_it != groupA.end(); ++a_it)
816 hypot->id = hypot_counter++;
831 fillNodePropsFromGroupParams(
835 fillNodePropsFromGroupParams(
857 bool found_edge = this->getICPEdge(
858 *b_it, *a_it, &edge, &icp_info, icp_ad_params);
860 hypot->setEdge(edge);
867 double goodness_thresh =
868 m_laser_params.goodness_threshold_win.getMedian() *
869 m_lc_icp_constraint_factor;
870 bool accept_goodness = icp_info.
goodness > goodness_thresh;
872 "generateHypotsPool:\nCurr. Goodness: "
873 << icp_info.
goodness <<
"|\t Threshold: " << goodness_thresh
874 <<
" => " << (accept_goodness ?
"ACCEPT" :
"REJECT")
877 if (!found_edge || !accept_goodness)
879 hypot->is_valid =
false;
882 generated_hypots->push_back(hypot);
888 delete icp_ad_params;
892 "Generated pool of hypotheses...\tsize = "
893 << generated_hypots->size()
894 <<
"\tinvalid hypotheses: " << invalid_hypots);
901 template <
class GRAPH_T>
907 using namespace mrpt;
912 this->m_time_logger.enter(
"DominantEigenvectorComputation");
914 double lambda1, lambda2;
915 bool is_valid_lambda_ratio =
false;
917 if (use_power_method)
920 "\nPower method for computing the first two "
921 "eigenvectors/eigenvalues hasn't been implemented yet\n");
926 consist_matrix.eigenVectors(eigvecs, eigvals);
932 eigvecs.size() == eigvals.size() &&
933 consist_matrix.size() == eigvals.size(),
935 "Size of eigvecs \"%lu\","
937 "consist_matrix \"%lu\" don't match",
938 static_cast<unsigned long>(eigvecs.size()),
939 static_cast<unsigned long>(eigvals.size()),
940 static_cast<unsigned long>(consist_matrix.size())));
942 eigvecs.extractCol(eigvecs.cols() - 1, *eigvec);
943 lambda1 = eigvals(eigvals.rows() - 1, eigvals.cols() - 1);
944 lambda2 = eigvals(eigvals.rows() - 2, eigvals.cols() - 2);
948 for (
int i = 0; i != eigvec->size(); ++i)
950 (*eigvec)(i) = abs((*eigvec)(i));
958 "Bad lambda2 value: "
959 << lambda2 <<
" => Skipping current evaluation." << endl);
962 double curr_lambda_ratio = lambda1 / lambda2;
964 "lambda1 = " << lambda1 <<
" | lambda2 = " << lambda2
965 <<
"| ratio = " << curr_lambda_ratio << endl);
967 is_valid_lambda_ratio =
968 (curr_lambda_ratio > m_lc_params.LC_eigenvalues_ratio_thresh);
970 this->m_time_logger.leave(
"DominantEigenvectorComputation");
971 return is_valid_lambda_ratio;
976 template <
class GRAPH_T>
978 const std::vector<uint32_t>& groupA,
const std::vector<uint32_t>& groupB,
980 const paths_t* groupA_opt_paths ,
981 const paths_t* groupB_opt_paths )
985 using namespace mrpt;
992 consist_matrix,
"Invalid pointer to the Consistency matrix is given");
994 static_cast<unsigned long>(consist_matrix->rows()) ==
995 hypots_pool.size() &&
996 static_cast<unsigned long>(consist_matrix->rows()) ==
998 "Consistency matrix dimensions aren't equal to the hypotheses pool "
1002 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
1004 <<
"In generatePWConsistencyMatrix:\n"
1007 <<
"\tHypots pool Size: " << hypots_pool.size());
1011 b1_it != groupB.end(); ++b1_it)
1017 b2_it != groupB.end(); ++b2_it)
1023 a1_it != groupA.end(); ++a1_it)
1027 this->findHypotByEnds(hypots_pool,
b2,
a1);
1033 a2_it != groupA.end(); ++a2_it)
1037 this->findHypotByEnds(hypots_pool,
b1,
a2);
1044 if (hypot_b2_a1->is_valid && hypot_b1_a2->is_valid)
1050 extracted_hypots.push_back(hypot_b2_a1);
1051 extracted_hypots.push_back(hypot_b1_a2);
1053 paths_t* curr_opt_paths = NULL;
1054 if (groupA_opt_paths || groupB_opt_paths)
1056 curr_opt_paths =
new paths_t();
1063 if (groupA_opt_paths)
1065 const path_t*
p = this->findPathByEnds(
1066 *groupA_opt_paths,
a1,
a2,
1068 curr_opt_paths->push_back(*
p);
1072 curr_opt_paths->push_back(
path_t());
1075 if (groupB_opt_paths)
1077 const path_t*
p = this->findPathByEnds(
1078 *groupB_opt_paths,
b1,
b2,
1080 curr_opt_paths->push_back(*
p);
1084 curr_opt_paths->push_back(
path_t());
1088 consistency = this->generatePWConsistencyElement(
1089 a1,
a2,
b1,
b2, extracted_hypots, curr_opt_paths);
1091 delete curr_opt_paths;
1106 int id1 = hypot_b2_a1->id;
1107 int id2 = hypot_b1_a2->id;
1109 (*consist_matrix)(id1, id2) = consistency;
1110 (*consist_matrix)(id2, id1) = consistency;
1127 template <
class GRAPH_T>
1134 using namespace std;
1135 using namespace mrpt;
1163 const path_t* path_a1_a2;
1164 if (!opt_paths || opt_paths->begin()->isEmpty())
1167 "Running dijkstra [a1] " <<
a1 <<
" => [a2] " <<
a2);
1168 execDijkstraProjection(
a1,
a2);
1169 path_a1_a2 = this->queryOptimalPath(
a2);
1174 path_a1_a2 = &(*opt_paths->begin());
1180 const path_t* path_b1_b2;
1181 if (!opt_paths || opt_paths->rend()->isEmpty())
1184 "Running djkstra [b1] " <<
b1 <<
" => [b2] " <<
b2);
1185 execDijkstraProjection(
b1,
b2);
1186 path_b1_b2 = this->queryOptimalPath(
b2);
1190 path_b1_b2 = &(*opt_paths->rbegin());
1198 hypot_t* hypot_b1_a2 = this->findHypotByEnds(hypots,
b1,
a2);
1200 hypot_t* hypot_b2_a1 = this->findHypotByEnds(hypots,
b2,
a1);
1205 res_transform += hypot_b1_a2->getInverseEdge();
1207 res_transform += hypot_b2_a1->getEdge();
1210 "\n-----------Resulting Transformation----------- Hypots: #"
1211 << hypot_b1_a2->id <<
", #" << hypot_b2_a1->id << endl
1212 <<
"a1 --> a2 => b1 --> b2 => a1: " <<
a1 <<
" --> " <<
a2 <<
" => "
1213 <<
b1 <<
" --> " <<
b2 <<
" => " <<
a1 << endl
1214 << res_transform << endl
1216 <<
"DIJKSTRA: " <<
a1 <<
" --> " <<
a2 <<
": "
1218 <<
"DIJKSTRA: " <<
b1 <<
" --> " <<
b2 <<
": "
1220 <<
"hypot_b1_a2(inv):\n"
1221 << hypot_b1_a2->getInverseEdge() << endl
1223 << hypot_b2_a1->getEdge() << endl);
1227 res_transform.getMeanVal().getAsVector(T);
1231 res_transform.getCovariance(cov_mat);
1237 double exponent = (-T.transpose() * cov_mat * T).
value();
1238 double consistency_elem = exp(exponent);
1245 return consistency_elem;
1249 template <
class GRAPH_T>
1255 using namespace mrpt;
1261 cit != vec_paths.end(); ++cit)
1263 if (cit->getSource() ==
src && cit->getDestination() ==
dst)
1269 if (throw_exc && !
res)
1273 "Path for %lu => %lu is not found. Exiting...\n",
1274 static_cast<unsigned long>(
src),
1275 static_cast<unsigned long>(
dst)));
1281 template <
class GRAPH_T>
1288 using namespace std;
1291 v_cit != vec_hypots.end(); v_cit++)
1293 if ((*v_cit)->hasEnds(from, to))
1312 template <
class GRAPH_T>
1315 const hypotsp_t& vec_hypots,
const size_t&
id,
bool throw_exc )
1320 v_cit != vec_hypots.end(); v_cit++)
1322 if ((*v_cit)->id ==
id)
1339 template <
class GRAPH_T>
1345 using namespace std;
1346 using namespace mrpt;
1354 this->m_time_logger.enter(
"Dijkstra Projection");
1356 "----------- Done with Dijkstra Projection... ----------";
1362 (ending_node >= 0 && ending_node < this->m_graph->nodeCount()));
1364 starting_node != ending_node,
"Starting and Ending nodes coincede");
1367 stringstream ss_debug(
"");
1368 ss_debug <<
"Executing Dijkstra Projection: " << starting_node <<
" => ";
1371 ss_debug <<
"..." << endl;
1375 ss_debug << ending_node << endl;
1378 if (this->m_graph->nodeCount() < m_dijkstra_node_count_thresh)
1384 std::vector<bool> visited_nodes(this->m_graph->nodeCount(),
false);
1385 m_node_optimal_paths.clear();
1388 std::map<TNodeID, std::set<TNodeID>> neighbors_of;
1389 this->m_graph->getAdjacencyMatrix(neighbors_of);
1394 std::set<path_t*> pool_of_paths;
1396 std::set<TNodeID> starting_node_neighbors(neighbors_of.at(starting_node));
1398 starting_node_neighbors.begin();
1399 n_it != starting_node_neighbors.end(); ++n_it)
1402 this->getMinUncertaintyPath(
1403 starting_node, *n_it, path_between_neighbors);
1405 pool_of_paths.insert(path_between_neighbors);
1408 visited_nodes.at(starting_node) =
true;
1426 it != visited_nodes.end(); ++it)
1439 if (visited_nodes.at(ending_node))
1442 this->m_time_logger.leave(
"Dijkstra Projection");
1447 path_t* optimal_path = this->popMinUncertaintyPath(&pool_of_paths);
1472 if (!visited_nodes.at(dest))
1474 m_node_optimal_paths[dest] = optimal_path;
1475 visited_nodes.at(dest) =
true;
1481 &pool_of_paths, *optimal_path, neighbors_of.at(dest));
1486 this->m_time_logger.leave(
"Dijkstra Projection");
1490 template <
class GRAPH_T>
1492 std::set<path_t*>* pool_of_paths,
const path_t& current_path,
1493 const std::set<mrpt::graphs::TNodeID>& neighbors)
const
1496 using namespace std;
1508 neigh_it != neighbors.end(); ++neigh_it)
1510 if (*neigh_it == second_to_last_node)
continue;
1513 path_t path_between_nodes;
1514 this->getMinUncertaintyPath(
1515 node_to_append_from, *neigh_it, &path_between_nodes);
1519 *path_to_append = current_path;
1520 *path_to_append += path_between_nodes;
1522 pool_of_paths->insert(path_to_append);
1528 template <
class GRAPH_T>
1532 using namespace std;
1536 search = m_node_optimal_paths.find(node);
1537 if (search != m_node_optimal_paths.end())
1539 path = search->second;
1547 template <
class GRAPH_T>
1550 path_t* path_between_nodes)
const
1554 using namespace std;
1557 this->m_graph->edgeExists(from, to) ||
1558 this->m_graph->edgeExists(to, from),
1560 "\nEdge between the provided nodeIDs"
1561 "(%lu <-> %lu) does not exist\n",
1568 path_between_nodes->
clear();
1572 double curr_determinant = 0;
1574 std::pair<edges_citerator, edges_citerator> fwd_edges_pair =
1575 this->m_graph->getEdges(from, to);
1585 edges_it != fwd_edges_pair.second; ++edges_it)
1590 curr_edge.copyFrom(edges_it->second);
1594 curr_edge.getInformationMatrix(inf_mat);
1599 curr_edge.cov_inv = inf_mat;
1610 *path_between_nodes = curr_path;
1614 std::pair<edges_citerator, edges_citerator> bwd_edges_pair =
1615 this->m_graph->getEdges(to, from);
1625 edges_it != bwd_edges_pair.second; ++edges_it)
1630 (edges_it->second).
inverse(curr_edge);
1634 curr_edge.getInformationMatrix(inf_mat);
1639 curr_edge.cov_inv = inf_mat;
1650 *path_between_nodes = curr_path;
1657 template <
class GRAPH_T>
1659 typename std::set<path_t*>* pool_of_paths)
const
1662 using namespace std;
1665 path_t* optimal_path = NULL;
1666 double curr_determinant = 0;
1668 it != pool_of_paths->end(); ++it)
1673 if (curr_determinant < (*it)->getDeterminant())
1681 pool_of_paths->erase(optimal_path);
1683 return optimal_path;
1687 template <
class GRAPH_T>
1694 using namespace std;
1699 this->m_graph->nodes.at(to) - this->m_graph->nodes.at(from);
1701 (rel_edge.getMeanVal() - initial_estim).getAsVector(mean_diff);
1705 rel_edge.getCovariance(cov_mat);
1708 double mahal_distance =
1710 bool mahal_distance_null = std::isnan(mahal_distance);
1711 if (!mahal_distance_null)
1713 m_laser_params.mahal_distance_ICP_odom_win.addNewMeasurement(
1720 m_laser_params.mahal_distance_ICP_odom_win.getMedian() * 4;
1722 (threshold >= mahal_distance && !mahal_distance_null) ?
true :
false;
1734 template <
class GRAPH_T>
1739 this->registerNewEdge(hypot.from, hypot.to, hypot.getEdge());
1742 template <
class GRAPH_T>
1749 using namespace std;
1750 parent_t::registerNewEdge(from, to, rel_edge);
1753 m_edge_types_to_nums[
"ICP2D"]++;
1756 if (
absDiff(to, from) > m_lc_params.LC_min_nodeid_diff)
1758 m_edge_types_to_nums[
"LC"]++;
1759 this->m_just_inserted_lc =
true;
1764 this->m_just_inserted_lc =
false;
1768 this->m_graph->insertEdge(from, to, rel_edge);
1773 template <
class GRAPH_T>
1778 parent_t::setWindowManagerPtr(win_manager);
1781 if (this->m_win_manager)
1783 if (this->m_win_observer)
1785 this->m_win_observer->registerKeystroke(
1786 m_laser_params.keystroke_laser_scans,
1787 "Toggle LaserScans Visualization");
1788 this->m_win_observer->registerKeystroke(
1789 m_lc_params.keystroke_map_partitions,
1790 "Toggle Map Partitions Visualization");
1794 "Fetched the window manager, window observer successfully.");
1797 template <
class GRAPH_T>
1799 const std::map<std::string, bool>& events_occurred)
1802 parent_t::notifyOfWindowEvents(events_occurred);
1805 if (events_occurred.at(m_laser_params.keystroke_laser_scans))
1807 this->toggleLaserScansVisualization();
1810 if (events_occurred.at(m_lc_params.keystroke_map_partitions))
1812 this->toggleMapPartitionsVisualization();
1818 template <
class GRAPH_T>
1821 using namespace mrpt;
1827 this->m_win_manager->assignTextMessageParameters(
1828 &m_lc_params.offset_y_map_partitions,
1829 &m_lc_params.text_index_map_partitions);
1833 mrpt::make_aligned_shared<CSetOfObjects>();
1834 map_partitions_obj->setName(
"map_partitions");
1837 scene->insert(map_partitions_obj);
1838 this->m_win->unlockAccess3DScene();
1839 this->m_win->forceRepaint();
1842 template <
class GRAPH_T>
1845 using namespace mrpt;
1852 std::stringstream title;
1853 title <<
"# Partitions: " << m_curr_partitions.size();
1854 this->m_win_manager->addTextMessage(
1855 5, -m_lc_params.offset_y_map_partitions, title.str(),
1857 m_lc_params.text_index_map_partitions);
1868 map_partitions_obj = std::dynamic_pointer_cast<CSetOfObjects>(
obj);
1871 int partitionID = 0;
1872 bool partition_contains_last_node =
false;
1873 bool found_last_node =
1876 "Searching for the partition of the last nodeID: "
1877 << (this->m_graph->nodeCount() - 1));
1880 p_it != m_curr_partitions.end(); ++p_it, ++partitionID)
1883 std::vector<uint32_t> nodes_list = *p_it;
1887 nodes_list.begin(), nodes_list.end(),
1888 this->m_graph->nodeCount() - 1) != nodes_list.end())
1890 partition_contains_last_node =
true;
1892 found_last_node =
true;
1896 partition_contains_last_node =
false;
1905 map_partitions_obj->getByName(partition_obj_name);
1912 curr_partition_obj = std::dynamic_pointer_cast<CSetOfObjects>(
obj);
1913 if (m_lc_params.LC_check_curr_partition_only)
1915 curr_partition_obj->setVisibility(partition_contains_last_node);
1921 "\tCreating a new CSetOfObjects partition object for partition "
1924 curr_partition_obj = mrpt::make_aligned_shared<CSetOfObjects>();
1925 curr_partition_obj->setName(partition_obj_name);
1926 if (m_lc_params.LC_check_curr_partition_only)
1929 curr_partition_obj->setVisibility(partition_contains_last_node);
1934 CSphere::Ptr balloon_obj = mrpt::make_aligned_shared<CSphere>();
1935 balloon_obj->setName(balloon_obj_name);
1936 balloon_obj->setRadius(m_lc_params.balloon_radius);
1937 balloon_obj->setColor_u8(m_lc_params.balloon_std_color);
1938 balloon_obj->enableShowName();
1940 curr_partition_obj->insert(balloon_obj);
1946 mrpt::make_aligned_shared<CSetOfLines>();
1947 connecting_lines_obj->setName(
"connecting_lines");
1948 connecting_lines_obj->setColor_u8(
1949 m_lc_params.connecting_lines_color);
1950 connecting_lines_obj->setLineWidth(0.1f);
1952 curr_partition_obj->insert(connecting_lines_obj);
1957 map_partitions_obj->insert(curr_partition_obj);
1964 std::pair<double, double> centroid_coords;
1965 this->computeCentroidOfNodesVector(nodes_list, ¢roid_coords);
1968 centroid_coords.first, centroid_coords.second,
1969 m_lc_params.balloon_elevation);
1978 curr_partition_obj->getByName(balloon_obj_name);
1979 balloon_obj = std::dynamic_pointer_cast<CSphere>(
obj);
1980 balloon_obj->setLocation(balloon_location);
1981 if (partition_contains_last_node)
1982 balloon_obj->setColor_u8(m_lc_params.balloon_curr_color);
1984 balloon_obj->setColor_u8(m_lc_params.balloon_std_color);
1997 curr_partition_obj->getByName(
"connecting_lines");
1998 connecting_lines_obj = std::dynamic_pointer_cast<CSetOfLines>(
obj);
2000 connecting_lines_obj->clear();
2003 it != nodes_list.end(); ++it)
2005 CPose3D curr_pose(this->m_graph->nodes.at(*it));
2009 curr_node_location, balloon_location);
2010 connecting_lines_obj->appendLine(connecting_line);
2016 if (!found_last_node)
2019 THROW_EXCEPTION(
"Last inserted nodeID was not found in any partition.");
2027 size_t prev_size = m_last_partitions.size();
2028 size_t curr_size = m_curr_partitions.size();
2029 if (curr_size < prev_size)
2032 for (
size_t partitionID = curr_size; partitionID != prev_size;
2037 "partition_%lu",
static_cast<unsigned long>(partitionID));
2040 map_partitions_obj->getByName(partition_obj_name);
2044 partition_obj_name.c_str())
2046 map_partitions_obj->removeObject(
obj);
2051 this->m_win->unlockAccess3DScene();
2052 this->m_win->forceRepaint();
2055 template <
class GRAPH_T>
2059 ASSERTDEBMSG_(this->m_win,
"No CDisplayWindow3D* was provided");
2060 ASSERTDEBMSG_(this->m_win_manager,
"No CWindowManager* was provided");
2066 if (m_lc_params.visualize_map_partitions)
2069 scene->getByName(
"map_partitions");
2070 obj->setVisibility(!
obj->isVisible());
2074 this->dumpVisibilityErrorMsg(
"visualize_map_partitions");
2077 this->m_win->unlockAccess3DScene();
2078 this->m_win->forceRepaint();
2083 template <
class GRAPH_T>
2085 const std::vector<uint32_t>& nodes_list,
2086 std::pair<double, double>* centroid_coords)
const
2092 double centroid_x = 0;
2093 double centroid_y = 0;
2095 node_it != nodes_list.end(); ++node_it)
2097 pose_t curr_node_pos = this->m_graph->nodes.at(*node_it);
2098 centroid_x += curr_node_pos.x();
2099 centroid_y += curr_node_pos.y();
2103 centroid_coords->first =
2104 centroid_x /
static_cast<double>(nodes_list.size());
2105 centroid_coords->second =
2106 centroid_y /
static_cast<double>(nodes_list.size());
2111 template <
class GRAPH_T>
2117 if (m_laser_params.visualize_laser_scans)
2120 this->m_win->get3DSceneAndLock();
2123 mrpt::make_aligned_shared<mrpt::opengl::CPlanarLaserScan>();
2124 laser_scan_viz->enablePoints(
true);
2125 laser_scan_viz->enableLine(
true);
2126 laser_scan_viz->enableSurface(
true);
2127 laser_scan_viz->setSurfaceColor(
2128 m_laser_params.laser_scans_color.R,
2129 m_laser_params.laser_scans_color.G,
2130 m_laser_params.laser_scans_color.B,
2131 m_laser_params.laser_scans_color.A);
2133 laser_scan_viz->setName(
"laser_scan_viz");
2135 scene->insert(laser_scan_viz);
2136 this->m_win->unlockAccess3DScene();
2137 this->m_win->forceRepaint();
2143 template <
class GRAPH_T>
2149 if (m_laser_params.visualize_laser_scans && m_last_laser_scan2D)
2152 this->m_win->get3DSceneAndLock();
2155 scene->getByName(
"laser_scan_viz");
2157 std::dynamic_pointer_cast<mrpt::opengl::CPlanarLaserScan>(
obj);
2158 laser_scan_viz->setScan(*m_last_laser_scan2D);
2162 this->m_graph->nodes.find(this->m_graph->nodeCount() - 1);
2163 if (search != this->m_graph->nodes.end())
2165 laser_scan_viz->setPose(search->second);
2168 laser_scan_viz->setPose(
2170 laser_scan_viz->getPoseX(), laser_scan_viz->getPoseY(),
2176 this->m_win->unlockAccess3DScene();
2177 this->m_win->forceRepaint();
2183 template <
class GRAPH_T>
2187 ASSERTDEBMSG_(this->m_win,
"No CDisplayWindow3D* was provided");
2188 ASSERTDEBMSG_(this->m_win_manager,
"No CWindowManager* was provided");
2194 if (m_laser_params.visualize_laser_scans)
2197 scene->getByName(
"laser_scan_viz");
2198 obj->setVisibility(!
obj->isVisible());
2202 this->dumpVisibilityErrorMsg(
"visualize_laser_scans");
2205 this->m_win->unlockAccess3DScene();
2206 this->m_win->forceRepaint();
2211 template <
class GRAPH_T>
2213 std::map<std::string, int>* edge_types_to_num)
const
2216 *edge_types_to_num = m_edge_types_to_nums;
2220 template <
class GRAPH_T>
2224 parent_t::initializeVisuals();
2226 this->m_time_logger.enter(
"Visuals");
2229 m_laser_params.has_read_config,
2230 "Configuration parameters aren't loaded yet");
2231 if (m_laser_params.visualize_laser_scans)
2233 this->initLaserScansVisualization();
2235 if (m_lc_params.visualize_map_partitions)
2237 this->initMapPartitionsVisualization();
2240 if (m_visualize_curr_node_covariance)
2242 this->initCurrCovarianceVisualization();
2245 this->m_time_logger.leave(
"Visuals");
2248 template <
class GRAPH_T>
2252 parent_t::updateVisuals();
2254 this->m_time_logger.enter(
"Visuals");
2256 if (m_laser_params.visualize_laser_scans)
2258 this->updateLaserScansVisualization();
2260 if (m_lc_params.visualize_map_partitions)
2262 this->updateMapPartitionsVisualization();
2264 if (m_visualize_curr_node_covariance)
2266 this->updateCurrCovarianceVisualization();
2269 this->m_time_logger.leave(
"Visuals");
2273 template <
class GRAPH_T>
2277 using namespace std;
2281 this->m_win_manager->assignTextMessageParameters(
2282 &m_offset_y_curr_node_covariance,
2283 &m_text_index_curr_node_covariance);
2286 this->m_win_manager->addTextMessage(
2287 5, -m_offset_y_curr_node_covariance, title,
2289 m_text_index_curr_node_covariance);
2292 CEllipsoid::Ptr cov_ellipsis_obj = mrpt::make_aligned_shared<CEllipsoid>();
2293 cov_ellipsis_obj->setName(
"cov_ellipsis_obj");
2294 cov_ellipsis_obj->setColor_u8(m_curr_node_covariance_color);
2295 cov_ellipsis_obj->setLocation(0, 0, 0);
2299 scene->insert(cov_ellipsis_obj);
2300 this->m_win->unlockAccess3DScene();
2301 this->m_win->forceRepaint();
2306 template <
class GRAPH_T>
2310 using namespace std;
2317 path_t* path = queryOptimalPath(curr_node);
2322 pose_t curr_position = this->m_graph->nodes.at(curr_node);
2325 "In updateCurrCovarianceVisualization\n"
2326 "Covariance matrix:\n"
2334 std::dynamic_pointer_cast<CEllipsoid>(
obj);
2337 cov_ellipsis_obj->setLocation(curr_position.x(), curr_position.y(), 0);
2340 cov_ellipsis_obj->setCovMatrix(mat, 2);
2342 this->m_win->unlockAccess3DScene();
2343 this->m_win->forceRepaint();
2348 template <
class GRAPH_T>
2356 "Cannot toggle visibility of specified object.\n "
2357 "Make sure that the corresponding visualization flag ( %s "
2358 ") is set to true in the .ini file.\n",
2360 std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
2365 template <
class GRAPH_T>
2369 parent_t::loadParams(source_fname);
2371 m_partitioner.options.loadFromConfigFileName(
2372 source_fname,
"EdgeRegistrationDeciderParameters");
2373 m_laser_params.loadFromConfigFileName(
2374 source_fname,
"EdgeRegistrationDeciderParameters");
2375 m_lc_params.loadFromConfigFileName(
2376 source_fname,
"EdgeRegistrationDeciderParameters");
2380 m_consec_icp_constraint_factor =
source.read_double(
2381 "EdgeRegistrationDeciderParameters",
"consec_icp_constraint_factor",
2383 m_lc_icp_constraint_factor =
source.read_double(
2384 "EdgeRegistrationDeciderParameters",
"lc_icp_constraint_factor", 0.70,
2388 int min_verbosity_level =
source.read_int(
2389 "EdgeRegistrationDeciderParameters",
"class_verbosity", 1,
false);
2394 template <
class GRAPH_T>
2398 using namespace std;
2400 cout <<
"------------------[Pair-wise Consistency of ICP Edges - "
2401 "Registration Procedure Summary]------------------"
2404 parent_t::printParams();
2405 m_partitioner.options.dumpToConsole();
2406 m_laser_params.dumpToConsole();
2407 m_lc_params.dumpToConsole();
2409 cout <<
"Scan-matching ICP Constraint factor: "
2410 << m_consec_icp_constraint_factor << endl;
2411 cout <<
"Loop-closure ICP Constraint factor: "
2412 << m_lc_icp_constraint_factor << endl;
2418 template <
class GRAPH_T>
2425 std::stringstream class_props_ss;
2426 class_props_ss <<
"Pair-wise Consistency of ICP Edges - Registration "
2427 "Procedure Summary: "
2429 class_props_ss << this->header_sep << std::endl;
2432 const std::string time_res = this->m_time_logger.getStatsAsText();
2433 const std::string output_res = this->getLogAsString();
2436 report_str->clear();
2437 parent_t::getDescriptiveReport(report_str);
2439 *report_str += class_props_ss.str();
2440 *report_str += this->report_sep;
2442 *report_str += time_res;
2443 *report_str += this->report_sep;
2445 *report_str += output_res;
2446 *report_str += this->report_sep;
2451 template <
class GRAPH_T>
2455 partitions_out = this->getCurrentPartitions();
2458 template <
class GRAPH_T>
2459 const std::vector<std::vector<uint32_t>>&
2462 return m_curr_partitions;
2465 template <
class GRAPH_T>
2467 bool full_update ,
bool is_first_time_node_reg )
2471 using namespace std;
2472 this->m_time_logger.enter(
"updateMapPartitions");
2479 "updateMapPartitions: Full partitioning of map was issued");
2482 m_partitioner.clear();
2483 nodes_to_scans = this->m_nodes_to_laser_scans2D;
2488 if (is_first_time_node_reg)
2490 nodes_to_scans.insert(
2492 this->m_graph->root,
2493 this->m_nodes_to_laser_scans2D.at(this->m_graph->root)));
2497 nodes_to_scans.insert(
2499 this->m_graph->nodeCount() - 1,
2500 this->m_nodes_to_laser_scans2D.at(
2501 this->m_graph->nodeCount() - 1)));
2507 for (
auto it = nodes_to_scans.begin(); it != nodes_to_scans.end(); ++it)
2512 "nodeID \"" << it->first <<
"\" has invalid laserScan");
2518 const auto& search = this->m_graph->nodes.find(it->first);
2519 if (search == this->m_graph->nodes.end())
2526 const auto curr_constraint =
constraint_t(search->second);
2533 m_partitioner.addMapFrame(sf, *pose3d);
2537 size_t curr_size = m_curr_partitions.size();
2538 m_last_partitions.resize(curr_size);
2539 for (
size_t i = 0; i < curr_size; i++)
2541 m_last_partitions[i] = m_curr_partitions[i];
2544 m_partitioner.updatePartitions(m_curr_partitions);
2547 this->m_time_logger.leave(
"updateMapPartitions");
2554 template <
class GRAPH_T>
2556 : laser_scans_color(0, 20, 255),
2557 keystroke_laser_scans(
"l"),
2558 has_read_config(false)
2565 template <
class GRAPH_T>
2570 template <
class GRAPH_T>
2572 std::ostream& out)
const
2576 out <<
"Use scan-matching constraints = "
2577 << (use_scan_matching ?
"TRUE" :
"FALSE") << std::endl;
2578 out <<
"Num. of previous nodes to check ICP against = "
2579 << prev_nodes_for_ICP << std::endl;
2580 out <<
"Visualize laser scans = "
2581 << (visualize_laser_scans ?
"TRUE" :
"FALSE") << std::endl;
2585 template <
class GRAPH_T>
2592 source.read_bool(section,
"use_scan_matching",
true,
false);
2593 prev_nodes_for_ICP =
source.read_int(
2595 "prev_nodes_for_ICP",
2597 visualize_laser_scans =
source.read_bool(
2598 "VisualizationParameters",
"visualize_laser_scans",
true,
false);
2600 has_read_config =
true;
2606 template <
class GRAPH_T>
2608 : keystroke_map_partitions(
"b"),
2609 balloon_elevation(3),
2610 balloon_radius(0.5),
2611 balloon_std_color(153, 0, 153),
2612 balloon_curr_color(62, 0, 80),
2613 connecting_lines_color(balloon_std_color),
2614 has_read_config(false)
2618 template <
class GRAPH_T>
2623 template <
class GRAPH_T>
2625 std::ostream& out)
const
2628 using namespace std;
2631 ss <<
"Min. node difference for loop closure = "
2632 << LC_min_nodeid_diff << endl;
2633 ss <<
"Remote NodeIDs to consider the potential loop closure = "
2634 << LC_min_remote_nodes << endl;
2635 ss <<
"Min EigenValues ratio for accepting a hypotheses set = "
2636 << LC_eigenvalues_ratio_thresh << endl;
2637 ss <<
"Check only current node's partition for loop closures = "
2638 << (LC_check_curr_partition_only ?
"TRUE" :
"FALSE") << endl;
2639 ss <<
"New registered nodes required for full partitioning = "
2640 << full_partition_per_nodes << endl;
2641 ss <<
"Visualize map partitions = "
2642 << (visualize_map_partitions ?
"TRUE" :
"FALSE") << endl;
2648 template <
class GRAPH_T>
2653 LC_min_nodeid_diff =
source.read_int(
2654 "GeneralConfiguration",
"LC_min_nodeid_diff", 30,
false);
2655 LC_min_remote_nodes =
2656 source.read_int(section,
"LC_min_remote_nodes", 3,
false);
2657 LC_eigenvalues_ratio_thresh =
2658 source.read_double(section,
"LC_eigenvalues_ratio_thresh", 2,
false);
2659 LC_check_curr_partition_only =
2660 source.read_bool(section,
"LC_check_curr_partition_only",
true,
false);
2661 full_partition_per_nodes =
2662 source.read_int(section,
"full_partition_per_nodes", 50,
false);
2663 visualize_map_partitions =
source.read_bool(
2664 "VisualizationParameters",
"visualize_map_partitions",
true,
false);
2666 has_read_config =
true;