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/pose_graph.h>
00031 #include <pose_graph/pose_graph_message.h>
00032 #include <pose_graph/utils.h>
00033 #include <pose_graph/exception.h>
00034 #include <pose_graph/spa_conversion.h>
00035 #include <pose_graph/spa_2d_conversion.h>
00036 #include <tf/transform_datatypes.h>
00037 #include <std_msgs/Int16.h>
00038 #include <gtest/gtest.h>
00039 #include <iostream>
00040 #include <boost/assign.hpp>
00041 #include <boost/foreach.hpp>
00042
00043
00044 using namespace pose_graph;
00045 using namespace std;
00046 using Eigen3::Quaterniond;
00047 using Eigen3::Vector3d;
00048 using boost::assign::operator+=;
00049 using occupancy_grid_utils::LocalizedCloud;
00050 using sensor_msgs::LaserScan;
00051
00052 namespace geometry_msgs
00053 {
00054
00055 bool operator==(const gm::Pose& p1, const gm::Pose& p2)
00056 {
00057 return (p1.position.x == p2.position.x) &&
00058 (p1.position.y == p2.position.y) &&
00059 (p1.position.z == p2.position.z) &&
00060 (p1.orientation.x == p2.orientation.x) &&
00061 (p1.orientation.y == p2.orientation.y) &&
00062 (p1.orientation.z == p2.orientation.z) &&
00063 (p1.orientation.w == p2.orientation.w);
00064 }
00065
00066 }
00067
00068 template <class T>
00069 ostream& operator<< (ostream& str, const set<T>& s)
00070 {
00071 std::copy(s.begin(), s.end(), std::ostream_iterator<NodeId>(str, " "));
00072 return str;
00073 }
00074
00075
00076 template <class S>
00077 bool equalSets (set<S> s1, set<S> s2)
00078 {
00079 if (s1.size()==s2.size()) {
00080 BOOST_FOREACH (S x, s1) {
00081 if (s2.find(x)==s2.end()) {
00082 return false;
00083 }
00084 }
00085 return true;
00086 }
00087 else {
00088 cout << "\nSets aren't equal:\n S1: ";
00089 copy(s1.begin(), s1.end(), ostream_iterator<S>(cout, " "));
00090 cout << "\n S2: ";
00091 copy(s2.begin(), s2.end(), ostream_iterator<S>(cout, " "));
00092 return false;
00093 }
00094 }
00095
00096 gm::Pose makePose (const double x, const double y, const double theta)
00097 {
00098 gm::Pose p;
00099 p.position.x = x;
00100 p.position.y = y;
00101 p.orientation = tf::createQuaternionMsgFromYaw(theta);
00102 return p;
00103 }
00104
00105 void printGraph (const PoseGraph& g)
00106 {
00107 BOOST_FOREACH (const NodeId n, g.allNodes()) {
00108 cout << "\nNode " << n << ". Edges: ";
00109 set<EdgeId> edges = g.incidentEdges(n);
00110 copy(edges.begin(), edges.end(), ostream_iterator<EdgeId>(cout, " "));
00111 }
00112 cout << "\n";
00113 }
00114
00115 const double TOL=1e-3;
00116
00117 bool approxEqual (const gm::Quaternion q1, const gm::Quaternion q2)
00118 {
00119 const double dx = q1.x - q2.x;
00120 const double dy = q1.y - q2.y;
00121 const double dz = q1.z - q2.z;
00122 const double dw = q1.w - q2.w;
00123 return dx*dx + dy*dy + dz*dz + dw*dw < TOL;
00124 }
00125
00126 bool approxEqualPoses (const gm::Pose& p1, const gm::Pose& p2)
00127 {
00128 return (euclideanDistance(p1.position, p2.position) < TOL) &&
00129 approxEqual(p1.orientation, p2.orientation);
00130 }
00131
00132
00133
00134 TEST(PoseGraph, GraphOps)
00135 {
00136 PoseGraph g;
00137 PoseConstraint constraint(Vector3d(1, 2, 3), Quaterniond(1, 0, 0, 0));
00138 PoseConstraint constraint2(Vector3d(2, -1, 1), Quaterniond(0, 1, 0, 0));
00139
00140 const set<NodeId> empty;
00141 EXPECT_PRED2(equalSets<NodeId>, empty, g.allNodes());
00142
00143
00144
00145
00146
00147
00148
00149 NodeId n1 = g.addNode();
00150 NodeId n2 = g.addNode();
00151 NodeId n3 = g.addNode();
00152 EdgeId e12 = g.addEdge(n1, n2, constraint);
00153 EdgeId e23 = g.addEdge(n2, n3, constraint2);
00154 EdgeId e23b = g.addEdge(n2, n3, constraint);
00155
00156 set<EdgeId> edges1, edges2, edges;
00157 edges1.insert(e12);
00158 edges2.insert(e12);
00159 edges2.insert(e23);
00160 edges2.insert(e23b);
00161 edges.insert(e12);
00162 edges.insert(e23);
00163 edges.insert(e23b);
00164
00165 set<NodeId> nodes;
00166 nodes.insert(n1);
00167 nodes.insert(n2);
00168 nodes.insert(n3);
00169
00170 gm::Pose pose, identity;
00171 identity.orientation.w=1.0;
00172 pose.position.y = 42.0;
00173 pose.orientation.z = 24.0;
00174 g.setInitialPoseEstimate(n2, pose);
00175 EXPECT_THROW(g.setInitialPoseEstimate(NodeId(42), pose), UnknownNodeIdException);
00176 EXPECT_EQ(identity, g.getInitialPoseEstimate(n1));
00177 EXPECT_EQ(pose, g.getInitialPoseEstimate(n2));
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 EXPECT_TRUE(e23!=e23b);
00189 EXPECT_TRUE(equalSets(g.incidentEdges(n1), edges1));
00190 EXPECT_TRUE(equalSets(g.incidentEdges(n2), edges2));
00191
00192
00193
00194
00195
00196 LocalizedCloud::Ptr s1(new LocalizedCloud());
00197 LocalizedCloud::Ptr s3(new LocalizedCloud());
00198 s1->cloud.points.resize(1);
00199 s3->cloud.points.resize(2);
00200 EXPECT_TRUE(!g.hasCloud(n3));
00201 g.attachCloud(n3, s3);
00202 EXPECT_TRUE(g.hasCloud(n3));
00203 EXPECT_TRUE(!g.hasCloud(n1));
00204 g.attachCloud(n1, s1);
00205
00206 EXPECT_EQ(2u, g.getCloud(n3)->cloud.points.size());
00207 EXPECT_EQ(1u, g.getCloud(n1)->cloud.points.size());
00208
00209
00210
00211
00212
00213
00214
00215 const PoseGraph g2(g);
00216
00217 EXPECT_TRUE(equalSets(g2.incidentEdges(n1), edges1));
00218 EXPECT_TRUE(equalSets(g2.incidentEdges(n2), edges2));
00219 EXPECT_TRUE(equalSets(g2.allEdges(), edges));
00220 EXPECT_TRUE(equalSets(g2.allNodes(), nodes));
00221
00222
00223
00224
00225
00226 const PoseGraphMessage m = poseGraphToRos(g2);
00227 const PoseGraph g3 = poseGraphFromRos(m);
00228
00229 EXPECT_TRUE(equalSets(g3.incidentEdges(n1), edges1));
00230 EXPECT_TRUE(equalSets(g3.incidentEdges(n2), edges2));
00231 EXPECT_TRUE(equalSets(g3.allEdges(), edges));
00232 EXPECT_TRUE(equalSets(g3.allNodes(), nodes));
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 const PoseGraph g4(g3);
00254
00255
00256
00257
00258
00259
00260 EXPECT_EQ(sqrt(14), g4.edgeLength(e12));
00261 EXPECT_EQ(sqrt(6), g4.edgeLength(e23));
00262
00263
00264
00265
00266
00267 EXPECT_EQ(identity, g4.getInitialPoseEstimate(n1));
00268 EXPECT_EQ(pose, g4.getInitialPoseEstimate(n2));
00269 EXPECT_THROW(g4.getInitialPoseEstimate(NodeId(42)), UnknownNodeIdException);
00270
00271
00272
00273
00274
00275 EXPECT_EQ(2u, g4.getCloud(n3)->cloud.points.size());
00276 EXPECT_EQ(1u, g4.getCloud(n1)->cloud.points.size());
00277
00278
00279
00280
00281
00282
00283
00284 set<NodeId> neighbors1, neighbors2;
00285 neighbors1.insert(n2);
00286 neighbors2.insert(n2);
00287 neighbors2.insert(n3);
00288
00289 EXPECT_TRUE(equalSets(g4.nearbyNodes(n2, 2.0), neighbors1));
00290 EXPECT_TRUE(equalSets(g4.nearbyNodes(n2, 3.0), neighbors2));
00291 EXPECT_TRUE(equalSets(g4.nearbyNodes(n2, 4.0), nodes));
00292
00293
00294
00295
00296
00297 PoseGraph g5(g4);
00298 const NodeId n4=g5.addNode();
00299 const EdgeId e42=g5.addEdge(n4, n2, constraint2);
00300 g5.addEdge(n1, n4, constraint);
00301 set<NodeId> nodes234;
00302 nodes234 += n2, n3, n4;
00303 const PoseGraph g6 = g5.subgraph(nodes234);
00304
00305 EXPECT_PRED2 (equalSets<NodeId>, nodes234, g6.allNodes());
00306 set<EdgeId> edges234;
00307 edges234 += e23b, e23, e42;
00308 EXPECT_TRUE (equalSets<EdgeId>(edges234, g6.allEdges()));
00309 EXPECT_TRUE (equalSets<EdgeId>(edges234, g6.incidentEdges(n2)));
00310 set<EdgeId> edges42;
00311 edges42 += e42;
00312 EXPECT_TRUE (equalSets<EdgeId>(edges42, g6.incidentEdges(n4)));
00313
00314
00315 }
00316
00317 TEST(PoseGraph, ShortestPath)
00318 {
00319 PoseGraph g;
00320 const PoseConstraint constraint(Vector3d(1, 2, 3), Quaterniond(1, 0, 0, 0));
00321 const PoseConstraint constraint2(Vector3d(2, -1, 1), Quaterniond(0.7071, 0, 0, 0.7071));
00322 const PoseConstraint constraint3(Vector3d(1, 0, 0), Quaterniond(0.9239,0,0,0.3827));
00323
00324 const NodeId n1 = g.addNode();
00325 const NodeId n2 = g.addNode();
00326 const NodeId n3 = g.addNode();
00327 const NodeId n4 = g.addNode();
00328 const NodeId n5 = g.addNode();
00329 const NodeId n6 = g.addNode();
00330 g.addEdge(n1, n2, constraint);
00331 g.addEdge(n2, n3, constraint2);
00332 g.addEdge(n1, n3, constraint3);
00333 g.addEdge(n1, n4, constraint3);
00334 g.addEdge(n5, n6, constraint);
00335
00336 const ShortestPathResult res = g.shortestPath(n1, n2);
00337 EXPECT_EQ(3u, res.nodes.size());
00338 EXPECT_EQ(n1, res.nodes[0]);
00339 EXPECT_EQ(n3, res.nodes[1]);
00340 EXPECT_EQ(n2, res.nodes[2]);
00341 EXPECT_EQ(1+sqrt(6), res.cost);
00342 EXPECT_TRUE(res.path_found);
00343 EXPECT_EQ(2u, res.edges.size());
00344 typedef std::pair<NodeId, NodeId> Nodes;
00345 Nodes nds1 = g.incidentNodes(res.edges[0]);
00346 Nodes nds2 = g.incidentNodes(res.edges[1]);
00347 EXPECT_EQ(n1, min(nds1.first, nds1.second));
00348 EXPECT_EQ(n3, max(nds1.first, nds1.second));
00349 EXPECT_EQ(n2, min(nds2.first, nds2.second));
00350 EXPECT_EQ(n3, max(nds2.first, nds2.second));
00351
00352 const ShortestPathResult res2 = g.shortestPath(n2, n4);
00353 EXPECT_EQ(4u, res2.nodes.size());
00354 EXPECT_EQ(2+sqrt(6), res2.cost);
00355 EXPECT_EQ(n4, res2.nodes[3]);
00356 EXPECT_EQ(n2, res2.nodes[0]);
00357 EXPECT_EQ(n1, res2.nodes[2]);
00358 EXPECT_EQ(n3, res2.nodes[1]);
00359 EXPECT_EQ(3u, res2.edges.size());
00360 EXPECT_TRUE(res2.path_found);
00361
00362 const ShortestPathResult res3 = g.shortestPath(n5, n1);
00363 EXPECT_TRUE(!res3.path_found);
00364
00365 const gm::Pose rel_pose = g.relativePose(n2, n1);
00366 gm::Pose expected_pose;
00367 expected_pose.position.x = 0.2928617;
00368 expected_pose.position.y = 2.12130969762;
00369 expected_pose.position.z = -1;
00370 expected_pose.orientation.z = -0.38267653;
00371 expected_pose.orientation.w = 0.923882353;
00372
00373 EXPECT_PRED2(approxEqualPoses, rel_pose, expected_pose);
00374 }
00375
00376
00377 double dist (const gm::Pose& p, const gm::Pose& p2)
00378 {
00379 const double dx=p.position.x-p2.position.x;
00380 const double dy=p.position.y-p2.position.y;
00381 return sqrt(dx*dx+dy*dy);
00382 }
00383
00384
00385 TEST(PoseGraph, Optimization)
00386 {
00387 PoseGraph g;
00388
00389 const NodeId n1 = g.addNode();
00390 const NodeId n2 = g.addNode();
00391 const NodeId n3 = g.addNode();
00392
00393 Eigen3::Matrix<double, 6, 6> p1, p2;
00394 p1 = Eigen3::MatrixXd::Identity(6,6);
00395 p2 = p1;
00396
00397 const PoseConstraint c12(Vector3d(1,0,0), Quaterniond(1, 0, 0, 0), p1);
00398 const PoseConstraint c13(Vector3d(0.5,1,0), Quaterniond(1, 0, 0, 0), p1);
00399 const PoseConstraint c32(Vector3d(0.5,-1,0), Quaterniond(1, 0, 0, 0), p2);
00400
00401 g.addEdge(n1, n2, c12);
00402 g.addEdge(n1, n3, c13);
00403 g.addEdge(n3, n2, c32);
00404
00405 g.setInitialPoseEstimate(n1, makePose(0, 0, 0));
00406 g.setInitialPoseEstimate(n2, makePose(2, 5, 0));
00407 g.setInitialPoseEstimate(n3, makePose(9, 1, 0));
00408
00409
00410 NodePoseMap opt=optimizeGraph(g);
00411
00412 EXPECT_TRUE (0.98 < dist(opt[n1], opt[n2]));
00413 EXPECT_TRUE (dist(opt[n1], opt[n2]) < 1.02);
00414 EXPECT_TRUE (1.1 < dist(opt[n3], opt[n2]));
00415 EXPECT_TRUE (dist(opt[n3], opt[n2]) < 1.2);
00416 EXPECT_TRUE (1.1 < dist(opt[n1], opt[n3]));
00417 EXPECT_TRUE (dist(opt[n1], opt[n3]) < 1.2);
00418
00419 NodePoseMap opt2d=optimizeGraph2D(g);
00420
00421 EXPECT_TRUE (0.98 < dist(opt2d[n1], opt2d[n2]));
00422 EXPECT_TRUE (dist(opt2d[n1], opt2d[n2]) < 1.02);
00423 EXPECT_TRUE (1.1 < dist(opt2d[n3], opt2d[n2]));
00424 EXPECT_TRUE (dist(opt2d[n3], opt2d[n2]) < 1.2);
00425 EXPECT_TRUE (1.1 < dist(opt2d[n1], opt2d[n3]));
00426 EXPECT_TRUE (dist(opt2d[n1], opt2d[n3]) < 1.2);
00427
00428
00429 }
00430
00431
00432 const double PI = 3.14159265;
00433
00434 LaserScan::Ptr makeScan ()
00435 {
00436 LaserScan::Ptr scan(new LaserScan());
00437 scan->angle_min = -PI/2;
00438 scan->angle_max = PI/2;
00439 scan->angle_increment = PI/4;
00440 scan->range_min = 0;
00441 scan->range_max = 10;
00442 scan->ranges.resize(5);
00443 fill(scan->ranges.begin(), scan->ranges.end(), 20);
00444 return scan;
00445 }
00446
00447
00448 int main (int argc, char** argv)
00449 {
00450 testing::InitGoogleTest(&argc, argv);
00451 return RUN_ALL_TESTS();
00452 }