38 #include <type_traits>      47 template <
class GRAPH_T>
    53     class MAPS_IMPLEMENTATION, 
class NODE_ANNOTATIONS, 
class EDGE_ANNOTATIONS>
    58     class MAPS_IMPLEMENTATION, 
class NODE_ANNOTATIONS, 
class EDGE_ANNOTATIONS>
   116     class MAPS_IMPLEMENTATION =
   131         CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>;
   151         return literal(
"mrpt::graphs::CNetworkOfPoses<") +
   164             CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS,
   170             return literal(
"global_pose_t<") +
   180         template <
typename ARG1>
   184         template <
typename ARG1, 
typename ARG2>
   194                 static_cast<const constraint_no_pdf_t>(*
this) ==
   195                     static_cast<const constraint_no_pdf_t>(other) &&
   196                 static_cast<const NODE_ANNOTATIONS>(*
this) ==
   197                     static_cast<const NODE_ANNOTATIONS>(other));
   201             return (!(*
this == other));
   205             std::ostream& o, 
const self_t& global_pose)
   207             o << global_pose.asString() << 
"| "   208               << global_pose.retAnnotsAsString();
   220     using global_poses_t = 
typename MAPS_IMPLEMENTATION::template map<
   290         const std::string& fileName, 
bool collapse_dup_edges = 
true)
   323             CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>;
   325             CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>;
   327         bool is_multirobot = 
false;
   328         std::unique_ptr<visualizer_t> viz;
   334             viz.reset(
new visualizer_multirobot_t(*
this));
   338             viz.reset(
new visualizer_t(*
this));
   340         viz->getAs3DObject(
object, viz_params);
   356         std::optional<std::reference_wrapper<std::map<TNodeID, size_t>>>
   357             topological_distances = std::nullopt)
   360             this, topological_distances ? &topological_distances.value().get()
   391                 this, it, ignoreCovariances);
   413         const std::set<TNodeID>& node_IDs, 
self_t* sub_graph,
   415         bool auto_expand_set = 
true)
 const   418         using namespace mrpt;
   427             "\nInvalid pointer to a CNetworkOfPoses instance is given. "   432         TNodeID root_node = root_node_in;
   436                 node_IDs.find(root_node) != node_IDs.end(),
   437                 "\nRoot_node does not exist in the given node_IDs set. "   443             node_IDs.size() >= 2,
   445                 "Very few nodes [%lu] for which to extract a subgraph. "   447                 static_cast<unsigned long>(node_IDs.size())));
   451         bool is_fully_connected_graph = 
true;
   452         std::set<TNodeID> node_IDs_real;  
   453         if (*node_IDs.rbegin() - *node_IDs.begin() + 1 == node_IDs.size())
   455             node_IDs_real = node_IDs;
   459             is_fully_connected_graph = 
false;
   463                 for (
TNodeID curr_node_ID = *node_IDs.begin();
   464                      curr_node_ID != *node_IDs.rbegin(); ++curr_node_ID)
   466                     node_IDs_real.insert(curr_node_ID);
   471                 node_IDs_real = node_IDs;
   476         for (
unsigned long node_IDs_it : node_IDs_real)
   479             typename global_poses_t::const_iterator own_it;
   480             for (own_it = 
nodes.begin(); own_it != 
nodes.end(); ++own_it)
   482                 if (node_IDs_it == own_it->first)
   488                 own_it != 
nodes.end(),
   490                     "NodeID [%lu] can't be found in the initial graph.",
   491                     static_cast<unsigned long>(node_IDs_it)));
   494             sub_graph->
nodes.insert(make_pair(node_IDs_it, curr_node));
   503             root_node = sub_graph->
nodes.begin()->first;
   505         sub_graph->
