335 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			335 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
| /*
 | |
|  * testNestedDissection.cpp
 | |
|  *
 | |
|  *   Created on: Nov 29, 2010
 | |
|  *       Author: nikai
 | |
|  *  Description: unit tests for NestedDissection
 | |
|  */
 | |
| 
 | |
| #include <boost/make_shared.hpp>
 | |
| #include <CppUnitLite/TestHarness.h>
 | |
| 
 | |
| #include "SubmapPlanarSLAM.h"
 | |
| #include "SubmapVisualSLAM.h"
 | |
| #include "SubmapExamples.h"
 | |
| #include "SubmapExamples3D.h"
 | |
| #include "GenericGraph.h"
 | |
| #include "NonlinearTSAM.h"
 | |
| #include "partition/NestedDissection-inl.h"
 | |
| 
 | |
| using namespace std;
 | |
| using namespace gtsam;
 | |
| using namespace gtsam::partition;
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| // x1 - x2
 | |
| //  \  /
 | |
| //   l1
 | |
| TEST ( NestedDissection, oneIsland )
 | |
| {
 | |
|   using namespace submapPlanarSLAM;
 | |
|   typedef TSAM2D::SubNLG SubNLG;
 | |
|   Graph fg;
 | |
|   fg.addOdometry(1, 2, Pose2(), odoNoise);
 | |
|   fg.addBearingRange(1, 1, Rot2(), 0., bearingRangeNoise);
 | |
|   fg.addBearingRange(2, 1, Rot2(), 0., bearingRangeNoise);
 | |
|   fg.addPoseConstraint(1, Pose2());
 | |
| 
 | |
|   Ordering ordering; ordering += x1, x2, l1;
 | |
| 
 | |
|   int numNodeStopPartition = 1e3;
 | |
|   int minNodesPerMap = 1e3;
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph2D> nd(fg, ordering, numNodeStopPartition, minNodesPerMap);
 | |
|   LONGS_EQUAL(4, nd.root()->size());
 | |
|   LONGS_EQUAL(3, nd.root()->frontal().size());
 | |
|   LONGS_EQUAL(0, nd.root()->children().size());
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| // x1\  /x4
 | |
| // |  x3  |
 | |
| // x2/  \x5
 | |
| TEST ( NestedDissection, TwoIslands )
 | |
| {
 | |
|   using namespace submapPlanarSLAM;
 | |
|   typedef TSAM2D::SubNLG SubNLG;
 | |
|   Graph fg;
 | |
|   fg.addOdometry(1, 2, Pose2(), odoNoise);
 | |
|   fg.addOdometry(1, 3, Pose2(), odoNoise);
 | |
|   fg.addOdometry(2, 3, Pose2(), odoNoise);
 | |
|   fg.addOdometry(3, 4, Pose2(), odoNoise);
 | |
|   fg.addOdometry(4, 5, Pose2(), odoNoise);
 | |
|   fg.addOdometry(3, 5, Pose2(), odoNoise);
 | |
|   fg.addPoseConstraint(1, Pose2());
 | |
|   fg.addPoseConstraint(4, Pose2());
 | |
|   Ordering ordering; ordering += x1, x2, x3, x4, x5;
 | |
| 
 | |
|   int numNodeStopPartition = 2;
 | |
|   int minNodesPerMap = 1;
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph2D> nd(fg, ordering, numNodeStopPartition, minNodesPerMap);
 | |
|   // root submap
 | |
|   LONGS_EQUAL(0, nd.root()->size());
 | |
|   LONGS_EQUAL(1, nd.root()->frontal().size());
 | |
|   LONGS_EQUAL(0, nd.root()->separator().size());
 | |
|   LONGS_EQUAL(2, nd.root()->children().size()); // 2 leaf submaps
 | |
| 
 | |
|   // the 1st submap
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->frontal().size());
 | |
|   LONGS_EQUAL(4, nd.root()->children()[0]->size());
 | |
| 
 | |
|   // the 2nd submap
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->frontal().size());
 | |
