test_pose_graph.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
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 } // namespace geometry_msgs
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    * Add a few nodes and edges
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   cout << "\nEdges2: ";
00181   copy(edges2.begin(), edges2.end(), ostream_iterator<EdgeId>(cout, " "));
00182   */
00183 
00184   /****************************************
00185    * Check they were added correctly
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    * Debug cloud attaching
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    * Test the copy constructor
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    * Conversion to Ros messages
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    * File input/output
00236    ****************************************/
00237 
00238   // Currently doesn't work
00239 
00240   /*
00241 
00242   string filename="/tmp/test_pose_graph.bag";
00243   writeToFile(g3, filename);
00244   const PoseGraph g4 = readFromFile(filename);
00245 
00246   EXPECT_TRUE(equalSets(g4.incidentEdges(n1), edges1));
00247   EXPECT_TRUE(equalSets(g4.incidentEdges(n2), edges2));
00248   EXPECT_TRUE(equalSets(g4.allEdges(), edges));
00249   EXPECT_TRUE(equalSets(g4.allNodes(), nodes));
00250   */
00251 
00252   // Instead
00253   const PoseGraph g4(g3);
00254   
00255 
00256   /****************************************
00257    * Test edge lengths were added and
00258    * copied correctly
00259    ****************************************/
00260   EXPECT_EQ(sqrt(14), g4.edgeLength(e12));
00261   EXPECT_EQ(sqrt(6), g4.edgeLength(e23));
00262 
00263   /****************************************
00264    * Initial pose estimates also
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    * Make sure the clouds are still there
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    * Test nearbyNodes
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    * Subgraph
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 }


pose_graph
Author(s): Bhaskara Marthi
autogenerated on Tue Jan 7 2014 11:17:15