root = root_node;
   512             const TNodeID& from = e.first.first;
   513             const TNodeID& to = e.first.second;
   514             const typename BASE::edge_t& curr_edge = e.second;
   517             if (sub_graph->
nodes.find(from) != sub_graph->
nodes.end() &&
   518                 sub_graph->
nodes.find(to) != sub_graph->
nodes.end())
   524         if (!auto_expand_set && !is_fully_connected_graph)
   531                 std::set<TNodeID> root_neighbors;
   534                 if (root_neighbors.empty())
   537                     typename global_poses_t::iterator root_it =
   540                     if ((*root_it == *sub_graph->
nodes.rbegin()))
   543                         TNodeID next_to_root = (--root_it)->first;
   545                             sub_graph, next_to_root, sub_graph->
root);
   550                         TNodeID next_to_root = (++root_it)->first;
   553                             sub_graph, sub_graph->
root, next_to_root);
   560             bool dijkstra_runs_successfully = 
false;
   564             while (!dijkstra_runs_successfully)
   568                     dijkstra_t dijkstra(*sub_graph, sub_graph->
root);
   569                     dijkstra_runs_successfully = 
true;
   573                     dijkstra_runs_successfully = 
false;
   575                     set<TNodeID> unconnected_nodeIDs;
   588                     const TNodeID& island_highest =
   589                         *unconnected_nodeIDs.rbegin();
   590                     const TNodeID& island_lowest = *unconnected_nodeIDs.begin();
   598                     std::set<TNodeID> mainland;
   600                     for (
typename global_poses_t::const_iterator n_it =
   601                              sub_graph->
nodes.begin();
   602                          n_it != sub_graph->
nodes.end(); ++n_it)
   604                         bool is_there = 
false;
   607                         for (
unsigned long unconnected_nodeID :
   610                             if (n_it->first == unconnected_nodeID)
   619                             mainland.insert(n_it->first);
   623                     bool is_single_island =
   624                         (island_highest - island_lowest + 1 ==
   625                          unconnected_nodeIDs.size());
   627                     if (is_single_island)
   641                         const std::set<TNodeID>& island = unconnected_nodeIDs;
   643                             sub_graph, island, mainland);
   654                         std::vector<std::set<TNodeID>> vec_of_islands;
   655                         std::set<TNodeID> curr_island;
   656                         TNodeID prev_nodeID = *unconnected_nodeIDs.begin();
   659                         for (
auto it = ++unconnected_nodeIDs.begin();
   660                              *it < sub_graph->
root &&
   661                              it != unconnected_nodeIDs.end();
   664                             if (!(
absDiff(*it, prev_nodeID) == 1))
   666                                 vec_of_islands.push_back(curr_island);
   669                             curr_island.insert(*it);
   674                         vec_of_islands.push_back(curr_island);
   682                             sub_graph, vec_of_islands.back(), mainland);
   713         const bool hypots_from_other_to_self = 
true,
   714         std::map<TNodeID, TNodeID>* old_to_new_nodeID_mappings_out = 
nullptr)
   723         using nodes_cit_t = 
typename global_poses_t::const_iterator;
   725         const self_t& graph_from = (hypots_from_other_to_self ? other : *
this);
   726         const self_t& graph_to = (hypots_from_other_to_self ? *this : other);
   734         for (hypots_cit_t h_cit = common_hypots.begin();
   735              h_cit != common_hypots.end(); ++h_cit)
   738                 graph_from.
nodes.find(h_cit->from) != graph_from.
nodes.end(),
   739                 format(
"NodeID %lu is not found in (from) graph", h_cit->from));
   741                 graph_to.