|   LONGS_EQUAL(4, nd.root()->children()[1]->size());
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| // x1\  /x4
 | |
| //    x3
 | |
| // x2/  \x5
 | |
| TEST ( NestedDissection, FourIslands )
 | |
| {
 | |
|   using namespace submapPlanarSLAM;
 | |
|   typedef TSAM2D::SubNLG SubNLG;
 | |
|   Graph fg;
 | |
|   fg.addOdometry(1, 3, Pose2(), odoNoise);
 | |
|   fg.addOdometry(2, 3, Pose2(), odoNoise);
 | |
|   fg.addOdometry(3, 4, Pose2(), odoNoise);
 | |
|   fg.addOdometry(3, 5, Pose2(), odoNoise);
 | |
|   fg.addPoseConstraint(1, Pose2());
 | |
|   fg.addPoseConstraint(4, Pose2());
 | |
|   Ordering ordering; ordering += x1, x2, x3, x4, x5;
 | |
| 
 | |
|   int numNodeStopPartition = 2;
 | |
|   int minNodesPerMap = 1;
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph2D> nd(fg, ordering, numNodeStopPartition, minNodesPerMap);
 | |
|   LONGS_EQUAL(0, nd.root()->size());
 | |
|   LONGS_EQUAL(1, nd.root()->frontal().size());
 | |
|   LONGS_EQUAL(0, nd.root()->separator().size());
 | |
|   LONGS_EQUAL(4, nd.root()->children().size()); // 4 leaf submaps
 | |
| 
 | |
|   // the 1st submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[0]->frontal().size());
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->size());
 | |
| 
 | |
|   // the 2nd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[1]->frontal().size());
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->size());
 | |
| 
 | |
|   // the 3rd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[2]->frontal().size());
 | |
|   LONGS_EQUAL(1, nd.root()->children()[2]->size());
 | |
| 
 | |
|   // the 4th submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[3]->frontal().size());
 | |
|   LONGS_EQUAL(1, nd.root()->children()[3]->size());
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| // x1\  /x3
 | |
| //  | x2  |
 | |
| // l6/  \x4
 | |
| //  |
 | |
| // x5
 | |
| TEST ( NestedDissection, weekLinks )
 | |
| {
 | |
|   using namespace submapPlanarSLAM;
 | |
|   typedef TSAM2D::SubNLG SubNLG;
 | |
|   Graph fg;
 | |
|   fg.addOdometry(1, 2, Pose2(), odoNoise);
 | |
|   fg.addOdometry(2, 3, Pose2(), odoNoise);
 | |
|   fg.addOdometry(2, 4, Pose2(), odoNoise);
 | |
|   fg.addOdometry(3, 4, Pose2(), odoNoise);
 | |
|   fg.addBearingRange(1, 6, Rot2(), 0., bearingRangeNoise);
 | |
|   fg.addBearingRange(2, 6, Rot2(), 0., bearingRangeNoise);
 | |
|   fg.addBearingRange(5, 6, Rot2(), 0., bearingRangeNoise);
 | |
|   fg.addPoseConstraint(1, Pose2());
 | |
|   fg.addPoseConstraint(4, Pose2());
 | |
|   fg.addPoseConstraint(5, Pose2());
 | |
|   Ordering ordering; ordering += x1, x2, x3, x4, x5, l6;
 | |
| 
 | |
|   int numNodeStopPartition = 2;
 | |
|   int minNodesPerMap = 1;
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph2D> nd(fg, ordering, numNodeStopPartition, minNodesPerMap);
 | |
|   LONGS_EQUAL(0, nd.root()->size()); // one weeklink
 | |
|   LONGS_EQUAL(1, nd.root()->frontal().size());
 | |
|   LONGS_EQUAL(0, nd.root()->separator().size());
 | |
|   LONGS_EQUAL(3, nd.root()->children().size()); // 4 leaf submaps
 | |
|   LONGS_EQUAL(1, nd.root()->weeklinks().size());
 | |
| 
 | |
|   // the 1st submap
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->frontal().size()); // x3 and x4
 | |
