diff --git a/.cproject b/.cproject
index 8eb74b58b..19d3912b0 100644
--- a/.cproject
+++ b/.cproject
@@ -1293,6 +1293,7 @@
make
+
testSimulated2DOriented.run
true
false
@@ -1332,6 +1333,7 @@
make
+
testSimulated2D.run
true
false
@@ -1339,6 +1341,7 @@
make
+
testSimulated3D.run
true
false
@@ -1442,7 +1445,6 @@
make
-
testErrors.run
true
false
@@ -1544,6 +1546,14 @@
true
true
+
+ make
+ -j4
+ testSmartProjectionPoseFactor.run
+ true
+ true
+ true
+
make
-j3
@@ -1745,7 +1755,6 @@
cpack
-
-G DEB
true
false
@@ -1753,7 +1762,6 @@
cpack
-
-G RPM
true
false
@@ -1761,7 +1769,6 @@
cpack
-
-G TGZ
true
false
@@ -1769,7 +1776,6 @@
cpack
-
--config CPackSourceConfig.cmake
true
false
@@ -1961,6 +1967,7 @@
make
+
tests/testGaussianISAM2
true
false
@@ -2112,7 +2119,6 @@
make
-
tests/testBayesTree.run
true
false
@@ -2120,7 +2126,6 @@
make
-
testBinaryBayesNet.run
true
false
@@ -2168,7 +2173,6 @@
make
-
testSymbolicBayesNet.run
true
false
@@ -2176,7 +2180,6 @@
make
-
tests/testSymbolicFactor.run
true
false
@@ -2184,7 +2187,6 @@
make
-
testSymbolicFactorGraph.run
true
false
@@ -2200,7 +2202,6 @@
make
-
tests/testBayesTree
true
false
@@ -3320,7 +3321,6 @@
make
-
testGraph.run
true
false
@@ -3328,7 +3328,6 @@
make
-
testJunctionTree.run
true
false
@@ -3336,7 +3335,6 @@
make
-
testSymbolicBayesNetB.run
true
false
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 000000000..ac7af2e80
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+/html/
diff --git a/examples/Pose2SLAMExampleExpressions.cpp b/examples/Pose2SLAMExampleExpressions.cpp
index 92f94c3f3..1f6de6cb1 100644
--- a/examples/Pose2SLAMExampleExpressions.cpp
+++ b/examples/Pose2SLAMExampleExpressions.cpp
@@ -14,20 +14,18 @@
* @brief Expressions version of Pose2SLAMExample.cpp
* @date Oct 2, 2014
* @author Frank Dellaert
- * @author Yong Dian Jian
*/
// The two new headers that allow using our Automatic Differentiation Expression framework
#include
#include
-// Header order is close to far
-#include
+// For an explanation of headers below, please see Pose2SLAMExample.cpp
+#include
+#include
+#include
#include
#include
-#include
-#include
-#include
using namespace std;
using namespace gtsam;
diff --git a/examples/Pose2SLAMExample_graph.cpp b/examples/Pose2SLAMExample_graph.cpp
index 46ca02ee4..c3d901507 100644
--- a/examples/Pose2SLAMExample_graph.cpp
+++ b/examples/Pose2SLAMExample_graph.cpp
@@ -16,11 +16,15 @@
* @author Frank Dellaert
*/
-#include
+// For an explanation of headers below, please see Pose2SLAMExample.cpp
#include
-#include
-#include
+#include
#include
+#include
+#include
+
+// This new header allows us to read examples easily from .graph files
+#include
using namespace std;
using namespace gtsam;
diff --git a/examples/Pose2SLAMExample_graphviz.cpp b/examples/Pose2SLAMExample_graphviz.cpp
index 818a9e577..314ccbdb4 100644
--- a/examples/Pose2SLAMExample_graphviz.cpp
+++ b/examples/Pose2SLAMExample_graphviz.cpp
@@ -16,11 +16,11 @@
* @author Frank Dellaert
*/
+// For an explanation of headers below, please see Pose2SLAMExample.cpp
#include
#include
-#include
-#include
#include
+#include
#include
using namespace std;
diff --git a/examples/Pose2SLAMwSPCG.cpp b/examples/Pose2SLAMwSPCG.cpp
index 4422586b0..2c25d2f8e 100644
--- a/examples/Pose2SLAMwSPCG.cpp
+++ b/examples/Pose2SLAMwSPCG.cpp
@@ -16,47 +16,15 @@
* @date June 2, 2012
*/
-/**
- * A simple 2D pose slam example solved using a Conjugate-Gradient method
- * - The robot moves in a 2 meter square
- * - The robot moves 2 meters each step, turning 90 degrees after each step
- * - The robot initially faces along the X axis (horizontal, to the right in 2D)
- * - We have full odometry between pose
- * - We have a loop closure constraint when the robot returns to the first position
- */
-
-// As this is a planar SLAM example, we will use Pose2 variables (x, y, theta) to represent
-// the robot positions
-#include
-#include
-
-// Each variable in the system (poses) must be identified with a unique key.
-// We can either use simple integer keys (1, 2, 3, ...) or symbols (X1, X2, L1).
-// Here we will use simple integer keys
-#include
-
-// In GTSAM, measurement functions are represented as 'factors'. Several common factors
-// have been provided with the library for solving robotics/SLAM/Bundle Adjustment problems.
-// Here we will use Between factors for the relative motion described by odometry measurements.
-// We will also use a Between Factor to encode the loop closure constraint
-// Also, we will initialize the robot at the origin using a Prior factor.
+// For an explanation of headers below, please see Pose2SLAMExample.cpp
#include
#include
-
-// When the factors are created, we will add them to a Factor Graph. As the factors we are using
-// are nonlinear factors, we will need a Nonlinear Factor Graph.
-#include
-
-// The nonlinear solvers within GTSAM are iterative solvers, meaning they linearize the
-// nonlinear functions around an initial linearization point, then solve the linear system
-// to update the linearization point. This happens repeatedly until the solver converges
-// to a consistent set of variable values. This requires us to specify an initial guess
-// for each variable, held in a Values container.
-#include
-
-#include
+#include
#include
+// In contrast to that example, however, we will use a PCG solver here
+#include
+
using namespace std;
using namespace gtsam;
@@ -66,32 +34,24 @@ int main(int argc, char** argv) {
NonlinearFactorGraph graph;
// 2a. Add a prior on the first pose, setting it to the origin
- // A prior factor consists of a mean and a noise model (covariance matrix)
Pose2 prior(0.0, 0.0, 0.0); // prior at origin
noiseModel::Diagonal::shared_ptr priorNoise = noiseModel::Diagonal::Sigmas(Vector3(0.3, 0.3, 0.1));
graph.push_back(PriorFactor(1, prior, priorNoise));
// 2b. Add odometry factors
- // For simplicity, we will use the same noise model for each odometry factor
noiseModel::Diagonal::shared_ptr odometryNoise = noiseModel::Diagonal::Sigmas(Vector3(0.2, 0.2, 0.1));
- // Create odometry (Between) factors between consecutive poses
graph.push_back(BetweenFactor(1, 2, Pose2(2.0, 0.0, M_PI_2), odometryNoise));
graph.push_back(BetweenFactor(2, 3, Pose2(2.0, 0.0, M_PI_2), odometryNoise));
graph.push_back(BetweenFactor(3, 4, Pose2(2.0, 0.0, M_PI_2), odometryNoise));
graph.push_back(BetweenFactor(4, 5, Pose2(2.0, 0.0, M_PI_2), odometryNoise));
// 2c. Add the loop closure constraint
- // This factor encodes the fact that we have returned to the same pose. In real systems,
- // these constraints may be identified in many ways, such as appearance-based techniques
- // with camera images.
- // We will use another Between Factor to enforce this constraint, with the distance set to zero,
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Sigmas(Vector3(0.2, 0.2, 0.1));
graph.push_back(BetweenFactor(5, 1, Pose2(0.0, 0.0, 0.0), model));
graph.print("\nFactor Graph:\n"); // print
// 3. Create the data structure to hold the initialEstimate estimate to the solution
- // For illustrative purposes, these have been deliberately set to incorrect values
Values initialEstimate;
initialEstimate.insert(1, Pose2(0.5, 0.0, 0.2));
initialEstimate.insert(2, Pose2(2.3, 0.1, 1.1));
@@ -104,15 +64,18 @@ int main(int argc, char** argv) {
LevenbergMarquardtParams parameters;
parameters.verbosity = NonlinearOptimizerParams::ERROR;
parameters.verbosityLM = LevenbergMarquardtParams::LAMBDA;
- parameters.linearSolverType = NonlinearOptimizerParams::Iterative;
- {
- parameters.iterativeParams = boost::make_shared();
- LevenbergMarquardtOptimizer optimizer(graph, initialEstimate, parameters);
- Values result = optimizer.optimize();
- result.print("Final Result:\n");
- cout << "subgraph solver final error = " << graph.error(result) << endl;
- }
+ // LM is still the outer optimization loop, but by specifying "Iterative" below
+ // We indicate that an iterative linear solver should be used.
+ // In addition, the *type* of the iterativeParams decides on the type of
+ // iterative solver, in this case the SPCG (subgraph PCG)
+ parameters.linearSolverType = NonlinearOptimizerParams::Iterative;
+ parameters.iterativeParams = boost::make_shared();
+
+ LevenbergMarquardtOptimizer optimizer(graph, initialEstimate, parameters);
+ Values result = optimizer.optimize();
+ result.print("Final Result:\n");
+ cout << "subgraph solver final error = " << graph.error(result) << endl;
return 0;
}
diff --git a/examples/SFMExample.cpp b/examples/SFMExample.cpp
index 5be266d11..38dd1ca81 100644
--- a/examples/SFMExample.cpp
+++ b/examples/SFMExample.cpp
@@ -15,13 +15,7 @@
* @author Duy-Nguyen Ta
*/
-/**
- * A structure-from-motion example with landmarks
- * - The landmarks form a 10 meter cube
- * - The robot rotates around the landmarks, always facing towards the cube
- */
-
-// For loading the data
+// For loading the data, see the comments therein for scenario (camera rotates around cube)
#include "SFMdata.h"
// Camera observations of landmarks (i.e. pixel coordinates) will be stored as Point2 (x, y).
diff --git a/examples/SFMExample_SmartFactor.cpp b/examples/SFMExample_SmartFactor.cpp
index b999e6600..fce046a59 100644
--- a/examples/SFMExample_SmartFactor.cpp
+++ b/examples/SFMExample_SmartFactor.cpp
@@ -17,51 +17,22 @@
* @author Frank Dellaert
*/
-/**
- * A structure-from-motion example with landmarks
- * - The landmarks form a 10 meter cube
- * - The robot rotates around the landmarks, always facing towards the cube
- */
-
-// For loading the data
-#include "SFMdata.h"
-
-// Camera observations of landmarks (i.e. pixel coordinates) will be stored as Point2 (x, y).
-#include
-
// In GTSAM, measurement functions are represented as 'factors'.
-// The factor we used here is SmartProjectionPoseFactor. Every smart factor represent a single landmark,
-// The SmartProjectionPoseFactor only optimize the pose of camera, not the calibration,
-// The calibration should be known.
+// The factor we used here is SmartProjectionPoseFactor.
+// Every smart factor represent a single landmark, seen from multiple cameras.
+// The SmartProjectionPoseFactor only optimizes for the poses of a camera,
+// not the calibration, which is assumed known.
#include
-// Also, we will initialize the robot at some location using a Prior factor.
-#include
-
-// When the factors are created, we will add them to a Factor Graph. As the factors we are using
-// are nonlinear factors, we will need a Nonlinear Factor Graph.
-#include
-
-// Finally, once all of the factors have been added to our factor graph, we will want to
-// solve/optimize to graph to find the best (Maximum A Posteriori) set of variable values.
-// GTSAM includes several nonlinear optimizers to perform this step. Here we will use a
-// trust-region method known as Powell's Degleg
-#include
-
-// The nonlinear solvers within GTSAM are iterative solvers, meaning they linearize the
-// nonlinear functions around an initial linearization point, then solve the linear system
-// to update the linearization point. This happens repeatedly until the solver converges
-// to a consistent set of variable values. This requires us to specify an initial guess
-// for each variable, held in a Values container.
-#include
-
-#include
+// For an explanation of these headers, see SFMExample.cpp
+#include "SFMdata.h"
+#include
using namespace std;
using namespace gtsam;
// Make the typename short so it looks much cleaner
-typedef gtsam::SmartProjectionPoseFactor SmartFactor;
+typedef SmartProjectionPoseFactor SmartFactor;
/* ************************************************************************* */
int main(int argc, char* argv[]) {
@@ -127,7 +98,8 @@ int main(int argc, char* argv[]) {
initialEstimate.print("Initial Estimates:\n");
// Optimize the graph and print results
- Values result = DoglegOptimizer(graph, initialEstimate).optimize();
+ LevenbergMarquardtOptimizer optimizer(graph, initialEstimate);
+ Values result = optimizer.optimize();
result.print("Final results:\n");
// A smart factor represent the 3D point inside the factor, not as a variable.
@@ -136,20 +108,20 @@ int main(int argc, char* argv[]) {
Values landmark_result;
for (size_t j = 0; j < points.size(); ++j) {
- // The output of point() is in boost::optional, as sometimes
- // the triangulation operation inside smart factor will encounter degeneracy.
- boost::optional point;
-
// The graph stores Factor shared_ptrs, so we cast back to a SmartFactor first
SmartFactor::shared_ptr smart = boost::dynamic_pointer_cast(graph[j]);
if (smart) {
- point = smart->point(result);
+ // The output of point() is in boost::optional, as sometimes
+ // the triangulation operation inside smart factor will encounter degeneracy.
+ boost::optional point = smart->point(result);
if (point) // ignore if boost::optional return NULL
landmark_result.insert(j, *point);
}
}
landmark_result.print("Landmark results:\n");
+ cout << "final error: " << graph.error(result) << endl;
+ cout << "number of iterations: " << optimizer.iterations() << endl;
return 0;
}
diff --git a/examples/SFMExample_SmartFactorPCG.cpp b/examples/SFMExample_SmartFactorPCG.cpp
new file mode 100644
index 000000000..49482292f
--- /dev/null
+++ b/examples/SFMExample_SmartFactorPCG.cpp
@@ -0,0 +1,126 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file SFMExample_SmartFactorPCG.cpp
+ * @brief Version of SFMExample_SmartFactor that uses Preconditioned Conjugate Gradient
+ * @author Frank Dellaert
+ */
+
+// For an explanation of these headers, see SFMExample_SmartFactor.cpp
+#include "SFMdata.h"
+#include
+
+// These extra headers allow us a LM outer loop with PCG linear solver (inner loop)
+#include
+#include
+#include
+
+using namespace std;
+using namespace gtsam;
+
+// Make the typename short so it looks much cleaner
+typedef SmartProjectionPoseFactor SmartFactor;
+
+/* ************************************************************************* */
+int main(int argc, char* argv[]) {
+
+ // Define the camera calibration parameters
+ Cal3_S2::shared_ptr K(new Cal3_S2(50.0, 50.0, 0.0, 50.0, 50.0));
+
+ // Define the camera observation noise model
+ noiseModel::Isotropic::shared_ptr measurementNoise =
+ noiseModel::Isotropic::Sigma(2, 1.0); // one pixel in u and v
+
+ // Create the set of ground-truth landmarks and poses
+ vector points = createPoints();
+ vector poses = createPoses();
+
+ // Create a factor graph
+ NonlinearFactorGraph graph;
+
+ // Simulated measurements from each camera pose, adding them to the factor graph
+ for (size_t j = 0; j < points.size(); ++j) {
+
+ // every landmark represent a single landmark, we use shared pointer to init the factor, and then insert measurements.
+ SmartFactor::shared_ptr smartfactor(new SmartFactor());
+
+ for (size_t i = 0; i < poses.size(); ++i) {
+
+ // generate the 2D measurement
+ SimpleCamera camera(poses[i], *K);
+ Point2 measurement = camera.project(points[j]);
+
+ // call add() function to add measurement into a single factor, here we need to add:
+ smartfactor->add(measurement, i, measurementNoise, K);
+ }
+
+ // insert the smart factor in the graph
+ graph.push_back(smartfactor);
+ }
+
+ // Add a prior on pose x0. This indirectly specifies where the origin is.
+ // 30cm std on x,y,z 0.1 rad on roll,pitch,yaw
+ noiseModel::Diagonal::shared_ptr poseNoise = noiseModel::Diagonal::Sigmas(
+ (Vector(6) << Vector3::Constant(0.3), Vector3::Constant(0.1)).finished());
+ graph.push_back(PriorFactor(0, poses[0], poseNoise));
+
+ // Fix the scale ambiguity by adding a prior
+ graph.push_back(PriorFactor(1, poses[1], poseNoise));
+
+ // Create the initial estimate to the solution
+ Values initialEstimate;
+ Pose3 delta(Rot3::rodriguez(-0.1, 0.2, 0.25), Point3(0.05, -0.10, 0.20));
+ for (size_t i = 0; i < poses.size(); ++i)
+ initialEstimate.insert(i, poses[i].compose(delta));
+
+ // We will use LM in the outer optimization loop, but by specifying "Iterative" below
+ // We indicate that an iterative linear solver should be used.
+ // In addition, the *type* of the iterativeParams decides on the type of
+ // iterative solver, in this case the SPCG (subgraph PCG)
+ LevenbergMarquardtParams parameters;
+ parameters.linearSolverType = NonlinearOptimizerParams::Iterative;
+ parameters.absoluteErrorTol = 1e-10;
+ parameters.relativeErrorTol = 1e-10;
+ parameters.maxIterations = 500;
+ PCGSolverParameters::shared_ptr pcg =
+ boost::make_shared();
+ pcg->preconditioner_ =
+ boost::make_shared();
+ // Following is crucial:
+ pcg->setEpsilon_abs(1e-10);
+ pcg->setEpsilon_rel(1e-10);
+ parameters.iterativeParams = pcg;
+
+ LevenbergMarquardtOptimizer optimizer(graph, initialEstimate, parameters);
+ Values result = optimizer.optimize();
+
+ // Display result as in SFMExample_SmartFactor.run
+ result.print("Final results:\n");
+ Values landmark_result;
+ for (size_t j = 0; j < points.size(); ++j) {
+ SmartFactor::shared_ptr smart = //
+ boost::dynamic_pointer_cast(graph[j]);
+ if (smart) {
+ boost::optional point = smart->point(result);
+ if (point) // ignore if boost::optional return NULL
+ landmark_result.insert(j, *point);
+ }
+ }
+
+ landmark_result.print("Landmark results:\n");
+ cout << "final error: " << graph.error(result) << endl;
+ cout << "number of iterations: " << optimizer.iterations() << endl;
+
+ return 0;
+}
+/* ************************************************************************* */
+
diff --git a/gtsam.h b/gtsam.h
index c5528309e..f8e0af28e 100644
--- a/gtsam.h
+++ b/gtsam.h
@@ -2310,23 +2310,23 @@ virtual class GeneralSFMFactor2 : gtsam::NoiseModelFactor {
};
#include
-template
+template
virtual class SmartProjectionPoseFactor : gtsam::NonlinearFactor {
SmartProjectionPoseFactor(double rankTol, double linThreshold,
- bool manageDegeneracy, bool enableEPI, const POSE& body_P_sensor);
+ bool manageDegeneracy, bool enableEPI, const gtsam::Pose3& body_P_sensor);
SmartProjectionPoseFactor(double rankTol);
SmartProjectionPoseFactor();
- void add(const gtsam::Point2& measured_i, size_t poseKey_i, const gtsam::noiseModel::Base* noise_i,
- const CALIBRATION* K_i);
+ void add(const gtsam::Point2& measured_i, size_t poseKey_i,
+ const gtsam::noiseModel::Base* noise_i, const CALIBRATION* K_i);
// enabling serialization functionality
//void serialize() const;
};
-typedef gtsam::SmartProjectionPoseFactor SmartProjectionPose3Factor;
+typedef gtsam::SmartProjectionPoseFactor SmartProjectionPose3Factor;
#include
diff --git a/gtsam/geometry/CameraSet.h b/gtsam/geometry/CameraSet.h
new file mode 100644
index 000000000..3a34ca1fd
--- /dev/null
+++ b/gtsam/geometry/CameraSet.h
@@ -0,0 +1,123 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file CameraSet.h
+ * @brief Base class to create smart factors on poses or cameras
+ * @author Frank Dellaert
+ * @date Feb 19, 2015
+ */
+
+#pragma once
+
+#include
+#include // for Cheirality exception
+#include
+#include
+
+namespace gtsam {
+
+/**
+ * @brief A set of cameras, all with their own calibration
+ * Assumes that a camera is laid out as 6 Pose3 parameters then calibration
+ */
+template
+class CameraSet: public std::vector {
+
+protected:
+
+ /**
+ * 2D measurement and noise model for each of the m views
+ * The order is kept the same as the keys that we use to create the factor.
+ */
+ typedef typename CAMERA::Measurement Z;
+
+ static const int ZDim = traits::dimension; ///< Measurement dimension
+ static const int Dim = traits::dimension; ///< Camera dimension
+
+public:
+
+ /// Definitions for blocks of F
+ typedef Eigen::Matrix MatrixZD; // F
+ typedef std::pair FBlock; // Fblocks
+
+ /**
+ * print
+ * @param s optional string naming the factor
+ * @param keyFormatter optional formatter useful for printing Symbols
+ */
+ void print(const std::string& s = "") const {
+ std::cout << s << "CameraSet, cameras = \n";
+ for (size_t k = 0; k < this->size(); ++k)
+ this->at(k).print();
+ }
+
+ /// equals
+ virtual bool equals(const CameraSet& p, double tol = 1e-9) const {
+ if (this->size() != p.size())
+ return false;
+ bool camerasAreEqual = true;
+ for (size_t i = 0; i < this->size(); i++) {
+ if (this->at(i).equals(p.at(i), tol) == false)
+ camerasAreEqual = false;
+ break;
+ }
+ return camerasAreEqual;
+ }
+
+ /**
+ * Project a point, with derivatives in this, point, and calibration
+ * throws CheiralityException
+ */
+ std::vector project(const Point3& point, boost::optional F =
+ boost::none, boost::optional E = boost::none,
+ boost::optional H = boost::none) const {
+
+ size_t nrCameras = this->size();
+ if (F) F->resize(ZDim * nrCameras, 6);
+ if (E) E->resize(ZDim * nrCameras, 3);
+ if (H && Dim > 6) H->resize(ZDim * nrCameras, Dim - 6);
+ std::vector z(nrCameras);
+
+ for (size_t i = 0; i < nrCameras; i++) {
+ Eigen::Matrix Fi;
+ Eigen::Matrix Ei;
+ Eigen::Matrix Hi;
+ z[i] = this->at(i).project(point, F ? &Fi : 0, E ? &Ei : 0, H ? &Hi : 0);
+ if (F) F->block(ZDim * i, 0) = Fi;
+ if (E) E->block(ZDim * i, 0) = Ei;
+ if (H) H->block(ZDim * i, 0) = Hi;
+ }
+ return z;
+ }
+
+private:
+
+ /// Serialization function
+ friend class boost::serialization::access;
+ template
+ void serialize(ARCHIVE & ar, const unsigned int version) {
+ ar & (*this);
+ }
+};
+
+template
+const int CameraSet::ZDim;
+
+template
+struct traits > : public Testable > {
+};
+
+template
+struct traits > : public Testable > {
+};
+
+} // \ namespace gtsam
diff --git a/gtsam/geometry/PinholeCamera.h b/gtsam/geometry/PinholeCamera.h
index 1edb1a496..bfc2bbb93 100644
--- a/gtsam/geometry/PinholeCamera.h
+++ b/gtsam/geometry/PinholeCamera.h
@@ -31,6 +31,14 @@ namespace gtsam {
template
class GTSAM_EXPORT PinholeCamera: public PinholeBaseK {
+public:
+
+ /**
+ * Some classes template on either PinholeCamera or StereoCamera,
+ * and this typedef informs those classes what "project" returns.
+ */
+ typedef Point2 Measurement;
+
private:
typedef PinholeBaseK Base; ///< base class has 3D pose as private member
diff --git a/gtsam/geometry/StereoCamera.cpp b/gtsam/geometry/StereoCamera.cpp
index 9170f8282..eb83fd10f 100644
--- a/gtsam/geometry/StereoCamera.cpp
+++ b/gtsam/geometry/StereoCamera.cpp
@@ -31,7 +31,7 @@ namespace gtsam {
/* ************************************************************************* */
StereoPoint2 StereoCamera::project(const Point3& point,
OptionalJacobian<3,6> H1, OptionalJacobian<3,3> H2,
- OptionalJacobian<3,6> H3) const {
+ OptionalJacobian<3,0> H3) const {
#ifdef STEREOCAMERA_CHAIN_RULE
const Point3 q = leftCamPose_.transform_to(point, H1, H2);
@@ -78,8 +78,7 @@ namespace gtsam {
}
#endif
if (H3)
- // TODO, this is set to zero, as Cal3_S2Stereo cannot be optimized yet
- H3->setZero();
+ throw std::runtime_error("StereoCamera::project does not support third derivative yet");
}
// finally translate
diff --git a/gtsam/geometry/StereoCamera.h b/gtsam/geometry/StereoCamera.h
index 913b1eab3..88ffbcdbd 100644
--- a/gtsam/geometry/StereoCamera.h
+++ b/gtsam/geometry/StereoCamera.h
@@ -28,32 +28,47 @@ namespace gtsam {
class GTSAM_EXPORT StereoCheiralityException: public std::runtime_error {
public:
- StereoCheiralityException() : std::runtime_error("Stereo Cheirality Exception") {}
+ StereoCheiralityException() :
+ std::runtime_error("Stereo Cheirality Exception") {
+ }
};
-
/**
* A stereo camera class, parameterize by left camera pose and stereo calibration
* @addtogroup geometry
*/
class GTSAM_EXPORT StereoCamera {
+public:
+
+ /**
+ * Some classes template on either PinholeCamera or StereoCamera,
+ * and this typedef informs those classes what "project" returns.
+ */
+ typedef StereoPoint2 Measurement;
+
private:
Pose3 leftCamPose_;
Cal3_S2Stereo::shared_ptr K_;
public:
- enum { dimension = 6 };
+ enum {
+ dimension = 6
+ };
/// @name Standard Constructors
/// @{
- StereoCamera() {
+ /// Default constructor allocates a calibration!
+ StereoCamera() :
+ K_(new Cal3_S2Stereo()) {
}
+ /// Construct from pose and shared calibration
StereoCamera(const Pose3& leftCamPose, const Cal3_S2Stereo::shared_ptr K);
+ /// Return shared pointer to calibration
const Cal3_S2Stereo::shared_ptr calibration() const {
return K_;
}
@@ -62,26 +77,28 @@ public:
/// @name Testable
/// @{
+ /// print
void print(const std::string& s = "") const {
leftCamPose_.print(s + ".camera.");
K_->print(s + ".calibration.");
}
+ /// equals
bool equals(const StereoCamera &camera, double tol = 1e-9) const {
- return leftCamPose_.equals(camera.leftCamPose_, tol) && K_->equals(
- *camera.K_, tol);
+ return leftCamPose_.equals(camera.leftCamPose_, tol)
+ && K_->equals(*camera.K_, tol);
}
/// @}
/// @name Manifold
/// @{
- /** Dimensionality of the tangent space */
+ /// Dimensionality of the tangent space
inline size_t dim() const {
return 6;
}
- /** Dimensionality of the tangent space */
+ /// Dimensionality of the tangent space
static inline size_t Dim() {
return 6;
}
@@ -100,10 +117,12 @@ public:
/// @name Standard Interface
/// @{
+ /// pose
const Pose3& pose() const {
return leftCamPose_;
}
+ /// baseline
double baseline() const {
return K_->baseline();
}
@@ -114,19 +133,16 @@ public:
* @param H2 derivative with respect to point
* @param H3 IGNORED (for calibration)
*/
- StereoPoint2 project(const Point3& point,
- OptionalJacobian<3, 6> H1 = boost::none,
- OptionalJacobian<3, 3> H2 = boost::none,
- OptionalJacobian<3, 6> H3 = boost::none) const;
+ StereoPoint2 project(const Point3& point, OptionalJacobian<3, 6> H1 =
+ boost::none, OptionalJacobian<3, 3> H2 = boost::none,
+ OptionalJacobian<3, 0> H3 = boost::none) const;
- /**
- *
- */
+ /// back-project a measurement
Point3 backproject(const StereoPoint2& z) const {
Vector measured = z.vector();
- double Z = K_->baseline()*K_->fx()/(measured[0]-measured[1]);
- double X = Z *(measured[0]- K_->px()) / K_->fx();
- double Y = Z *(measured[2]- K_->py()) / K_->fy();
+ double Z = K_->baseline() * K_->fx() / (measured[0] - measured[1]);
+ double X = Z * (measured[0] - K_->px()) / K_->fx();
+ double Y = Z * (measured[2] - K_->py()) / K_->fy();
Point3 world_point = leftCamPose_.transform_from(Point3(X, Y, Z));
return world_point;
}
@@ -134,7 +150,8 @@ public:
/// @}
private:
- /** utility functions */
+
+ /// utility function
Matrix3 Dproject_to_stereo_camera1(const Point3& P) const;
friend class boost::serialization::access;
@@ -147,8 +164,10 @@ private:
};
template<>
-struct traits : public internal::Manifold {};
+struct traits : public internal::Manifold {
+};
template<>
-struct traits : public internal::Manifold {};
+struct traits : public internal::Manifold {
+};
}
diff --git a/gtsam/geometry/tests/testCameraSet.cpp b/gtsam/geometry/tests/testCameraSet.cpp
new file mode 100644
index 000000000..42cf0f299
--- /dev/null
+++ b/gtsam/geometry/tests/testCameraSet.cpp
@@ -0,0 +1,114 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file testCameraSet.cpp
+ * @brief Unit tests for testCameraSet Class
+ * @author Frank Dellaert
+ * @date Feb 19, 2015
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+using namespace gtsam;
+
+/* ************************************************************************* */
+// Cal3Bundler test
+#include
+#include
+TEST(CameraSet, Pinhole) {
+ typedef PinholeCamera Camera;
+ typedef vector ZZ;
+ CameraSet set;
+ Camera camera;
+ set.push_back(camera);
+ set.push_back(camera);
+ Point3 p(0, 0, 1);
+ CHECK(assert_equal(set, set));
+ CameraSet set2 = set;
+ set2.push_back(camera);
+ CHECK(!set.equals(set2));
+
+ // Check measurements
+ Point2 expected;
+ ZZ z = set.project(p);
+ CHECK(assert_equal(expected, z[0]));
+ CHECK(assert_equal(expected, z[1]));
+
+ // Calculate expected derivatives using Pinhole
+ Matrix46 actualF;
+ Matrix43 actualE;
+ Matrix43 actualH;
+ {
+ Matrix26 F1;
+ Matrix23 E1;
+ Matrix23 H1;
+ camera.project(p, F1, E1, H1);
+ actualE << E1, E1;
+ actualF << F1, F1;
+ actualH << H1, H1;
+ }
+
+ // Check computed derivatives
+ Matrix F, E, H;
+ set.project(p, F, E, H);
+ CHECK(assert_equal(actualF, F));
+ CHECK(assert_equal(actualE, E));
+ CHECK(assert_equal(actualH, H));
+}
+
+/* ************************************************************************* */
+#include
+TEST(CameraSet, Stereo) {
+ typedef vector ZZ;
+ CameraSet set;
+ StereoCamera camera;
+ set.push_back(camera);
+ set.push_back(camera);
+ Point3 p(0, 0, 1);
+ EXPECT_LONGS_EQUAL(6, traits::dimension);
+
+ // Check measurements
+ StereoPoint2 expected(0, -1, 0);
+ ZZ z = set.project(p);
+ CHECK(assert_equal(expected, z[0]));
+ CHECK(assert_equal(expected, z[1]));
+
+ // Calculate expected derivatives using Pinhole
+ Matrix66 actualF;
+ Matrix63 actualE;
+ {
+ Matrix36 F1;
+ Matrix33 E1;
+ camera.project(p, F1, E1);
+ actualE << E1, E1;
+ actualF << F1, F1;
+ }
+
+ // Check computed derivatives
+ Matrix F, E;
+ set.project(p, F, E);
+ CHECK(assert_equal(actualF, F));
+ CHECK(assert_equal(actualE, E));
+}
+
+/* ************************************************************************* */
+int main() {
+ TestResult tr;
+ return TestRegistry::runAllTests(tr);
+}
+/* ************************************************************************* */
+
diff --git a/gtsam/linear/ConjugateGradientSolver.cpp b/gtsam/linear/ConjugateGradientSolver.cpp
index 5e7e08f61..a1b9e2d83 100644
--- a/gtsam/linear/ConjugateGradientSolver.cpp
+++ b/gtsam/linear/ConjugateGradientSolver.cpp
@@ -1,8 +1,20 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
/*
- * ConjugateGradientSolver.cpp
- *
- * Created on: Jun 4, 2014
- * Author: Yong-Dian Jian
+ * @file ConjugateGradientSolver.cpp
+ * @brief Implementation of Conjugate Gradient solver for a linear system
+ * @author Yong-Dian Jian
+ * @author Sungtae An
+ * @date Nov 6, 2014
*/
#include
@@ -35,7 +47,8 @@ std::string ConjugateGradientParameters::blasTranslator(const BLASKernel value)
}
/*****************************************************************************/
-ConjugateGradientParameters::BLASKernel ConjugateGradientParameters::blasTranslator(const std::string &src) {
+ConjugateGradientParameters::BLASKernel ConjugateGradientParameters::blasTranslator(
+ const std::string &src) {
std::string s = src; boost::algorithm::to_upper(s);
if (s == "GTSAM") return ConjugateGradientParameters::GTSAM;
@@ -43,6 +56,7 @@ ConjugateGradientParameters::BLASKernel ConjugateGradientParameters::blasTransla
return ConjugateGradientParameters::GTSAM;
}
+/*****************************************************************************/
}
diff --git a/gtsam/linear/ConjugateGradientSolver.h b/gtsam/linear/ConjugateGradientSolver.h
index 20e0c5262..2596a7403 100644
--- a/gtsam/linear/ConjugateGradientSolver.h
+++ b/gtsam/linear/ConjugateGradientSolver.h
@@ -20,7 +20,6 @@
#pragma once
#include
-#include
namespace gtsam {
@@ -87,9 +86,8 @@ public:
static BLASKernel blasTranslator(const std::string &s) ;
};
-/*********************************************************************************************/
/*
- * A template of linear preconditioned conjugate gradient method.
+ * A template for the linear preconditioned conjugate gradient method.
* System class should support residual(v, g), multiply(v,Av), scal(alpha,v), dot(v,v), axpy(alpha,x,y)
* leftPrecondition(v, L^{-1}v, rightPrecondition(v, L^{-T}v) where preconditioner M = L*L^T
* Note that the residual is in the preconditioned domain. Refer to Section 9.2 of Saad's book.
@@ -98,8 +96,9 @@ public:
* [1] Y. Saad, "Preconditioned Iterations," in Iterative Methods for Sparse Linear Systems,
* 2nd ed. SIAM, 2003, ch. 9, sec. 2, pp.276-281.
*/
-template
-V preconditionedConjugateGradient(const S &system, const V &initial, const ConjugateGradientParameters ¶meters) {
+template
+V preconditionedConjugateGradient(const S &system, const V &initial,
+ const ConjugateGradientParameters ¶meters) {
V estimate, residual, direction, q1, q2;
estimate = residual = direction = q1 = q2 = initial;
diff --git a/gtsam/linear/IterativeSolver.cpp b/gtsam/linear/IterativeSolver.cpp
index ab3ccde13..79ade1c8a 100644
--- a/gtsam/linear/IterativeSolver.cpp
+++ b/gtsam/linear/IterativeSolver.cpp
@@ -1,6 +1,17 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
/**
* @file IterativeSolver.cpp
- * @brief
+ * @brief Some support classes for iterative solvers
* @date Sep 3, 2012
* @author Yong-Dian Jian
*/
@@ -9,18 +20,22 @@
#include
#include
#include
+#include
#include
-#include
using namespace std;
namespace gtsam {
/*****************************************************************************/
-string IterativeOptimizationParameters::getVerbosity() const { return verbosityTranslator(verbosity_); }
+string IterativeOptimizationParameters::getVerbosity() const {
+ return verbosityTranslator(verbosity_);
+}
/*****************************************************************************/
-void IterativeOptimizationParameters::setVerbosity(const string &src) { verbosity_ = verbosityTranslator(src); }
+void IterativeOptimizationParameters::setVerbosity(const string &src) {
+ verbosity_ = verbosityTranslator(src);
+}
/*****************************************************************************/
void IterativeOptimizationParameters::print() const {
@@ -29,78 +44,82 @@ void IterativeOptimizationParameters::print() const {
/*****************************************************************************/
void IterativeOptimizationParameters::print(ostream &os) const {
- os << "IterativeOptimizationParameters:" << endl
- << "verbosity: " << verbosityTranslator(verbosity_) << endl;
+ os << "IterativeOptimizationParameters:" << endl << "verbosity: "
+ << verbosityTranslator(verbosity_) << endl;
}
/*****************************************************************************/
- ostream& operator<<(ostream &os, const IterativeOptimizationParameters &p) {
+ostream& operator<<(ostream &os, const IterativeOptimizationParameters &p) {
p.print(os);
return os;
}
/*****************************************************************************/
-IterativeOptimizationParameters::Verbosity IterativeOptimizationParameters::verbosityTranslator(const string &src) {
- string s = src; boost::algorithm::to_upper(s);
- if (s == "SILENT") return IterativeOptimizationParameters::SILENT;
- else if (s == "COMPLEXITY") return IterativeOptimizationParameters::COMPLEXITY;
- else if (s == "ERROR") return IterativeOptimizationParameters::ERROR;
+IterativeOptimizationParameters::Verbosity IterativeOptimizationParameters::verbosityTranslator(
+ const string &src) {
+ string s = src;
+ boost::algorithm::to_upper(s);
+ if (s == "SILENT")
+ return IterativeOptimizationParameters::SILENT;
+ else if (s == "COMPLEXITY")
+ return IterativeOptimizationParameters::COMPLEXITY;
+ else if (s == "ERROR")
+ return IterativeOptimizationParameters::ERROR;
/* default is default */
- else return IterativeOptimizationParameters::SILENT;
+ else
+ return IterativeOptimizationParameters::SILENT;
}
/*****************************************************************************/
- string IterativeOptimizationParameters::verbosityTranslator(IterativeOptimizationParameters::Verbosity verbosity) {
- if (verbosity == SILENT) return "SILENT";
- else if (verbosity == COMPLEXITY) return "COMPLEXITY";
- else if (verbosity == ERROR) return "ERROR";
- else return "UNKNOWN";
+string IterativeOptimizationParameters::verbosityTranslator(
+ IterativeOptimizationParameters::Verbosity verbosity) {
+ if (verbosity == SILENT)
+ return "SILENT";
+ else if (verbosity == COMPLEXITY)
+ return "COMPLEXITY";
+ else if (verbosity == ERROR)
+ return "ERROR";
+ else
+ return "UNKNOWN";
}
/*****************************************************************************/
-VectorValues IterativeSolver::optimize(
- const GaussianFactorGraph &gfg,
+VectorValues IterativeSolver::optimize(const GaussianFactorGraph &gfg,
boost::optional keyInfo,
- boost::optional&> lambda)
-{
- return optimize(
- gfg,
- keyInfo ? *keyInfo : KeyInfo(gfg),
- lambda ? *lambda : std::map());
+ boost::optional&> lambda) {
+ return optimize(gfg, keyInfo ? *keyInfo : KeyInfo(gfg),
+ lambda ? *lambda : std::map());
}
/*****************************************************************************/
-VectorValues IterativeSolver::optimize (
- const GaussianFactorGraph &gfg,
- const KeyInfo &keyInfo,
- const std::map &lambda)
-{
+VectorValues IterativeSolver::optimize(const GaussianFactorGraph &gfg,
+ const KeyInfo &keyInfo, const std::map &lambda) {
return optimize(gfg, keyInfo, lambda, keyInfo.x0());
}
/****************************************************************************/
-KeyInfo::KeyInfo(const GaussianFactorGraph &fg, const Ordering &ordering)
- : ordering_(ordering) {
+KeyInfo::KeyInfo(const GaussianFactorGraph &fg, const Ordering &ordering) :
+ ordering_(ordering) {
initialize(fg);
}
/****************************************************************************/
-KeyInfo::KeyInfo(const GaussianFactorGraph &fg)
- : ordering_(Ordering::Natural(fg)) {
+KeyInfo::KeyInfo(const GaussianFactorGraph &fg) :
+ ordering_(Ordering::Natural(fg)) {
initialize(fg);
}
/****************************************************************************/
-void KeyInfo::initialize(const GaussianFactorGraph &fg){
+void KeyInfo::initialize(const GaussianFactorGraph &fg) {
const map colspec = fg.getKeyDimMap();
const size_t n = ordering_.size();
size_t start = 0;
- for ( size_t i = 0 ; i < n ; ++i ) {
+ for (size_t i = 0; i < n; ++i) {
const size_t key = ordering_[i];
const size_t dim = colspec.find(key)->second;
insert(make_pair(key, KeyInfoEntry(i, dim, start)));
- start += dim ;
+ start += dim;
}
numCols_ = start;
}
@@ -108,7 +127,7 @@ void KeyInfo::initialize(const GaussianFactorGraph &fg){
/****************************************************************************/
vector KeyInfo::colSpec() const {
std::vector result(size(), 0);
- BOOST_FOREACH ( const gtsam::KeyInfo::value_type &item, *this ) {
+ BOOST_FOREACH ( const KeyInfo::value_type &item, *this ) {
result[item.second.index()] = item.second.dim();
}
return result;
@@ -117,7 +136,7 @@ vector KeyInfo::colSpec() const {
/****************************************************************************/
VectorValues KeyInfo::x0() const {
VectorValues result;
- BOOST_FOREACH ( const gtsam::KeyInfo::value_type &item, *this ) {
+ BOOST_FOREACH ( const KeyInfo::value_type &item, *this ) {
result.insert(item.first, Vector::Zero(item.second.dim()));
}
return result;
@@ -128,7 +147,5 @@ Vector KeyInfo::x0vector() const {
return Vector::Zero(numCols_);
}
-
}
-
diff --git a/gtsam/linear/IterativeSolver.h b/gtsam/linear/IterativeSolver.h
index d6e1b6e98..442a5fc6b 100644
--- a/gtsam/linear/IterativeSolver.h
+++ b/gtsam/linear/IterativeSolver.h
@@ -9,131 +9,178 @@
* -------------------------------------------------------------------------- */
+/**
+ * @file IterativeSolver.h
+ * @brief Some support classes for iterative solvers
+ * @date 2010
+ * @author Yong-Dian Jian
+ */
+
#pragma once
-#include
-#include
#include
+#include
+
#include
-#include
-#include
+#include
+#include
+
#include
-#include