nodes.find(h_cit->to) != graph_to.
nodes.end(),
   742                 format(
"NodeID %lu is not found in (to) graph", h_cit->to));
   747         for (nodes_cit_t n_cit = this->nodes.begin();
   748              n_cit != this->nodes.end(); ++n_cit)
   750             if (n_cit->first > max_nodeID)
   752                 max_nodeID = n_cit->first;
   755         TNodeID renum_start = max_nodeID + 1;
   756         size_t renum_counter = 0;
   761         std::map<TNodeID, TNodeID>* old_to_new_nodeID_mappings;
   767         std::map<TNodeID, TNodeID> mappings_tmp;
   770         if (old_to_new_nodeID_mappings_out)
   772             old_to_new_nodeID_mappings = old_to_new_nodeID_mappings_out;
   776             old_to_new_nodeID_mappings = &mappings_tmp;
   778         old_to_new_nodeID_mappings->clear();
   783         for (nodes_cit_t n_cit = other.
nodes.begin();
   784              n_cit != other.
nodes.end(); ++n_cit)
   786             TNodeID new_nodeID = renum_start + renum_counter++;
   787             old_to_new_nodeID_mappings->insert(
   788                 make_pair(n_cit->first, new_nodeID));
   789             this->nodes.insert(make_pair(new_nodeID, n_cit->second));
   797         for (hypots_cit_t h_cit = common_hypots.begin();
   798              h_cit != common_hypots.end(); ++h_cit)
   801             if (hypots_from_other_to_self)
   803                 from = old_to_new_nodeID_mappings->at(h_cit->from);
   809                 to = old_to_new_nodeID_mappings->at(h_cit->to);
   820              g_cit != other.
end(); ++g_cit)
   823                 old_to_new_nodeID_mappings->at(g_cit->first.first);
   825                 old_to_new_nodeID_mappings->at(g_cit->first.second);
   826             this->
insertEdge(new_from, new_to, g_cit->second);
   848         self_t* sub_graph, 
const std::set<TNodeID>& groupA,
   849         const std::set<TNodeID>& groupB)
   855             "\nInvalid pointer to a CNetworkOfPoses instance is given. "   857         ASSERTMSG_(!groupA.empty(), 
"\ngroupA is empty.");
   858         ASSERTMSG_(!groupB.empty(), 
"\ngroupB is empty.");
   862             *groupA.rend() < *groupB.rbegin() ||
   863                 *groupA.rbegin() > *groupB.rend(),
   864             "Groups A, B contain overlapping nodeIDs");
   868         const std::set<TNodeID>& low_nodeIDs =
   869             *groupA.rbegin() < *groupB.rbegin() ? groupA : groupB;
   870         const std::set<TNodeID>& high_nodeIDs =
   871             *groupA.rbegin() > *groupB.rbegin() ? groupA : groupB;
   874         const TNodeID& from_nodeID = *low_nodeIDs.rbegin();
   875         const TNodeID& to_nodeID = *high_nodeIDs.begin();
   888         bool ignoreCovariances = 
true)
 const   891             this, itEdge, ignoreCovariances);
   904         bool ignoreCovariances = 
true)
 const   911                 "Request for edge %u->%u that doesn't exist in graph.",
   912                 static_cast<unsigned int>(from_id),
   913                 static_cast<unsigned int>(to_id)));
   944             graph, 
"Invalid pointer to the graph instance was provided.");
   948         const typename BASE::edge_t& virt_edge(p_to - p_from);
   956     class CPOSE, 
class MAPS_IMPLEMENTATION, 
class NODE_ANNOTATIONS,
   957     class EDGE_ANNOTATIONS>
   961         CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>& obj)
   964         CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>;
   971     class CPOSE, 
class MAPS_IMPLEMENTATION, 
class NODE_ANNOTATIONS,
   972     class EDGE_ANNOTATIONS>
   976         CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>& obj)
   979         CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS>;
 
static constexpr auto getClassName()
 
A directed graph with the argument of the template specifying the type of the annotations in the edge...
 
static void save_graph_of_poses_to_binary_file(const graph_t *g, mrpt::serialization::CArchive &out)
 
mrpt::serialization::CArchive & operator>>(mrpt::serialization::CArchive &in, CNetworkOfPoses< CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS > &obj)
Binary serialization (read) operator "stream >> graph". 
 
double getGlobalSquareError(bool ignoreCovariances=true) const
Evaluates the graph total square error (ignoreCovariances=true) or chi2 (ignoreCovariances=false) fro...
 
typename MAPS_IMPLEMENTATION::template map< mrpt::graphs::TNodeID, CPOSE > global_poses_pdf_t
A map from pose IDs to their global coordinate estimates, with uncertainty. 
 
void clear()
Empty all edges, nodes and set root to ID 0. 
 