|   LONGS_EQUAL(4, nd.root()->children()[0]->size());
 | |
| 
 | |
|   // the 2nd submap
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->frontal().size()); // x1 and l6
 | |
|   LONGS_EQUAL(4, nd.root()->children()[1]->size());
 | |
|   //
 | |
|   // the 3rd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[2]->frontal().size()); // x5
 | |
|   LONGS_EQUAL(1, nd.root()->children()[2]->size());
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| /**
 | |
|  *  l1   l2   l3
 | |
|  *   | X  | X |
 | |
|  *  x0 - x1 - x2
 | |
|  *   | X  | X |
 | |
|  *  l4   l5   l6
 | |
|  */
 | |
| TEST ( NestedDissection, manual_cuts )
 | |
| {
 | |
|   using namespace submapPlanarSLAM;
 | |
|   typedef partition::Cuts Cuts;
 | |
|   typedef TSAM2D::SubNLG SubNLG;
 | |
|   typedef partition::PartitionTable PartitionTable;
 | |
|   Graph fg;
 | |
|   fg.addOdometry(x0, x1, Pose2(1.0, 0, 0), odoNoise);
 | |
|   fg.addOdometry(x1, x2, Pose2(1.0, 0, 0), odoNoise);
 | |
| 
 | |
|   fg.addBearingRange(x0, l1, Rot2::fromAngle( M_PI_2), 1,       bearingRangeNoise);
 | |
|   fg.addBearingRange(x0, l4, Rot2::fromAngle(-M_PI_2), 1,       bearingRangeNoise);
 | |
|   fg.addBearingRange(x0, l2, Rot2::fromAngle( M_PI_4), sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x0, l5, Rot2::fromAngle(-M_PI_4), sqrt(2), bearingRangeNoise);
 | |
| 
 | |
|   fg.addBearingRange(x1, l1, Rot2::fromAngle( M_PI_4 * 3), sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x1, l2, Rot2::fromAngle( M_PI_2),     1,       bearingRangeNoise);
 | |
|   fg.addBearingRange(x1, l3, Rot2::fromAngle( M_PI_4),     sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x1, l4, Rot2::fromAngle(-M_PI_4 * 3), sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x1, l5, Rot2::fromAngle( M_PI_2),     1,       bearingRangeNoise);
 | |
|   fg.addBearingRange(x1, l6, Rot2::fromAngle(-M_PI_4),     sqrt(2), bearingRangeNoise);
 | |
| 
 | |
|   fg.addBearingRange(x2, l2, Rot2::fromAngle( M_PI_4 * 3), sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x2, l5, Rot2::fromAngle(-M_PI_4 * 3), sqrt(2), bearingRangeNoise);
 | |
|   fg.addBearingRange(x2, l3, Rot2::fromAngle( M_PI_2),     1,       bearingRangeNoise);
 | |
|   fg.addBearingRange(x2, l6, Rot2::fromAngle(-M_PI_2),     1,       bearingRangeNoise);
 | |
| 
 | |
|   fg.addPrior(x0, Pose2(0.1, 0, 0), priorNoise);
 | |
| 
 | |
|   // generate ordering
 | |
|   Ordering ordering; ordering += x0, x1, x2, l1, l2, l3, l4, l5, l6;
 | |
| 
 | |
|   // define cuts
 | |
|   boost::shared_ptr<Cuts> cuts(new Cuts());
 | |
|   cuts->partitionTable = PartitionTable(9, -1); PartitionTable* p = &cuts->partitionTable;
 | |
|   //x0        x1         x2         l1         l2         l3         l4         l5         l6
 | |
|   (*p)[0]=1; (*p)[1]=0; (*p)[2]=2; (*p)[3]=1; (*p)[4]=0; (*p)[5]=2; (*p)[6]=1; (*p)[7]=0; (*p)[8]=2;
 | |
| 
 | |
|   cuts->children.push_back(boost::shared_ptr<Cuts>(new Cuts()));
 | |
|   cuts->children[0]->partitionTable = PartitionTable(9, -1); p = &cuts->children[0]->partitionTable;
 | |
|   //x0        x1          x2          l1         l2          l3          l4         l5          l6
 | |
|   (*p)[0]=0; (*p)[1]=-1; (*p)[2]=-1; (*p)[3]=1; (*p)[4]=-1; (*p)[5]=-1; (*p)[6]=2; (*p)[7]=-1; (*p)[8]=-1;
 | |
| 
 | |
|   cuts->children.push_back(boost::shared_ptr<Cuts>(new Cuts()));
 | |
|   cuts->children[1]->partitionTable = PartitionTable(9, -1); p = &cuts->children[1]->partitionTable;
 | |
|   //x0         x1          x2         l1          l2          l3         l4          l5          l6
 | |
|   (*p)[0]=-1; (*p)[1]=-1; (*p)[2]=0; (*p)[3]=-1; (*p)[4]=-1; (*p)[5]=1; (*p)[6]=-1; (*p)[7]=-1; (*p)[8]=2;
 | |
| 
 | |
| 
 | |
|   // nested dissection
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph2D> nd(fg, ordering, cuts);
 | |
|   LONGS_EQUAL(2, nd.root()->size());
 | |
|   LONGS_EQUAL(3, nd.root()->frontal().size());
 | |
|   LONGS_EQUAL(0, nd.root()->separator().size());
 | |
|   LONGS_EQUAL(2, nd.root()->children().size()); // 2 leaf submaps
 | |
|   LONGS_EQUAL(0, nd.root()->weeklinks().size());
 | |
| 
 | |
|   // the 1st submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[0]->frontal().size()); // x0
 | |
|   LONGS_EQUAL(4, nd.root()->children()[0]->size());
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->children().size());
 | |
