00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifndef CONSTRAINED_POSE_NETWORK_IMPL_H
00029 #define CONSTRAINED_POSE_NETWORK_IMPL_H
00030
00031 #include <mrpt/graphs/dijkstra.h>
00032 #include <mrpt/utils/CTextFileLinesParser.h>
00033
00034
00035 namespace mrpt
00036 {
00037 namespace graphs
00038 {
00039 namespace detail
00040 {
00041 using namespace std;
00042 using namespace mrpt;
00043 using namespace mrpt::utils;
00044 using namespace mrpt::poses;
00045 using namespace mrpt::graphs;
00046
00047 template <class POSE_PDF> struct TPosePDFHelper
00048 {
00049 static inline void copyFrom2D(POSE_PDF &p, const CPosePDFGaussianInf &pdf ) { p.copyFrom( pdf ); }
00050 static inline void copyFrom3D(POSE_PDF &p, const CPose3DPDFGaussianInf &pdf ) { p.copyFrom( pdf ); }
00051 };
00052 template <> struct TPosePDFHelper<CPose2D>
00053 {
00054 static inline void copyFrom2D(CPose2D &p, const CPosePDFGaussianInf &pdf ) { p = pdf.mean; }
00055 static inline void copyFrom3D(CPose2D &p, const CPose3DPDFGaussianInf &pdf ) { p = CPose2D(pdf.mean); }
00056 };
00057 template <> struct TPosePDFHelper<CPose3D>
00058 {
00059 static inline void copyFrom2D(CPose3D &p, const CPosePDFGaussianInf &pdf ) { p = pdf.mean; }
00060 static inline void copyFrom3D(CPose3D &p, const CPose3DPDFGaussianInf &pdf ) { p = pdf.mean; }
00061 };
00062
00063
00064 template <class graph_t>
00065 struct graph_ops
00066 {
00067 static void write_VERTEX_line(const TNodeID id, const CPose2D &p, std::ofstream &f)
00068 {
00069
00070 f << "VERTEX2 " << id << format(" %.04f %.04f %.04f\n",p.x(),p.y(),p.phi() );
00071 }
00072 static void write_VERTEX_line(const TNodeID id, const CPose3D &p, std::ofstream &f)
00073 {
00074
00075
00076 f << "VERTEX3 " << id << format(" %.04f %.04f %.04f %.04f %.04f %.04f\n",p.x(),p.y(),p.z(),p.roll(),p.pitch(),p.yaw() );
00077 }
00078
00079
00080 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPosePDFGaussianInf & edge, std::ofstream &f)
00081 {
00082
00083
00084 f << "EDGE2 " << edgeIDs.first << " " << edgeIDs.second << " " <<
00085
00086 edge.mean.x()<<" "<<edge.mean.y()<<" "<<edge.mean.phi()<<" "<<
00087 edge.cov_inv(0,0)<<" "<<edge.cov_inv(0,1)<<" "<<edge.cov_inv(1,1)<<" "<<
00088 edge.cov_inv(2,2)<<" "<<edge.cov_inv(0,2)<<" "<<edge.cov_inv(1,2) << endl;
00089 }
00090 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPose3DPDFGaussianInf & edge, std::ofstream &f)
00091 {
00092
00093
00094
00095 f << "EDGE3 " << edgeIDs.first << " " << edgeIDs.second << " " <<
00096
00097 edge.mean.x()<<" "<<edge.mean.y()<<" "<<edge.mean.z()<<" "<<
00098 edge.mean.roll()<<" "<<edge.mean.pitch()<<" "<<edge.mean.yaw()<<" "<<
00099 edge.cov_inv(0,0)<<" "<<edge.cov_inv(0,1)<<" "<<edge.cov_inv(0,2)<<" "<<edge.cov_inv(0,5)<<" "<<edge.cov_inv(0,4)<<" "<<edge.cov_inv(0,3)<<" "<<
00100 edge.cov_inv(1,1)<<" "<<edge.cov_inv(1,2)<<" "<<edge.cov_inv(1,5)<<" "<<edge.cov_inv(1,4)<<" "<<edge.cov_inv(1,3)<<" "<<
00101 edge.cov_inv(2,2)<<" "<<edge.cov_inv(2,5)<<" "<<edge.cov_inv(2,4)<<" "<<edge.cov_inv(2,3)<<" "<<
00102 edge.cov_inv(5,5)<<" "<<edge.cov_inv(5,4)<<" "<<edge.cov_inv(5,3)<<" "<<
00103 edge.cov_inv(4,4)<<" "<<edge.cov_inv(4,3)<<" "<<
00104 edge.cov_inv(3,3) << endl;
00105 }
00106 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPosePDFGaussian & edge, std::ofstream &f)
00107 {
00108 CPosePDFGaussianInf p;
00109 p.copyFrom(edge);
00110 write_EDGE_line(edgeIDs,p,f);
00111 }
00112 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPose3DPDFGaussian & edge, std::ofstream &f)
00113 {
00114 CPose3DPDFGaussianInf p;
00115 p.copyFrom(edge);
00116 write_EDGE_line(edgeIDs,p,f);
00117 }
00118 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPose2D & edge, std::ofstream &f)
00119 {
00120 CPosePDFGaussianInf p;
00121 p.mean = edge;
00122 p.cov_inv.unit(3,1.0);
00123 write_EDGE_line(edgeIDs,p,f);
00124 }
00125 static void write_EDGE_line( const TPairNodeIDs &edgeIDs,const CPose3D & edge, std::ofstream &f)
00126 {
00127 CPose3DPDFGaussianInf p;
00128 p.mean = edge;
00129 p.cov_inv.unit(6,1.0);
00130 write_EDGE_line(edgeIDs,p,f);
00131 }
00132
00133
00134
00135
00136
00137 static void save_graph_of_poses_from_text_file(const graph_t *g, const std::string &fil)
00138 {
00139 typedef typename graph_t::constraint_t CPOSE;
00140
00141 std::ofstream f;
00142 f.open(fil.c_str());
00143 if (!f.is_open())
00144 THROW_EXCEPTION_CUSTOM_MSG1("Error opening file '%s' for writing",fil.c_str());
00145
00146
00147 for (typename graph_t::global_poses_t::const_iterator itNod = g->nodes.begin();itNod!=g->nodes.end();++itNod)
00148 write_VERTEX_line(itNod->first, itNod->second, f);
00149
00150
00151 for (typename graph_t::const_iterator it=g->begin();it!=g->end();++it)
00152 if (it->first.first!=it->first.second)
00153 write_EDGE_line( it->first, it->second, f);
00154
00155 }
00156
00157
00158
00159
00160
00161 static void load_graph_of_poses_from_text_file(graph_t *g, const std::string &fil)
00162 {
00163 typedef typename graph_t::constraint_t CPOSE;
00164
00165 set<string> alreadyWarnedUnknowns;
00166
00167
00168 g->clear();
00169
00170
00171
00172 const bool graph_is_3D = CPOSE::is_3D();
00173
00174 CTextFileLinesParser filParser(fil);
00175
00176
00177
00178
00179
00180
00181 map<TNodeID,TNodeID> lstEquivs;
00182
00183
00184 istringstream s;
00185 while (filParser.getNextLine(s))
00186 {
00187 const unsigned int lineNum = filParser.getCurrentLineNumber();
00188 const string lin = s.str();
00189
00190 string key;
00191 if ( !(s >> key) || key.empty() )
00192 THROW_EXCEPTION(format("Line %u: Can't read string for entry type in: '%s'", lineNum, lin.c_str() ) );
00193
00194 if ( strCmpI(key,"EQUIV") )
00195 {
00196
00197 TNodeID id1,id2;
00198 if (!(s>> id1 >> id2))
00199 THROW_EXCEPTION(format("Line %u: Can't read id1 & id2 in EQUIV line: '%s'", lineNum, lin.c_str() ) );
00200 lstEquivs[std::max(id1,id2)] = std::min(id1,id2);
00201 }
00202 }
00203
00204
00205
00206
00207 filParser.rewind();
00208
00209
00210 while (filParser.getNextLine(s))
00211 {
00212 const unsigned int lineNum = filParser.getCurrentLineNumber();
00213 const string lin = s.str();
00214
00215
00216
00217
00218
00219
00220
00221 string key;
00222 if ( !(s >> key) || key.empty() )
00223 THROW_EXCEPTION(format("Line %u: Can't read string for entry type in: '%s'", lineNum, lin.c_str() ) );
00224
00225 if ( strCmpI(key,"VERTEX2") )
00226 {
00227 TNodeID id;
00228 TPose2D p2D;
00229 if (!(s>> id >> p2D.x >> p2D.y >> p2D.phi))
00230 THROW_EXCEPTION(format("Line %u: Error parsing VERTEX2 line: '%s'", lineNum, lin.c_str() ) );
00231
00232
00233 if (g->nodes.find(id)!=g->nodes.end())
00234 THROW_EXCEPTION(format("Line %u: Error, duplicated verted ID %u in line: '%s'", lineNum, static_cast<unsigned int>(id), lin.c_str() ) );
00235
00236
00237 {
00238 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(id);
00239 if (itEq!=lstEquivs.end()) id = itEq->second;
00240 }
00241
00242
00243 if (g->nodes.find(id)==g->nodes.end())
00244 {
00245 typename CNetworkOfPoses<CPOSE>::constraint_t::type_value & newNode = g->nodes[id];
00246 newNode = CPose2D(p2D);
00247 }
00248 }
00249 else if ( strCmpI(key,"VERTEX3") )
00250 {
00251 if (!graph_is_3D)
00252 THROW_EXCEPTION(format("Line %u: Try to load VERTEX3 into a 2D graph: '%s'", lineNum, lin.c_str() ) );
00253
00254
00255 TNodeID id;
00256 TPose3D p3D;
00257
00258 if (!(s>> id >> p3D.x >> p3D.y >> p3D.z >> p3D.roll >> p3D.pitch >> p3D.yaw ))
00259 THROW_EXCEPTION(format("Line %u: Error parsing VERTEX3 line: '%s'", lineNum, lin.c_str() ) );
00260
00261
00262 if (g->nodes.find(id)!=g->nodes.end())
00263 THROW_EXCEPTION(format("Line %u: Error, duplicated verted ID %u in line: '%s'", lineNum, static_cast<unsigned int>(id), lin.c_str() ) );
00264
00265
00266 {
00267 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(id);
00268 if (itEq!=lstEquivs.end()) id = itEq->second;
00269 }
00270
00271
00272 if (g->nodes.find(id)==g->nodes.end())
00273 {
00274 g->nodes[id] = typename CNetworkOfPoses<CPOSE>::constraint_t::type_value( CPose3D(p3D) );
00275 }
00276 }
00277 else if ( strCmpI(key,"EDGE2") )
00278 {
00279
00280
00281
00282
00283
00284
00285
00286 TNodeID to_id, from_id;
00287 if (!(s>> from_id >> to_id ))
00288 THROW_EXCEPTION(format("Line %u: Error parsing EDGE2 line: '%s'", lineNum, lin.c_str() ) );
00289
00290
00291 {
00292 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(to_id);
00293 if (itEq!=lstEquivs.end()) to_id = itEq->second;
00294 }
00295 {
00296 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(from_id);
00297 if (itEq!=lstEquivs.end()) from_id = itEq->second;
00298 }
00299
00300 if (from_id!=to_id)
00301 {
00302 TPose2D Ap_mean;
00303 CMatrixDouble33 Ap_cov_inv;
00304 if (!(s>>
00305 Ap_mean.x >> Ap_mean.y >> Ap_mean.phi >>
00306 Ap_cov_inv(0,0) >> Ap_cov_inv(0,1) >> Ap_cov_inv(1,1) >>
00307 Ap_cov_inv(2,2) >> Ap_cov_inv(0,2) >> Ap_cov_inv(1,2) ))
00308 THROW_EXCEPTION(format("Line %u: Error parsing EDGE2 line: '%s'", lineNum, lin.c_str() ) );
00309
00310
00311 Ap_cov_inv(1,0) = Ap_cov_inv(0,1);
00312 Ap_cov_inv(2,0) = Ap_cov_inv(0,2);
00313 Ap_cov_inv(2,1) = Ap_cov_inv(1,2);
00314
00315
00316 typename CNetworkOfPoses<CPOSE>::edge_t newEdge;
00317 TPosePDFHelper<CPOSE>::copyFrom2D(newEdge, CPosePDFGaussianInf( CPose2D(Ap_mean), Ap_cov_inv ) );
00318 g->insertEdge(from_id, to_id, newEdge);
00319 }
00320 }
00321 else if ( strCmpI(key,"EDGE3") )
00322 {
00323 if (!graph_is_3D)
00324 THROW_EXCEPTION(format("Line %u: Try to load EDGE3 into a 2D graph: '%s'", lineNum, lin.c_str() ) );
00325
00326
00327 TNodeID to_id, from_id;
00328 if (!(s>> from_id >> to_id ))
00329 THROW_EXCEPTION(format("Line %u: Error parsing EDGE3 line: '%s'", lineNum, lin.c_str() ) );
00330
00331
00332 {
00333 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(to_id);
00334 if (itEq!=lstEquivs.end()) to_id = itEq->second;
00335 }
00336 {
00337 const map<TNodeID,TNodeID>::const_iterator itEq = lstEquivs.find(from_id);
00338 if (itEq!=lstEquivs.end()) from_id = itEq->second;
00339 }
00340
00341 if (from_id!=to_id)
00342 {
00343 TPose3D Ap_mean;
00344 CMatrixDouble66 Ap_cov_inv;
00345
00346 if (!(s>> Ap_mean.x >> Ap_mean.y >> Ap_mean.z >> Ap_mean.roll >> Ap_mean.pitch >> Ap_mean.yaw ))
00347 THROW_EXCEPTION(format("Line %u: Error parsing EDGE3 line: '%s'", lineNum, lin.c_str() ) );
00348
00349
00350 if (!(s>>
00351 Ap_cov_inv(0,0) >> Ap_cov_inv(0,1) >> Ap_cov_inv(0,2) >> Ap_cov_inv(0,5) >> Ap_cov_inv(0,4) >> Ap_cov_inv(0,3) >>
00352 Ap_cov_inv(1,1) >> Ap_cov_inv(1,2) >> Ap_cov_inv(1,5) >> Ap_cov_inv(1,4) >> Ap_cov_inv(1,3) >>
00353 Ap_cov_inv(2,2) >> Ap_cov_inv(2,5) >> Ap_cov_inv(2,4) >> Ap_cov_inv(2,3) >>
00354 Ap_cov_inv(5,5) >> Ap_cov_inv(5,4) >> Ap_cov_inv(5,3) >>
00355 Ap_cov_inv(4,4) >> Ap_cov_inv(4,3) >>
00356 Ap_cov_inv(3,3) ))
00357 {
00358
00359 Ap_cov_inv.unit(6,1.0);
00360
00361 if (alreadyWarnedUnknowns.find("MISSING_3D")==alreadyWarnedUnknowns.end())
00362 {
00363 alreadyWarnedUnknowns.insert("MISSING_3D");
00364 cerr << "[CNetworkOfPoses::loadFromTextFile] " << fil << ":" << lineNum << ": Warning: Information matrix missing, assuming unity.\n";
00365 }
00366 }
00367 else
00368 {
00369
00370 for (size_t r=1;r<6;r++)
00371 for (size_t c=0;c<r;c++)
00372 Ap_cov_inv(r,c) = Ap_cov_inv(c,r);
00373 }
00374
00375
00376 typename CNetworkOfPoses<CPOSE>::edge_t newEdge;
00377 TPosePDFHelper<CPOSE>::copyFrom3D(newEdge, CPose3DPDFGaussianInf( CPose3D(Ap_mean), Ap_cov_inv ) );
00378 g->insertEdge(from_id, to_id, newEdge);
00379 }
00380 }
00381 else if ( strCmpI(key,"EQUIV") )
00382 {
00383
00384 }
00385 else
00386 {
00387 if (alreadyWarnedUnknowns.find(key)==alreadyWarnedUnknowns.end())
00388 {
00389 alreadyWarnedUnknowns.insert(key);
00390 cerr << "[CNetworkOfPoses::loadFromTextFile] " << fil << ":" << lineNum << ": Warning: unknown entry type: " << key << endl;
00391 }
00392 }
00393 }
00394
00395 }
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405 static size_t graph_of_poses_collapse_dup_edges(graph_t *g)
00406 {
00407 MRPT_START
00408 typedef typename graph_t::edges_map_t::iterator TEdgeIterator;
00409
00410
00411
00412 typedef map<pair<TNodeID,TNodeID>, vector<TEdgeIterator> > TListAllEdges;
00413 TListAllEdges lstAllEdges;
00414
00415
00416 for (TEdgeIterator itEd=g->edges.begin();itEd!=g->edges.end();++itEd)
00417 {
00418
00419 const pair<TNodeID,TNodeID> arc_id = make_pair( std::min(itEd->first.first,itEd->first.second),std::max(itEd->first.first,itEd->first.second) );
00420
00421 vector<TEdgeIterator> &lstEdges = lstAllEdges[arc_id];
00422
00423 lstEdges.push_back(itEd);
00424 }
00425
00426
00427 size_t nRemoved = 0;
00428 for (typename TListAllEdges::const_iterator it=lstAllEdges.begin();it!=lstAllEdges.end();++it)
00429 {
00430 const size_t N = it->second.size();
00431 for (size_t i=1;i<N;i++)
00432 g->edges.erase( it->second[i] );
00433
00434 if (N>=2) nRemoved+=N-1;
00435 }
00436
00437 return nRemoved;
00438 MRPT_END
00439 }
00440
00441
00442
00443
00444
00445
00446
00447 static void graph_of_poses_dijkstra_init(graph_t *g)
00448 {
00449 MRPT_START
00450
00451
00452 typedef CDijkstra<graph_t,typename graph_t::maps_implementation_t> dijkstra_t;
00453 typedef typename graph_t::constraint_t constraint_t;
00454
00455 dijkstra_t dijkstra(*g, g->root);
00456
00457
00458
00459 typename dijkstra_t::tree_graph_t treeView;
00460 dijkstra.getTreeGraph(treeView);
00461
00462
00463 struct VisitorComputePoses : public dijkstra_t::tree_graph_t::Visitor
00464 {
00465 graph_t * m_g;
00466
00467 VisitorComputePoses(graph_t *g) : m_g(g) { }
00468 virtual void OnVisitNode( const TNodeID parent_id, const typename dijkstra_t::tree_graph_t::Visitor::tree_t::TEdgeInfo &edge_to_child, const size_t depth_level )
00469 {
00470 const TNodeID child_id = edge_to_child.id;
00471
00472
00473
00474 if ( (!edge_to_child.reverse && !m_g->edges_store_inverse_poses) ||
00475 ( edge_to_child.reverse && m_g->edges_store_inverse_poses)
00476 )
00477 {
00478 m_g->nodes[child_id].composeFrom( m_g->nodes[parent_id], edge_to_child.data->getPoseMean() );
00479 }
00480 else
00481 {
00482 m_g->nodes[child_id].composeFrom( m_g->nodes[parent_id], - edge_to_child.data->getPoseMean() );
00483 }
00484 }
00485 };
00486
00487
00488 g->nodes.clear();
00489 g->nodes[g->root] = typename constraint_t::type_value();
00490
00491
00492 VisitorComputePoses myVisitor(g);
00493 treeView.visitBreadthFirst(treeView.root, myVisitor);
00494
00495 MRPT_END
00496 }
00497
00498
00499
00500 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPosePDFGaussianInf &p) {
00501 math::wrapToPiInPlace(err[2]);
00502 return mrpt::math::multiply_HCHt_scalar(err,p.cov_inv);
00503 }
00504 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose3DPDFGaussianInf &p) {
00505 math::wrapToPiInPlace(err[3]);
00506 math::wrapToPiInPlace(err[4]);
00507 math::wrapToPiInPlace(err[5]);
00508 return mrpt::math::multiply_HCHt_scalar(err,p.cov_inv);
00509 }
00510 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPosePDFGaussian &p) {
00511 math::wrapToPiInPlace(err[2]);
00512 CMatrixDouble33 COV_INV(UNINITIALIZED_MATRIX);
00513 p.cov.inv(COV_INV);
00514 return mrpt::math::multiply_HCHt_scalar(err,COV_INV);
00515 }
00516 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose3DPDFGaussian &p) {
00517 math::wrapToPiInPlace(err[3]);
00518 math::wrapToPiInPlace(err[4]);
00519 math::wrapToPiInPlace(err[5]);
00520 CMatrixDouble66 COV_INV(UNINITIALIZED_MATRIX);
00521 p.cov.inv(COV_INV);
00522 return mrpt::math::multiply_HCHt_scalar(err,COV_INV);
00523 }
00524
00525 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose2D &p) {
00526 math::wrapToPiInPlace(err[2]);
00527 return square(err[0])+square(err[1])+square(err[2]);
00528 }
00529 template <class VEC> static inline double auxMaha2Dist(VEC &err,const CPose3D &p) {
00530 math::wrapToPiInPlace(err[3]);
00531 math::wrapToPiInPlace(err[4]);
00532 math::wrapToPiInPlace(err[5]);
00533 return square(err[0])+square(err[1])+square(err[2])+square(err[3])+square(err[4])+square(err[5]);
00534 }
00535
00536
00537 static inline double auxEuclid2Dist(const CPose2D &p1,const CPose2D &p2) {
00538 return
00539 square(p1.x()-p2.x())+
00540 square(p1.y()-p2.y())+
00541 square( mrpt::math::wrapToPi(p1.phi()-p2.phi() ) );
00542 }
00543 static inline double auxEuclid2Dist(const CPose3D &p1,const CPose3D &p2) {
00544 return
00545 square(p1.x()-p2.x())+
00546 square(p1.y()-p2.y())+
00547 square(p1.z()-p2.z())+
00548 square( mrpt::math::wrapToPi(p1.yaw()-p2.yaw() ) )+
00549 square( mrpt::math::wrapToPi(p1.pitch()-p2.pitch() ) )+
00550 square( mrpt::math::wrapToPi(p1.roll()-p2.roll() ) );
00551 }
00552
00553
00554
00555
00556
00557
00558 static double graph_edge_sqerror(
00559 const graph_t *g,
00560 const typename mrpt::graphs::CDirectedGraph<typename graph_t::constraint_t>::edges_map_t::const_iterator &itEdge,
00561 bool ignoreCovariances )
00562 {
00563 MRPT_START
00564
00565
00566 const TNodeID from_id = itEdge->first.first;
00567 const TNodeID to_id = itEdge->first.second;
00568
00569
00570 typename graph_t::global_poses_t::const_iterator itPoseFrom = g->nodes.find(from_id);
00571 typename graph_t::global_poses_t::const_iterator itPoseTo = g->nodes.find(to_id);
00572 ASSERTMSG_(itPoseFrom!=g->nodes.end(), format("Node %u doesn't have a global pose in 'nodes'.", static_cast<unsigned int>(from_id)))
00573 ASSERTMSG_(itPoseTo!=g->nodes.end(), format("Node %u doesn't have a global pose in 'nodes'.", static_cast<unsigned int>(to_id)))
00574
00575
00576 typedef typename graph_t::constraint_t constraint_t;
00577
00578 const typename constraint_t::type_value &from_mean = itPoseFrom->second;
00579 const typename constraint_t::type_value &to_mean = itPoseTo->second;
00580
00581
00582 const constraint_t &edge_delta_pose = itEdge->second;
00583 const typename constraint_t::type_value &edge_delta_pose_mean = edge_delta_pose.getPoseMean();
00584
00585 if (ignoreCovariances)
00586 {
00587
00588 typename constraint_t::type_value from_plus_delta(UNINITIALIZED_POSE);
00589 from_plus_delta.composeFrom(from_mean, edge_delta_pose_mean);
00590
00591
00592 return auxEuclid2Dist(from_plus_delta,to_mean);
00593 }
00594 else
00595 {
00596
00597
00598 constraint_t from_plus_delta = edge_delta_pose;
00599 from_plus_delta.changeCoordinatesReference(from_mean);
00600
00601
00602
00603
00604
00605 CArrayDouble<constraint_t::type_value::static_size> err;
00606 for (size_t i=0;i<constraint_t::type_value::static_size;i++)
00607 err[i] = from_plus_delta.getPoseMean()[i] - to_mean[i];
00608
00609
00610 return auxMaha2Dist(err,from_plus_delta);
00611 }
00612 MRPT_END
00613 }
00614
00615 };
00616
00617 }
00618 }
00619 }
00620
00621 #endif