std::string std::string format(std::string_view fmt, ARGS &&... args)
 
mrpt::graphs::TNodeID root
The ID of the node that is the origin of coordinates, used as reference by all coordinates in nodes...
 
void dijkstra_nodes_estimate(std::optional< std::reference_wrapper< std::map< TNodeID, size_t >>> topological_distances=std::nullopt)
Spanning tree computation of a simple estimation of the global coordinates of each node just from the...
 
size_t collapseDuplicatedEdges()
Look for duplicated edges (even in opposite directions) between all pairs of nodes and fuse them...
 
edges_map_t edges
The public member with the directed edges in the graph. 
 
EDGE_ANNOTATIONS edge_annotations_t
The extra annotations in edges, apart from a constraint_t. 
 
void saveToTextFile(const std::string &fileName) const
Saves to a text file in the format used by TORO, HoG-man, G2O. 
 
friend std::ostream & operator<<(std::ostream &o, const self_t &global_pose)
 
Abstract graph and tree data structures, plus generic graph algorithms. 
 
static constexpr auto getClassName()
 
void getNeighborsOf(const TNodeID nodeID, std::set< TNodeID > &neighborIDs) const
Return the list of all neighbors of "nodeID", by creating a list of their node IDs. 
 
void loadFromTextFile(const std::string &fileName, bool collapse_dup_edges=true)
Loads from a text file in the format used by TORO & HoG-man (more on the format here) Recognized line...
 
CPOSE constraint_t
The type of PDF poses in the contraints (edges) (=CPOSE template argument) 
 
double chi2() const
Returns the total chi-squared error of the graph. 
 
void getUnconnectedNodeIDs(std::set< mrpt::graphs::TNodeID > *set_nodeIDs) const
Fill set with the nodeIDs Dijkstra algorithm could not reach starting from the root node...
 
static void read_graph_of_poses_from_binary_file(graph_t *g, mrpt::serialization::CArchive &in)
 
double getEdgeSquareError(const typename BASE::edges_map_t::const_iterator &itEdge, bool ignoreCovariances=true) const
Computes the square error of one pose constraints (edge) with respect to the global poses in nodes If...
 
void clearEdges()
Erase all edges. 
 
double getEdgeSquareError(const mrpt::graphs::TNodeID from_id, const mrpt::graphs::TNodeID to_id, bool ignoreCovariances=true) const
Computes the square error of one pose constraints (edge) with respect to the global poses in nodes If...
 
Base class for C*Visualizer classes. 
 
A directed graph of pose constraints, with edges being the relative poses between pairs of nodes iden...
 
#define ASSERT_(f)
Defines an assertion mechanism. 
 
Internal functions for MRPT. 
 
static void save_graph_of_poses_to_ostream(const graph_t *g, std::ostream &f)
 
This base provides a set of functions for maths stuff. 
 
typename CPOSE::type_value constraint_no_pdf_t
The type of edges or their means if they are PDFs (that is, a simple "edge" value) ...
 
global_pose_t(const ARG1 &a1)
 
The type of each global pose in nodes: an extension of the constraint_no_pdf_t pose with any optional...
 
void insertEdge(TNodeID from_nodeID, TNodeID to_nodeID, const edge_t &edge_value)
Insert an edge (from -> to) with the given edge value. 
 
void getAs3DObject(mrpt::opengl::CSetOfObjects::Ptr object, const mrpt::system::TParametersDouble &viz_params) const
Return 3D Visual Representation of the edges and nodes in the network of poses. 
 