| 
 | |
|   // the 1-1st submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[0]->children()[0]->frontal().size()); // l1
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->children()[0]->size());
 | |
| 
 | |
|   // the 1-2nd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[0]->children()[1]->frontal().size()); // l4
 | |
|   LONGS_EQUAL(2, nd.root()->children()[0]->children()[1]->size());
 | |
| 
 | |
|   // the 2nd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[1]->frontal().size()); // x2
 | |
|   LONGS_EQUAL(3, nd.root()->children()[1]->size());
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->children().size());
 | |
| 
 | |
|   // the 2-1st submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[1]->children()[0]->frontal().size()); // l3
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->children()[0]->size());
 | |
| 
 | |
|   // the 2-2nd submap
 | |
|   LONGS_EQUAL(1, nd.root()->children()[1]->children()[1]->frontal().size()); // l6
 | |
|   LONGS_EQUAL(2, nd.root()->children()[1]->children()[1]->size());
 | |
| 
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| //   l1-l8   l9-16    l17-124
 | |
| //    / |    /    \    | \
 | |
| // x0  x1             x2  x3
 | |
| TEST( NestedDissection, Graph3D) {
 | |
|   using namespace gtsam::submapVisualSLAM;
 | |
|   typedef TSAM3D::SubNLG SubNLG;
 | |
|   typedef partition::PartitionTable PartitionTable;
 | |
|   vector<GeneralCamera> cameras;
 | |
|   cameras.push_back(GeneralCamera(Pose3(Rot3(), Point3(-2., 0., 0.))));
 | |
|   cameras.push_back(GeneralCamera(Pose3(Rot3(), Point3(-1., 0., 0.))));
 | |
|   cameras.push_back(GeneralCamera(Pose3(Rot3(), Point3( 1., 0., 0.))));
 | |
|   cameras.push_back(GeneralCamera(Pose3(Rot3(), Point3( 2., 0., 0.))));
 | |
| 
 | |
|   vector<Point3> points;
 | |
|   for (int cube_index = 0; cube_index <= 3; cube_index++) {
 | |
|     Point3 center((cube_index-1) * 3, 0.5, 10.);
 | |
|     points.push_back(center + Point3(-0.5, -0.5, -0.5));
 | |
|     points.push_back(center + Point3(-0.5,  0.5, -0.5));
 | |
|     points.push_back(center + Point3( 0.5,  0.5, -0.5));
 | |
|     points.push_back(center + Point3( 0.5, -0.5, -0.5));
 | |
|     points.push_back(center + Point3(-0.5, -0.5,  0.5));
 | |
|     points.push_back(center + Point3(-0.5,  0.5,  0.5));
 | |
|     points.push_back(center + Point3( 0.5,  0.5,  0.5));
 | |
|     points.push_back(center + Point3( 0.5,  0.5,  0.5));
 | |
|   }
 | |
| 
 | |
|   Graph graph;
 | |
|   SharedDiagonal measurementNoise(gtsam::Vector_(2, 1., 1.));
 | |
|   SharedDiagonal measurementZeroNoise(gtsam::Vector_(2, 0., 0.));
 | |
|   for (int j=1; j<=8; j++)
 | |
|     graph.addMeasurement(0, j, cameras[0].project(points[j-1]).expmap(measurementZeroNoise->sample()), measurementNoise);
 | |
|   for (int j=1; j<=16; j++)
 | |
|     graph.addMeasurement(1, j, cameras[1].project(points[j-1]).expmap(measurementZeroNoise->sample()), measurementNoise);
 | |
|   for (int j=9; j<=24; j++)
 | |
|     graph.addMeasurement(2, j, cameras[2].project(points[j-1]).expmap(measurementZeroNoise->sample()), measurementNoise);
 | |
|   for (int j=17; j<=24; j++)
 | |
|     graph.addMeasurement(3, j, cameras[3].project(points[j-1]).expmap(measurementZeroNoise->sample()), measurementNoise);
 | |
| 
 | |
|   // make an easy ordering
 | |
|   Ordering ordering; ordering += x0, x1, x2, x3;
 | |
|   for (int j=1; j<=24; j++)
 | |
|     ordering += Symbol('l', j);
 | |
| 
 | |
|   // nested dissection
 | |
|   const int numNodeStopPartition = 10;
 | |
|   const int minNodesPerMap = 5;
 | |
|   NestedDissection<Graph, SubNLG, GenericGraph3D> nd(graph, ordering, numNodeStopPartition, minNodesPerMap);
 | |
| 
 | |
|   LONGS_EQUAL(0, nd.root()->size());
 | |
|   LONGS_EQUAL(8, nd.root()->frontal().size()); // l9-l16
 | |
|   LONGS_EQUAL(0, nd.root()->separator().size());
 | |
|   LONGS_EQUAL(2, nd.root()->children().size()); // 2 leaf submaps
 | |
|   LONGS_EQUAL(0, nd.root()->weeklinks().size());
 | |
| 
 | |
|   // the 1st submap
 | |
|   LONGS_EQUAL(10, nd.root()->children()[0]->frontal().size()); // x0, x1, l1-l8
 | |
|   LONGS_EQUAL(24, nd.root()->children()[0]->size()); // 8 + 16
 | |
|   LONGS_EQUAL(0, nd.root()->children()[0]->children().size());
 | |
| 
 | |
|   // the 2nd submap
 | |
|   LONGS_EQUAL(10, nd.root()->children()[1]->frontal().size()); // x2, x3, l1-l8
 | |
|   LONGS_EQUAL(24, nd.root()->children()[1]->size()); // 16 + 8
 | |
|   LONGS_EQUAL(0, nd.root()->children()[1]->children().size());
 | |
| }
 | |
| 
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
 | |
| /* ************************************************************************* */
 |