diff --git a/.cproject b/.cproject
index 9726fec60..7c8190e6a 100644
--- a/.cproject
+++ b/.cproject
@@ -532,14 +532,6 @@
true
true
-
- make
- -j4
- testSimilarity3.run
- true
- true
- true
-
make
-j2
@@ -755,6 +747,14 @@
true
true
+
+ make
+ -j4
+ testSimilarity3.run
+ true
+ true
+ true
+
make
-j5
@@ -1301,7 +1301,6 @@
make
-
testSimulated2DOriented.run
true
false
@@ -1341,7 +1340,6 @@
make
-
testSimulated2D.run
true
false
@@ -1349,7 +1347,6 @@
make
-
testSimulated3D.run
true
false
@@ -1453,6 +1450,7 @@
make
+
testErrors.run
true
false
@@ -1763,6 +1761,7 @@
cpack
+
-G DEB
true
false
@@ -1770,6 +1769,7 @@
cpack
+
-G RPM
true
false
@@ -1777,6 +1777,7 @@
cpack
+
-G TGZ
true
false
@@ -1784,6 +1785,7 @@
cpack
+
--config CPackSourceConfig.cmake
true
false
@@ -1975,7 +1977,6 @@
make
-
tests/testGaussianISAM2
true
false
@@ -2037,22 +2038,6 @@
true
true
-
- make
- -j5
- testExpressionMeta.run
- true
- true
- true
-
-
- make
- -j4
- testCustomChartExpression.run
- true
- true
- true
-
make
-j5
@@ -2127,6 +2112,7 @@
make
+
tests/testBayesTree.run
true
false
@@ -2134,6 +2120,7 @@
make
+
testBinaryBayesNet.run
true
false
@@ -2181,6 +2168,7 @@
make
+
testSymbolicBayesNet.run
true
false
@@ -2188,6 +2176,7 @@
make
+
tests/testSymbolicFactor.run
true
false
@@ -2195,6 +2184,7 @@
make
+
testSymbolicFactorGraph.run
true
false
@@ -2210,6 +2200,7 @@
make
+
tests/testBayesTree
true
false
@@ -2783,6 +2774,14 @@
true
true
+
+ make
+ -j4
+ testExecutionTrace.run
+ true
+ true
+ true
+
make
-j5
@@ -3329,6 +3328,7 @@
make
+
testGraph.run
true
false
@@ -3336,6 +3336,7 @@
make
+
testJunctionTree.run
true
false
@@ -3343,6 +3344,7 @@
make
+
testSymbolicBayesNetB.run
true
false
diff --git a/cmake/GtsamBuildTypes.cmake b/cmake/GtsamBuildTypes.cmake
index 81c4adaeb..c2cd7b449 100644
--- a/cmake/GtsamBuildTypes.cmake
+++ b/cmake/GtsamBuildTypes.cmake
@@ -98,7 +98,8 @@ if( NOT cmake_build_type_tolower STREQUAL ""
AND NOT cmake_build_type_tolower STREQUAL "release"
AND NOT cmake_build_type_tolower STREQUAL "timing"
AND NOT cmake_build_type_tolower STREQUAL "profiling"
- AND NOT cmake_build_type_tolower STREQUAL "relwithdebinfo")
+ AND NOT cmake_build_type_tolower STREQUAL "relwithdebinfo"
+ AND NOT cmake_build_type_tolower STREQUAL "minsizerel")
message(FATAL_ERROR "Unknown build type \"${CMAKE_BUILD_TYPE}\". Allowed values are None, Debug, Release, Timing, Profiling, RelWithDebInfo (case-insensitive).")
endif()
diff --git a/examples/SolverComparer.cpp b/examples/SolverComparer.cpp
index 923b0b9de..0393affe1 100644
--- a/examples/SolverComparer.cpp
+++ b/examples/SolverComparer.cpp
@@ -31,28 +31,29 @@
*
*/
-#include
-#include
-#include
-#include
-#include
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
-#include
-#include
-#include
#include
-#include
+#include
#include
#include
#include
+#include
+
+#include
+#include
#ifdef GTSAM_USE_TBB
#include
@@ -72,23 +73,6 @@ typedef NoiseModelFactor1 NM1;
typedef NoiseModelFactor2 NM2;
typedef BearingRangeFactor BR;
-//GTSAM_VALUE_EXPORT(Value);
-//GTSAM_VALUE_EXPORT(Pose);
-//GTSAM_VALUE_EXPORT(Rot2);
-//GTSAM_VALUE_EXPORT(Point2);
-//GTSAM_VALUE_EXPORT(NonlinearFactor);
-//GTSAM_VALUE_EXPORT(NoiseModelFactor);
-//GTSAM_VALUE_EXPORT(NM1);
-//GTSAM_VALUE_EXPORT(NM2);
-//GTSAM_VALUE_EXPORT(BetweenFactor);
-//GTSAM_VALUE_EXPORT(PriorFactor);
-//GTSAM_VALUE_EXPORT(BR);
-//GTSAM_VALUE_EXPORT(noiseModel::Base);
-//GTSAM_VALUE_EXPORT(noiseModel::Isotropic);
-//GTSAM_VALUE_EXPORT(noiseModel::Gaussian);
-//GTSAM_VALUE_EXPORT(noiseModel::Diagonal);
-//GTSAM_VALUE_EXPORT(noiseModel::Unit);
-
double chi2_red(const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& config) {
// Compute degrees of freedom (observations - variables)
// In ocaml, +1 was added to the observations to account for the prior, but
@@ -269,12 +253,12 @@ void runIncremental()
boost::dynamic_pointer_cast >(datasetMeasurements[nextMeasurement]))
{
Key key1 = measurement->key1(), key2 = measurement->key2();
- if((key1 >= firstStep && key1 < key2) || (key2 >= firstStep && key2 < key1)) {
+ if(((int)key1 >= firstStep && key1 < key2) || ((int)key2 >= firstStep && key2 < key1)) {
// We found an odometry starting at firstStep
firstPose = std::min(key1, key2);
break;
}
- if((key2 >= firstStep && key1 < key2) || (key1 >= firstStep && key2 < key1)) {
+ if(((int)key2 >= firstStep && key1 < key2) || ((int)key1 >= firstStep && key2 < key1)) {
// We found an odometry joining firstStep with a previous pose
havePreviousPose = true;
firstPose = std::max(key1, key2);
@@ -303,7 +287,9 @@ void runIncremental()
cout << "Playing forward time steps..." << endl;
- for(size_t step = firstPose; nextMeasurement < datasetMeasurements.size() && (lastStep == -1 || step <= lastStep); ++step)
+ for (size_t step = firstPose;
+ nextMeasurement < datasetMeasurements.size() && (lastStep == -1 || (int)step <= lastStep);
+ ++step)
{
Values newVariables;
NonlinearFactorGraph newFactors;
diff --git a/gtsam/3rdparty/ceres/eigen.h b/gtsam/3rdparty/ceres/eigen.h
index 18a602cf4..a25fde97f 100644
--- a/gtsam/3rdparty/ceres/eigen.h
+++ b/gtsam/3rdparty/ceres/eigen.h
@@ -31,7 +31,7 @@
#ifndef CERES_INTERNAL_EIGEN_H_
#define CERES_INTERNAL_EIGEN_H_
-#include
+#include
namespace ceres {
diff --git a/gtsam/3rdparty/ceres/fixed_array.h b/gtsam/3rdparty/ceres/fixed_array.h
index db1591636..455fce383 100644
--- a/gtsam/3rdparty/ceres/fixed_array.h
+++ b/gtsam/3rdparty/ceres/fixed_array.h
@@ -33,7 +33,7 @@
#define CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
#include
-#include
+#include
#include
#include
diff --git a/gtsam/3rdparty/ceres/jet.h b/gtsam/3rdparty/ceres/jet.h
index 12d4e8bc9..4a7683f50 100644
--- a/gtsam/3rdparty/ceres/jet.h
+++ b/gtsam/3rdparty/ceres/jet.h
@@ -162,7 +162,7 @@
#include
#include
-#include
+#include
#include
namespace ceres {
diff --git a/gtsam/CMakeLists.txt b/gtsam/CMakeLists.txt
index 0ebd6c07d..e26d85ff8 100644
--- a/gtsam/CMakeLists.txt
+++ b/gtsam/CMakeLists.txt
@@ -131,12 +131,6 @@ else()
set(GTSAM_EXPORTED_TARGETS "${GTSAM_EXPORTED_TARGETS}" PARENT_SCOPE)
endif()
-# Set dataset paths
-set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/slam/dataset.cpp"
- APPEND PROPERTY COMPILE_DEFINITIONS
- "SOURCE_TREE_DATASET_DIR=\"${PROJECT_SOURCE_DIR}/examples/Data\""
- "INSTALLED_DATASET_DIR=\"${GTSAM_TOOLBOX_INSTALL_PATH}/gtsam_examples/Data\"")
-
# Special cases
if(MSVC)
set_property(SOURCE
diff --git a/gtsam/base/OptionalJacobian.h b/gtsam/base/OptionalJacobian.h
index a83333caa..7055a287a 100644
--- a/gtsam/base/OptionalJacobian.h
+++ b/gtsam/base/OptionalJacobian.h
@@ -168,5 +168,20 @@ public:
Jacobian* operator->(){ return pointer_; }
};
+// forward declare
+template struct traits;
+
+/**
+ * @brief: meta-function to generate JacobianTA optional reference
+ * Used mainly by Expressions
+ * @param T return type
+ * @param A argument type
+ */
+template
+struct MakeOptionalJacobian {
+ typedef OptionalJacobian::dimension,
+ traits::dimension> type;
+};
+
} // namespace gtsam
diff --git a/gtsam/base/Vector.cpp b/gtsam/base/Vector.cpp
index 0a708427a..ed6373f5b 100644
--- a/gtsam/base/Vector.cpp
+++ b/gtsam/base/Vector.cpp
@@ -222,11 +222,7 @@ double norm_2(const Vector& v) {
/* ************************************************************************* */
Vector reciprocal(const Vector &a) {
- size_t n = a.size();
- Vector b(n);
- for( size_t i = 0; i < n; i++ )
- b(i) = 1.0/a(i);
- return b;
+ return a.array().inverse();
}
/* ************************************************************************* */
diff --git a/gtsam/base/VectorSpace.h b/gtsam/base/VectorSpace.h
index e1f20ea0c..558fe52c9 100644
--- a/gtsam/base/VectorSpace.h
+++ b/gtsam/base/VectorSpace.h
@@ -24,13 +24,6 @@ namespace internal {
template
struct VectorSpaceImpl {
- /// @name Group
- /// @{
- static Class Compose(const Class& v1, const Class& v2) { return v1+v2;}
- static Class Between(const Class& v1, const Class& v2) { return v2-v1;}
- static Class Inverse(const Class& m) { return -m;}
- /// @}
-
/// @name Manifold
/// @{
typedef Eigen::Matrix TangentVector;
@@ -68,21 +61,21 @@ struct VectorSpaceImpl {
return Class(v);
}
- static Class Compose(const Class& v1, const Class& v2, ChartJacobian H1,
- ChartJacobian H2) {
+ static Class Compose(const Class& v1, const Class& v2, ChartJacobian H1 = boost::none,
+ ChartJacobian H2 = boost::none) {
if (H1) *H1 = Jacobian::Identity();
if (H2) *H2 = Jacobian::Identity();
return v1 + v2;
}
- static Class Between(const Class& v1, const Class& v2, ChartJacobian H1,
- ChartJacobian H2) {
+ static Class Between(const Class& v1, const Class& v2, ChartJacobian H1 = boost::none,
+ ChartJacobian H2 = boost::none) {
if (H1) *H1 = - Jacobian::Identity();
if (H2) *H2 = Jacobian::Identity();
return v2 - v1;
}
- static Class Inverse(const Class& v, ChartJacobian H) {
+ static Class Inverse(const Class& v, ChartJacobian H = boost::none) {
if (H) *H = - Jacobian::Identity();
return -v;
}
diff --git a/gtsam/base/tests/testTreeTraversal.cpp b/gtsam/base/tests/testTreeTraversal.cpp
index c9083f781..8fe5e53ef 100644
--- a/gtsam/base/tests/testTreeTraversal.cpp
+++ b/gtsam/base/tests/testTreeTraversal.cpp
@@ -45,11 +45,11 @@ struct TestForest {
};
TestForest makeTestForest() {
- // 0 1
- // / \
- // 2 3
- // |
- // 4
+ // 0 1
+ // / |
+ // 2 3
+ // |
+ // 4
TestForest forest;
forest.roots_.push_back(boost::make_shared(0));
forest.roots_.push_back(boost::make_shared(1));
diff --git a/gtsam/config.h.in b/gtsam/config.h.in
index 8d406e21f..20073152e 100644
--- a/gtsam/config.h.in
+++ b/gtsam/config.h.in
@@ -25,7 +25,7 @@
#define GTSAM_VERSION_STRING "@GTSAM_VERSION_STRING@"
// Paths to example datasets distributed with GTSAM
-#define GTSAM_SOURCE_TREE_DATASET_DIR "@CMAKE_SOURCE_DIR@/examples/Data"
+#define GTSAM_SOURCE_TREE_DATASET_DIR "@PROJECT_SOURCE_DIR@/examples/Data"
#define GTSAM_INSTALLED_DATASET_DIR "@GTSAM_TOOLBOX_INSTALL_PATH@/gtsam_examples/Data"
// Whether GTSAM is compiled to use quaternions for Rot3 (otherwise uses rotation matrices)
diff --git a/gtsam/discrete/DecisionTree-inl.h b/gtsam/discrete/DecisionTree-inl.h
index cd56780e5..c1648888e 100644
--- a/gtsam/discrete/DecisionTree-inl.h
+++ b/gtsam/discrete/DecisionTree-inl.h
@@ -467,7 +467,7 @@ namespace gtsam {
// find highest label among branches
boost::optional highestLabel;
- boost::optional nrChoices;
+ size_t nrChoices = 0;
for (Iterator it = begin; it != end; it++) {
if (it->root_->isLeaf())
continue;
@@ -475,22 +475,21 @@ namespace gtsam {
boost::dynamic_pointer_cast(it->root_);
if (!highestLabel || c->label() > *highestLabel) {
highestLabel.reset(c->label());
- nrChoices.reset(c->nrChoices());
+ nrChoices = c->nrChoices();
}
}
// if label is already in correct order, just put together a choice on label
- if (!highestLabel || !nrChoices || label > *highestLabel) {
+ if (!nrChoices || !highestLabel || label > *highestLabel) {
boost::shared_ptr choiceOnLabel(new Choice(label, end - begin));
for (Iterator it = begin; it != end; it++)
choiceOnLabel->push_back(it->root_);
return Choice::Unique(choiceOnLabel);
} else {
// Set up a new choice on the highest label
- boost::shared_ptr choiceOnHighestLabel(
- new Choice(*highestLabel, *nrChoices));
+ boost::shared_ptr choiceOnHighestLabel(new Choice(*highestLabel, nrChoices));
// now, for all possible values of highestLabel
- for (size_t index = 0; index < *nrChoices; index++) {
+ for (size_t index = 0; index < nrChoices; index++) {
// make a new set of functions for composing by iterating over the given
// functions, and selecting the appropriate branch.
std::vector functions;
diff --git a/gtsam/geometry/Cal3_S2.h b/gtsam/geometry/Cal3_S2.h
index 3ab6cfd36..3d5342c92 100644
--- a/gtsam/geometry/Cal3_S2.h
+++ b/gtsam/geometry/Cal3_S2.h
@@ -21,7 +21,6 @@
#pragma once
-#include
#include
namespace gtsam {
diff --git a/gtsam/geometry/OrientedPlane3.cpp b/gtsam/geometry/OrientedPlane3.cpp
index 088a84243..e96708942 100644
--- a/gtsam/geometry/OrientedPlane3.cpp
+++ b/gtsam/geometry/OrientedPlane3.cpp
@@ -73,7 +73,7 @@ Vector3 OrientedPlane3::error(const gtsam::OrientedPlane3& plane) const {
}
/* ************************************************************************* */
-OrientedPlane3 OrientedPlane3::retract(const Vector& v) const {
+OrientedPlane3 OrientedPlane3::retract(const Vector3& v) const {
// Retract the Unit3
Vector2 n_v(v(0), v(1));
Unit3 n_retracted = n_.retract(n_v);
@@ -83,7 +83,7 @@ OrientedPlane3 OrientedPlane3::retract(const Vector& v) const {
/* ************************************************************************* */
Vector3 OrientedPlane3::localCoordinates(const OrientedPlane3& y) const {
- Vector n_local = n_.localCoordinates(y.n_);
+ Vector2 n_local = n_.localCoordinates(y.n_);
double d_local = d_ - y.d_;
Vector3 e;
e << n_local(0), n_local(1), -d_local;
diff --git a/gtsam/geometry/OrientedPlane3.h b/gtsam/geometry/OrientedPlane3.h
index dad283760..d987ad7b3 100644
--- a/gtsam/geometry/OrientedPlane3.h
+++ b/gtsam/geometry/OrientedPlane3.h
@@ -51,7 +51,7 @@ public:
n_(s), d_(d) {
}
- OrientedPlane3(Vector vec) :
+ OrientedPlane3(const Vector4& vec) :
n_(vec(0), vec(1), vec(2)), d_(vec(3)) {
}
@@ -89,7 +89,7 @@ public:
}
/// The retract function
- OrientedPlane3 retract(const Vector& v) const;
+ OrientedPlane3 retract(const Vector3& v) const;
/// The local coordinates function
Vector3 localCoordinates(const OrientedPlane3& s) const;
diff --git a/gtsam/geometry/PinholePose.h b/gtsam/geometry/PinholePose.h
index 0f4685770..a35887384 100644
--- a/gtsam/geometry/PinholePose.h
+++ b/gtsam/geometry/PinholePose.h
@@ -270,7 +270,10 @@ public:
/// print
void print(const std::string& s = "PinholePose") const {
Base::print(s);
- K_->print(s + ".calibration");
+ if (!K_)
+ std::cout << "s No calibration given" << std::endl;
+ else
+ K_->print(s + ".calibration");
}
/// @}
diff --git a/gtsam/geometry/Pose3.cpp b/gtsam/geometry/Pose3.cpp
index be8e1bfed..7b7c861fd 100644
--- a/gtsam/geometry/Pose3.cpp
+++ b/gtsam/geometry/Pose3.cpp
@@ -112,7 +112,9 @@ bool Pose3::equals(const Pose3& pose, double tol) const {
/* ************************************************************************* */
/** Modified from Murray94book version (which assumes w and v normalized?) */
Pose3 Pose3::Expmap(const Vector& xi, OptionalJacobian<6, 6> H) {
- if (H) *H = ExpmapDerivative(xi);
+ if (H) {
+ *H = ExpmapDerivative(xi);
+ }
// get angular velocity omega and translational velocity v from twist xi
Point3 w(xi(0), xi(1), xi(2)), v(xi(3), xi(4), xi(5));
@@ -254,6 +256,14 @@ Matrix6 Pose3::LogmapDerivative(const Pose3& pose) {
return J;
}
+/* ************************************************************************* */
+const Point3& Pose3::translation(OptionalJacobian<3, 6> H) const {
+ if (H) {
+ *H << Z_3x3, rotation().matrix();
+ }
+ return t_;
+}
+
/* ************************************************************************* */
Matrix4 Pose3::matrix() const {
const Matrix3 R = R_.matrix();
@@ -280,8 +290,9 @@ Point3 Pose3::transform_from(const Point3& p, OptionalJacobian<3,6> Dpose,
Matrix3 DR = R * skewSymmetric(-p.x(), -p.y(), -p.z());
(*Dpose) << DR, R;
}
- if (Dpoint)
+ if (Dpoint) {
*Dpoint = R_.matrix();
+ }
return R_ * p + t_;
}
@@ -299,17 +310,18 @@ Point3 Pose3::transform_to(const Point3& p, OptionalJacobian<3,6> Dpose,
+wz, 0.0, -wx, 0.0,-1.0, 0.0,
-wy, +wx, 0.0, 0.0, 0.0,-1.0;
}
- if (Dpoint)
+ if (Dpoint) {
*Dpoint = Rt;
+ }
return q;
}
/* ************************************************************************* */
double Pose3::range(const Point3& point, OptionalJacobian<1, 6> H1,
OptionalJacobian<1, 3> H2) const {
- if (!H1 && !H2)
+ if (!H1 && !H2) {
return transform_to(point).norm();
- else {
+ } else {
Matrix36 D1;
Matrix3 D2;
Point3 d = transform_to(point, H1 ? &D1 : 0, H2 ? &D2 : 0);
diff --git a/gtsam/geometry/Pose3.h b/gtsam/geometry/Pose3.h
index 1ea8e8d5c..b11ae2587 100644
--- a/gtsam/geometry/Pose3.h
+++ b/gtsam/geometry/Pose3.h
@@ -223,9 +223,7 @@ public:
}
/// get translation
- const Point3& translation() const {
- return t_;
- }
+ const Point3& translation(OptionalJacobian<3, 6> H = boost::none) const;
/// get x
double x() const {
diff --git a/gtsam/geometry/Unit3.cpp b/gtsam/geometry/Unit3.cpp
index be48aecc9..cc3865b8e 100644
--- a/gtsam/geometry/Unit3.cpp
+++ b/gtsam/geometry/Unit3.cpp
@@ -119,7 +119,7 @@ Matrix3 Unit3::skew() const {
}
/* ************************************************************************* */
-Vector Unit3::error(const Unit3& q, OptionalJacobian<2,2> H) const {
+Vector2 Unit3::error(const Unit3& q, OptionalJacobian<2,2> H) const {
// 2D error is equal to B'*q, as B is 3x2 matrix and q is 3x1
Matrix23 Bt = basis().transpose();
Vector2 xi = Bt * q.p_.vector();
diff --git a/gtsam/geometry/Unit3.h b/gtsam/geometry/Unit3.h
index 8e10b839b..12bfac5ce 100644
--- a/gtsam/geometry/Unit3.h
+++ b/gtsam/geometry/Unit3.h
@@ -108,7 +108,7 @@ public:
}
/// Return unit-norm Vector
- Vector unitVector(boost::optional H = boost::none) const {
+ Vector3 unitVector(boost::optional H = boost::none) const {
if (H)
*H = basis();
return (p_.vector());
@@ -120,7 +120,7 @@ public:
}
/// Signed, vector-valued error between two directions
- Vector error(const Unit3& q, OptionalJacobian<2, 2> H = boost::none) const;
+ Vector2 error(const Unit3& q, OptionalJacobian<2, 2> H = boost::none) const;
/// Distance between two directions
double distance(const Unit3& q, OptionalJacobian<1, 2> H = boost::none) const;
diff --git a/gtsam/geometry/tests/testCalibratedCamera.cpp b/gtsam/geometry/tests/testCalibratedCamera.cpp
index b1e265266..c02a11928 100644
--- a/gtsam/geometry/tests/testCalibratedCamera.cpp
+++ b/gtsam/geometry/tests/testCalibratedCamera.cpp
@@ -90,11 +90,11 @@ TEST( CalibratedCamera, project)
/* ************************************************************************* */
TEST( CalibratedCamera, Dproject_to_camera1) {
Point3 pp(155,233,131);
- Matrix test1;
- CalibratedCamera::project_to_camera(pp, test1);
- Matrix test2 = numericalDerivative11(
+ Matrix actual;
+ CalibratedCamera::project_to_camera(pp, actual);
+ Matrix expected_numerical = numericalDerivative11(
boost::bind(CalibratedCamera::project_to_camera, _1, boost::none), pp);
- CHECK(assert_equal(test1, test2));
+ CHECK(assert_equal(expected_numerical, actual));
}
/* ************************************************************************* */
diff --git a/gtsam/geometry/tests/testPose3.cpp b/gtsam/geometry/tests/testPose3.cpp
index a716406a4..d749ba6af 100644
--- a/gtsam/geometry/tests/testPose3.cpp
+++ b/gtsam/geometry/tests/testPose3.cpp
@@ -186,6 +186,17 @@ TEST(Pose3, expmaps_galore_full)
EXPECT(assert_equal(xi, Pose3::Logmap(actual),1e-6));
}
+/* ************************************************************************* */
+// Check translation and its pushforward
+TEST(Pose3, translation) {
+ Matrix actualH;
+ EXPECT(assert_equal(Point3(3.5, -8.2, 4.2), T.translation(actualH), 1e-8));
+
+ Matrix numericalH = numericalDerivative11(
+ boost::bind(&Pose3::translation, _1, boost::none), T);
+ EXPECT(assert_equal(numericalH, actualH, 1e-6));
+}
+
/* ************************************************************************* */
TEST(Pose3, Adjoint_compose_full)
{
diff --git a/gtsam/geometry/triangulation.cpp b/gtsam/geometry/triangulation.cpp
index 474689525..c0f69217c 100644
--- a/gtsam/geometry/triangulation.cpp
+++ b/gtsam/geometry/triangulation.cpp
@@ -23,14 +23,8 @@
namespace gtsam {
-/**
- * DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312
- * @param projection_matrices Projection matrices (K*P^-1)
- * @param measurements 2D measurements
- * @param rank_tol SVD rank tolerance
- * @return Triangulated Point3
- */
-Point3 triangulateDLT(const std::vector& projection_matrices,
+Vector4 triangulateHomogeneousDLT(
+ const std::vector& projection_matrices,
const std::vector& measurements, double rank_tol) {
// number of cameras
@@ -57,7 +51,16 @@ Point3 triangulateDLT(const std::vector& projection_matrices,
if (rank < 3)
throw(TriangulationUnderconstrainedException());
- // Create 3D point from eigenvector
+ return v;
+}
+
+Point3 triangulateDLT(const std::vector& projection_matrices,
+ const std::vector& measurements, double rank_tol) {
+
+ Vector4 v = triangulateHomogeneousDLT(projection_matrices, measurements,
+ rank_tol);
+
+ // Create 3D point from homogeneous coordinates
return Point3(sub((v / v(3)), 0, 3));
}
@@ -89,6 +92,5 @@ Point3 optimize(const NonlinearFactorGraph& graph, const Values& values,
return result.at(landmarkKey);
}
-
} // \namespace gtsam
diff --git a/gtsam/geometry/triangulation.h b/gtsam/geometry/triangulation.h
index ce83f780b..0eb59f016 100644
--- a/gtsam/geometry/triangulation.h
+++ b/gtsam/geometry/triangulation.h
@@ -18,7 +18,6 @@
#pragma once
-
#include
#include
#include
@@ -43,6 +42,17 @@ public:
}
};
+/**
+ * DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312
+ * @param projection_matrices Projection matrices (K*P^-1)
+ * @param measurements 2D measurements
+ * @param rank_tol SVD rank tolerance
+ * @return Triangulated point, in homogeneous coordinates
+ */
+GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
+ const std::vector& projection_matrices,
+ const std::vector& measurements, double rank_tol = 1e-9);
+
/**
* DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312
* @param projection_matrices Projection matrices (K*P^-1)
@@ -52,7 +62,7 @@ public:
*/
GTSAM_EXPORT Point3 triangulateDLT(
const std::vector& projection_matrices,
- const std::vector& measurements, double rank_tol);
+ const std::vector& measurements, double rank_tol = 1e-9);
///
/**
@@ -164,6 +174,25 @@ Point3 triangulateNonlinear(
return optimize(graph, values, Symbol('p', 0));
}
+/**
+ * Create a 3*4 camera projection matrix from calibration and pose.
+ * Functor for partial application on calibration
+ * @param pose The camera pose
+ * @param cal The calibration
+ * @return Returns a Matrix34
+ */
+template
+struct CameraProjectionMatrix {
+ CameraProjectionMatrix(const CALIBRATION& calibration) :
+ K_(calibration.K()) {
+ }
+ Matrix34 operator()(const Pose3& pose) const {
+ return K_ * (pose.inverse().matrix()).block<3, 4>(0, 0);
+ }
+private:
+ const Matrix3 K_;
+};
+
/**
* Function to triangulate 3D landmark point from an arbitrary number
* of poses (at least 2) using the DLT. The function checks that the
@@ -188,10 +217,10 @@ Point3 triangulatePoint3(const std::vector& poses,
// construct projection matrices from poses & calibration
std::vector projection_matrices;
- BOOST_FOREACH(const Pose3& pose, poses) {
- projection_matrices.push_back(
- sharedCal->K() * (pose.inverse().matrix()).block<3,4>(0,0));
- }
+ CameraProjectionMatrix createP(*sharedCal); // partially apply
+ BOOST_FOREACH(const Pose3& pose, poses)
+ projection_matrices.push_back(createP(pose));
+
// Triangulate linearly
Point3 point = triangulateDLT(projection_matrices, measurements, rank_tol);
@@ -204,7 +233,7 @@ Point3 triangulatePoint3(const std::vector& poses,
BOOST_FOREACH(const Pose3& pose, poses) {
const Point3& p_local = pose.transform_to(point);
if (p_local.z() <= 0)
- throw(TriangulationCheiralityException());
+ throw(TriangulationCheiralityException());
}
#endif
@@ -230,7 +259,7 @@ Point3 triangulatePoint3(
bool optimize = false) {
size_t m = cameras.size();
- assert(measurements.size()==m);
+ assert(measurements.size() == m);
if (m < 2)
throw(TriangulationUnderconstrainedException());
@@ -238,10 +267,10 @@ Point3 triangulatePoint3(
// construct projection matrices from poses & calibration
typedef PinholeCamera Camera;
std::vector projection_matrices;
- BOOST_FOREACH(const Camera& camera, cameras) {
- Matrix P_ = (camera.pose().inverse().matrix());
- projection_matrices.push_back(camera.calibration().K()* (P_.block<3,4>(0,0)) );
- }
+ BOOST_FOREACH(const Camera& camera, cameras)
+ projection_matrices.push_back(
+ CameraProjectionMatrix(camera.calibration())(
+ camera.pose()));
Point3 point = triangulateDLT(projection_matrices, measurements, rank_tol);
// The n refine using non-linear optimization
@@ -253,7 +282,7 @@ Point3 triangulatePoint3(
BOOST_FOREACH(const Camera& camera, cameras) {
const Point3& p_local = camera.pose().transform_to(point);
if (p_local.z() <= 0)
- throw(TriangulationCheiralityException());
+ throw(TriangulationCheiralityException());
}
#endif
diff --git a/gtsam/linear/JacobianFactor.cpp b/gtsam/linear/JacobianFactor.cpp
index eba06f99a..11025fc0f 100644
--- a/gtsam/linear/JacobianFactor.cpp
+++ b/gtsam/linear/JacobianFactor.cpp
@@ -365,32 +365,50 @@ void JacobianFactor::print(const string& s,
/* ************************************************************************* */
// Check if two linear factors are equal
bool JacobianFactor::equals(const GaussianFactor& f_, double tol) const {
- if (!dynamic_cast(&f_))
+ static const bool verbose = false;
+ if (!dynamic_cast(&f_)) {
+ if (verbose)
+ cout << "JacobianFactor::equals: Incorrect type" << endl;
return false;
- else {
+ } else {
const JacobianFactor& f(static_cast(f_));
// Check keys
- if (keys() != f.keys())
+ if (keys() != f.keys()) {
+ if (verbose)
+ cout << "JacobianFactor::equals: keys do not match" << endl;
return false;
+ }
// Check noise model
- if ((model_ && !f.model_) || (!model_ && f.model_))
+ if ((model_ && !f.model_) || (!model_ && f.model_)) {
+ if (verbose)
+ cout << "JacobianFactor::equals: noise model mismatch" << endl;
return false;
- if (model_ && f.model_ && !model_->equals(*f.model_, tol))
+ }
+ if (model_ && f.model_ && !model_->equals(*f.model_, tol)) {
+ if (verbose)
+ cout << "JacobianFactor::equals: noise modesl are not equal" << endl;
return false;
+ }
// Check matrix sizes
- if (!(Ab_.rows() == f.Ab_.rows() && Ab_.cols() == f.Ab_.cols()))
+ if (!(Ab_.rows() == f.Ab_.rows() && Ab_.cols() == f.Ab_.cols())) {
+ if (verbose)
+ cout << "JacobianFactor::equals: augmented size mismatch" << endl;
return false;
+ }
// Check matrix contents
constABlock Ab1(Ab_.range(0, Ab_.nBlocks()));
constABlock Ab2(f.Ab_.range(0, f.Ab_.nBlocks()));
for (size_t row = 0; row < (size_t) Ab1.rows(); ++row)
if (!equal_with_abs_tol(Ab1.row(row), Ab2.row(row), tol)
- && !equal_with_abs_tol(-Ab1.row(row), Ab2.row(row), tol))
+ && !equal_with_abs_tol(-Ab1.row(row), Ab2.row(row), tol)) {
+ if (verbose)
+ cout << "JacobianFactor::equals: matrix mismatch at row " << row << endl;
return false;
+ }
return true;
}
diff --git a/gtsam/linear/NoiseModel.cpp b/gtsam/linear/NoiseModel.cpp
index 2031a4b73..a8b177b43 100644
--- a/gtsam/linear/NoiseModel.cpp
+++ b/gtsam/linear/NoiseModel.cpp
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
using namespace std;
@@ -70,6 +71,11 @@ boost::optional checkIfDiagonal(const Matrix M) {
}
}
+/* ************************************************************************* */
+Vector Base::sigmas() const {
+ throw("Base::sigmas: sigmas() not implemented for this noise model");
+}
+
/* ************************************************************************* */
Gaussian::shared_ptr Gaussian::SqrtInformation(const Matrix& R, bool smart) {
size_t m = R.rows(), n = R.cols();
@@ -79,24 +85,25 @@ Gaussian::shared_ptr Gaussian::SqrtInformation(const Matrix& R, bool smart) {
if (smart)
diagonal = checkIfDiagonal(R);
if (diagonal)
- return Diagonal::Sigmas(reciprocal(*diagonal), true);
+ return Diagonal::Sigmas(diagonal->array().inverse(), true);
else
return shared_ptr(new Gaussian(R.rows(), R));
}
/* ************************************************************************* */
-Gaussian::shared_ptr Gaussian::Information(const Matrix& M, bool smart) {
- size_t m = M.rows(), n = M.cols();
+Gaussian::shared_ptr Gaussian::Information(const Matrix& information, bool smart) {
+ size_t m = information.rows(), n = information.cols();
if (m != n)
throw invalid_argument("Gaussian::Information: R not square");
boost::optional diagonal = boost::none;
if (smart)
- diagonal = checkIfDiagonal(M);
+ diagonal = checkIfDiagonal(information);
if (diagonal)
return Diagonal::Precisions(*diagonal, true);
else {
- Matrix R = RtR(M);
- return shared_ptr(new Gaussian(R.rows(), R));
+ Eigen::LLT llt(information);
+ Matrix R = llt.matrixU();
+ return shared_ptr(new Gaussian(n, R));
}
}
@@ -111,13 +118,15 @@ Gaussian::shared_ptr Gaussian::Covariance(const Matrix& covariance,
variances = checkIfDiagonal(covariance);
if (variances)
return Diagonal::Variances(*variances, true);
- else
- return shared_ptr(new Gaussian(n, inverse_square_root(covariance)));
+ else {
+ // TODO: can we do this more efficiently and still get an upper triangular nmatrix??
+ return Information(covariance.inverse(), false);
+ }
}
/* ************************************************************************* */
void Gaussian::print(const string& name) const {
- gtsam::print(thisR(), "Gaussian");
+ gtsam::print(thisR(), name + "Gaussian");
}
/* ************************************************************************* */
@@ -129,6 +138,12 @@ bool Gaussian::equals(const Base& expected, double tol) const {
return equal_with_abs_tol(R(), p->R(), sqrt(tol));
}
+/* ************************************************************************* */
+Vector Gaussian::sigmas() const {
+ // TODO(frank): can this be done faster?
+ return Vector((thisR().transpose() * thisR()).inverse().diagonal()).cwiseSqrt();
+}
+
/* ************************************************************************* */
Vector Gaussian::whiten(const Vector& v) const {
return thisR() * v;
@@ -221,9 +236,11 @@ Diagonal::Diagonal() :
}
/* ************************************************************************* */
-Diagonal::Diagonal(const Vector& sigmas) :
- Gaussian(sigmas.size()), sigmas_(sigmas), invsigmas_(reciprocal(sigmas)), precisions_(
- emul(invsigmas_, invsigmas_)) {
+Diagonal::Diagonal(const Vector& sigmas)
+ : Gaussian(sigmas.size()),
+ sigmas_(sigmas),
+ invsigmas_(sigmas.array().inverse()),
+ precisions_(invsigmas_.array().square()) {
}
/* ************************************************************************* */
@@ -262,12 +279,12 @@ void Diagonal::print(const string& name) const {
/* ************************************************************************* */
Vector Diagonal::whiten(const Vector& v) const {
- return emul(v, invsigmas());
+ return v.cwiseProduct(invsigmas_);
}
/* ************************************************************************* */
Vector Diagonal::unwhiten(const Vector& v) const {
- return emul(v, sigmas_);
+ return v.cwiseProduct(sigmas_);
}
/* ************************************************************************* */
@@ -293,7 +310,7 @@ namespace internal {
// switch precisions and invsigmas to finite value
// TODO: why?? And, why not just ask s==0.0 below ?
static void fix(const Vector& sigmas, Vector& precisions, Vector& invsigmas) {
- for (size_t i = 0; i < sigmas.size(); ++i)
+ for (Vector::Index i = 0; i < sigmas.size(); ++i)
if (!std::isfinite(1. / sigmas[i])) {
precisions[i] = 0.0;
invsigmas[i] = 0.0;
@@ -342,7 +359,7 @@ Vector Constrained::whiten(const Vector& v) const {
assert (b.size()==a.size());
Vector c(n);
for( size_t i = 0; i < n; i++ ) {
- const double& ai = a(i), &bi = b(i);
+ const double& ai = a(i), bi = b(i);
c(i) = (bi==0.0) ? ai : ai/bi; // NOTE: not ediv_()
}
return c;
@@ -404,8 +421,8 @@ SharedDiagonal Constrained::QR(Matrix& Ab) const {
list Rd;
Vector pseudo(m); // allocate storage for pseudo-inverse
- Vector invsigmas = reciprocal(sigmas_);
- Vector weights = emul(invsigmas,invsigmas); // calculate weights once
+ Vector invsigmas = sigmas_.array().inverse();
+ Vector weights = invsigmas.array().square(); // calculate weights once
// We loop over all columns, because the columns that can be eliminated
// are not necessarily contiguous. For each one, estimate the corresponding
@@ -542,16 +559,6 @@ Vector Base::weight(const Vector &error) const {
return w;
}
-/** square root version of the weight function */
-Vector Base::sqrtWeight(const Vector &error) const {
- const size_t n = error.rows();
- Vector w(n);
- for ( size_t i = 0 ; i < n ; ++i )
- w(i) = sqrtWeight(error(i));
- return w;
-}
-
-
/** The following three functions reweight block matrices and a vector
* according to their weight implementation */
@@ -560,8 +567,7 @@ void Base::reweight(Vector& error) const {
const double w = sqrtWeight(error.norm());
error *= w;
} else {
- const Vector w = sqrtWeight(error);
- error.array() *= w.array();
+ error.array() *= weight(error).cwiseSqrt().array();
}
}
@@ -579,7 +585,7 @@ void Base::reweight(vector &A, Vector &error) const {
BOOST_FOREACH(Matrix& Aj, A) {
vector_scale_inplace(W,Aj);
}
- error = emul(W, error);
+ error = W.cwiseProduct(error);
}
}
@@ -593,7 +599,7 @@ void Base::reweight(Matrix &A, Vector &error) const {
else {
const Vector W = sqrtWeight(error);
vector_scale_inplace(W,A);
- error = emul(W, error);
+ error = W.cwiseProduct(error);
}
}
@@ -609,7 +615,7 @@ void Base::reweight(Matrix &A1, Matrix &A2, Vector &error) const {
const Vector W = sqrtWeight(error);
vector_scale_inplace(W,A1);
vector_scale_inplace(W,A2);
- error = emul(W, error);
+ error = W.cwiseProduct(error);
}
}
@@ -627,7 +633,7 @@ void Base::reweight(Matrix &A1, Matrix &A2, Matrix &A3, Vector &error) const {
vector_scale_inplace(W,A1);
vector_scale_inplace(W,A2);
vector_scale_inplace(W,A3);
- error = emul(W, error);
+ error = W.cwiseProduct(error);
}
}
@@ -641,7 +647,7 @@ void Null::print(const std::string &s="") const
Null::shared_ptr Null::Create()
{ return shared_ptr(new Null()); }
-Fair::Fair(const double c, const ReweightScheme reweight)
+Fair::Fair(double c, const ReweightScheme reweight)
: Base(reweight), c_(c) {
if ( c_ <= 0 ) {
cout << "mEstimator Fair takes only positive double in constructor. forced to 1.0" << endl;
@@ -653,26 +659,26 @@ Fair::Fair(const double c, const ReweightScheme reweight)
// Fair
/* ************************************************************************* */
-double Fair::weight(const double &error) const
+double Fair::weight(double error) const
{ return 1.0 / (1.0 + fabs(error)/c_); }
void Fair::print(const std::string &s="") const
{ cout << s << "fair (" << c_ << ")" << endl; }
-bool Fair::equals(const Base &expected, const double tol) const {
+bool Fair::equals(const Base &expected, double tol) const {
const Fair* p = dynamic_cast (&expected);
if (p == NULL) return false;
return fabs(c_ - p->c_ ) < tol;
}
-Fair::shared_ptr Fair::Create(const double c, const ReweightScheme reweight)
+Fair::shared_ptr Fair::Create(double c, const ReweightScheme reweight)
{ return shared_ptr(new Fair(c, reweight)); }
/* ************************************************************************* */
// Huber
/* ************************************************************************* */
-Huber::Huber(const double k, const ReweightScheme reweight)
+Huber::Huber(double k, const ReweightScheme reweight)
: Base(reweight), k_(k) {
if ( k_ <= 0 ) {
cout << "mEstimator Huber takes only positive double in constructor. forced to 1.0" << endl;
@@ -680,7 +686,7 @@ Huber::Huber(const double k, const ReweightScheme reweight)
}
}
-double Huber::weight(const double &error) const {
+double Huber::weight(double error) const {
return (error < k_) ? (1.0) : (k_ / fabs(error));
}
@@ -688,13 +694,13 @@ void Huber::print(const std::string &s="") const {
cout << s << "huber (" << k_ << ")" << endl;
}
-bool Huber::equals(const Base &expected, const double tol) const {
+bool Huber::equals(const Base &expected, double tol) const {
const Huber* p = dynamic_cast(&expected);
if (p == NULL) return false;
return fabs(k_ - p->k_) < tol;
}
-Huber::shared_ptr Huber::Create(const double c, const ReweightScheme reweight) {
+Huber::shared_ptr Huber::Create(double c, const ReweightScheme reweight) {
return shared_ptr(new Huber(c, reweight));
}
@@ -702,7 +708,7 @@ Huber::shared_ptr Huber::Create(const double c, const ReweightScheme reweight) {
// Cauchy
/* ************************************************************************* */
-Cauchy::Cauchy(const double k, const ReweightScheme reweight)
+Cauchy::Cauchy(double k, const ReweightScheme reweight)
: Base(reweight), k_(k) {
if ( k_ <= 0 ) {
cout << "mEstimator Cauchy takes only positive double in constructor. forced to 1.0" << endl;
@@ -710,7 +716,7 @@ Cauchy::Cauchy(const double k, const ReweightScheme reweight)
}
}
-double Cauchy::weight(const double &error) const {
+double Cauchy::weight(double error) const {
return k_*k_ / (k_*k_ + error*error);
}
@@ -718,24 +724,24 @@ void Cauchy::print(const std::string &s="") const {
cout << s << "cauchy (" << k_ << ")" << endl;
}
-bool Cauchy::equals(const Base &expected, const double tol) const {
+bool Cauchy::equals(const Base &expected, double tol) const {
const Cauchy* p = dynamic_cast(&expected);
if (p == NULL) return false;
return fabs(k_ - p->k_) < tol;
}
-Cauchy::shared_ptr Cauchy::Create(const double c, const ReweightScheme reweight) {
+Cauchy::shared_ptr Cauchy::Create(double c, const ReweightScheme reweight) {
return shared_ptr(new Cauchy(c, reweight));
}
/* ************************************************************************* */
// Tukey
/* ************************************************************************* */
-Tukey::Tukey(const double c, const ReweightScheme reweight)
+Tukey::Tukey(double c, const ReweightScheme reweight)
: Base(reweight), c_(c) {
}
-double Tukey::weight(const double &error) const {
+double Tukey::weight(double error) const {
if (fabs(error) <= c_) {
double xc2 = (error/c_)*(error/c_);
double one_xc22 = (1.0-xc2)*(1.0-xc2);
@@ -748,24 +754,24 @@ void Tukey::print(const std::string &s="") const {
std::cout << s << ": Tukey (" << c_ << ")" << std::endl;
}
-bool Tukey::equals(const Base &expected, const double tol) const {
+bool Tukey::equals(const Base &expected, double tol) const {
const Tukey* p = dynamic_cast(&expected);
if (p == NULL) return false;
return fabs(c_ - p->c_) < tol;
}
-Tukey::shared_ptr Tukey::Create(const double c, const ReweightScheme reweight) {
+Tukey::shared_ptr Tukey::Create(double c, const ReweightScheme reweight) {
return shared_ptr(new Tukey(c, reweight));
}
/* ************************************************************************* */
// Welsh
/* ************************************************************************* */
-Welsh::Welsh(const double c, const ReweightScheme reweight)
+Welsh::Welsh(double c, const ReweightScheme reweight)
: Base(reweight), c_(c) {
}
-double Welsh::weight(const double &error) const {
+double Welsh::weight(double error) const {
double xc2 = (error/c_)*(error/c_);
return std::exp(-xc2);
}
@@ -774,13 +780,13 @@ void Welsh::print(const std::string &s="") const {
std::cout << s << ": Welsh (" << c_ << ")" << std::endl;
}
-bool Welsh::equals(const Base &expected, const double tol) const {
+bool Welsh::equals(const Base &expected, double tol) const {
const Welsh* p = dynamic_cast(&expected);
if (p == NULL) return false;
return fabs(c_ - p->c_) < tol;
}
-Welsh::shared_ptr Welsh::Create(const double c, const ReweightScheme reweight) {
+Welsh::shared_ptr Welsh::Create(double c, const ReweightScheme reweight) {
return shared_ptr(new Welsh(c, reweight));
}
diff --git a/gtsam/linear/NoiseModel.h b/gtsam/linear/NoiseModel.h
index 2532ac27e..923e7c7e9 100644
--- a/gtsam/linear/NoiseModel.h
+++ b/gtsam/linear/NoiseModel.h
@@ -26,7 +26,6 @@
#include
#include
#include
-#include
namespace gtsam {
@@ -59,7 +58,7 @@ namespace gtsam {
public:
- /** primary constructor @param dim is the dimension of the model */
+ /// primary constructor @param dim is the dimension of the model
Base(size_t dim = 1):dim_(dim) {}
virtual ~Base() {}
@@ -75,14 +74,13 @@ namespace gtsam {
virtual bool equals(const Base& expected, double tol=1e-9) const = 0;
- /**
- * Whiten an error vector.
- */
+ /// Calculate standard deviations
+ virtual Vector sigmas() const;
+
+ /// Whiten an error vector.
virtual Vector whiten(const Vector& v) const = 0;
- /**
- * Unwhiten an error vector.
- */
+ /// Unwhiten an error vector.
virtual Vector unwhiten(const Vector& v) const = 0;
virtual double distance(const Vector& v) const = 0;
@@ -189,6 +187,7 @@ namespace gtsam {
virtual void print(const std::string& name) const;
virtual bool equals(const Base& expected, double tol=1e-9) const;
+ virtual Vector sigmas() const;
virtual Vector whiten(const Vector& v) const;
virtual Vector unwhiten(const Vector& v) const;
@@ -220,9 +219,9 @@ namespace gtsam {
/**
* Whiten a system, in place as well
*/
- virtual void WhitenSystem(std::vector& A, Vector& b) const ;
- virtual void WhitenSystem(Matrix& A, Vector& b) const ;
- virtual void WhitenSystem(Matrix& A1, Matrix& A2, Vector& b) const ;
+ virtual void WhitenSystem(std::vector& A, Vector& b) const;
+ virtual void WhitenSystem(Matrix& A, Vector& b) const;
+ virtual void WhitenSystem(Matrix& A1, Matrix& A2, Vector& b) const;
virtual void WhitenSystem(Matrix& A1, Matrix& A2, Matrix& A3, Vector& b) const;
/**
@@ -234,11 +233,15 @@ namespace gtsam {
*/
virtual boost::shared_ptr QR(Matrix& Ab) const;
- /**
- * Return R itself, but note that Whiten(H) is cheaper than R*H
- */
+ /// Return R itself, but note that Whiten(H) is cheaper than R*H
virtual Matrix R() const { return thisR();}
+ /// Compute information matrix
+ virtual Matrix information() const { return thisR().transpose() * thisR(); }
+
+ /// Compute covariance matrix
+ virtual Matrix covariance() const { return information().inverse(); }
+
private:
/** Serialization function */
friend class boost::serialization::access;
@@ -303,6 +306,7 @@ namespace gtsam {
}
virtual void print(const std::string& name) const;
+ virtual Vector sigmas() const { return sigmas_; }
virtual Vector whiten(const Vector& v) const;
virtual Vector unwhiten(const Vector& v) const;
virtual Matrix Whiten(const Matrix& H) const;
@@ -312,7 +316,6 @@ namespace gtsam {
/**
* Return standard deviations (sqrt of diagonal)
*/
- inline const Vector& sigmas() const { return sigmas_; }
inline double sigma(size_t i) const { return sigmas_(i); }
/**
@@ -629,20 +632,23 @@ namespace gtsam {
virtual ~Base() {}
/// robust error function to implement
- virtual double weight(const double &error) const = 0;
+ virtual double weight(double error) const = 0;
virtual void print(const std::string &s) const = 0;
- virtual bool equals(const Base& expected, const double tol=1e-8) const = 0;
+ virtual bool equals(const Base& expected, double tol=1e-8) const = 0;
- inline double sqrtWeight(const double &error) const
- { return std::sqrt(weight(error)); }
+ double sqrtWeight(double error) const {
+ return std::sqrt(weight(error));
+ }
/** produce a weight vector according to an error vector and the implemented
* robust function */
Vector weight(const Vector &error) const;
/** square root version of the weight function */
- Vector sqrtWeight(const Vector &error) const;
+ Vector sqrtWeight(const Vector &error) const {
+ return weight(error).cwiseSqrt();
+ }
/** reweight block matrices and a vector according to their weight implementation */
void reweight(Vector &error) const;
@@ -667,9 +673,9 @@ namespace gtsam {
Null(const ReweightScheme reweight = Block) : Base(reweight) {}
virtual ~Null() {}
- virtual double weight(const double& /*error*/) const { return 1.0; }
+ virtual double weight(double /*error*/) const { return 1.0; }
virtual void print(const std::string &s) const;
- virtual bool equals(const Base& /*expected*/, const double /*tol*/) const { return true; }
+ virtual bool equals(const Base& /*expected*/, double /*tol*/) const { return true; }
static shared_ptr Create() ;
private:
@@ -686,12 +692,12 @@ namespace gtsam {
public:
typedef boost::shared_ptr shared_ptr;
- Fair(const double c = 1.3998, const ReweightScheme reweight = Block);
+ Fair(double c = 1.3998, const ReweightScheme reweight = Block);
virtual ~Fair() {}
- virtual double weight(const double &error) const ;
- virtual void print(const std::string &s) const ;
- virtual bool equals(const Base& expected, const double tol=1e-8) const ;
- static shared_ptr Create(const double c, const ReweightScheme reweight = Block) ;
+ virtual double weight(double error) const;
+ virtual void print(const std::string &s) const;
+ virtual bool equals(const Base& expected, double tol=1e-8) const;
+ static shared_ptr Create(double c, const ReweightScheme reweight = Block) ;
protected:
double c_;
@@ -712,11 +718,11 @@ namespace gtsam {
typedef boost::shared_ptr shared_ptr;
virtual ~Huber() {}
- Huber(const double k = 1.345, const ReweightScheme reweight = Block);
- virtual double weight(const double &error) const ;
- virtual void print(const std::string &s) const ;
- virtual bool equals(const Base& expected, const double tol=1e-8) const ;
- static shared_ptr Create(const double k, const ReweightScheme reweight = Block) ;
+ Huber(double k = 1.345, const ReweightScheme reweight = Block);
+ virtual double weight(double error) const;
+ virtual void print(const std::string &s) const;
+ virtual bool equals(const Base& expected, double tol=1e-8) const;
+ static shared_ptr Create(double k, const ReweightScheme reweight = Block) ;
protected:
double k_;
@@ -741,11 +747,11 @@ namespace gtsam {
typedef boost::shared_ptr shared_ptr;
virtual ~Cauchy() {}
- Cauchy(const double k = 0.1, const ReweightScheme reweight = Block);
- virtual double weight(const double &error) const ;
- virtual void print(const std::string &s) const ;
- virtual bool equals(const Base& expected, const double tol=1e-8) const ;
- static shared_ptr Create(const double k, const ReweightScheme reweight = Block) ;
+ Cauchy(double k = 0.1, const ReweightScheme reweight = Block);
+ virtual double weight(double error) const;
+ virtual void print(const std::string &s) const;
+ virtual bool equals(const Base& expected, double tol=1e-8) const;
+ static shared_ptr Create(double k, const ReweightScheme reweight = Block) ;
protected:
double k_;
@@ -765,12 +771,12 @@ namespace gtsam {
public:
typedef boost::shared_ptr shared_ptr;
- Tukey(const double c = 4.6851, const ReweightScheme reweight = Block);
+ Tukey(double c = 4.6851, const ReweightScheme reweight = Block);
virtual ~Tukey() {}
- virtual double weight(const double &error) const ;
- virtual void print(const std::string &s) const ;
- virtual bool equals(const Base& expected, const double tol=1e-8) const ;
- static shared_ptr Create(const double k, const ReweightScheme reweight = Block) ;
+ virtual double weight(double error) const;
+ virtual void print(const std::string &s) const;
+ virtual bool equals(const Base& expected, double tol=1e-8) const;
+ static shared_ptr Create(double k, const ReweightScheme reweight = Block) ;
protected:
double c_;
@@ -790,12 +796,12 @@ namespace gtsam {
public:
typedef boost::shared_ptr shared_ptr;
- Welsh(const double c = 2.9846, const ReweightScheme reweight = Block);
+ Welsh(double c = 2.9846, const ReweightScheme reweight = Block);
virtual ~Welsh() {}
- virtual double weight(const double &error) const ;
- virtual void print(const std::string &s) const ;
- virtual bool equals(const Base& expected, const double tol=1e-8) const ;
- static shared_ptr Create(const double k, const ReweightScheme reweight = Block) ;
+ virtual double weight(double error) const;
+ virtual void print(const std::string &s) const;
+ virtual bool equals(const Base& expected, double tol=1e-8) const;
+ static shared_ptr Create(double k, const ReweightScheme reweight = Block) ;
protected:
double c_;
diff --git a/gtsam/linear/SubgraphPreconditioner.h b/gtsam/linear/SubgraphPreconditioner.h
index d7dbbd124..350963bcf 100644
--- a/gtsam/linear/SubgraphPreconditioner.h
+++ b/gtsam/linear/SubgraphPreconditioner.h
@@ -70,7 +70,7 @@ namespace gtsam {
Subgraph(const std::vector &indices) ;
inline const Edges& edges() const { return edges_; }
- inline const size_t size() const { return edges_.size(); }
+ inline size_t size() const { return edges_.size(); }
EdgeIndices edgeIndices() const;
iterator begin() { return edges_.begin(); }
diff --git a/gtsam/linear/tests/testNoiseModel.cpp b/gtsam/linear/tests/testNoiseModel.cpp
index 5c6b2729d..b2afb1709 100644
--- a/gtsam/linear/tests/testNoiseModel.cpp
+++ b/gtsam/linear/tests/testNoiseModel.cpp
@@ -33,15 +33,16 @@ using namespace gtsam;
using namespace noiseModel;
using namespace boost::assign;
-static double sigma = 2, s_1=1.0/sigma, var = sigma*sigma, prc = 1.0/var;
-static Matrix R = (Matrix(3, 3) <<
+static const double kSigma = 2, s_1=1.0/kSigma, kVariance = kSigma*kSigma, prc = 1.0/kVariance;
+static const Matrix R = (Matrix(3, 3) <<
s_1, 0.0, 0.0,
0.0, s_1, 0.0,
0.0, 0.0, s_1).finished();
-static Matrix Sigma = (Matrix(3, 3) <<
- var, 0.0, 0.0,
- 0.0, var, 0.0,
- 0.0, 0.0, var).finished();
+static const Matrix kCovariance = (Matrix(3, 3) <<
+ kVariance, 0.0, 0.0,
+ 0.0, kVariance, 0.0,
+ 0.0, 0.0, kVariance).finished();
+static const Vector3 kSigmas(kSigma, kSigma, kSigma);
//static double inf = numeric_limits::infinity();
@@ -53,15 +54,19 @@ TEST(NoiseModel, constructors)
// Construct noise models
vector m;
- m.push_back(Gaussian::SqrtInformation(R));
- m.push_back(Gaussian::Covariance(Sigma));
- //m.push_back(Gaussian::Information(Q));
- m.push_back(Diagonal::Sigmas((Vector(3) << sigma, sigma, sigma).finished()));
- m.push_back(Diagonal::Variances((Vector(3) << var, var, var).finished()));
- m.push_back(Diagonal::Precisions((Vector(3) << prc, prc, prc).finished()));
- m.push_back(Isotropic::Sigma(3, sigma));
- m.push_back(Isotropic::Variance(3, var));
- m.push_back(Isotropic::Precision(3, prc));
+ m.push_back(Gaussian::SqrtInformation(R,false));
+ m.push_back(Gaussian::Covariance(kCovariance,false));
+ m.push_back(Gaussian::Information(kCovariance.inverse(),false));
+ m.push_back(Diagonal::Sigmas(kSigmas,false));
+ m.push_back(Diagonal::Variances((Vector(3) << kVariance, kVariance, kVariance).finished(),false));
+ m.push_back(Diagonal::Precisions((Vector(3) << prc, prc, prc).finished(),false));
+ m.push_back(Isotropic::Sigma(3, kSigma,false));
+ m.push_back(Isotropic::Variance(3, kVariance,false));
+ m.push_back(Isotropic::Precision(3, prc,false));
+
+ // test kSigmas
+ BOOST_FOREACH(Gaussian::shared_ptr mi, m)
+ EXPECT(assert_equal(kSigmas,mi->sigmas()));
// test whiten
BOOST_FOREACH(Gaussian::shared_ptr mi, m)
@@ -117,9 +122,9 @@ TEST(NoiseModel, equals)
{
Gaussian::shared_ptr g1 = Gaussian::SqrtInformation(R),
g2 = Gaussian::SqrtInformation(eye(3,3));
- Diagonal::shared_ptr d1 = Diagonal::Sigmas((Vector(3) << sigma, sigma, sigma).finished()),
+ Diagonal::shared_ptr d1 = Diagonal::Sigmas((Vector(3) << kSigma, kSigma, kSigma).finished()),
d2 = Diagonal::Sigmas(Vector3(0.1, 0.2, 0.3));
- Isotropic::shared_ptr i1 = Isotropic::Sigma(3, sigma),
+ Isotropic::shared_ptr i1 = Isotropic::Sigma(3, kSigma),
i2 = Isotropic::Sigma(3, 0.7);
EXPECT(assert_equal(*g1,*g1));
@@ -155,7 +160,7 @@ TEST(NoiseModel, ConstrainedConstructors )
Constrained::shared_ptr actual;
size_t d = 3;
double m = 100.0;
- Vector sigmas = (Vector(3) << sigma, 0.0, 0.0).finished();
+ Vector sigmas = (Vector(3) << kSigma, 0.0, 0.0).finished();
Vector mu = Vector3(200.0, 300.0, 400.0);
actual = Constrained::All(d);
// TODO: why should this be a thousand ??? Dummy variable?
@@ -182,7 +187,7 @@ TEST(NoiseModel, ConstrainedMixed )
{
Vector feasible = Vector3(1.0, 0.0, 1.0),
infeasible = Vector3(1.0, 1.0, 1.0);
- Diagonal::shared_ptr d = Constrained::MixedSigmas((Vector(3) << sigma, 0.0, sigma).finished());
+ Diagonal::shared_ptr d = Constrained::MixedSigmas((Vector(3) << kSigma, 0.0, kSigma).finished());
// NOTE: we catch constrained variables elsewhere, so whitening does nothing
EXPECT(assert_equal(Vector3(0.5, 1.0, 0.5),d->whiten(infeasible)));
EXPECT(assert_equal(Vector3(0.5, 0.0, 0.5),d->whiten(feasible)));
@@ -357,6 +362,44 @@ TEST(NoiseModel, robustNoise)
DOUBLES_EQUAL(sqrt(k/100.0)*1000.0, A(1,1), 1e-8);
}
+/* ************************************************************************* */
+#define TEST_GAUSSIAN(gaussian)\
+ EQUALITY(info, gaussian->information());\
+ EQUALITY(cov, gaussian->covariance());\
+ EXPECT(assert_equal(white, gaussian->whiten(e)));\
+ EXPECT(assert_equal(e, gaussian->unwhiten(white)));\
+ EXPECT_DOUBLES_EQUAL(251, gaussian->distance(e), 1e-9);\
+ Matrix A = R.inverse(); Vector b = e;\
+ gaussian->WhitenSystem(A, b);\
+ EXPECT(assert_equal(I, A));\
+ EXPECT(assert_equal(white, b));
+
+TEST(NoiseModel, NonDiagonalGaussian)
+{
+ Matrix3 R;
+ R << 6, 5, 4, 0, 3, 2, 0, 0, 1;
+ const Matrix3 info = R.transpose() * R;
+ const Matrix3 cov = info.inverse();
+ const Vector3 e(1, 1, 1), white = R * e;
+ Matrix I = Matrix3::Identity();
+
+
+ {
+ SharedGaussian gaussian = Gaussian::SqrtInformation(R);
+ TEST_GAUSSIAN(gaussian);
+ }
+
+ {
+ SharedGaussian gaussian = Gaussian::Information(info);
+ TEST_GAUSSIAN(gaussian);
+ }
+
+ {
+ SharedGaussian gaussian = Gaussian::Covariance(cov);
+ TEST_GAUSSIAN(gaussian);
+ }
+}
+
/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
/* ************************************************************************* */
diff --git a/gtsam/navigation/ImuBias.h b/gtsam/navigation/ImuBias.h
index 6281efb27..571fb7249 100644
--- a/gtsam/navigation/ImuBias.h
+++ b/gtsam/navigation/ImuBias.h
@@ -17,8 +17,10 @@
#pragma once
+#include
+#include
+#include
#include
-#include
/*
* NOTES:
@@ -131,8 +133,8 @@ public:
/// print with optional string
void print(const std::string& s = "") const {
// explicit printing for now.
- std::cout << s + ".biasAcc [" << biasAcc_.transpose() << "]" << std::endl;
- std::cout << s + ".biasGyro [" << biasGyro_.transpose() << "]" << std::endl;
+ std::cout << s + ".Acc [" << biasAcc_.transpose() << "]" << std::endl;
+ std::cout << s + ".Gyro [" << biasGyro_.transpose() << "]" << std::endl;
}
/** equality up to tolerance */
diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp
index 728251636..01de5a8f3 100644
--- a/gtsam/navigation/ImuFactor.cpp
+++ b/gtsam/navigation/ImuFactor.cpp
@@ -44,7 +44,6 @@ ImuFactor::PreintegratedMeasurements::PreintegratedMeasurements(
//------------------------------------------------------------------------------
void ImuFactor::PreintegratedMeasurements::print(const string& s) const {
PreintegrationBase::print(s);
- cout << " preintMeasCov = \n [ " << preintMeasCov_ << " ]" << endl;
}
//------------------------------------------------------------------------------
@@ -75,8 +74,7 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement(
const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); // rotation increment computed from the current rotation rate measurement
// Update Jacobians
- updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr,
- deltaT);
+ updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT);
// Update preintegrated measurements (also get Jacobian)
const Matrix3 R_i = deltaRij(); // store this, which is useful to compute G_test
@@ -89,8 +87,8 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement(
// preintMeasCov = F * preintMeasCov * F.transpose() + G * (1/deltaT) * measurementCovariance * G.transpose();
// NOTE 1: (1/deltaT) allows to pass from continuous time noise to discrete time noise
// measurementCovariance_discrete = measurementCovariance_contTime * (1/deltaT)
- // NOTE 2: the computation of G * (1/deltaT) * measurementCovariance * G.transpose() is done blockwise,
- // as G and measurementCovariance are blockdiagonal matrices
+ // NOTE 2: the computation of G * (1/deltaT) * measurementCovariance * G.transpose() is done block-wise,
+ // as G and measurementCovariance are block-diagonal matrices
preintMeasCov_ = F * preintMeasCov_ * F.transpose();
preintMeasCov_.block<3, 3>(0, 0) += integrationCovariance() * deltaT;
preintMeasCov_.block<3, 3>(3, 3) += R_i * accelerometerCovariance()
@@ -156,7 +154,8 @@ void ImuFactor::print(const string& s, const KeyFormatter& keyFormatter) const {
<< ")\n";
ImuFactorBase::print("");
_PIM_.print(" preintegrated measurements:");
- this->noiseModel_->print(" noise model: ");
+ // Print standard deviations on covariance only
+ cout << " noise model sigmas: " << this->noiseModel_->sigmas().transpose() << endl;
}
//------------------------------------------------------------------------------
diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h
index 6d870bee0..50e6c835f 100644
--- a/gtsam/navigation/ImuFactor.h
+++ b/gtsam/navigation/ImuFactor.h
@@ -110,7 +110,7 @@ public:
* Add a single IMU measurement to the preintegration.
* @param measuredAcc Measured acceleration (in body frame, as given by the sensor)
* @param measuredOmega Measured angular velocity (as given by the sensor)
- * @param deltaT Time interval between two consecutive IMU measurements
+ * @param deltaT Time interval between this and the last IMU measurement
* @param body_P_sensor Optional sensor frame (pose of the IMU in the body frame)
* @param Fout, Gout Jacobians used internally (only needed for testing)
*/
diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h
index 3cce1b86e..2379f58af 100644
--- a/gtsam/navigation/PreintegratedRotation.h
+++ b/gtsam/navigation/PreintegratedRotation.h
@@ -69,11 +69,8 @@ public:
/// Needed for testable
void print(const std::string& s) const {
std::cout << s << std::endl;
- std::cout << "deltaTij [" << deltaTij_ << "]" << std::endl;
- deltaRij_.print(" deltaRij ");
- std::cout << "delRdelBiasOmega [" << delRdelBiasOmega_ << "]" << std::endl;
- std::cout << "gyroscopeCovariance [" << gyroscopeCovariance_ << "]"
- << std::endl;
+ std::cout << " deltaTij [" << deltaTij_ << "]" << std::endl;
+ std::cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << std::endl;
}
/// Needed for testable
diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp
new file mode 100644
index 000000000..496569599
--- /dev/null
+++ b/gtsam/navigation/PreintegrationBase.cpp
@@ -0,0 +1,357 @@
+/* ----------------------------------------------------------------------------
+
+ * 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 PreintegrationBase.h
+ * @author Luca Carlone
+ * @author Stephen Williams
+ * @author Richard Roberts
+ * @author Vadim Indelman
+ * @author David Jensen
+ * @author Frank Dellaert
+ **/
+
+#include "PreintegrationBase.h"
+
+namespace gtsam {
+
+PreintegrationBase::PreintegrationBase(const imuBias::ConstantBias& bias,
+ const Matrix3& measuredAccCovariance,
+ const Matrix3& measuredOmegaCovariance,
+ const Matrix3&integrationErrorCovariance,
+ const bool use2ndOrderIntegration)
+ : PreintegratedRotation(measuredOmegaCovariance),
+ biasHat_(bias),
+ use2ndOrderIntegration_(use2ndOrderIntegration),
+ deltaPij_(Vector3::Zero()),
+ deltaVij_(Vector3::Zero()),
+ delPdelBiasAcc_(Z_3x3),
+ delPdelBiasOmega_(Z_3x3),
+ delVdelBiasAcc_(Z_3x3),
+ delVdelBiasOmega_(Z_3x3),
+ accelerometerCovariance_(measuredAccCovariance),
+ integrationCovariance_(integrationErrorCovariance) {
+}
+
+/// Needed for testable
+void PreintegrationBase::print(const std::string& s) const {
+ PreintegratedRotation::print(s);
+ std::cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << std::endl;
+ std::cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << std::endl;
+ biasHat_.print(" biasHat");
+}
+
+/// Needed for testable
+bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const {
+ return PreintegratedRotation::equals(other, tol) && biasHat_.equals(other.biasHat_, tol)
+ && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol)
+ && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol)
+ && equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol)
+ && equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol)
+ && equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol)
+ && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol)
+ && equal_with_abs_tol(accelerometerCovariance_, other.accelerometerCovariance_, tol)
+ && equal_with_abs_tol(integrationCovariance_, other.integrationCovariance_, tol);
+}
+
+/// Re-initialize PreintegratedMeasurements
+void PreintegrationBase::resetIntegration() {
+ PreintegratedRotation::resetIntegration();
+ deltaPij_ = Vector3::Zero();
+ deltaVij_ = Vector3::Zero();
+ delPdelBiasAcc_ = Z_3x3;
+ delPdelBiasOmega_ = Z_3x3;
+ delVdelBiasAcc_ = Z_3x3;
+ delVdelBiasOmega_ = Z_3x3;
+}
+
+/// Update preintegrated measurements
+void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correctedAcc,
+ const Rot3& incrR, const double deltaT,
+ OptionalJacobian<9, 9> F) {
+
+ const Matrix3 dRij = deltaRij(); // expensive
+ const Vector3 temp = dRij * correctedAcc * deltaT;
+ if (!use2ndOrderIntegration_) {
+ deltaPij_ += deltaVij_ * deltaT;
+ } else {
+ deltaPij_ += deltaVij_ * deltaT + 0.5 * temp * deltaT;
+ }
+ deltaVij_ += temp;
+
+ Matrix3 R_i, F_angles_angles;
+ if (F)
+ R_i = deltaRij(); // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij
+ updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0);
+
+ if (F) {
+ const Matrix3 F_vel_angles = -R_i * skewSymmetric(correctedAcc) * deltaT;
+ Matrix3 F_pos_angles;
+ if (use2ndOrderIntegration_)
+ F_pos_angles = 0.5 * F_vel_angles * deltaT;
+ else
+ F_pos_angles = Z_3x3;
+
+ // pos vel angle
+ *F << //
+ I_3x3, I_3x3 * deltaT, F_pos_angles, // pos
+ Z_3x3, I_3x3, F_vel_angles, // vel
+ Z_3x3, Z_3x3, F_angles_angles; // angle
+ }
+}
+
+/// Update Jacobians to be used during preintegration
+void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAcc,
+ const Matrix3& D_Rincr_integratedOmega,
+ const Rot3& incrR, double deltaT) {
+ const Matrix3 dRij = deltaRij(); // expensive
+ const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT * delRdelBiasOmega();
+ if (!use2ndOrderIntegration_) {
+ delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT;
+ delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT;
+ } else {
+ delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - 0.5 * dRij * deltaT * deltaT;
+ delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5);
+ }
+ delVdelBiasAcc_ += -dRij * deltaT;
+ delVdelBiasOmega_ += temp;
+ update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT);
+}
+
+void PreintegrationBase::correctMeasurementsByBiasAndSensorPose(
+ const Vector3& measuredAcc, const Vector3& measuredOmega, Vector3& correctedAcc,
+ Vector3& correctedOmega, boost::optional body_P_sensor) {
+ correctedAcc = biasHat_.correctAccelerometer(measuredAcc);
+ correctedOmega = biasHat_.correctGyroscope(measuredOmega);
+
+ // Then compensate for sensor-body displacement: we express the quantities
+ // (originally in the IMU frame) into the body frame
+ if (body_P_sensor) {
+ Matrix3 body_R_sensor = body_P_sensor->rotation().matrix();
+ correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame
+ Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega);
+ correctedAcc = body_R_sensor * correctedAcc
+ - body_omega_body__cross * body_omega_body__cross * body_P_sensor->translation().vector();
+ // linear acceleration vector in the body frame
+ }
+}
+
+/// Predict state at time j
+//------------------------------------------------------------------------------
+PoseVelocityBias PreintegrationBase::predict(
+ const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i,
+ const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis,
+ boost::optional deltaPij_biascorrected_out,
+ boost::optional deltaVij_biascorrected_out) const {
+
+ const imuBias::ConstantBias biasIncr = bias_i - biasHat();
+ const Vector3& biasAccIncr = biasIncr.accelerometer();
+ const Vector3& biasOmegaIncr = biasIncr.gyroscope();
+
+ const Rot3& Rot_i = pose_i.rotation();
+ const Matrix3 Rot_i_matrix = Rot_i.matrix();
+ const Vector3 pos_i = pose_i.translation().vector();
+
+ // Predict state at time j
+ /* ---------------------------------------------------------------------------------------------------- */
+ const Vector3 deltaPij_biascorrected = deltaPij() + delPdelBiasAcc() * biasAccIncr
+ + delPdelBiasOmega() * biasOmegaIncr;
+ if (deltaPij_biascorrected_out) // if desired, store this
+ *deltaPij_biascorrected_out = deltaPij_biascorrected;
+
+ const double dt = deltaTij(), dt2 = dt * dt;
+ Vector3 pos_j = pos_i + Rot_i_matrix * deltaPij_biascorrected + vel_i * dt
+ - omegaCoriolis.cross(vel_i) * dt2 // Coriolis term - we got rid of the 2 wrt ins paper
+ + 0.5 * gravity * dt2;
+
+ const Vector3 deltaVij_biascorrected = deltaVij() + delVdelBiasAcc() * biasAccIncr
+ + delVdelBiasOmega() * biasOmegaIncr;
+ if (deltaVij_biascorrected_out) // if desired, store this
+ *deltaVij_biascorrected_out = deltaVij_biascorrected;
+
+ Vector3 vel_j = Vector3(
+ vel_i + Rot_i_matrix * deltaVij_biascorrected - 2 * omegaCoriolis.cross(vel_i) * dt // Coriolis term
+ + gravity * dt);
+
+ if (use2ndOrderCoriolis) {
+ pos_j += -0.5 * omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt2; // 2nd order coriolis term for position
+ vel_j += -omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt; // 2nd order term for velocity
+ }
+
+ const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasOmegaIncr);
+ // deltaRij_biascorrected = deltaRij * expmap(delRdelBiasOmega * biasOmegaIncr)
+
+ const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected);
+ const Vector3 correctedOmega = biascorrectedOmega
+ - Rot_i_matrix.transpose() * omegaCoriolis * dt; // Coriolis term
+ const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega);
+ const Rot3 Rot_j = Rot_i.compose(correctedDeltaRij);
+
+ const Pose3 pose_j = Pose3(Rot_j, Point3(pos_j));
+ return PoseVelocityBias(pose_j, vel_j, bias_i); // bias is predicted as a constant
+}
+
+/// Compute errors w.r.t. preintegrated measurements and Jacobians wrpt pose_i, vel_i, bias_i, pose_j, bias_j
+//------------------------------------------------------------------------------
+Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i,
+ const Pose3& pose_j, const Vector3& vel_j,
+ const imuBias::ConstantBias& bias_i,
+ const Vector3& gravity,
+ const Vector3& omegaCoriolis,
+ const bool use2ndOrderCoriolis,
+ OptionalJacobian<9, 6> H1,
+ OptionalJacobian<9, 3> H2,
+ OptionalJacobian<9, 6> H3,
+ OptionalJacobian<9, 3> H4,
+ OptionalJacobian<9, 6> H5) const {
+
+ // We need the mismatch w.r.t. the biases used for preintegration
+ const Vector3 biasOmegaIncr = bias_i.gyroscope() - biasHat().gyroscope();
+
+ // we give some shorter name to rotations and translations
+ const Rot3& Ri = pose_i.rotation();
+ const Matrix3 Ri_transpose_matrix = Ri.transpose();
+
+ const Rot3& Rj = pose_j.rotation();
+ const Vector3 pos_j = pose_j.translation().vector();
+
+ // Evaluate residual error, according to [3]
+ /* ---------------------------------------------------------------------------------------------------- */
+ Vector3 deltaPij_biascorrected, deltaVij_biascorrected;
+ PoseVelocityBias predictedState_j = predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis,
+ use2ndOrderCoriolis, deltaPij_biascorrected,
+ deltaVij_biascorrected);
+
+ // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance
+ const Vector3 fp = Ri_transpose_matrix * (pos_j - predictedState_j.pose.translation().vector());
+
+ // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance
+ const Vector3 fv = Ri_transpose_matrix * (vel_j - predictedState_j.velocity);
+
+ // fR will be computed later. Note: it is the same as: fR = (predictedState_j.pose.translation()).between(Rot_j)
+
+ /* ---------------------------------------------------------------------------------------------------- */
+ // Get Get so<3> version of bias corrected rotation
+ // If H5 is asked for, we will need the Jacobian, which we store in H5
+ // H5 will then be corrected below to take into account the Coriolis effect
+ Matrix3 D_cThetaRij_biasOmegaIncr;
+ const Vector3 biascorrectedOmega = biascorrectedThetaRij(biasOmegaIncr,
+ H5 ? &D_cThetaRij_biasOmegaIncr : 0);
+
+ // Coriolis term, note inconsistent with AHRS, where coriolisHat is *after* integration
+ const Vector3 coriolis = integrateCoriolis(Ri, omegaCoriolis);
+ const Vector3 correctedOmega = biascorrectedOmega - coriolis;
+
+ // Calculate Jacobians, matrices below needed only for some Jacobians...
+ Vector3 fR;
+ Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot, Ritranspose_omegaCoriolisHat;
+
+ if (H1 || H2)
+ Ritranspose_omegaCoriolisHat = Ri_transpose_matrix * skewSymmetric(omegaCoriolis);
+
+ // This is done to save computation: we ask for the jacobians only when they are needed
+ const double dt = deltaTij(), dt2 = dt*dt;
+ Rot3 fRrot;
+ const Rot3 RiBetweenRj = Rot3(Ri_transpose_matrix) * Rj;
+ if (H1 || H2 || H3 || H4 || H5) {
+ const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, D_cDeltaRij_cOmega);
+ // Residual rotation error
+ fRrot = correctedDeltaRij.between(RiBetweenRj);
+ fR = Rot3::Logmap(fRrot, D_fR_fRrot);
+ D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis);
+ } else {
+ const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega);
+ // Residual rotation error
+ fRrot = correctedDeltaRij.between(RiBetweenRj);
+ fR = Rot3::Logmap(fRrot);
+ }
+ if (H1) {
+ Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3;
+ if (use2ndOrderCoriolis) {
+ // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri.matrix()
+ const Matrix3 temp = Ritranspose_omegaCoriolisHat
+ * (-Ritranspose_omegaCoriolisHat.transpose());
+ dfPdPi += 0.5 * temp * dt2;
+ dfVdPi += temp * dt;
+ }
+ (*H1) <<
+ // dfP/dRi
+ skewSymmetric(fp + deltaPij_biascorrected),
+ // dfP/dPi
+ dfPdPi,
+ // dfV/dRi
+ skewSymmetric(fv + deltaVij_biascorrected),
+ // dfV/dPi
+ dfVdPi,
+ // dfR/dRi
+ D_fR_fRrot * (-Rj.between(Ri).matrix() - fRrot.inverse().matrix() * D_coriolis),
+ // dfR/dPi
+ Z_3x3;
+ }
+ if (H2) {
+ (*H2) <<
+ // dfP/dVi
+ -Ri_transpose_matrix * dt + Ritranspose_omegaCoriolisHat * dt2, // Coriolis term - we got rid of the 2 wrt ins paper
+ // dfV/dVi
+ -Ri_transpose_matrix + 2 * Ritranspose_omegaCoriolisHat * dt, // Coriolis term
+ // dfR/dVi
+ Z_3x3;
+ }
+ if (H3) {
+ (*H3) <<
+ // dfP/dPosej
+ Z_3x3, Ri_transpose_matrix * Rj.matrix(),
+ // dfV/dPosej
+ Matrix::Zero(3, 6),
+ // dfR/dPosej
+ D_fR_fRrot, Z_3x3;
+ }
+ if (H4) {
+ (*H4) <<
+ // dfP/dVj
+ Z_3x3,
+ // dfV/dVj
+ Ri_transpose_matrix,
+ // dfR/dVj
+ Z_3x3;
+ }
+ if (H5) {
+ // H5 by this point already contains 3*3 biascorrectedThetaRij derivative
+ const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_cThetaRij_biasOmegaIncr;
+ (*H5) <<
+ // dfP/dBias
+ -delPdelBiasAcc(), -delPdelBiasOmega(),
+ // dfV/dBias
+ -delVdelBiasAcc(), -delVdelBiasOmega(),
+ // dfR/dBias
+ Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega);
+ }
+ Vector9 r;
+ r << fp, fv, fR;
+ return r;
+}
+
+ImuBase::ImuBase()
+ : gravity_(Vector3(0.0, 0.0, 9.81)),
+ omegaCoriolis_(Vector3(0.0, 0.0, 0.0)),
+ body_P_sensor_(boost::none),
+ use2ndOrderCoriolis_(false) {
+}
+
+ImuBase::ImuBase(const Vector3& gravity, const Vector3& omegaCoriolis,
+ boost::optional body_P_sensor, const bool use2ndOrderCoriolis)
+ : gravity_(gravity),
+ omegaCoriolis_(omegaCoriolis),
+ body_P_sensor_(body_P_sensor),
+ use2ndOrderCoriolis_(use2ndOrderCoriolis) {
+}
+
+} /// namespace gtsam
diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h
index 522b564a8..f6b24e752 100644
--- a/gtsam/navigation/PreintegrationBase.h
+++ b/gtsam/navigation/PreintegrationBase.h
@@ -23,6 +23,9 @@
#include
#include
+#include
+#include
+#include
namespace gtsam {
@@ -34,9 +37,10 @@ struct PoseVelocityBias {
Vector3 velocity;
imuBias::ConstantBias bias;
- PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity,
- const imuBias::ConstantBias _bias) :
- pose(_pose), velocity(_velocity), bias(_bias) {
+ PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, const imuBias::ConstantBias _bias)
+ : pose(_pose),
+ velocity(_velocity),
+ bias(_bias) {
}
};
@@ -46,24 +50,24 @@ struct PoseVelocityBias {
* It includes the definitions of the preintegrated variables and the methods
* to access, print, and compare them.
*/
-class PreintegrationBase: public PreintegratedRotation {
+class PreintegrationBase : public PreintegratedRotation {
- imuBias::ConstantBias biasHat_; ///< Acceleration and angular rate bias values used during preintegration
- bool use2ndOrderIntegration_; ///< Controls the order of integration
+ imuBias::ConstantBias biasHat_; ///< Acceleration and angular rate bias values used during preintegration
+ bool use2ndOrderIntegration_; ///< Controls the order of integration
- Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i)
- Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame)
+ Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i)
+ Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame)
- Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias
- Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias
- Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias
- Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias
+ Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias
+ Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias
+ Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias
+ Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias
- Matrix3 accelerometerCovariance_; ///< continuous-time "Covariance" of accelerometer measurements
- Matrix3 integrationCovariance_; ///< continuous-time "Covariance" describing integration uncertainty
+ const Matrix3 accelerometerCovariance_; ///< continuous-time "Covariance" of accelerometer measurements
+ const Matrix3 integrationCovariance_; ///< continuous-time "Covariance" describing integration uncertainty
/// (to compensate errors in Euler integration)
-public:
+ public:
/**
* Default constructor, initializes the variables in the base class
@@ -71,18 +75,9 @@ public:
* @param use2ndOrderIntegration Controls the order of integration
* (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2)
*/
- PreintegrationBase(const imuBias::ConstantBias& bias,
- const Matrix3& measuredAccCovariance,
- const Matrix3& measuredOmegaCovariance,
- const Matrix3&integrationErrorCovariance,
- const bool use2ndOrderIntegration) :
- PreintegratedRotation(measuredOmegaCovariance), biasHat_(bias), use2ndOrderIntegration_(
- use2ndOrderIntegration), deltaPij_(Vector3::Zero()), deltaVij_(
- Vector3::Zero()), delPdelBiasAcc_(Z_3x3), delPdelBiasOmega_(Z_3x3), delVdelBiasAcc_(
- Z_3x3), delVdelBiasOmega_(Z_3x3), accelerometerCovariance_(
- measuredAccCovariance), integrationCovariance_(
- integrationErrorCovariance) {
- }
+ PreintegrationBase(const imuBias::ConstantBias& bias, const Matrix3& measuredAccCovariance,
+ const Matrix3& measuredOmegaCovariance,
+ const Matrix3&integrationErrorCovariance, const bool use2ndOrderIntegration);
/// methods to access class variables
bool use2ndOrderIntegration() const {
@@ -99,7 +94,7 @@ public:
}
Vector6 biasHatVector() const {
return biasHat_.vector();
- } // expensive
+ } // expensive
const Matrix3& delPdelBiasAcc() const {
return delPdelBiasAcc_;
}
@@ -120,319 +115,48 @@ public:
return integrationCovariance_;
}
- /// Needed for testable
- void print(const std::string& s) const {
- PreintegratedRotation::print(s);
- std::cout << " accelerometerCovariance [ " << accelerometerCovariance_
- << " ]" << std::endl;
- std::cout << " integrationCovariance [ " << integrationCovariance_ << " ]"
- << std::endl;
- std::cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << std::endl;
- std::cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << std::endl;
- biasHat_.print(" biasHat");
- }
+ /// print
+ void print(const std::string& s) const;
- /// Needed for testable
- bool equals(const PreintegrationBase& other, double tol) const {
- return PreintegratedRotation::equals(other, tol)
- && biasHat_.equals(other.biasHat_, tol)
- && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol)
- && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol)
- && equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol)
- && equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol)
- && equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol)
- && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol)
- && equal_with_abs_tol(accelerometerCovariance_,
- other.accelerometerCovariance_, tol)
- && equal_with_abs_tol(integrationCovariance_,
- other.integrationCovariance_, tol);
- }
+ /// check equality
+ bool equals(const PreintegrationBase& other, double tol) const;
/// Re-initialize PreintegratedMeasurements
- void resetIntegration() {
- PreintegratedRotation::resetIntegration();
- deltaPij_ = Vector3::Zero();
- deltaVij_ = Vector3::Zero();
- delPdelBiasAcc_ = Z_3x3;
- delPdelBiasOmega_ = Z_3x3;
- delVdelBiasAcc_ = Z_3x3;
- delVdelBiasOmega_ = Z_3x3;
- }
+ void resetIntegration();
/// Update preintegrated measurements
- void updatePreintegratedMeasurements(const Vector3& correctedAcc,
- const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F) {
-
- Matrix3 dRij = deltaRij(); // expensive
- Vector3 temp = dRij * correctedAcc * deltaT;
- if (!use2ndOrderIntegration_) {
- deltaPij_ += deltaVij_ * deltaT;
- } else {
- deltaPij_ += deltaVij_ * deltaT + 0.5 * temp * deltaT;
- }
- deltaVij_ += temp;
-
- Matrix3 R_i, F_angles_angles;
- if (F)
- R_i = deltaRij(); // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij
- updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0);
-
- if (F) {
- Matrix3 F_vel_angles = -R_i * skewSymmetric(correctedAcc) * deltaT;
- Matrix3 F_pos_angles;
- if (use2ndOrderIntegration_)
- F_pos_angles = 0.5 * F_vel_angles * deltaT;
- else
- F_pos_angles = Z_3x3;
-
- // pos vel angle
- *F << //
- I_3x3, I_3x3 * deltaT, F_pos_angles, // pos
- Z_3x3, I_3x3, F_vel_angles, // vel
- Z_3x3, Z_3x3, F_angles_angles; // angle
- }
- }
+ void updatePreintegratedMeasurements(const Vector3& correctedAcc, const Rot3& incrR,
+ const double deltaT, OptionalJacobian<9, 9> F);
/// Update Jacobians to be used during preintegration
void updatePreintegratedJacobians(const Vector3& correctedAcc,
- const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR,
- double deltaT) {
- Matrix3 dRij = deltaRij(); // expensive
- Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT
- * delRdelBiasOmega();
- if (!use2ndOrderIntegration_) {
- delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT;
- delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT;
- } else {
- delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT
- - 0.5 * dRij * deltaT * deltaT;
- delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5);
- }
- delVdelBiasAcc_ += -dRij * deltaT;
- delVdelBiasOmega_ += temp;
- update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT);
- }
+ const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR,
+ double deltaT);
void correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc,
- const Vector3& measuredOmega, Vector3& correctedAcc,
- Vector3& correctedOmega, boost::optional body_P_sensor) {
- correctedAcc = biasHat_.correctAccelerometer(measuredAcc);
- correctedOmega = biasHat_.correctGyroscope(measuredOmega);
-
- // Then compensate for sensor-body displacement: we express the quantities
- // (originally in the IMU frame) into the body frame
- if (body_P_sensor) {
- Matrix3 body_R_sensor = body_P_sensor->rotation().matrix();
- correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame
- Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega);
- correctedAcc = body_R_sensor * correctedAcc
- - body_omega_body__cross * body_omega_body__cross
- * body_P_sensor->translation().vector();
- // linear acceleration vector in the body frame
- }
- }
+ const Vector3& measuredOmega, Vector3& correctedAcc,
+ Vector3& correctedOmega,
+ boost::optional body_P_sensor);
/// Predict state at time j
- //------------------------------------------------------------------------------
- PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i,
- const imuBias::ConstantBias& bias_i, const Vector3& gravity,
- const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false,
+ PoseVelocityBias predict(
+ const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i,
+ const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false,
boost::optional deltaPij_biascorrected_out = boost::none,
- boost::optional deltaVij_biascorrected_out = boost::none) const {
-
- const imuBias::ConstantBias biasIncr = bias_i - biasHat();
- const Vector3& biasAccIncr = biasIncr.accelerometer();
- const Vector3& biasOmegaIncr = biasIncr.gyroscope();
-
- const Rot3& Rot_i = pose_i.rotation();
- const Vector3& pos_i = pose_i.translation().vector();
-
- // Predict state at time j
- /* ---------------------------------------------------------------------------------------------------- */
- Vector3 deltaPij_biascorrected = deltaPij() + delPdelBiasAcc() * biasAccIncr
- + delPdelBiasOmega() * biasOmegaIncr;
- if (deltaPij_biascorrected_out) // if desired, store this
- *deltaPij_biascorrected_out = deltaPij_biascorrected;
-
- Vector3 pos_j = pos_i + Rot_i.matrix() * deltaPij_biascorrected
- + vel_i * deltaTij()
- - omegaCoriolis.cross(vel_i) * deltaTij() * deltaTij() // Coriolis term - we got rid of the 2 wrt ins paper
- + 0.5 * gravity * deltaTij() * deltaTij();
-
- Vector3 deltaVij_biascorrected = deltaVij() + delVdelBiasAcc() * biasAccIncr
- + delVdelBiasOmega() * biasOmegaIncr;
- if (deltaVij_biascorrected_out) // if desired, store this
- *deltaVij_biascorrected_out = deltaVij_biascorrected;
-
- Vector3 vel_j = Vector3(
- vel_i + Rot_i.matrix() * deltaVij_biascorrected
- - 2 * omegaCoriolis.cross(vel_i) * deltaTij() // Coriolis term
- + gravity * deltaTij());
-
- if (use2ndOrderCoriolis) {
- pos_j += -0.5 * omegaCoriolis.cross(omegaCoriolis.cross(pos_i))
- * deltaTij() * deltaTij(); // 2nd order coriolis term for position
- vel_j += -omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * deltaTij(); // 2nd order term for velocity
- }
-
- const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasOmegaIncr);
- // deltaRij_biascorrected = deltaRij * expmap(delRdelBiasOmega * biasOmegaIncr)
-
- Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected);
- Vector3 correctedOmega = biascorrectedOmega
- - Rot_i.inverse().matrix() * omegaCoriolis * deltaTij(); // Coriolis term
- const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega);
- const Rot3 Rot_j = Rot_i.compose(correctedDeltaRij);
-
- Pose3 pose_j = Pose3(Rot_j, Point3(pos_j));
- return PoseVelocityBias(pose_j, vel_j, bias_i); // bias is predicted as a constant
- }
+ boost::optional deltaVij_biascorrected_out = boost::none) const;
/// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j
- //------------------------------------------------------------------------------
- Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i,
- const Pose3& pose_j, const Vector3& vel_j,
- const imuBias::ConstantBias& bias_i, const Vector3& gravity,
- const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis,
- OptionalJacobian<9, 6> H1 = boost::none, OptionalJacobian<9, 3> H2 =
- boost::none, OptionalJacobian<9, 6> H3 = boost::none,
- OptionalJacobian<9, 3> H4 = boost::none, OptionalJacobian<9, 6> H5 =
- boost::none) const {
+ Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j,
+ const Vector3& vel_j, const imuBias::ConstantBias& bias_i,
+ const Vector3& gravity, const Vector3& omegaCoriolis,
+ const bool use2ndOrderCoriolis, OptionalJacobian<9, 6> H1 =
+ boost::none,
+ OptionalJacobian<9, 3> H2 = boost::none,
+ OptionalJacobian<9, 6> H3 = boost::none,
+ OptionalJacobian<9, 3> H4 = boost::none,
+ OptionalJacobian<9, 6> H5 = boost::none) const;
- // We need the mismatch w.r.t. the biases used for preintegration
- const Vector3 biasOmegaIncr = bias_i.gyroscope() - biasHat().gyroscope();
-
- // we give some shorter name to rotations and translations
- const Rot3& Ri = pose_i.rotation();
- const Rot3& Rj = pose_j.rotation();
- const Vector3& pos_j = pose_j.translation().vector();
-
- // Evaluate residual error, according to [3]
- /* ---------------------------------------------------------------------------------------------------- */
- Vector3 deltaPij_biascorrected, deltaVij_biascorrected;
- PoseVelocityBias predictedState_j = predict(pose_i, vel_i, bias_i, gravity,
- omegaCoriolis, use2ndOrderCoriolis, deltaPij_biascorrected,
- deltaVij_biascorrected);
-
- // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance
- const Vector3 fp = Ri.transpose()
- * (pos_j - predictedState_j.pose.translation().vector());
-
- // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance
- const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity);
-
- // fR will be computed later. Note: it is the same as: fR = (predictedState_j.pose.translation()).between(Rot_j)
-
- /* ---------------------------------------------------------------------------------------------------- */
- // Get Get so<3> version of bias corrected rotation
- // If H5 is asked for, we will need the Jacobian, which we store in H5
- // H5 will then be corrected below to take into account the Coriolis effect
- Matrix3 D_cThetaRij_biasOmegaIncr;
- Vector3 biascorrectedOmega = biascorrectedThetaRij(biasOmegaIncr,
- H5 ? &D_cThetaRij_biasOmegaIncr : 0);
-
- // Coriolis term, note inconsistent with AHRS, where coriolisHat is *after* integration
- const Vector3 coriolis = integrateCoriolis(Ri, omegaCoriolis);
- Vector3 correctedOmega = biascorrectedOmega - coriolis;
-
- // Calculate Jacobians, matrices below needed only for some Jacobians...
- Vector3 fR;
- Rot3 correctedDeltaRij, fRrot;
- Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot,
- Ritranspose_omegaCoriolisHat;
-
- if (H1 || H2)
- Ritranspose_omegaCoriolisHat = Ri.transpose()
- * skewSymmetric(omegaCoriolis);
-
- // This is done to save computation: we ask for the jacobians only when they are needed
- if (H1 || H2 || H3 || H4 || H5) {
- correctedDeltaRij = Rot3::Expmap(correctedOmega, D_cDeltaRij_cOmega);
- // Residual rotation error
- fRrot = correctedDeltaRij.between(Ri.between(Rj));
- fR = Rot3::Logmap(fRrot, D_fR_fRrot);
- D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis);
- } else {
- correctedDeltaRij = Rot3::Expmap(correctedOmega);
- // Residual rotation error
- fRrot = correctedDeltaRij.between(Ri.between(Rj));
- fR = Rot3::Logmap(fRrot);
- }
- if (H1) {
- H1->resize(9, 6);
- Matrix3 dfPdPi = -I_3x3;
- Matrix3 dfVdPi = Z_3x3;
- if (use2ndOrderCoriolis) {
- // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri.matrix()
- Matrix3 temp = Ritranspose_omegaCoriolisHat
- * (-Ritranspose_omegaCoriolisHat.transpose());
- dfPdPi += 0.5 * temp * deltaTij() * deltaTij();
- dfVdPi += temp * deltaTij();
- }
- (*H1) <<
- // dfP/dRi
- skewSymmetric(fp + deltaPij_biascorrected),
- // dfP/dPi
- dfPdPi,
- // dfV/dRi
- skewSymmetric(fv + deltaVij_biascorrected),
- // dfV/dPi
- dfVdPi,
- // dfR/dRi
- D_fR_fRrot
- * (-Rj.between(Ri).matrix() - fRrot.inverse().matrix() * D_coriolis),
- // dfR/dPi
- Z_3x3;
- }
- if (H2) {
- H2->resize(9, 3);
- (*H2) <<
- // dfP/dVi
- -Ri.transpose() * deltaTij()
- + Ritranspose_omegaCoriolisHat * deltaTij() * deltaTij(), // Coriolis term - we got rid of the 2 wrt ins paper
- // dfV/dVi
- -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * deltaTij(), // Coriolis term
- // dfR/dVi
- Z_3x3;
- }
- if (H3) {
- H3->resize(9, 6);
- (*H3) <<
- // dfP/dPosej
- Z_3x3, Ri.transpose() * Rj.matrix(),
- // dfV/dPosej
- Matrix::Zero(3, 6),
- // dfR/dPosej
- D_fR_fRrot, Z_3x3;
- }
- if (H4) {
- H4->resize(9, 3);
- (*H4) <<
- // dfP/dVj
- Z_3x3,
- // dfV/dVj
- Ri.transpose(),
- // dfR/dVj
- Z_3x3;
- }
- if (H5) {
- // H5 by this point already contains 3*3 biascorrectedThetaRij derivative
- const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_cThetaRij_biasOmegaIncr;
- H5->resize(9, 6);
- (*H5) <<
- // dfP/dBias
- -delPdelBiasAcc(), -delPdelBiasOmega(),
- // dfV/dBias
- -delVdelBiasAcc(), -delVdelBiasOmega(),
- // dfR/dBias
- Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega);
- }
- Vector9 r;
- r << fp, fv, fR;
- return r;
- }
-
-private:
+ private:
/** Serialization function */
friend class boost::serialization::access;
template
@@ -450,26 +174,22 @@ private:
class ImuBase {
-protected:
+ protected:
Vector3 gravity_;
Vector3 omegaCoriolis_;
- boost::optional body_P_sensor_; ///< The pose of the sensor in the body frame
- bool use2ndOrderCoriolis_; ///< Controls whether higher order terms are included when calculating the Coriolis Effect
+ boost::optional body_P_sensor_; ///< The pose of the sensor in the body frame
+ bool use2ndOrderCoriolis_; ///< Controls whether higher order terms are included when calculating the Coriolis Effect
-public:
+ public:
- ImuBase() :
- gravity_(Vector3(0.0, 0.0, 9.81)), omegaCoriolis_(Vector3(0.0, 0.0, 0.0)), body_P_sensor_(
- boost::none), use2ndOrderCoriolis_(false) {
- }
+ /// Default constructor, with decent gravity and zero coriolis
+ ImuBase();
+ /// Fully fledge constructor
ImuBase(const Vector3& gravity, const Vector3& omegaCoriolis,
- boost::optional body_P_sensor = boost::none,
- const bool use2ndOrderCoriolis = false) :
- gravity_(gravity), omegaCoriolis_(omegaCoriolis), body_P_sensor_(
- body_P_sensor), use2ndOrderCoriolis_(use2ndOrderCoriolis) {
- }
+ boost::optional body_P_sensor = boost::none,
+ const bool use2ndOrderCoriolis = false);
const Vector3& gravity() const {
return gravity_;
@@ -480,4 +200,4 @@ public:
};
-} /// namespace gtsam
+} /// namespace gtsam
diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp
index b8fc8062c..c27921fc9 100644
--- a/gtsam/navigation/tests/testImuFactor.cpp
+++ b/gtsam/navigation/tests/testImuFactor.cpp
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include
#include
@@ -81,19 +82,27 @@ Rot3 updatePreintegratedRot(const Rot3& deltaRij_old,
return deltaRij_new;
}
-// Auxiliary functions to test preintegrated Jacobians
-// delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_
+// Define covariance matrices
/* ************************************************************************* */
double accNoiseVar = 0.01;
double omegaNoiseVar = 0.03;
double intNoiseVar = 0.0001;
+const Matrix3 kMeasuredAccCovariance = accNoiseVar * Matrix3::Identity();
+const Matrix3 kMeasuredOmegaCovariance = omegaNoiseVar * Matrix3::Identity();
+const Matrix3 kIntegrationErrorCovariance = intNoiseVar * Matrix3::Identity();
+
+// Auxiliary functions to test preintegrated Jacobians
+// delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_
+/* ************************************************************************* */
ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements(
const imuBias::ConstantBias& bias, const list& measuredAccs,
const list& measuredOmegas, const list& deltaTs,
const bool use2ndOrderIntegration = false) {
ImuFactor::PreintegratedMeasurements result(bias,
- accNoiseVar * Matrix3::Identity(), omegaNoiseVar * Matrix3::Identity(),
- intNoiseVar * Matrix3::Identity(), use2ndOrderIntegration);
+ kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance,
+ kIntegrationErrorCovariance,
+ use2ndOrderIntegration);
list::const_iterator itAcc = measuredAccs.begin();
list::const_iterator itOmega = measuredOmegas.begin();
@@ -156,8 +165,11 @@ TEST( ImuFactor, PreintegratedMeasurements ) {
bool use2ndOrderIntegration = true;
// Actual preintegrated values
- ImuFactor::PreintegratedMeasurements actual1(bias, Matrix3::Zero(),
- Matrix3::Zero(), Matrix3::Zero(), use2ndOrderIntegration);
+ ImuFactor::PreintegratedMeasurements actual1(bias,
+ kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance,
+ kIntegrationErrorCovariance,
+ use2ndOrderIntegration);
actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
EXPECT(
@@ -208,8 +220,11 @@ TEST( ImuFactor, ErrorAndJacobians ) {
Vector3 measuredAcc = x1.rotation().unrotate(-Point3(gravity)).vector();
double deltaT = 1.0;
bool use2ndOrderIntegration = true;
- ImuFactor::PreintegratedMeasurements pre_int_data(bias, Matrix3::Zero(),
- Matrix3::Zero(), Matrix3::Zero(), use2ndOrderIntegration);
+ ImuFactor::PreintegratedMeasurements pre_int_data(bias,
+ kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance,
+ kIntegrationErrorCovariance,
+ use2ndOrderIntegration);
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
// Create factor
@@ -260,6 +275,40 @@ TEST( ImuFactor, ErrorAndJacobians ) {
Matrix H5e = numericalDerivative11(
boost::bind(&callEvaluateError, factor, x1, v1, x2, v2, _1), bias);
EXPECT(assert_equal(H5e, H5a));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Evaluate error with wrong values
+ Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1);
+ errorActual = factor.evaluateError(x1, v1, x2, v2_wrong, bias);
+ errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0;
+ EXPECT(assert_equal(errorExpected, errorActual, 1e-6));
+
+ Values values;
+ values.insert(X(1), x1);
+ values.insert(V(1), v1);
+ values.insert(X(2), x2);
+ values.insert(V(2), v2_wrong);
+ values.insert(B(1), bias);
+ errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0;
+ EXPECT(assert_equal(factor.unwhitenedError(values), errorActual, 1e-6));
+
+ // Make sure the whitening is done correctly
+ Matrix cov = pre_int_data.preintMeasCov();
+ Matrix R = RtR(cov.inverse());
+ Vector whitened = R * errorActual;
+ EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-6));
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Make sure linearization is correct
+ // Create expected value by numerical differentiation
+ JacobianFactor expected = linearizeNumerically(factor, values, 1e-8);
+
+ // Create actual value by linearize
+ GaussianFactor::shared_ptr linearized = factor.linearize(values);
+ JacobianFactor* actual = dynamic_cast(linearized.get());
+
+ // Check cast result and then equality
+ EXPECT(assert_equal(expected, *actual, 1e-4));
}
/* ************************************************************************* */
@@ -284,8 +333,8 @@ TEST( ImuFactor, ErrorAndJacobianWithBiases ) {
double deltaT = 1.0;
ImuFactor::PreintegratedMeasurements pre_int_data(
- imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)),
- Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero());
+ imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance, kIntegrationErrorCovariance);
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
// Create factor
@@ -354,8 +403,8 @@ TEST( ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis ) {
double deltaT = 1.0;
ImuFactor::PreintegratedMeasurements pre_int_data(
- imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)),
- Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero());
+ imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance, kIntegrationErrorCovariance);
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
// Create factor
@@ -874,8 +923,8 @@ TEST( ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement ) {
Point3(1, 0, 0));
ImuFactor::PreintegratedMeasurements pre_int_data(
- imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)),
- Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero());
+ imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance, kIntegrationErrorCovariance);
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
@@ -933,8 +982,8 @@ TEST(ImuFactor, PredictPositionAndVelocity) {
I6x6 = Matrix::Identity(6, 6);
ImuFactor::PreintegratedMeasurements pre_int_data(
- imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)),
- Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), true);
+ imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true);
for (int i = 0; i < 1000; ++i)
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
@@ -974,8 +1023,8 @@ TEST(ImuFactor, PredictRotation) {
I6x6 = Matrix::Identity(6, 6);
ImuFactor::PreintegratedMeasurements pre_int_data(
- imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)),
- Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), true);
+ imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance,
+ kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true);
for (int i = 0; i < 1000; ++i)
pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
diff --git a/gtsam/nonlinear/Expression-inl.h b/gtsam/nonlinear/Expression-inl.h
index a86e7312a..6c6c155c7 100644
--- a/gtsam/nonlinear/Expression-inl.h
+++ b/gtsam/nonlinear/Expression-inl.h
@@ -19,773 +19,244 @@
#pragma once
-#include
-#include
-#include
+#include
-#include
#include
-#include
-#include
-
-// template meta-programming headers
-#include
-namespace MPL = boost::mpl::placeholders;
-
-#include // operator typeid
-#include