typename MAPS_IMPLEMENTATION::template map< mrpt::graphs::TNodeID, global_pose_t > global_poses_t
A map from pose IDs to their global coordinate estimates, without uncertainty (the "most-likely value...
 
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism. 
 
void mergeGraph(const self_t &other, const typename std::vector< detail::THypothesis< self_t >> &common_hypots, const bool hypots_from_other_to_self=true, std::map< TNodeID, TNodeID > *old_to_new_nodeID_mappings_out=nullptr)
Integrate given graph into own graph using the list of provided common THypotheses. 
 
bool operator!=(const global_pose_t &other) const
 
static void connectGraphPartitions(self_t *sub_graph, const std::set< TNodeID > &groupA, const std::set< TNodeID > &groupB)
Add an edge between the last node of the group with the lower nodeIDs and the first node of the highe...
 
void writeAsText(std::ostream &o) const
Writes as text in the format used by TORO, HoG-man, G2O. 
 
static void load_graph_of_poses_from_text_stream(graph_t *g, std::istream &f, const std::string &fil=std::string("(none)"))
 
size_t nodeCount() const
Return number of nodes in the list nodes of global coordinates (may be different that all nodes appea...
 
Traits for using a std::map<> (sparse representation) 
 
static size_t graph_of_poses_collapse_dup_edges(graph_t *g)
 
static void save_graph_of_poses_to_text_file(const graph_t *g, const std::string &fil)
 
void extractSubGraph(const std::set< TNodeID > &node_IDs, self_t *sub_graph, const TNodeID root_node_in=INVALID_NODEID, bool auto_expand_set=true) const
Find the edges between the nodes in the node_IDs set and fill given graph pointer accordingly...
 
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries. 
 
A Probability Density function (PDF) of a 2D pose  as a Gaussian with a mean and the inverse of the c...
 
Traits for using a mrpt::containers::map_as_vector<> (dense, fastest representation) ...
 
Virtual base class for "archives": classes abstracting I/O streams. 
 
global_pose_t()=default
Potential class constructors. 
 
mrpt::vision::TStereoCalibResults out
 
static double graph_edge_sqerror(const graph_t *g, const typename mrpt::graphs::CDirectedGraph< typename graph_t::constraint_t >::edges_map_t::const_iterator &itEdge, bool ignoreCovariances)
 
a helper struct with static template functions 
 
Declares a class that represents a Probability Density function (PDF) of a 3D pose  as a Gaussian des...
 
void readAsText(std::istream &i)
Reads as text in the format used by TORO, HoG-man, G2O. 
 
typename edges_map_t::const_iterator const_iterator
 
uint64_t TNodeID
A generic numeric type for unique IDs of nodes or entities. 
 
static void graph_of_poses_dijkstra_init(graph_t *g, std::map< TNodeID, size_t > *topological_distances=nullptr)
 
static void load_graph_of_poses_from_text_file(graph_t *g, const std::string &fil)
 
MAPS_IMPLEMENTATION maps_implementation_t
The type of map's implementation (=MAPS_IMPLEMENTATION template argument) 
 
global_poses_t nodes
The nodes (vertices) of the graph, with their estimated "global" (with respect to root) position...
 
global_pose_t(const ARG1 &a1, const ARG2 &a2)
 
The Dijkstra algorithm for finding the shortest path between a given source node in a (weighted) dire...
 
T absDiff(const T &lhs, const T &rhs)
Absolute difference between two numbers. 
 
bool edges_store_inverse_poses
False (default) if an edge i->j stores the normal relative pose of j as seen from i:  True if an edge...
 
bool operator==(const global_pose_t &other) const
 
An edge hypothesis between two nodeIDs. 
 
Struct to be used as the NODE_ANNOTATIONS template argument in CNetworkOfPoses class instances for us...
 
Struct to be used as the NODE_ANNOTATIONS template argument in CNetworkOfPoses class instances for us...
 
Wrapper class that provides visualization of a network of poses that have been registered by many gra...
 
Custom exception class that passes information in case an unconnected graph is passed to a Dijkstra i...
 
NODE_ANNOTATIONS node_annotations_t
The extra annotations in nodes, apart from a constraint_no_pdf_t. 
 
typename CNetworkOfPoses< CPOSE, MAPS_IMPLEMENTATION, NODE_ANNOTATIONS, EDGE_ANNOTATIONS >::global_pose_t self_t
 
#define MRPT_DECLARE_TTYPENAME(_TYPE)
 
static void addVirtualEdge(self_t *graph, const TNodeID &from, const TNodeID &to)
Add a virtual edge between two nodes in the given graph.