37 template <
class POSE_PDF>
75 template <
class graph_t>
82 f <<
"VERTEX_SE2 " <<
id <<
" " <<
p.x() <<
" " <<
p.y() <<
" " 91 f <<
"VERTEX3 " <<
id <<
" " <<
p.x() <<
" " <<
p.y() <<
" " <<
p.z()
92 <<
" " <<
p.roll() <<
" " <<
p.pitch() <<
" " <<
p.yaw();
101 f <<
"EDGE_SE2 " << edgeIDs.first <<
" " << edgeIDs.second <<
" " 117 f <<
"EDGE3 " << edgeIDs.first <<
" " << edgeIDs.second <<
" " 138 write_EDGE_line(edgeIDs,
p, f);
146 write_EDGE_line(edgeIDs,
p, f);
154 p.cov_inv.setIdentity();
155 write_EDGE_line(edgeIDs,
p, f);
163 p.cov_inv.setIdentity();
164 write_EDGE_line(edgeIDs,
p, f);
168 const graph_t*
g, std::ostream& f)
171 for (
const auto&
n :
g->nodes)
173 write_VERTEX_line(
n.first,
n.second, f);
175 const auto sAnnot =
n.second.retAnnotsAsString();
176 if (!sAnnot.empty()) f <<
" | " << sAnnot;
179 if (
n.first ==
g->root) f <<
"FIX " <<
n.first << endl;
183 for (
const auto& e : *
g)
184 if (e.first.first != e.first.second)
185 write_EDGE_line(e.first, e.second, f);
199 "Error opening file '%s' for writing", fil.c_str());
200 save_graph_of_poses_to_ostream(
g, f);
217 out <<
g->nodes <<
g->edges <<
g->root;
230 in >> sStoredClassName;
235 in >> stored_version;
238 switch (stored_version)
241 in >>
g->nodes >>
g->edges >>
g->root;
252 graph_t*
g, std::istream& f,
258 using CPOSE =
typename graph_t::constraint_t;
260 set<string> alreadyWarnedUnknowns;
269 const bool graph_is_3D = CPOSE::is_3D();
278 map<TNodeID, TNodeID> lstEquivs;
287 const string lin =
s.str();
290 if (!(
s >> key) || key.empty())
292 "Line %u: Can't read string for entry type in: '%s'",
293 lineNum, lin.c_str()));
299 if (!(
s >> id1 >> id2))
301 "Line %u: Can't read id1 & id2 in EQUIV line: '%s'",
302 lineNum, lin.c_str()));
303 lstEquivs[std::max(id1, id2)] =
std::min(id1, id2);
316 const string lin =
s.str();
332 if (!(
s >> key) || key.empty())
334 "Line %u: Can't read string for entry type in: '%s'",
335 lineNum, lin.c_str()));
342 if (!(
s >>
id >> p2D.
x >> p2D.
y >> p2D.
phi))
344 "Line %u: Error parsing VERTEX2 line: '%s'", lineNum,
348 if (
g->nodes.find(
id) !=
g->nodes.end())
350 "Line %u: Error, duplicated verted ID %u in line: " 352 lineNum, static_cast<unsigned int>(
id), lin.c_str()));
356 const auto itEq = lstEquivs.find(
id);
357 if (itEq != lstEquivs.end())
id = itEq->second;
361 if (
g->nodes.find(
id) ==
g->nodes.end())
364 CPOSE>::constraint_t::type_value;
370 else if (
strCmpI(key,
"VERTEX3"))
374 "Line %u: Try to load VERTEX3 into a 2D graph: " 376 lineNum, lin.c_str()));
383 if (!(
s >>
id >> p3D.
x >> p3D.
y >> p3D.
z >> p3D.
roll >>
386 "Line %u: Error parsing VERTEX3 line: '%s'", lineNum,
390 if (
g->nodes.find(
id) !=
g->nodes.end())
392 "Line %u: Error, duplicated verted ID %u in line: " 394 lineNum, static_cast<unsigned int>(
id), lin.c_str()));
398 const auto itEq = lstEquivs.find(
id);
399 if (itEq != lstEquivs.end())
id = itEq->second;
403 if (
g->nodes.find(
id) ==
g->nodes.end())
410 else if (
strCmpI(key,
"VERTEX_SE3:QUAT"))
414 "Line %u: Try to load VERTEX_SE3:QUAT into a 2D " 416 lineNum, lin.c_str()));
421 if (!(
s >>
id >> p3D.
x >> p3D.
y >> p3D.
z >> p3D.
qx >> p3D.
qy >>
424 "Line %u: Error parsing VERTEX_SE3:QUAT line: '%s'",
425 lineNum, lin.c_str()));
428 if (
g->nodes.find(
id) !=
g->nodes.end())
430 "Line %u: Error, duplicated verted ID %u in line: " 432 lineNum, static_cast<unsigned int>(
id), lin.c_str()));
436 const auto itEq = lstEquivs.find(
id);
437 if (itEq != lstEquivs.end())
id = itEq->second;
441 if (
g->nodes.find(
id) ==
g->nodes.end())
460 if (!(
s >> from_id >> to_id))
462 "Line %u: Error parsing EDGE2 line: '%s'", lineNum,
467 const auto itEq = lstEquivs.find(to_id);
468 if (itEq != lstEquivs.end()) to_id = itEq->second;
471 const auto itEq = lstEquivs.find(from_id);
472 if (itEq != lstEquivs.end()) from_id = itEq->second;
475 if (from_id != to_id)
480 if (!(
s >> Ap_mean.
x >> Ap_mean.
y >> Ap_mean.
phi >>
481 Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
482 Ap_cov_inv(0, 2) >> Ap_cov_inv(1, 1) >>
483 Ap_cov_inv(1, 2) >> Ap_cov_inv(2, 2)))
485 "Line %u: Error parsing EDGE2 line: '%s'", lineNum,
489 Ap_cov_inv(1, 0) = Ap_cov_inv(0, 1);
490 Ap_cov_inv(2, 0) = Ap_cov_inv(0, 2);
491 Ap_cov_inv(2, 1) = Ap_cov_inv(1, 2);
498 g->insertEdge(from_id, to_id, newEdge);
501 else if (
strCmpI(key,
"EDGE3"))
505 "Line %u: Try to load EDGE3 into a 2D graph: '%s'",
506 lineNum, lin.c_str()));
511 if (!(
s >> from_id >> to_id))
513 "Line %u: Error parsing EDGE3 line: '%s'", lineNum,
518 const auto itEq = lstEquivs.find(to_id);
519 if (itEq != lstEquivs.end()) to_id = itEq->second;
522 const auto itEq = lstEquivs.find(from_id);
523 if (itEq != lstEquivs.end()) from_id = itEq->second;
526 if (from_id != to_id)
533 if (!(
s >> Ap_mean.
x >> Ap_mean.
y >> Ap_mean.
z >>
536 "Line %u: Error parsing EDGE3 line: '%s'", lineNum,
541 if (!(
s >> Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
542 Ap_cov_inv(0, 2) >> Ap_cov_inv(0, 5) >>
543 Ap_cov_inv(0, 4) >> Ap_cov_inv(0, 3) >>
544 Ap_cov_inv(1, 1) >> Ap_cov_inv(1, 2) >>
545 Ap_cov_inv(1, 5) >> Ap_cov_inv(1, 4) >>
546 Ap_cov_inv(1, 3) >> Ap_cov_inv(2, 2) >>
547 Ap_cov_inv(2, 5) >> Ap_cov_inv(2, 4) >>
548 Ap_cov_inv(2, 3) >> Ap_cov_inv(5, 5) >>
549 Ap_cov_inv(5, 4) >> Ap_cov_inv(5, 3) >>
550 Ap_cov_inv(4, 4) >> Ap_cov_inv(4, 3) >>
556 if (alreadyWarnedUnknowns.find(
"MISSING_3D") ==
557 alreadyWarnedUnknowns.end())
559 alreadyWarnedUnknowns.insert(
"MISSING_3D");
560 cerr <<
"[CNetworkOfPoses::loadFromTextFile] " 561 << fil <<
":" << lineNum
562 <<
": Warning: Information matrix missing, " 569 for (
size_t r = 1;
r < 6;
r++)
570 for (
size_t c = 0;
c <
r;
c++)
571 Ap_cov_inv(
r,
c) = Ap_cov_inv(
c,
r);
579 g->insertEdge(from_id, to_id, newEdge);
582 else if (
strCmpI(key,
"EDGE_SE3:QUAT"))
586 "Line %u: Try to load EDGE3 into a 2D graph: '%s'",
587 lineNum, lin.c_str()));
594 if (!(
s >> from_id >> to_id))
596 "Line %u: Error parsing EDGE_SE3:QUAT line: '%s'",
597 lineNum, lin.c_str()));
601 const auto itEq = lstEquivs.find(to_id);
602 if (itEq != lstEquivs.end()) to_id = itEq->second;
605 const auto itEq = lstEquivs.find(from_id);
606 if (itEq != lstEquivs.end()) from_id = itEq->second;
609 if (from_id != to_id)
614 if (!(
s >> Ap_mean.
x >> Ap_mean.
y >> Ap_mean.
z >>
615 Ap_mean.
qx >> Ap_mean.
qy >> Ap_mean.
qz >> Ap_mean.
qr))
617 "Line %u: Error parsing EDGE_SE3:QUAT line: " 619 lineNum, lin.c_str()));
623 if (!(
s >> Ap_cov_inv(0, 0) >> Ap_cov_inv(0, 1) >>
624 Ap_cov_inv(0, 2) >> Ap_cov_inv(0, 5) >>
625 Ap_cov_inv(0, 4) >> Ap_cov_inv(0, 3) >>
626 Ap_cov_inv(1, 1) >> Ap_cov_inv(1, 2) >>
627 Ap_cov_inv(1, 5) >> Ap_cov_inv(1, 4) >>
628 Ap_cov_inv(1, 3) >> Ap_cov_inv(2, 2) >>
629 Ap_cov_inv(2, 5) >> Ap_cov_inv(2, 4) >>
630 Ap_cov_inv(2, 3) >> Ap_cov_inv(5, 5) >>
631 Ap_cov_inv(5, 4) >> Ap_cov_inv(5, 3) >>
632 Ap_cov_inv(4, 4) >> Ap_cov_inv(4, 3) >>
638 if (alreadyWarnedUnknowns.find(
"MISSING_3D") ==
639 alreadyWarnedUnknowns.end())
641 alreadyWarnedUnknowns.insert(
"MISSING_3D");
642 cerr <<
"[CNetworkOfPoses::loadFromTextFile] " 643 << fil <<
":" << lineNum
644 <<
": Warning: Information matrix missing, " 651 for (
size_t r = 1;
r < 6;
r++)
652 for (
size_t c = 0;
c <
r;
c++)
653 Ap_cov_inv(
r,
c) = Ap_cov_inv(
c,
r);
662 g->insertEdge(from_id, to_id, newEdge);
665 else if (
strCmpI(key,
"EQUIV"))
674 "Line %u: Can't read id in FIX line: '%s'", lineNum,
680 if (alreadyWarnedUnknowns.find(key) ==
681 alreadyWarnedUnknowns.end())
683 alreadyWarnedUnknowns.insert(key);
684 cerr <<
"[CNetworkOfPoses::loadFromTextFile] " << fil <<
":" 685 << lineNum <<
": Warning: unknown entry type: " << key
696 std::ifstream f(fil);
699 "Error opening file '%s' for reading", fil.c_str());
700 load_graph_of_poses_from_text_stream(
g, f, fil);
716 using TEdgeIterator =
typename graph_t::edges_map_t::iterator;
720 using TListAllEdges =
721 map<pair<TNodeID, TNodeID>,
722 vector<TEdgeIterator>>;
725 TListAllEdges lstAllEdges;
728 for (
auto itEd =
g->edges.begin(); itEd !=
g->edges.end(); ++itEd)
731 const pair<TNodeID, TNodeID> arc_id = make_pair(
732 std::min(itEd->first.first, itEd->first.second),
733 std::max(itEd->first.first, itEd->first.second));
735 vector<TEdgeIterator>& lstEdges = lstAllEdges[arc_id];
737 lstEdges.push_back(itEd);
742 for (
auto it = lstAllEdges.begin(); it != lstAllEdges.end(); ++it)
744 const size_t N = it->second.size();
745 for (
size_t i = 1; i < N; i++)
746 g->edges.erase(it->second[i]);
748 if (N >= 2) nRemoved += N - 1;
765 graph_t*
g, std::map<TNodeID, size_t>* topological_distances =
nullptr)
773 using constraint_t =
typename graph_t::constraint_t;
776 dijkstra_t dijkstra(*
g,
g->root);
780 typename dijkstra_t::tree_graph_t treeView;
781 dijkstra.getTreeGraph(treeView);
784 struct VisitorComputePoses :
public dijkstra_t::tree_graph_t::Visitor
787 std::map<TNodeID, size_t>* m_topo_dists{
nullptr};
789 VisitorComputePoses(graph_t*
g) : m_g(
g) {}
792 const typename dijkstra_t::tree_graph_t::Visitor::tree_t::
793 TEdgeInfo& edge_to_child,
794 const size_t depth_level)
override 797 const TNodeID child_id = edge_to_child.id;
800 if (m_topo_dists) (*m_topo_dists)[child_id] = depth_level;
806 if ((!edge_to_child.reverse &&
807 !m_g->edges_store_inverse_poses) ||
808 (edge_to_child.reverse && m_g->edges_store_inverse_poses))
810 m_g->nodes[child_id].composeFrom(
811 m_g->nodes[parent_id],
812 edge_to_child.data->getPoseMean());
816 m_g->nodes[child_id].composeFrom(
817 m_g->nodes[parent_id],
818 -edge_to_child.data->getPoseMean());
828 bool empty_node_annots =
g->nodes.begin()->second.is_node_annots_empty;
829 map<const TNodeID, TNodeAnnotations*> nodeID_to_annots;
830 if (!empty_node_annots)
832 for (
auto poses_cit =
g->nodes.begin(); poses_cit !=
g->nodes.end();
835 nodeID_to_annots.insert(make_pair(
836 poses_cit->first, poses_cit->second.getCopyOfAnnots()));
842 typename constraint_t::type_value();
845 VisitorComputePoses myVisitor(
g);
846 myVisitor.m_topo_dists = topological_distances;
847 treeView.visitBreadthFirst(treeView.root, myVisitor);
850 if (topological_distances) (*topological_distances)[
g->root] = 0;
853 if (!empty_node_annots)
855 for (
auto poses_cit =
g->nodes.begin(); poses_cit !=
g->nodes.end();
859 nodeID_to_annots.at(poses_cit->first);
860 bool res = poses_cit->second.setAnnots(*node_annots);
864 node_annots =
nullptr;
869 "Setting annotations for nodeID \"%lu\" was " 871 static_cast<unsigned long>(poses_cit->first)));
952 typename graph_t::constraint_t>::edges_map_t::const_iterator&
954 bool ignoreCovariances)
959 const TNodeID from_id = itEdge->first.first;
960 const TNodeID to_id = itEdge->first.second;
963 auto itPoseFrom =
g->nodes.find(from_id);
964 auto itPoseTo =
g->nodes.find(to_id);
966 itPoseFrom !=
g->nodes.end(),
968 "Node %u doesn't have a global pose in 'nodes'.",
969 static_cast<unsigned int>(from_id)));
971 itPoseTo !=
g->nodes.end(),
973 "Node %u doesn't have a global pose in 'nodes'.",
974 static_cast<unsigned int>(to_id)));
977 using constraint_t =
typename graph_t::constraint_t;
979 const typename constraint_t::type_value& from_mean = itPoseFrom->second;
980 const typename constraint_t::type_value& to_mean = itPoseTo->second;
983 const constraint_t& edge_delta_pose = itEdge->second;
984 const typename constraint_t::type_value& edge_delta_pose_mean =
985 edge_delta_pose.getPoseMean();
987 if (ignoreCovariances)
990 typename constraint_t::type_value from_plus_delta(
992 from_plus_delta.composeFrom(from_mean, edge_delta_pose_mean);
995 return auxEuclid2Dist(from_plus_delta, to_mean);
1001 constraint_t from_plus_delta = edge_delta_pose;
1002 from_plus_delta.changeCoordinatesReference(from_mean);
1010 constraint_t::type_value::static_size>
1012 for (
size_t i = 0; i < constraint_t::type_value::static_size; i++)
1013 err[i] = from_plus_delta.getPoseMean()[i] - to_mean[i];
1016 return auxMaha2Dist(err, from_plus_delta);
A directed graph with the argument of the template specifying the type of the annotations in the edge...
static double auxMaha2Dist(VEC &err, const CPosePDFGaussian &p)
static void save_graph_of_poses_to_binary_file(const graph_t *g, mrpt::serialization::CArchive &out)
MAT_C::Scalar multiply_HtCH_scalar(const VECTOR_H &H, const MAT_C &C)
r (a scalar) = H * C * H^t (with a column vector H and a symmetric matrix C)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPosePDFGaussianInf &edge, std::ostream &f)
#define THROW_EXCEPTION(msg)
Abstract class from which NodeAnnotations related classes can be implemented.
double roll
Roll coordinate (rotation angle over X coordinate).
static double auxMaha2Dist(VEC &err, const mrpt::poses::CPose2D &p)
Abstract graph and tree data structures, plus generic graph algorithms.
mrpt::math::CMatrixDouble66 cov_inv
The inverse of the 6x6 covariance matrix.
A class for parsing text files, returning each non-empty and non-comment line, along its line number...
double pitch() const
Get the PITCH angle (in radians)
double yaw() const
Get the YAW angle (in radians)
This file implements miscelaneous matrix and matrix/vector operations, and internal functions in mrpt...
MAT_C::Scalar multiply_HCHt_scalar(const VECTOR_H &H, const MAT_C &C)
r (a scalar) = H^t * C * H (with a row vector H and a symmetric matrix C)
double yaw
Yaw coordinate (rotation angle over Z axis).
static double auxMaha2Dist(VEC &err, const CPosePDFGaussianInf &p)
static void read_graph_of_poses_from_binary_file(graph_t *g, mrpt::serialization::CArchive &in)
static double auxEuclid2Dist(const mrpt::poses::CPose2D &p1, const mrpt::poses::CPose2D &p2)
static double auxEuclid2Dist(const mrpt::poses::CPose3D &p1, const mrpt::poses::CPose3D &p2)
A directed graph of pose constraints, with edges being the relative poses between pairs of nodes iden...
static void copyFrom2D(CPose2D &p, const CPosePDFGaussianInf &pdf)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPose3DPDFGaussian &edge, std::ostream &f)
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
T square(const T x)
Inline function for the square of a number.
void rewind()
Reset the read pointer to the beginning of the file.
size_t getCurrentLineNumber() const
Return the line number of the last line returned with getNextLine.
void wrapToPiInPlace(T &a)
Modifies the given angle to translate it into the ]-pi,pi] range.
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.
Declares a class that represents a Probability Density function (PDF) of a 2D pose ...
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPosePDFGaussian &edge, std::ostream &f)
#define ASSERT_EQUAL_(__A, __B)
Assert comparing two values, reporting their actual values upon failure.
static void copyFrom2D(POSE_PDF &p, const CPosePDFGaussianInf &pdf)
static void copyFrom2D(CPose3D &p, const CPosePDFGaussianInf &pdf)
double x
Translation in x,y,z.
static double auxMaha2Dist(VEC &err, const CPose3DPDFGaussian &p)
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism.
bool getNextLine(std::string &out_str)
Reads from the file and return the next (non-comment) line, as a std::string.
CPose3D mean
The mean value.
double x() const
Common members of all points & poses classes.
A class used to store a 3D pose as a translation (x,y,z) and a quaternion (qr,qx,qy,qz).
GLsizei const GLchar ** string
T wrapToPi(T a)
Modifies the given angle to translate it into the ]-pi,pi] range.
double qr
Unit quaternion part, qr,qx,qy,qz.
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
double roll() const
Get the ROLL angle (in radians)
static void load_graph_of_poses_from_text_stream(graph_t *g, std::istream &f, const std::string &fil=std::string("(none)"))
double pitch
Pitch coordinate (rotation angle over Y axis).
Lightweight 3D pose (three spatial coordinates, plus a quaternion ).
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)
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...
Virtual base class for "archives": classes abstracting I/O streams.
GLdouble GLdouble GLdouble r
CPose2D mean
The mean value.
static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose2D &p, std::ostream &f)
A class used to store a 2D pose, including the 2D coordinate point and a heading (phi) angle...
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
This file implements matrix/vector text and binary serialization.
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)
const double & phi() const
Get the phi angle of the 2D pose (in radians)
static void copyFrom3D(CPose3D &p, const CPose3DPDFGaussianInf &pdf)
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const mrpt::poses::CPose3D &edge, std::ostream &f)
Lightweight 3D pose (three spatial coordinates, plus three angular coordinates).
Declares a class that represents a Probability Density function (PDF) of a 3D pose as a Gaussian des...
uint64_t TNodeID
A generic numeric type for unique IDs of nodes or entities.
Declares a class that represents a Probability Density function (PDF) of a 3D pose ...
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)
std::pair< TNodeID, TNodeID > TPairNodeIDs
A pair of node IDs.
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const mrpt::poses::CPose2D &edge, std::ostream &f)
global_poses_t nodes
The nodes (vertices) of the graph, with their estimated "global" (with respect to root) position...
static double auxMaha2Dist(VEC &err, const CPose3DPDFGaussianInf &p)
The Dijkstra algorithm for finding the shortest path between a given source node in a (weighted) dire...
static void write_EDGE_line(const TPairNodeIDs &edgeIDs, const CPose3DPDFGaussianInf &edge, std::ostream &f)
static void copyFrom3D(CPose2D &p, const CPose3DPDFGaussianInf &pdf)
static void copyFrom3D(POSE_PDF &p, const CPose3DPDFGaussianInf &pdf)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
bool strCmpI(const std::string &s1, const std::string &s2)
Return true if the two strings are equal (case insensitive)
unsigned __int32 uint32_t
static void write_VERTEX_line(const TNodeID id, const mrpt::poses::CPose3D &p, std::ostream &f)
double phi
Orientation (rads)
mrpt::math::CMatrixDouble33 cov_inv
The inverse of the 3x3 covariance matrix (the "information" matrix)
static double auxMaha2Dist(VEC &err, const mrpt::poses::CPose3D &p)
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.