Linting and getAnchor wrap
parent
02cd45d4b8
commit
4dd9ee5f1f
|
@ -2925,6 +2925,7 @@ class ShonanAveragingParameters2 {
|
||||||
void setOptimalityThreshold(double value);
|
void setOptimalityThreshold(double value);
|
||||||
double getOptimalityThreshold() const;
|
double getOptimalityThreshold() const;
|
||||||
void setAnchor(size_t index, const gtsam::Rot2& value);
|
void setAnchor(size_t index, const gtsam::Rot2& value);
|
||||||
|
pair<size_t, Rot2> getAnchor();
|
||||||
void setAnchorWeight(double value);
|
void setAnchorWeight(double value);
|
||||||
double getAnchorWeight() const;
|
double getAnchorWeight() const;
|
||||||
void setKarcherWeight(double value);
|
void setKarcherWeight(double value);
|
||||||
|
@ -2940,6 +2941,7 @@ class ShonanAveragingParameters3 {
|
||||||
void setOptimalityThreshold(double value);
|
void setOptimalityThreshold(double value);
|
||||||
double getOptimalityThreshold() const;
|
double getOptimalityThreshold() const;
|
||||||
void setAnchor(size_t index, const gtsam::Rot3& value);
|
void setAnchor(size_t index, const gtsam::Rot3& value);
|
||||||
|
pair<size_t, Rot3> getAnchor();
|
||||||
void setAnchorWeight(double value);
|
void setAnchorWeight(double value);
|
||||||
double getAnchorWeight() const;
|
double getAnchorWeight() const;
|
||||||
void setKarcherWeight(double value);
|
void setKarcherWeight(double value);
|
||||||
|
|
|
@ -16,26 +16,25 @@
|
||||||
* @brief Shonan Averaging algorithm
|
* @brief Shonan Averaging algorithm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/sfm/ShonanAveraging.h>
|
#include <SymEigsSolver.h>
|
||||||
|
|
||||||
#include <gtsam/linear/PCGSolver.h>
|
#include <gtsam/linear/PCGSolver.h>
|
||||||
#include <gtsam/linear/SubgraphPreconditioner.h>
|
#include <gtsam/linear/SubgraphPreconditioner.h>
|
||||||
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
|
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
|
||||||
#include <gtsam/nonlinear/NonlinearEquality.h>
|
#include <gtsam/nonlinear/NonlinearEquality.h>
|
||||||
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
||||||
|
#include <gtsam/sfm/ShonanAveraging.h>
|
||||||
|
#include <gtsam/sfm/ShonanFactor.h>
|
||||||
#include <gtsam/sfm/ShonanGaugeFactor.h>
|
#include <gtsam/sfm/ShonanGaugeFactor.h>
|
||||||
#include <gtsam/slam/FrobeniusFactor.h>
|
#include <gtsam/slam/FrobeniusFactor.h>
|
||||||
#include <gtsam/slam/KarcherMeanFactor-inl.h>
|
#include <gtsam/slam/KarcherMeanFactor-inl.h>
|
||||||
#include <gtsam/sfm/ShonanFactor.h>
|
|
||||||
|
|
||||||
#include <Eigen/Eigenvalues>
|
#include <Eigen/Eigenvalues>
|
||||||
#include <SymEigsSolver.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
@ -50,8 +49,11 @@ template <size_t d>
|
||||||
ShonanAveragingParameters<d>::ShonanAveragingParameters(
|
ShonanAveragingParameters<d>::ShonanAveragingParameters(
|
||||||
const LevenbergMarquardtParams &_lm, const std::string &method,
|
const LevenbergMarquardtParams &_lm, const std::string &method,
|
||||||
double optimalityThreshold, double alpha, double beta, double gamma)
|
double optimalityThreshold, double alpha, double beta, double gamma)
|
||||||
: lm(_lm), optimalityThreshold(optimalityThreshold), alpha(alpha),
|
: lm(_lm),
|
||||||
beta(beta), gamma(gamma) {
|
optimalityThreshold(optimalityThreshold),
|
||||||
|
alpha(alpha),
|
||||||
|
beta(beta),
|
||||||
|
gamma(gamma) {
|
||||||
// By default, we will do conjugate gradient
|
// By default, we will do conjugate gradient
|
||||||
lm.linearSolverType = LevenbergMarquardtParams::Iterative;
|
lm.linearSolverType = LevenbergMarquardtParams::Iterative;
|
||||||
|
|
||||||
|
@ -92,29 +94,40 @@ template struct ShonanAveragingParameters<3>;
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
// Calculate number of unknown rotations referenced by measurements
|
// Calculate number of unknown rotations referenced by measurements
|
||||||
|
// Throws exception of keys are not numbered as expected (may change in future).
|
||||||
template <size_t d>
|
template <size_t d>
|
||||||
static size_t
|
static size_t NrUnknowns(
|
||||||
NrUnknowns(const typename ShonanAveraging<d>::Measurements &measurements) {
|
const typename ShonanAveraging<d>::Measurements &measurements) {
|
||||||
|
Key maxKey = 0;
|
||||||
std::set<Key> keys;
|
std::set<Key> keys;
|
||||||
for (const auto &measurement : measurements) {
|
for (const auto &measurement : measurements) {
|
||||||
keys.insert(measurement.key1());
|
for (const Key &key : measurement.keys()) {
|
||||||
keys.insert(measurement.key2());
|
maxKey = std::max(key, maxKey);
|
||||||
|
keys.insert(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return keys.size();
|
size_t N = keys.size();
|
||||||
|
if (maxKey != N - 1) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"As of now, ShonanAveraging expects keys numbered 0..N-1");
|
||||||
|
}
|
||||||
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d>
|
template <size_t d>
|
||||||
ShonanAveraging<d>::ShonanAveraging(const Measurements &measurements,
|
ShonanAveraging<d>::ShonanAveraging(const Measurements &measurements,
|
||||||
const Parameters ¶meters)
|
const Parameters ¶meters)
|
||||||
: parameters_(parameters), measurements_(measurements),
|
: parameters_(parameters),
|
||||||
|
measurements_(measurements),
|
||||||
nrUnknowns_(NrUnknowns<d>(measurements)) {
|
nrUnknowns_(NrUnknowns<d>(measurements)) {
|
||||||
for (const auto &measurement : measurements_) {
|
for (const auto &measurement : measurements_) {
|
||||||
const auto &model = measurement.noiseModel();
|
const auto &model = measurement.noiseModel();
|
||||||
if (model && model->dim() != SO<d>::dimension) {
|
if (model && model->dim() != SO<d>::dimension) {
|
||||||
measurement.print("Factor with incorrect noise model:\n");
|
measurement.print("Factor with incorrect noise model:\n");
|
||||||
throw std::invalid_argument("ShonanAveraging: measurements passed to "
|
throw std::invalid_argument(
|
||||||
"constructor have incorrect dimension.");
|
"ShonanAveraging: measurements passed to "
|
||||||
|
"constructor have incorrect dimension.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Q_ = buildQ();
|
Q_ = buildQ();
|
||||||
|
@ -196,7 +209,7 @@ Matrix ShonanAveraging<d>::StiefelElementMatrix(const Values &values) {
|
||||||
Matrix S(p, N * d);
|
Matrix S(p, N * d);
|
||||||
for (const auto it : values.filter<SOn>()) {
|
for (const auto it : values.filter<SOn>()) {
|
||||||
S.middleCols<d>(it.key * d) =
|
S.middleCols<d>(it.key * d) =
|
||||||
it.value.matrix().leftCols<d>(); // project Qj to Stiefel
|
it.value.matrix().leftCols<d>(); // project Qj to Stiefel
|
||||||
}
|
}
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +240,8 @@ Values ShonanAveraging<3>::projectFrom(size_t p, const Values &values) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d> static Matrix RoundSolutionS(const Matrix &S) {
|
template <size_t d>
|
||||||
|
static Matrix RoundSolutionS(const Matrix &S) {
|
||||||
const size_t N = S.cols() / d;
|
const size_t N = S.cols() / d;
|
||||||
// First, compute a thin SVD of S
|
// First, compute a thin SVD of S
|
||||||
Eigen::JacobiSVD<Matrix> svd(S, Eigen::ComputeThinV);
|
Eigen::JacobiSVD<Matrix> svd(S, Eigen::ComputeThinV);
|
||||||
|
@ -246,8 +260,7 @@ template <size_t d> static Matrix RoundSolutionS(const Matrix &S) {
|
||||||
for (size_t i = 0; i < N; ++i) {
|
for (size_t i = 0; i < N; ++i) {
|
||||||
// Compute the determinant of the ith dxd block of R
|
// Compute the determinant of the ith dxd block of R
|
||||||
double determinant = R.middleCols<d>(d * i).determinant();
|
double determinant = R.middleCols<d>(d * i).determinant();
|
||||||
if (determinant > 0)
|
if (determinant > 0) ++numPositiveBlocks;
|
||||||
++numPositiveBlocks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numPositiveBlocks < N / 2) {
|
if (numPositiveBlocks < N / 2) {
|
||||||
|
@ -263,7 +276,8 @@ template <size_t d> static Matrix RoundSolutionS(const Matrix &S) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <> Values ShonanAveraging<2>::roundSolutionS(const Matrix &S) const {
|
template <>
|
||||||
|
Values ShonanAveraging<2>::roundSolutionS(const Matrix &S) const {
|
||||||
// Round to a 2*2N matrix
|
// Round to a 2*2N matrix
|
||||||
Matrix R = RoundSolutionS<2>(S);
|
Matrix R = RoundSolutionS<2>(S);
|
||||||
|
|
||||||
|
@ -276,7 +290,8 @@ template <> Values ShonanAveraging<2>::roundSolutionS(const Matrix &S) const {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> Values ShonanAveraging<3>::roundSolutionS(const Matrix &S) const {
|
template <>
|
||||||
|
Values ShonanAveraging<3>::roundSolutionS(const Matrix &S) const {
|
||||||
// Round to a 3*3N matrix
|
// Round to a 3*3N matrix
|
||||||
Matrix R = RoundSolutionS<3>(S);
|
Matrix R = RoundSolutionS<3>(S);
|
||||||
|
|
||||||
|
@ -332,7 +347,8 @@ static double Kappa(const BinaryMeasurement<T> &measurement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d> Sparse ShonanAveraging<d>::buildD() const {
|
template <size_t d>
|
||||||
|
Sparse ShonanAveraging<d>::buildD() const {
|
||||||
// Each measurement contributes 2*d elements along the diagonal of the
|
// Each measurement contributes 2*d elements along the diagonal of the
|
||||||
// degree matrix.
|
// degree matrix.
|
||||||
static constexpr size_t stride = 2 * d;
|
static constexpr size_t stride = 2 * d;
|
||||||
|
@ -366,7 +382,8 @@ template <size_t d> Sparse ShonanAveraging<d>::buildD() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d> Sparse ShonanAveraging<d>::buildQ() const {
|
template <size_t d>
|
||||||
|
Sparse ShonanAveraging<d>::buildQ() const {
|
||||||
// Each measurement contributes 2*d^2 elements on a pair of symmetric
|
// Each measurement contributes 2*d^2 elements on a pair of symmetric
|
||||||
// off-diagonal blocks
|
// off-diagonal blocks
|
||||||
static constexpr size_t stride = 2 * d * d;
|
static constexpr size_t stride = 2 * d * d;
|
||||||
|
@ -513,12 +530,12 @@ struct MatrixProdFunctor {
|
||||||
// - We've been using 10^-4 for the nonnegativity tolerance
|
// - We've been using 10^-4 for the nonnegativity tolerance
|
||||||
// - for numLanczosVectors, 20 is a good default value
|
// - for numLanczosVectors, 20 is a good default value
|
||||||
|
|
||||||
static bool
|
static bool SparseMinimumEigenValue(
|
||||||
SparseMinimumEigenValue(const Sparse &A, const Matrix &S, double *minEigenValue,
|
const Sparse &A, const Matrix &S, double *minEigenValue,
|
||||||
Vector *minEigenVector = 0, size_t *numIterations = 0,
|
Vector *minEigenVector = 0, size_t *numIterations = 0,
|
||||||
size_t maxIterations = 1000,
|
size_t maxIterations = 1000,
|
||||||
double minEigenvalueNonnegativityTolerance = 10e-4,
|
double minEigenvalueNonnegativityTolerance = 10e-4,
|
||||||
Eigen::Index numLanczosVectors = 20) {
|
Eigen::Index numLanczosVectors = 20) {
|
||||||
// a. Estimate the largest-magnitude eigenvalue of this matrix using Lanczos
|
// a. Estimate the largest-magnitude eigenvalue of this matrix using Lanczos
|
||||||
MatrixProdFunctor lmOperator(A);
|
MatrixProdFunctor lmOperator(A);
|
||||||
Spectra::SymEigsSolver<double, Spectra::SELECT_EIGENVALUE::LARGEST_MAGN,
|
Spectra::SymEigsSolver<double, Spectra::SELECT_EIGENVALUE::LARGEST_MAGN,
|
||||||
|
@ -530,8 +547,7 @@ SparseMinimumEigenValue(const Sparse &A, const Matrix &S, double *minEigenValue,
|
||||||
maxIterations, 1e-4, Spectra::SELECT_EIGENVALUE::LARGEST_MAGN);
|
maxIterations, 1e-4, Spectra::SELECT_EIGENVALUE::LARGEST_MAGN);
|
||||||
|
|
||||||
// Check convergence and bail out if necessary
|
// Check convergence and bail out if necessary
|
||||||
if (lmConverged != 1)
|
if (lmConverged != 1) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
const double lmEigenValue = lmEigenValueSolver.eigenvalues()(0);
|
const double lmEigenValue = lmEigenValueSolver.eigenvalues()(0);
|
||||||
|
|
||||||
|
@ -541,7 +557,7 @@ SparseMinimumEigenValue(const Sparse &A, const Matrix &S, double *minEigenValue,
|
||||||
*minEigenValue = lmEigenValue;
|
*minEigenValue = lmEigenValue;
|
||||||
if (minEigenVector) {
|
if (minEigenVector) {
|
||||||
*minEigenVector = lmEigenValueSolver.eigenvectors(1).col(0);
|
*minEigenVector = lmEigenValueSolver.eigenvectors(1).col(0);
|
||||||
minEigenVector->normalize(); // Ensure that this is a unit vector
|
minEigenVector->normalize(); // Ensure that this is a unit vector
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +594,7 @@ SparseMinimumEigenValue(const Sparse &A, const Matrix &S, double *minEigenValue,
|
||||||
Vector perturbation(v0.size());
|
Vector perturbation(v0.size());
|
||||||
perturbation.setRandom();
|
perturbation.setRandom();
|
||||||
perturbation.normalize();
|
perturbation.normalize();
|
||||||
Vector xinit = v0 + (.03 * v0.norm()) * perturbation; // Perturb v0 by ~3%
|
Vector xinit = v0 + (.03 * v0.norm()) * perturbation; // Perturb v0 by ~3%
|
||||||
|
|
||||||
// Use this to initialize the eigensolver
|
// Use this to initialize the eigensolver
|
||||||
minEigenValueSolver.init(xinit.data());
|
minEigenValueSolver.init(xinit.data());
|
||||||
|
@ -590,21 +606,20 @@ SparseMinimumEigenValue(const Sparse &A, const Matrix &S, double *minEigenValue,
|
||||||
maxIterations, minEigenvalueNonnegativityTolerance / lmEigenValue,
|
maxIterations, minEigenvalueNonnegativityTolerance / lmEigenValue,
|
||||||
Spectra::SELECT_EIGENVALUE::LARGEST_MAGN);
|
Spectra::SELECT_EIGENVALUE::LARGEST_MAGN);
|
||||||
|
|
||||||
if (minConverged != 1)
|
if (minConverged != 1) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
*minEigenValue = minEigenValueSolver.eigenvalues()(0) + 2 * lmEigenValue;
|
*minEigenValue = minEigenValueSolver.eigenvalues()(0) + 2 * lmEigenValue;
|
||||||
if (minEigenVector) {
|
if (minEigenVector) {
|
||||||
*minEigenVector = minEigenValueSolver.eigenvectors(1).col(0);
|
*minEigenVector = minEigenValueSolver.eigenvectors(1).col(0);
|
||||||
minEigenVector->normalize(); // Ensure that this is a unit vector
|
minEigenVector->normalize(); // Ensure that this is a unit vector
|
||||||
}
|
}
|
||||||
if (numIterations)
|
if (numIterations) *numIterations = minEigenValueSolver.num_iterations();
|
||||||
*numIterations = minEigenValueSolver.num_iterations();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d> Sparse ShonanAveraging<d>::computeA(const Matrix &S) const {
|
template <size_t d>
|
||||||
|
Sparse ShonanAveraging<d>::computeA(const Matrix &S) const {
|
||||||
auto Lambda = computeLambda(S);
|
auto Lambda = computeLambda(S);
|
||||||
return Lambda - Q_;
|
return Lambda - Q_;
|
||||||
}
|
}
|
||||||
|
@ -628,8 +643,8 @@ double ShonanAveraging<d>::computeMinEigenValue(const Values &values,
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d>
|
template <size_t d>
|
||||||
std::pair<double, Vector>
|
std::pair<double, Vector> ShonanAveraging<d>::computeMinEigenVector(
|
||||||
ShonanAveraging<d>::computeMinEigenVector(const Values &values) const {
|
const Values &values) const {
|
||||||
Vector minEigenVector;
|
Vector minEigenVector;
|
||||||
double minEigenValue = computeMinEigenValue(values, &minEigenVector);
|
double minEigenValue = computeMinEigenValue(values, &minEigenVector);
|
||||||
return std::make_pair(minEigenValue, minEigenVector);
|
return std::make_pair(minEigenValue, minEigenVector);
|
||||||
|
@ -750,7 +765,8 @@ Values ShonanAveraging<d>::initializeRandomly(std::mt19937 &rng) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <size_t d> Values ShonanAveraging<d>::initializeRandomly() const {
|
template <size_t d>
|
||||||
|
Values ShonanAveraging<d>::initializeRandomly() const {
|
||||||
return initializeRandomly(kRandomNumberGenerator);
|
return initializeRandomly(kRandomNumberGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +775,7 @@ template <size_t d>
|
||||||
Values ShonanAveraging<d>::initializeRandomlyAt(size_t p,
|
Values ShonanAveraging<d>::initializeRandomlyAt(size_t p,
|
||||||
std::mt19937 &rng) const {
|
std::mt19937 &rng) const {
|
||||||
const Values randomRotations = initializeRandomly(rng);
|
const Values randomRotations = initializeRandomly(rng);
|
||||||
return LiftTo<Rot3>(p, randomRotations); // lift to p!
|
return LiftTo<Rot3>(p, randomRotations); // lift to p!
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
@ -823,8 +839,8 @@ ShonanAveraging3::ShonanAveraging3(string g2oFile, const Parameters ¶meters)
|
||||||
|
|
||||||
// Extract Rot3 measurement from Pose3 betweenfactors
|
// Extract Rot3 measurement from Pose3 betweenfactors
|
||||||
// Modeled after similar function in dataset.cpp
|
// Modeled after similar function in dataset.cpp
|
||||||
static BinaryMeasurement<Rot3>
|
static BinaryMeasurement<Rot3> convert(
|
||||||
convert(const BetweenFactor<Pose3>::shared_ptr &f) {
|
const BetweenFactor<Pose3>::shared_ptr &f) {
|
||||||
auto gaussian =
|
auto gaussian =
|
||||||
boost::dynamic_pointer_cast<noiseModel::Gaussian>(f->noiseModel());
|
boost::dynamic_pointer_cast<noiseModel::Gaussian>(f->noiseModel());
|
||||||
if (!gaussian)
|
if (!gaussian)
|
||||||
|
@ -837,12 +853,11 @@ convert(const BetweenFactor<Pose3>::shared_ptr &f) {
|
||||||
noiseModel::Gaussian::Covariance(M.block<3, 3>(3, 3), true));
|
noiseModel::Gaussian::Covariance(M.block<3, 3>(3, 3), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ShonanAveraging3::Measurements
|
static ShonanAveraging3::Measurements extractRot3Measurements(
|
||||||
extractRot3Measurements(const BetweenFactorPose3s &factors) {
|
const BetweenFactorPose3s &factors) {
|
||||||
ShonanAveraging3::Measurements result;
|
ShonanAveraging3::Measurements result;
|
||||||
result.reserve(factors.size());
|
result.reserve(factors.size());
|
||||||
for (auto f : factors)
|
for (auto f : factors) result.push_back(convert(f));
|
||||||
result.push_back(convert(f));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,4 +866,4 @@ ShonanAveraging3::ShonanAveraging3(const BetweenFactorPose3s &factors,
|
||||||
: ShonanAveraging<3>(extractRot3Measurements(factors), parameters) {}
|
: ShonanAveraging<3>(extractRot3Measurements(factors), parameters) {}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
|
@ -20,36 +20,38 @@
|
||||||
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
#include <gtsam/base/Vector.h>
|
#include <gtsam/base/Vector.h>
|
||||||
|
#include <gtsam/dllexport.h>
|
||||||
#include <gtsam/geometry/Rot2.h>
|
#include <gtsam/geometry/Rot2.h>
|
||||||
#include <gtsam/geometry/Rot3.h>
|
#include <gtsam/geometry/Rot3.h>
|
||||||
#include <gtsam/nonlinear/LevenbergMarquardtParams.h>
|
#include <gtsam/nonlinear/LevenbergMarquardtParams.h>
|
||||||
#include <gtsam/sfm/BinaryMeasurement.h>
|
#include <gtsam/sfm/BinaryMeasurement.h>
|
||||||
#include <gtsam/slam/dataset.h>
|
#include <gtsam/slam/dataset.h>
|
||||||
#include <gtsam/dllexport.h>
|
|
||||||
|
|
||||||
#include <Eigen/Sparse>
|
#include <Eigen/Sparse>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
class NonlinearFactorGraph;
|
class NonlinearFactorGraph;
|
||||||
class LevenbergMarquardtOptimizer;
|
class LevenbergMarquardtOptimizer;
|
||||||
|
|
||||||
/// Parameters governing optimization etc.
|
/// Parameters governing optimization etc.
|
||||||
template <size_t d> struct GTSAM_EXPORT ShonanAveragingParameters {
|
template <size_t d>
|
||||||
|
struct GTSAM_EXPORT ShonanAveragingParameters {
|
||||||
// Select Rot2 or Rot3 interface based template parameter d
|
// Select Rot2 or Rot3 interface based template parameter d
|
||||||
using Rot = typename std::conditional<d == 2, Rot2, Rot3>::type;
|
using Rot = typename std::conditional<d == 2, Rot2, Rot3>::type;
|
||||||
using Anchor = std::pair<size_t, Rot>;
|
using Anchor = std::pair<size_t, Rot>;
|
||||||
|
|
||||||
// Paremeters themselves:
|
// Paremeters themselves:
|
||||||
LevenbergMarquardtParams lm; // LM parameters
|
LevenbergMarquardtParams lm; // LM parameters
|
||||||
double optimalityThreshold; // threshold used in checkOptimality
|
double optimalityThreshold; // threshold used in checkOptimality
|
||||||
Anchor anchor; // pose to use as anchor if not Karcher
|
Anchor anchor; // pose to use as anchor if not Karcher
|
||||||
double alpha; // weight of anchor-based prior (default 0)
|
double alpha; // weight of anchor-based prior (default 0)
|
||||||
double beta; // weight of Karcher-based prior (default 1)
|
double beta; // weight of Karcher-based prior (default 1)
|
||||||
double gamma; // weight of gauge-fixing factors (default 0)
|
double gamma; // weight of gauge-fixing factors (default 0)
|
||||||
|
|
||||||
ShonanAveragingParameters(const LevenbergMarquardtParams &lm =
|
ShonanAveragingParameters(const LevenbergMarquardtParams &lm =
|
||||||
LevenbergMarquardtParams::CeresDefaults(),
|
LevenbergMarquardtParams::CeresDefaults(),
|
||||||
|
@ -64,6 +66,7 @@ template <size_t d> struct GTSAM_EXPORT ShonanAveragingParameters {
|
||||||
double getOptimalityThreshold() const { return optimalityThreshold; }
|
double getOptimalityThreshold() const { return optimalityThreshold; }
|
||||||
|
|
||||||
void setAnchor(size_t index, const Rot &value) { anchor = {index, value}; }
|
void setAnchor(size_t index, const Rot &value) { anchor = {index, value}; }
|
||||||
|
std::pair<size_t, Rot> getAnchor() { return anchor; }
|
||||||
|
|
||||||
void setAnchorWeight(double value) { alpha = value; }
|
void setAnchorWeight(double value) { alpha = value; }
|
||||||
double getAnchorWeight() { return alpha; }
|
double getAnchorWeight() { return alpha; }
|
||||||
|
@ -93,8 +96,9 @@ using ShonanAveragingParameters3 = ShonanAveragingParameters<3>;
|
||||||
* European Computer Vision Conference, 2020.
|
* European Computer Vision Conference, 2020.
|
||||||
* You can view our ECCV spotlight video at https://youtu.be/5ppaqMyHtE0
|
* You can view our ECCV spotlight video at https://youtu.be/5ppaqMyHtE0
|
||||||
*/
|
*/
|
||||||
template <size_t d> class GTSAM_EXPORT ShonanAveraging {
|
template <size_t d>
|
||||||
public:
|
class GTSAM_EXPORT ShonanAveraging {
|
||||||
|
public:
|
||||||
using Sparse = Eigen::SparseMatrix<double>;
|
using Sparse = Eigen::SparseMatrix<double>;
|
||||||
|
|
||||||
// Define the Parameters type and use its typedef of the rotation type:
|
// Define the Parameters type and use its typedef of the rotation type:
|
||||||
|
@ -105,13 +109,13 @@ public:
|
||||||
// TODO(frank): use BinaryMeasurement?
|
// TODO(frank): use BinaryMeasurement?
|
||||||
using Measurements = std::vector<BinaryMeasurement<Rot>>;
|
using Measurements = std::vector<BinaryMeasurement<Rot>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parameters parameters_;
|
Parameters parameters_;
|
||||||
Measurements measurements_;
|
Measurements measurements_;
|
||||||
size_t nrUnknowns_;
|
size_t nrUnknowns_;
|
||||||
Sparse D_; // Sparse (diagonal) degree matrix
|
Sparse D_; // Sparse (diagonal) degree matrix
|
||||||
Sparse Q_; // Sparse measurement matrix, == \tilde{R} in Eriksson18cvpr
|
Sparse Q_; // Sparse measurement matrix, == \tilde{R} in Eriksson18cvpr
|
||||||
Sparse L_; // connection Laplacian L = D - Q, needed for optimality check
|
Sparse L_; // connection Laplacian L = D - Q, needed for optimality check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build 3Nx3N sparse matrix consisting of rotation measurements, arranged as
|
* Build 3Nx3N sparse matrix consisting of rotation measurements, arranged as
|
||||||
|
@ -122,7 +126,7 @@ private:
|
||||||
/// Build 3Nx3N sparse degree matrix D
|
/// Build 3Nx3N sparse degree matrix D
|
||||||
Sparse buildD() const;
|
Sparse buildD() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
@ -156,12 +160,12 @@ public:
|
||||||
/// @name Matrix API (advanced use, debugging)
|
/// @name Matrix API (advanced use, debugging)
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
Sparse D() const { return D_; } ///< Sparse version of D
|
Sparse D() const { return D_; } ///< Sparse version of D
|
||||||
Matrix denseD() const { return Matrix(D_); } ///< Dense version of D
|
Matrix denseD() const { return Matrix(D_); } ///< Dense version of D
|
||||||
Sparse Q() const { return Q_; } ///< Sparse version of Q
|
Sparse Q() const { return Q_; } ///< Sparse version of Q
|
||||||
Matrix denseQ() const { return Matrix(Q_); } ///< Dense version of Q
|
Matrix denseQ() const { return Matrix(Q_); } ///< Dense version of Q
|
||||||
Sparse L() const { return L_; } ///< Sparse version of L
|
Sparse L() const { return L_; } ///< Sparse version of L
|
||||||
Matrix denseL() const { return Matrix(L_); } ///< Dense version of L
|
Matrix denseL() const { return Matrix(L_); } ///< Dense version of L
|
||||||
|
|
||||||
/// Version that takes pxdN Stiefel manifold elements
|
/// Version that takes pxdN Stiefel manifold elements
|
||||||
Sparse computeLambda(const Matrix &S) const;
|
Sparse computeLambda(const Matrix &S) const;
|
||||||
|
@ -220,11 +224,10 @@ public:
|
||||||
* @param minEigenVector corresponding to minEigenValue at level p-1
|
* @param minEigenVector corresponding to minEigenValue at level p-1
|
||||||
* @return values of type SO(p)
|
* @return values of type SO(p)
|
||||||
*/
|
*/
|
||||||
Values
|
Values initializeWithDescent(
|
||||||
initializeWithDescent(size_t p, const Values &values,
|
size_t p, const Values &values, const Vector &minEigenVector,
|
||||||
const Vector &minEigenVector, double minEigenValue,
|
double minEigenValue, double gradienTolerance = 1e-2,
|
||||||
double gradienTolerance = 1e-2,
|
double preconditionedGradNormTolerance = 1e-4) const;
|
||||||
double preconditionedGradNormTolerance = 1e-4) const;
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Advanced API
|
/// @name Advanced API
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -237,11 +240,11 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create initial Values of type SO(p)
|
* Create initial Values of type SO(p)
|
||||||
* @param p the dimensionality of the rotation manifold
|
* @param p the dimensionality of the rotation manifold
|
||||||
*/
|
*/
|
||||||
Values initializeRandomlyAt(size_t p, std::mt19937 &rng) const;
|
Values initializeRandomlyAt(size_t p, std::mt19937 &rng) const;
|
||||||
|
|
||||||
/// Version of initializeRandomlyAt with fixed random seed.
|
/// Version of initializeRandomlyAt with fixed random seed.
|
||||||
Values initializeRandomlyAt(size_t p) const;
|
Values initializeRandomlyAt(size_t p) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -300,7 +303,8 @@ public:
|
||||||
Values roundSolution(const Values &values) const;
|
Values roundSolution(const Values &values) const;
|
||||||
|
|
||||||
/// Lift Values of type T to SO(p)
|
/// Lift Values of type T to SO(p)
|
||||||
template <class T> static Values LiftTo(size_t p, const Values &values) {
|
template <class T>
|
||||||
|
static Values LiftTo(size_t p, const Values &values) {
|
||||||
Values result;
|
Values result;
|
||||||
for (const auto it : values.filter<T>()) {
|
for (const auto it : values.filter<T>()) {
|
||||||
result.insert(it.key, SOn::Lift(p, it.value.matrix()));
|
result.insert(it.key, SOn::Lift(p, it.value.matrix()));
|
||||||
|
@ -327,7 +331,7 @@ public:
|
||||||
*/
|
*/
|
||||||
Values initializeRandomly(std::mt19937 &rng) const;
|
Values initializeRandomly(std::mt19937 &rng) const;
|
||||||
|
|
||||||
/// Random initialization for wrapper, fixed random seed.
|
/// Random initialization for wrapper, fixed random seed.
|
||||||
Values initializeRandomly() const;
|
Values initializeRandomly() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,20 +350,22 @@ public:
|
||||||
// convenience interface with file access.
|
// convenience interface with file access.
|
||||||
|
|
||||||
class ShonanAveraging2 : public ShonanAveraging<2> {
|
class ShonanAveraging2 : public ShonanAveraging<2> {
|
||||||
public:
|
public:
|
||||||
ShonanAveraging2(const Measurements &measurements,
|
ShonanAveraging2(const Measurements &measurements,
|
||||||
const Parameters ¶meters = Parameters());
|
const Parameters ¶meters = Parameters());
|
||||||
ShonanAveraging2(string g2oFile, const Parameters ¶meters = Parameters());
|
explicit ShonanAveraging2(string g2oFile,
|
||||||
|
const Parameters ¶meters = Parameters());
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShonanAveraging3 : public ShonanAveraging<3> {
|
class ShonanAveraging3 : public ShonanAveraging<3> {
|
||||||
public:
|
public:
|
||||||
ShonanAveraging3(const Measurements &measurements,
|
ShonanAveraging3(const Measurements &measurements,
|
||||||
const Parameters ¶meters = Parameters());
|
const Parameters ¶meters = Parameters());
|
||||||
ShonanAveraging3(string g2oFile, const Parameters ¶meters = Parameters());
|
explicit ShonanAveraging3(string g2oFile,
|
||||||
|
const Parameters ¶meters = Parameters());
|
||||||
|
|
||||||
// TODO(frank): Deprecate after we land pybind wrapper
|
// TODO(frank): Deprecate after we land pybind wrapper
|
||||||
ShonanAveraging3(const BetweenFactorPose3s &factors,
|
ShonanAveraging3(const BetweenFactorPose3s &factors,
|
||||||
const Parameters ¶meters = Parameters());
|
const Parameters ¶meters = Parameters());
|
||||||
};
|
};
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
Loading…
Reference in New Issue