testNonlinearISAM.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /**
  2. * @file testNonlinearISAM
  3. * @author Alex Cunningham
  4. */
  5. #include <CppUnitLite/TestHarness.h>
  6. #include <gtsam/slam/BetweenFactor.h>
  7. #include <gtsam/sam/BearingRangeFactor.h>
  8. #include <gtsam/slam/dataset.h>
  9. #include <gtsam/nonlinear/NonlinearEquality.h>
  10. #include <gtsam/nonlinear/NonlinearISAM.h>
  11. #include <gtsam/nonlinear/NonlinearFactorGraph.h>
  12. #include <gtsam/nonlinear/Values.h>
  13. #include <gtsam/inference/Symbol.h>
  14. #include <gtsam/linear/Sampler.h>
  15. #include <gtsam/geometry/Pose2.h>
  16. #include <iostream>
  17. #include <sstream>
  18. using namespace std;
  19. using namespace gtsam;
  20. const double tol=1e-5;
  21. /* ************************************************************************* */
  22. TEST(testNonlinearISAM, markov_chain ) {
  23. int reorder_interval = 2;
  24. NonlinearISAM isamChol(reorder_interval, EliminatePreferCholesky); // create an ISAM object
  25. NonlinearISAM isamQR(reorder_interval, EliminateQR); // create an ISAM object
  26. SharedDiagonal model = noiseModel::Diagonal::Sigmas(Vector3(3.0, 3.0, 0.5));
  27. Sampler sampler(model, 42u);
  28. // create initial graph
  29. Pose2 cur_pose; // start at origin
  30. NonlinearFactorGraph start_factors;
  31. start_factors += NonlinearEquality<Pose2>(0, cur_pose);
  32. Values init;
  33. Values expected;
  34. init.insert(0, cur_pose);
  35. expected.insert(0, cur_pose);
  36. isamChol.update(start_factors, init);
  37. isamQR.update(start_factors, init);
  38. // loop for a period of time to verify memory usage
  39. size_t nrPoses = 21;
  40. Pose2 z(1.0, 2.0, 0.1);
  41. for (size_t i=1; i<=nrPoses; ++i) {
  42. NonlinearFactorGraph new_factors;
  43. new_factors += BetweenFactor<Pose2>(i-1, i, z, model);
  44. Values new_init;
  45. cur_pose = cur_pose.compose(z);
  46. new_init.insert(i, cur_pose.retract(sampler.sample()));
  47. expected.insert(i, cur_pose);
  48. isamChol.update(new_factors, new_init);
  49. isamQR.update(new_factors, new_init);
  50. }
  51. // verify values - all but the last one should be very close
  52. Values actualChol = isamChol.estimate();
  53. for (size_t i=0; i<nrPoses; ++i) {
  54. EXPECT(assert_equal(expected.at<Pose2>(i), actualChol.at<Pose2>(i), tol));
  55. }
  56. Values actualQR = isamQR.estimate();
  57. for (size_t i=0; i<nrPoses; ++i) {
  58. EXPECT(assert_equal(expected.at<Pose2>(i), actualQR.at<Pose2>(i), tol));
  59. }
  60. }
  61. /* ************************************************************************* */
  62. TEST(testNonlinearISAM, markov_chain_with_disconnects ) {
  63. int reorder_interval = 2;
  64. NonlinearISAM isamChol(reorder_interval, EliminatePreferCholesky); // create an ISAM object
  65. NonlinearISAM isamQR(reorder_interval, EliminateQR); // create an ISAM object
  66. SharedDiagonal model3 = noiseModel::Diagonal::Sigmas(Vector3(3.0, 3.0, 0.5));
  67. SharedDiagonal model2 = noiseModel::Diagonal::Sigmas(Vector2(2.0, 2.0));
  68. Sampler sampler(model3, 42u);
  69. // create initial graph
  70. Pose2 cur_pose; // start at origin
  71. NonlinearFactorGraph start_factors;
  72. start_factors += NonlinearEquality<Pose2>(0, cur_pose);
  73. Values init;
  74. Values expected;
  75. init.insert(0, cur_pose);
  76. expected.insert(0, cur_pose);
  77. isamChol.update(start_factors, init);
  78. isamQR.update(start_factors, init);
  79. size_t nrPoses = 21;
  80. // create a constrained constellation of landmarks
  81. Key lm1 = nrPoses+1, lm2 = nrPoses+2, lm3 = nrPoses+3;
  82. Point2 landmark1(3., 4.), landmark2(6., 4.), landmark3(6., 9.);
  83. expected.insert(lm1, landmark1);
  84. expected.insert(lm2, landmark2);
  85. expected.insert(lm3, landmark3);
  86. // loop for a period of time to verify memory usage
  87. Pose2 z(1.0, 2.0, 0.1);
  88. for (size_t i=1; i<=nrPoses; ++i) {
  89. NonlinearFactorGraph new_factors;
  90. new_factors += BetweenFactor<Pose2>(i-1, i, z, model3);
  91. Values new_init;
  92. cur_pose = cur_pose.compose(z);
  93. new_init.insert(i, cur_pose.retract(sampler.sample()));
  94. expected.insert(i, cur_pose);
  95. // Add a floating landmark constellation
  96. if (i == 7) {
  97. new_factors.addPrior(lm1, landmark1, model2);
  98. new_factors.addPrior(lm2, landmark2, model2);
  99. new_factors.addPrior(lm3, landmark3, model2);
  100. // Initialize to origin
  101. new_init.insert(lm1, Point2(0,0));
  102. new_init.insert(lm2, Point2(0,0));
  103. new_init.insert(lm3, Point2(0,0));
  104. }
  105. isamChol.update(new_factors, new_init);
  106. isamQR.update(new_factors, new_init);
  107. }
  108. // verify values - all but the last one should be very close
  109. Values actualChol = isamChol.estimate();
  110. for (size_t i=0; i<nrPoses; ++i)
  111. EXPECT(assert_equal(expected.at<Pose2>(i), actualChol.at<Pose2>(i), tol));
  112. Values actualQR = isamQR.estimate();
  113. for (size_t i=0; i<nrPoses; ++i)
  114. EXPECT(assert_equal(expected.at<Pose2>(i), actualQR.at<Pose2>(i), tol));
  115. // Check landmarks
  116. EXPECT(assert_equal(expected.at<Point2>(lm1), actualChol.at<Point2>(lm1), tol));
  117. EXPECT(assert_equal(expected.at<Point2>(lm2), actualChol.at<Point2>(lm2), tol));
  118. EXPECT(assert_equal(expected.at<Point2>(lm3), actualChol.at<Point2>(lm3), tol));
  119. EXPECT(assert_equal(expected.at<Point2>(lm1), actualQR.at<Point2>(lm1), tol));
  120. EXPECT(assert_equal(expected.at<Point2>(lm2), actualQR.at<Point2>(lm2), tol));
  121. EXPECT(assert_equal(expected.at<Point2>(lm3), actualQR.at<Point2>(lm3), tol));
  122. }
  123. /* ************************************************************************* */
  124. TEST(testNonlinearISAM, markov_chain_with_reconnect ) {
  125. int reorder_interval = 2;
  126. NonlinearISAM isamChol(reorder_interval, EliminatePreferCholesky); // create an ISAM object
  127. NonlinearISAM isamQR(reorder_interval, EliminateQR); // create an ISAM object
  128. SharedDiagonal model3 = noiseModel::Diagonal::Sigmas(Vector3(3.0, 3.0, 0.5));
  129. SharedDiagonal model2 = noiseModel::Diagonal::Sigmas(Vector2(2.0, 2.0));
  130. Sampler sampler(model3, 42u);
  131. // create initial graph
  132. Pose2 cur_pose; // start at origin
  133. NonlinearFactorGraph start_factors;
  134. start_factors += NonlinearEquality<Pose2>(0, cur_pose);
  135. Values init;
  136. Values expected;
  137. init.insert(0, cur_pose);
  138. expected.insert(0, cur_pose);
  139. isamChol.update(start_factors, init);
  140. isamQR.update(start_factors, init);
  141. size_t nrPoses = 21;
  142. // create a constrained constellation of landmarks
  143. Key lm1 = nrPoses+1, lm2 = nrPoses+2, lm3 = nrPoses+3;
  144. Point2 landmark1(3., 4.), landmark2(6., 4.), landmark3(6., 9.);
  145. expected.insert(lm1, landmark1);
  146. expected.insert(lm2, landmark2);
  147. expected.insert(lm3, landmark3);
  148. // loop for a period of time to verify memory usage
  149. Pose2 z(1.0, 2.0, 0.1);
  150. for (size_t i=1; i<=nrPoses; ++i) {
  151. NonlinearFactorGraph new_factors;
  152. new_factors += BetweenFactor<Pose2>(i-1, i, z, model3);
  153. Values new_init;
  154. cur_pose = cur_pose.compose(z);
  155. new_init.insert(i, cur_pose.retract(sampler.sample()));
  156. expected.insert(i, cur_pose);
  157. // Add a floating landmark constellation
  158. if (i == 7) {
  159. new_factors.addPrior(lm1, landmark1, model2);
  160. new_factors.addPrior(lm2, landmark2, model2);
  161. new_factors.addPrior(lm3, landmark3, model2);
  162. // Initialize to origin
  163. new_init.insert(lm1, Point2(0,0));
  164. new_init.insert(lm2, Point2(0,0));
  165. new_init.insert(lm3, Point2(0,0));
  166. }
  167. // Reconnect with observation later
  168. if (i == 15) {
  169. new_factors += BearingRangeFactor<Pose2, Point2>(
  170. i, lm1, cur_pose.bearing(landmark1), cur_pose.range(landmark1), model2);
  171. }
  172. isamChol.update(new_factors, new_init);
  173. isamQR.update(new_factors, new_init);
  174. }
  175. // verify values - all but the last one should be very close
  176. Values actualChol = isamChol.estimate();
  177. for (size_t i=0; i<nrPoses; ++i)
  178. EXPECT(assert_equal(expected.at<Pose2>(i), actualChol.at<Pose2>(i), 1e-4));
  179. Values actualQR = isamQR.estimate();
  180. for (size_t i=0; i<nrPoses; ++i)
  181. EXPECT(assert_equal(expected.at<Pose2>(i), actualQR.at<Pose2>(i), 1e-4));
  182. // Check landmarks
  183. EXPECT(assert_equal(expected.at<Point2>(lm1), actualChol.at<Point2>(lm1), tol));
  184. EXPECT(assert_equal(expected.at<Point2>(lm2), actualChol.at<Point2>(lm2), tol));
  185. EXPECT(assert_equal(expected.at<Point2>(lm3), actualChol.at<Point2>(lm3), tol));
  186. EXPECT(assert_equal(expected.at<Point2>(lm1), actualQR.at<Point2>(lm1), tol));
  187. EXPECT(assert_equal(expected.at<Point2>(lm2), actualQR.at<Point2>(lm2), tol));
  188. EXPECT(assert_equal(expected.at<Point2>(lm3), actualQR.at<Point2>(lm3), tol));
  189. }
  190. /* ************************************************************************* */
  191. TEST(testNonlinearISAM, loop_closures ) {
  192. int relinearizeInterval = 100;
  193. NonlinearISAM isam(relinearizeInterval);
  194. // Create a Factor Graph and Values to hold the new data
  195. NonlinearFactorGraph graph;
  196. Values initialEstimate;
  197. vector<string> lines;
  198. lines.emplace_back("VERTEX2 0 0.000000 0.000000 0.000000");
  199. lines.emplace_back("EDGE2 1 0 1.030390 0.011350 -0.012958");
  200. lines.emplace_back("VERTEX2 1 1.030390 0.011350 -0.012958");
  201. lines.emplace_back("EDGE2 2 1 1.013900 -0.058639 -0.013225");
  202. lines.emplace_back("VERTEX2 2 2.043445 -0.060422 -0.026183");
  203. lines.emplace_back("EDGE2 3 2 1.027650 -0.007456 0.004833");
  204. lines.emplace_back("VERTEX2 3 3.070548 -0.094779 -0.021350");
  205. lines.emplace_back("EDGE2 4 3 -0.012016 1.004360 1.566790");
  206. lines.emplace_back("VERTEX2 4 3.079976 0.909609 1.545440");
  207. lines.emplace_back("EDGE2 5 4 1.016030 0.014565 -0.016304");
  208. lines.emplace_back("VERTEX2 5 3.091176 1.925681 1.529136");
  209. lines.emplace_back("EDGE2 6 5 1.023890 0.006808 0.010981");
  210. lines.emplace_back("VERTEX2 6 3.127018 2.948966 1.540117");
  211. lines.emplace_back("EDGE2 7 6 0.957734 0.003159 0.010901");
  212. lines.emplace_back("VERTEX2 7 3.153237 3.906347 1.551018");
  213. lines.emplace_back("EDGE2 8 7 -1.023820 -0.013668 -3.093240");
  214. lines.emplace_back("VERTEX2 8 3.146655 2.882457 -1.542222");
  215. lines.emplace_back("EDGE2 9 8 1.023440 0.013984 -0.007802");
  216. lines.emplace_back("EDGE2 9 5 0.033943 0.032439 -3.127400");
  217. lines.emplace_back("VERTEX2 9 3.189873 1.859834 -1.550024");
  218. lines.emplace_back("EDGE2 10 9 1.003350 0.022250 0.023491");
  219. lines.emplace_back("EDGE2 10 3 0.044020 0.988477 -1.563530");
  220. lines.emplace_back("VERTEX2 10 3.232959 0.857162 -1.526533");
  221. lines.emplace_back("EDGE2 11 10 0.977245 0.019042 -0.028623");
  222. lines.emplace_back("VERTEX2 11 3.295225 -0.118283 -1.555156");
  223. lines.emplace_back("EDGE2 12 11 -0.996880 -0.025512 -3.126915");
  224. lines.emplace_back("VERTEX2 12 3.254125 0.878076 1.601114");
  225. lines.emplace_back("EDGE2 13 12 0.990646 0.018396 -0.016519");
  226. lines.emplace_back("VERTEX2 13 3.205708 1.867709 1.584594");
  227. lines.emplace_back("EDGE2 14 13 0.945873 0.008893 -0.002602");
  228. lines.emplace_back("EDGE2 14 8 0.015808 0.021059 3.128310");
  229. lines.emplace_back("VERTEX2 14 3.183765 2.813370 1.581993");
  230. lines.emplace_back("EDGE2 15 14 1.000010 0.006428 0.028234");
  231. lines.emplace_back("EDGE2 15 7 -0.014728 -0.001595 -0.019579");
  232. lines.emplace_back("VERTEX2 15 3.166141 3.813245 1.610227");
  233. auto model = noiseModel::Diagonal::Sigmas(Vector3(3.0, 3.0, 0.5));
  234. // Loop over the different poses, adding the observations to iSAM incrementally
  235. for (const string& str : lines) {
  236. // scan the tag
  237. string tag;
  238. istringstream is(str);
  239. if (!(is >> tag))
  240. break;
  241. // Check if vertex
  242. const auto indexedPose = parseVertexPose(is, tag);
  243. if (indexedPose) {
  244. Key id = indexedPose->first;
  245. initialEstimate.insert(Symbol('x', id), indexedPose->second);
  246. if (id == 0) {
  247. noiseModel::Diagonal::shared_ptr priorNoise =
  248. noiseModel::Diagonal::Sigmas(Vector3(0.001, 0.001, 0.001));
  249. graph.addPrior(Symbol('x', id), Pose2(0, 0, 0), priorNoise);
  250. } else {
  251. isam.update(graph, initialEstimate);
  252. // Clear the factor graph and values for the next iteration
  253. graph.resize(0);
  254. initialEstimate.clear();
  255. }
  256. }
  257. // check if edge
  258. const auto betweenPose = parseEdge(is, tag);
  259. if (betweenPose) {
  260. size_t id1, id2;
  261. tie(id1, id2) = betweenPose->first;
  262. graph.emplace_shared<BetweenFactor<Pose2> >(Symbol('x', id2),
  263. Symbol('x', id1), betweenPose->second, model);
  264. }
  265. }
  266. EXPECT_LONGS_EQUAL(16, isam.estimate().size())
  267. }
  268. /* ************************************************************************* */
  269. int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
  270. /* ************************************************************************* */