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
00029
00030 #include <pose_graph/constraint_graph.h>
00031 #include <pose_graph/graph_db.h>
00032 #include <graph_mapping_utils/utils.h>
00033 #include <pose_graph/exception.h>
00034 #include <pose_graph/message_conversion.h>
00035 #include <pose_graph/spa_2d_conversion.h>
00036 #include <pose_graph/graph_search.h>
00037 #include <tf/transform_datatypes.h>
00038 #include <gtest/gtest.h>
00039 #include <iostream>
00040 #include <boost/assign.hpp>
00041 #include <boost/foreach.hpp>
00042 #include <boost/filesystem.hpp>
00043
00044 using namespace pose_graph;
00045 using namespace std;
00046 using Eigen::Quaterniond;
00047 using Eigen::Vector3d;
00048 using boost::assign::operator+=;
00049 using graph_mapping_utils::makePose;
00050
00051 namespace gm=geometry_msgs;
00052
00053 namespace geometry_msgs
00054 {
00055
00056
00057 bool operator==(const gm::Pose& p1, const gm::Pose& p2)
00058 {
00059 return (p1.position.x == p2.position.x) &&
00060 (p1.position.y == p2.position.y) &&
00061 (p1.position.z == p2.position.z) &&
00062 (p1.orientation.x == p2.orientation.x) &&
00063 (p1.orientation.y == p2.orientation.y) &&
00064 (p1.orientation.z == p2.orientation.z) &&
00065 (p1.orientation.w == p2.orientation.w);
00066 }
00067
00068 }
00069
00070
00071 template <class T>
00072 ostream& operator<< (ostream& str, const set<T>& s)
00073 {
00074 std::copy(s.begin(), s.end(), std::ostream_iterator<unsigned>(str, " "));
00075 return str;
00076 }
00077
00078 ostream& operator<< (ostream& str, const tf::Pose& p)
00079 {
00080 const btVector3& v = p.getOrigin();
00081 const btQuaternion& q = p.getRotation();
00082 str << "[(" << v.x() << ", " << v.y() << ", " << v.z() << "), (" <<
00083 q.x() << ", " << q.y() << ", " << q.z() << ", " << q.w() << ")]";
00084 return str;
00085 }
00086
00087 template <class S>
00088 bool equalSets (set<S> s1, set<S> s2)
00089 {
00090 if (s1.size()==s2.size()) {
00091 BOOST_FOREACH (S x, s1) {
00092 if (s2.find(x)==s2.end()) {
00093 return false;
00094 }
00095 }
00096 return true;
00097 }
00098 else {
00099 cout << "\nSets aren't equal:\n S1: ";
00100 copy(s1.begin(), s1.end(), ostream_iterator<S>(cout, " "));
00101 cout << "\n S2: ";
00102 copy(s2.begin(), s2.end(), ostream_iterator<S>(cout, " "));
00103 return false;
00104 }
00105 }
00106
00107 template <class T>
00108 ostream& operator<< (ostream& str, const vector<T>& s)
00109 {
00110 std::copy(s.begin(), s.end(), std::ostream_iterator<T>(str, " "));
00111 return str;
00112 }
00113
00114
00115 void printGraph (const ConstraintGraph& g)
00116 {
00117 BOOST_FOREACH (const unsigned n, g.allNodes()) {
00118 cout << "\nNode " << n << ". Edges: ";
00119 EdgeSet edges = g.incidentEdges(n);
00120 copy(edges.begin(), edges.end(), ostream_iterator<unsigned>(cout, " "));
00121 }
00122 cout << "\n";
00123 }
00124
00125 const double TOL=1e-3;
00126
00127 bool closeTo (const double x, const double y)
00128 {
00129 return fabs(x-y) < TOL;
00130 }
00131
00132 bool approxEqualPoses (const tf::Pose& p1, const tf::Pose& p2)
00133 {
00134 const btVector3& v1 = p1.getOrigin();
00135 const btVector3& v2 = p2.getOrigin();
00136 const btQuaternion& q1 = p1.getRotation();
00137 const btQuaternion& q2 = p2.getRotation();
00138
00139 return (closeTo(v1.x(), v2.x()) &&
00140 closeTo(v1.y(), v2.y()) &&
00141 closeTo(v1.z(), v2.z()) &&
00142 closeTo(q1.x(), q2.x()) &&
00143 closeTo(q1.y(), q2.y()) &&
00144 closeTo(q1.z(), q2.z()) &&
00145 closeTo(q1.w(), q2.w()));
00146 }
00147
00148
00149 bool approxEqual (const gm::Pose& p1, const gm::Pose& p2)
00150 {
00151 return approxEqualPoses(graph_mapping_utils::toPose(p1), graph_mapping_utils::toPose(p2));
00152 }
00153
00154 double euclideanDistance (const gm::Point& p1, const gm::Point& p2)
00155 {
00156 const double dx = p1.x-p2.x;
00157 const double dy = p1.y-p2.y;
00158 const double dz = p1.z-p2.z;
00159 return sqrt(dx*dx + dy*dy + dz*dz);
00160 }
00161
00162
00163 typedef gm::Point::ConstPtr PointPtr;
00164
00165 PointPtr makePoint (const double x, const double y, const double z = 0.0)
00166 {
00167 gm::Point p;
00168 p.x = x;
00169 p.y = y;
00170 p.z = z;
00171 return PointPtr(new gm::Point(p));
00172 }
00173
00174 bool approxEqualPts (const gm::Point& p1, const gm::Point& p2)
00175 {
00176 return (euclideanDistance(p1, p2) < TOL);
00177 }
00178
00179 unsigned ind(const unsigned i, const unsigned j)
00180 {
00181 return i*6+j;
00182 }
00183
00184 PoseWithPrecision makeConstraint(double x, double y, double yaw, double x_prec=1,
00185 double y_prec=1, double xy_prec=0, double theta_prec=1)
00186 {
00187 PoseWithPrecision p;
00188 p.pose.position.x = x;
00189 p.pose.position.y = y;
00190 p.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
00191 p.precision[ind(0,0)] = x_prec;
00192 p.precision[ind(1,1)] = y_prec;
00193 p.precision[ind(5,5)] = theta_prec/4;
00194 p.precision[ind(2,2)] = p.precision[ind(3,3)] = p.precision[ind(4,4)] = 1;
00195 p.precision[ind(0,1)] = p.precision[ind(1,0)] = xy_prec;
00196 return p;
00197 }
00198
00199
00200 TEST(PoseGraph, GraphDB)
00201 {
00202 const unsigned n1(1);
00203 const unsigned n2(2);
00204 const unsigned n3(3);
00205 const unsigned n4(4);
00206
00207 PointPtr p1 = makePoint(24, 3, 7);
00208 PointPtr p2 = makePoint(42, 5, 4);
00209 gm::Pose::Ptr p4(new gm::Pose());
00210 gm::Pose::Ptr p6(new gm::Pose());
00211 p4->position = *p2;
00212 p4->orientation.w = 1.0;
00213 p6->position = *p2;
00214 p6->orientation.z = 1.0;
00215
00216 {
00217 wh::WarehouseClient client("test_pose_graph");
00218 EXPECT_EQ(0u, client.listCollections().size());
00219 vector<string> indexes;
00220 indexes.push_back("x");
00221 indexes.push_back("z");
00222 wh::Collection<gm::Point> points = client.setupCollection<gm::Point>("foo", indexes);
00223
00224 ASSERT_EQ(1u, client.listCollections().size());
00225 EXPECT_EQ("foo", client.listCollections()[0]);
00226
00227 points.publish(*p1, nodeMetadata(n1));
00228 EXPECT_PRED2(approxEqualPts, *p1, *getNodeData(&points, n1));
00229 EXPECT_THROW(getNodeData(&points, n2), DataNotFoundException);
00230
00231 wh::Collection<gm::Pose> poses = client.setupCollection<gm::Pose>("bar", vector<string>());
00232 poses.publish(*p4, nodeMetadata(n1));
00233 gm::Pose::ConstPtr p5 = getNodeData(&poses, n1);
00234 EXPECT_PRED2(approxEqual, *p5, *p4);
00235 }
00236
00237 {
00238 wh::WarehouseClient client("test_pose_graph");
00239 vector<string> colls = client.listCollections();
00240 ASSERT_EQ(2u, colls.size());
00241 vector<string> indexes;
00242 indexes.push_back("x");
00243 indexes.push_back("z");
00244 EXPECT_TRUE(colls[0]=="foo" || colls[1]=="foo");
00245 EXPECT_TRUE(colls[0]=="bar" || colls[1]=="bar");
00246 wh::Collection<gm::Point> points = client.setupCollection<gm::Point>("foo", indexes);
00247 wh::Collection<gm::Pose> poses = client.setupCollection<gm::Pose>("bar", vector<string>());
00248 PointPtr p3 = getNodeData(&points, n1);
00249 EXPECT_PRED2(approxEqualPts, *p3, *p1);
00250 EXPECT_THROW(getNodeData(&points, n2), DataNotFoundException);
00251 gm::Pose::ConstPtr p5 = getNodeData(&poses, n1);
00252 EXPECT_PRED2(approxEqual, *p5, *p4);
00253
00254
00255
00256 CachedNodeMap<gm::Point> cached_pts(&points);
00257 EXPECT_PRED2(approxEqualPts, *p1, *cached_pts.get(n1));
00258 EXPECT_PRED2(approxEqualPts, *p1, *cached_pts.get(n1));
00259 EXPECT_THROW(cached_pts.get(n2), DataNotFoundException);
00260 points.publish(*p2, nodeMetadata(n2));
00261 EXPECT_PRED2(approxEqualPts, *p1, *cached_pts.get(n1));
00262 EXPECT_PRED2(approxEqualPts, *p2, *cached_pts.get(n2));
00263 EXPECT_THROW(cached_pts.get(n3), DataNotFoundException);
00264
00265
00266
00267
00268 cached_pts.set(n3, p3);
00269 EXPECT_PRED2(approxEqualPts, *p3, *cached_pts.get(n3));
00270 EXPECT_TRUE(cached_pts.hasData(n2));
00271 EXPECT_TRUE(!cached_pts.hasData(n4));
00272 }
00273 }
00274
00275 TEST(PoseGraph, GraphOps)
00276 {
00277 ConstraintGraph g;
00278
00279 PoseWithPrecision constraint = makeConstraint(1, 2, 0);
00280 PoseWithPrecision constraint2 = makeConstraint(2, 1, 1);
00281
00282 const set<unsigned> empty;
00283 EXPECT_PRED2(equalSets<unsigned>, empty, g.allNodes());
00284
00285
00286
00287
00288
00289
00290
00291 unsigned n1 = g.addNode();
00292 unsigned n2 = g.addNode();
00293 unsigned n3 = g.addNode();
00294 unsigned e12 = g.addEdge(n1, n2, constraint);
00295 unsigned e23 = g.addEdge(n2, n3, constraint2);
00296 unsigned e23b = g.addEdge(n2, n3, constraint);
00297
00298 EdgeSet edges1, edges2, edges;
00299 edges1.insert(e12);
00300 edges2.insert(e12);
00301 edges2.insert(e23);
00302 edges2.insert(e23b);
00303 edges.insert(e12);
00304 edges.insert(e23);
00305 edges.insert(e23b);
00306
00307 NodeSet nodes;
00308 nodes.insert(n1);
00309 nodes.insert(n2);
00310 nodes.insert(n3);
00311
00312
00313
00314
00315
00316 EXPECT_TRUE(e23!=e23b);
00317 EXPECT_TRUE(equalSets(g.incidentEdges(n1), edges1));
00318 EXPECT_TRUE(equalSets(g.incidentEdges(n2), edges2));
00319
00320
00321
00322
00323
00324 const tf::Pose p1(btQuaternion(0,0,0,1), btVector3(0, 42.24, 0));
00325 const tf::Pose p2(btQuaternion(0,0,1,0), btVector3(0, 100, 200));
00326 EXPECT_THROW (g.getOptimizedPose(n1), NoOptimizedPoseException);
00327 g.setOptimizedPose(n1, p1);
00328 const tf::Pose p3 = g.getOptimizedPose(n1);
00329 EXPECT_PRED2 (approxEqualPoses, p1, p3);
00330 EXPECT_THROW (g.getOptimizedPose(n2), NoOptimizedPoseException);
00331 g.setOptimizedPose(n2, p2);
00332 EXPECT_PRED2 (approxEqualPoses, p2, g.getOptimizedPose(n2));
00333 EXPECT_PRED2 (approxEqualPoses, p1, g.getOptimizedPose(n1));
00334 NodePoseMap poses;
00335 poses[n2] = p2;
00336 g.setOptimizedPoses(poses);
00337 EXPECT_PRED2 (approxEqualPoses, p2, g.getOptimizedPose(n2));
00338 EXPECT_THROW (g.getOptimizedPose(n1), NoOptimizedPoseException);
00339
00340
00341
00342
00343
00344 const ConstraintGraph g2(g);
00345
00346 EXPECT_TRUE(equalSets(g2.incidentEdges(n1), edges1));
00347 EXPECT_TRUE(equalSets(g2.incidentEdges(n2), edges2));
00348 EXPECT_TRUE(equalSets(g2.allEdges(), edges));
00349 EXPECT_TRUE(equalSets(g2.allNodes(), nodes));
00350
00351
00352
00353
00354
00355
00356 msg::ConstraintGraphMessage m = constraintGraphToMessage(g2);
00357 ConstraintGraph g3 = constraintGraphFromMessage(m);
00358
00359 EXPECT_TRUE(equalSets(g3.incidentEdges(n1), edges1));
00360 EXPECT_TRUE(equalSets(g3.incidentEdges(n2), edges2));
00361 EXPECT_TRUE(equalSets(g3.allEdges(), edges));
00362 EXPECT_TRUE(equalSets(g3.allNodes(), nodes));
00363
00364 ConstraintGraph g4(g3);
00365
00366
00367
00368
00369
00370 EXPECT_PRED2 (approxEqualPoses, p2, g4.getOptimizedPose(n2));
00371 EXPECT_THROW (g4.getOptimizedPose(n1), NoOptimizedPoseException);
00372
00373
00374
00375
00376
00377
00378 const tf::Pose pose3(btQuaternion(), btVector3(0, 500, 500));
00379 const tf::Pose pose4(btQuaternion(), btVector3(0, 100, 150));
00380 const unsigned n4 = g4.addNode();
00381 g4.addNode();
00382 const unsigned n6 = g4.addNode();
00383 g4.addEdge(n3, n4, constraint2);
00384 g4.setOptimizedPose(n3, pose3);
00385 g4.setOptimizedPose(n4, pose4);
00386 g4.setOptimizedPose(n1, p1);
00387 g4.setOptimizedPose(n6, p1);
00388 NodeSet neighbors2;
00389 neighbors2.insert(n2);
00390 neighbors2.insert(n1);
00391 OptimizedDistancePredicate pred(g4, p2.getOrigin(), 300);
00392 EXPECT_TRUE(equalSets(filterNearbyNodes(g4, n2, pred), neighbors2));
00393
00394
00395
00396
00397
00398 const unsigned e36 = g4.addEdge(n3, n6, constraint2);
00399 g4.addEdge(n4, n6, constraint);
00400 ShortestPath path = shortestPath(g4, n1, n6);
00401 ShortestPath exp_path;
00402 exp_path.first += n1, n2, n3, n6;
00403 exp_path.second += e12, e23, e36;
00404 EXPECT_EQ (exp_path.first, path.first);
00405 EXPECT_EQ (exp_path.second, path.second);
00406
00407
00408
00409
00410
00411 msg::ConstraintGraphDiff diff;
00412 diff.new_nodes.resize(1);
00413 diff.new_node_timestamps.resize(1);
00414 diff.new_nodes[0].id = 42;
00415 diff.new_edges.resize(1);
00416 diff.new_edges[0].id = 100;
00417 diff.new_edges[0].constraint.src = n1;
00418 diff.new_edges[0].constraint.dest = n3;
00419 EXPECT_EQ(g4.allNodes().size(), 6);
00420 EXPECT_EQ(g4.incidentEdges(n1).size(), 1);
00421 applyDiff(&g4, diff);
00422 EXPECT_EQ(g4.allNodes().size(), 7);
00423 EXPECT_EQ(g4.incidentEdges(n1).size(), 2);
00424
00425 }
00426
00427
00428
00429 double dist (const tf::Pose& p, const tf::Pose& p2)
00430 {
00431 const double dx=p.getOrigin().x()-p2.getOrigin().x();
00432 const double dy=p.getOrigin().y()-p2.getOrigin().y();
00433 return sqrt(dx*dx+dy*dy);
00434 }
00435
00436
00437
00438 TEST(PoseGraph, Optimization)
00439 {
00440 ConstraintGraph g;
00441
00442 const unsigned n1 = g.addNode();
00443 const unsigned n2 = g.addNode();
00444 const unsigned n3 = g.addNode();
00445
00446 Eigen::Matrix<double, 6, 6> p1, p2;
00447 p1 = Eigen::MatrixXd::Identity(6,6);
00448 p2 = p1;
00449
00450 const PoseWithPrecision c12 = makeConstraint(1, 0, 0);
00451 const PoseWithPrecision c13 = makeConstraint(0.5, 1, 0);
00452 const PoseWithPrecision c32 = makeConstraint(0.5, -1, 0);
00453
00454 g.addEdge(n1, n2, c12);
00455 g.addEdge(n1, n3, c13);
00456 g.addEdge(n3, n2, c32);
00457
00458 NodePoseMap init;
00459 init[n1] = makePose(0, 0, 0);
00460 init[n2] = makePose(2, 5, 0);
00461 init[n3] = makePose(9, 1, 0);
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473 NodePoseMap opt2d=optimizeGraph2D(g, init);
00474
00475 EXPECT_TRUE (0.98 < dist(opt2d[n1], opt2d[n2]));
00476 EXPECT_TRUE (dist(opt2d[n1], opt2d[n2]) < 1.02);
00477 EXPECT_TRUE (1.1 < dist(opt2d[n3], opt2d[n2]));
00478 EXPECT_TRUE (dist(opt2d[n3], opt2d[n2]) < 1.2);
00479 EXPECT_TRUE (1.1 < dist(opt2d[n1], opt2d[n3]));
00480 EXPECT_TRUE (dist(opt2d[n1], opt2d[n3]) < 1.2);
00481
00483 const unsigned n4 = g.addNode();
00484 init[n4] = makePose(0, 1, 0);
00485 EXPECT_THROW(optimizeGraph2D(g, init), DisconnectedComponentException);
00486 NodeSet nodes1, nodes2, nodes3;
00487 nodes1 += n1, n2, n3;
00488 nodes2 += n4;
00489 nodes3 += n1, n2, n3, n4;
00490 opt2d = optimizeGraph2D(g, init, nodes1);
00491 EXPECT_EQ (3, opt2d.size());
00492
00493 EXPECT_PRED2(equalSets<unsigned>, nodes1, componentContaining(g, n2));
00494 EXPECT_PRED2(equalSets<unsigned>, nodes2, componentContaining(g, n4));
00495
00497 g.addEdge(n2, n4, c12);
00498 opt2d = optimizeGraph2D(g, init);
00499 EXPECT_TRUE (0.98 < dist(opt2d[n1], opt2d[n2]));
00500 EXPECT_TRUE (dist(opt2d[n1], opt2d[n2]) < 1.02);
00501 EXPECT_TRUE (1.1 < dist(opt2d[n3], opt2d[n2]));
00502 EXPECT_TRUE (dist(opt2d[n3], opt2d[n2]) < 1.2);
00503 EXPECT_TRUE (1.1 < dist(opt2d[n1], opt2d[n3]));
00504 EXPECT_TRUE (dist(opt2d[n1], opt2d[n3]) < 1.2);
00505 EXPECT_TRUE (0.99 < dist(opt2d[n2], opt2d[n4]));
00506 EXPECT_TRUE (dist(opt2d[n2], opt2d[n4]) < 1.01);
00507
00508 EXPECT_PRED2(equalSets<unsigned>, nodes3, componentContaining(g, n2));
00509 }
00510
00511
00512 const double PI = 3.14159265;
00513
00514
00515 int main (int argc, char** argv)
00516 {
00517 ros::init(argc, argv, "pose_graph_test");
00518 testing::InitGoogleTest(&argc, argv);
00519 return RUN_ALL_TESTS();
00520 }