gtsam/gtsam/geometry/SOn.h

332 lines
10 KiB
C
Raw Normal View History

2019-04-17 11:46:08 +08:00
/* ----------------------------------------------------------------------------
* GTSAM Copyright 2010-2019, 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 SOn.h
* @brief n*n matrix representation of SO(n), template on N, which can be
* Eigen::Dynamic
2019-04-17 11:46:08 +08:00
* @author Frank Dellaert
* @date March 2019
*/
#pragma once
#include <gtsam/base/Lie.h>
2019-04-17 11:46:08 +08:00
#include <gtsam/base/Manifold.h>
#include <Eigen/Core>
#include <boost/random.hpp>
#include <string>
#include <type_traits>
2019-05-07 07:13:26 +08:00
#include <vector>
2019-04-17 11:46:08 +08:00
namespace gtsam {
2019-04-17 11:46:08 +08:00
namespace internal {
/// Calculate dimensionality of SO<N> manifold, or return Dynamic if so
constexpr int DimensionSO(int N) {
return (N < 0) ? Eigen::Dynamic : N * (N - 1) / 2;
}
2019-04-17 11:46:08 +08:00
// Calculate N^2 at compile time, or return Dynamic if so
constexpr int NSquaredSO(int N) { return (N < 0) ? Eigen::Dynamic : N * N; }
2019-04-17 11:46:08 +08:00
} // namespace internal
/**
* Manifold of special orthogonal rotation matrices SO<N>.
* Template paramater N can be a fizxed integer or can be Eigen::Dynamic
2019-04-17 11:46:08 +08:00
*/
template <int N>
class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
2019-04-17 11:46:08 +08:00
public:
enum { dimension = internal::DimensionSO(N) };
using MatrixNN = Eigen::Matrix<double, N, N>;
using VectorN2 = Eigen::Matrix<double, internal::NSquaredSO(N), 1>;
using MatrixDD = Eigen::Matrix<double, dimension, dimension>;
protected:
MatrixNN matrix_; ///< Rotation matrix
// enable_if_t aliases, used to specialize constructors/methods, see
// https://www.fluentcpp.com/2018/05/18/make-sfinae-pretty-2-hidden-beauty-sfinae/
template <int N_>
using IsDynamic = typename std::enable_if<N_ == Eigen::Dynamic, void>::type;
template <int N_>
using IsFixed = typename std::enable_if<N_ >= 2, void>::type;
template <int N_>
using IsSO3 = typename std::enable_if<N_ == 3, void>::type;
2019-04-17 11:46:08 +08:00
public:
/// @name Constructors
/// @{
/// Construct SO<N> identity for N >= 2
template <int N_ = N, typename = IsFixed<N_>>
SO() : matrix_(MatrixNN::Identity()) {}
/// Construct SO<N> identity for N == Eigen::Dynamic
template <int N_ = N, typename = IsDynamic<N_>>
explicit SO(size_t n = 0) {
2019-05-15 08:43:44 +08:00
// We allow for n=0 as the default constructor, needed for serialization,
// wrappers etc.
matrix_ = Eigen::MatrixXd::Identity(n, n);
2019-04-17 11:46:08 +08:00
}
2019-05-15 08:43:44 +08:00
/// Constructor from Eigen Matrix, dynamic version
template <typename Derived>
explicit SO(const Eigen::MatrixBase<Derived>& R) : matrix_(R.eval()) {}
2019-05-15 08:43:44 +08:00
/// Named constructor from Eigen Matrix
template <typename Derived>
static SO FromMatrix(const Eigen::MatrixBase<Derived>& R) {
return SO(R);
}
2019-05-07 06:45:16 +08:00
/// Construct dynamic SO(n) from Fixed SO<M>
template <int M, int N_ = N, typename = IsDynamic<N_>>
explicit SO(const SO<M>& R) : matrix_(R.matrix()) {}
/// Constructor from AngleAxisd
template <int N_ = N, typename = IsSO3<N_>>
SO(const Eigen::AngleAxisd& angleAxis) : matrix_(angleAxis) {}
2019-04-17 11:46:08 +08:00
2019-05-07 07:13:26 +08:00
/// Constructor from axis and angle. Only defined for SO3
static SO AxisAngle(const Vector3& axis, double theta);
/// Named constructor that finds SO(n) matrix closest to M in Frobenius norm,
/// currently only defined for SO3.
static SO ClosestTo(const MatrixNN& M);
/// Named constructor that finds chordal mean = argmin_R \sum sqr(|R-R_i|_F),
/// currently only defined for SO3.
static SO ChordalMean(const std::vector<SO>& rotations);
2019-04-17 11:46:08 +08:00
/// Random SO(n) element (no big claims about uniformity)
template <int N_ = N, typename = IsDynamic<N_>>
static SO Random(boost::mt19937& rng, size_t n = 0) {
if (n == 0) throw std::runtime_error("SO: Dimensionality not known.");
2019-04-17 11:46:08 +08:00
// This needs to be re-thought!
static boost::uniform_real<double> randomAngle(-M_PI, M_PI);
const size_t d = SO::Dimension(n);
2019-04-17 11:46:08 +08:00
Vector xi(d);
for (size_t j = 0; j < d; j++) {
xi(j) = randomAngle(rng);
}
return SO::Retract(xi);
}
/// Random SO(N) element (no big claims about uniformity)
template <int N_ = N, typename = IsFixed<N_>>
static SO Random(boost::mt19937& rng) {
// By default, use dynamic implementation above. Specialized for SO(3).
return SO(SO<Eigen::Dynamic>::Random(rng, N).matrix());
}
/// @}
/// @name Standard methods
/// @{
/// Return matrix
const MatrixNN& matrix() const { return matrix_; }
size_t rows() const { return matrix_.rows(); }
size_t cols() const { return matrix_.cols(); }
/// @}
/// @name Testable
/// @{
void print(const std::string& s) const {
std::cout << s << matrix_ << std::endl;
}
bool equals(const SO& other, double tol) const {
return equal_with_abs_tol(matrix_, other.matrix_, tol);
}
/// @}
/// @name Group
/// @{
/// Multiplication
2019-05-09 06:41:53 +08:00
SO operator*(const SO& other) const {
assert(dim() == other.dim());
return SO(matrix_ * other.matrix_);
}
/// SO<N> identity for N >= 2
template <int N_ = N, typename = IsFixed<N_>>
static SO identity() {
return SO();
}
/// SO<N> identity for N == Eigen::Dynamic
template <int N_ = N, typename = IsDynamic<N_>>
static SO identity(size_t n = 0) {
return SO(n);
2019-04-17 11:46:08 +08:00
}
/// inverse of a rotation = transpose
SO inverse() const { return SO(matrix_.transpose()); }
2019-04-17 11:46:08 +08:00
/// @}
/// @name Manifold
/// @{
using TangentVector = Eigen::Matrix<double, dimension, 1>;
using ChartJacobian = OptionalJacobian<dimension, dimension>;
/// Return compile-time dimensionality: fixed size N or Eigen::Dynamic
static int Dim() { return dimension; }
// Calculate manifold dimensionality for SO(n).
// Available as dimension or Dim() for fixed N.
static size_t Dimension(size_t n) { return n * (n - 1) / 2; }
// Calculate ambient dimension n from manifold dimensionality d.
static size_t AmbientDim(size_t d) { return (1 + std::sqrt(1 + 8 * d)) / 2; }
// Calculate run-time dimensionality of manifold.
// Available as dimension or Dim() for fixed N.
size_t dim() const { return Dimension(matrix_.rows()); }
2019-04-17 11:46:08 +08:00
/**
* Hat operator creates Lie algebra element corresponding to d-vector, where d
* is the dimensionality of the manifold. This function is implemented
* recursively, and the d-vector is assumed to laid out such that the last
2019-04-30 20:47:34 +08:00
* element corresponds to so(2), the last 3 to so(3), the last 6 to so(4)
2019-04-17 11:46:08 +08:00
* etc... For example, the vector-space isomorphic to so(5) is laid out as:
* a b c d | u v w | x y | z
* where the latter elements correspond to "telescoping" sub-algebras:
* 0 -z y -w d
* z 0 -x v -c
* -y x 0 -u b
* w -v u 0 -a
* -d c -b a 0
* This scheme behaves exactly as expected for SO(2) and SO(3).
*/
2019-05-07 06:45:16 +08:00
static MatrixNN Hat(const TangentVector& xi);
2019-04-17 11:46:08 +08:00
/**
* Inverse of Hat. See note about xi element order in Hat.
*/
2019-05-07 06:45:16 +08:00
static TangentVector Vee(const MatrixNN& X);
2019-04-17 11:46:08 +08:00
// Chart at origin
struct ChartAtOrigin {
/**
* Retract uses Cayley map. See note about xi element order in Hat.
* Deafault implementation has no Jacobian implemented
*/
static SO Retract(const TangentVector& xi, ChartJacobian H = boost::none);
/**
* Inverse of Retract. See note about xi element order in Hat.
*/
static TangentVector Local(const SO& R, ChartJacobian H = boost::none);
};
// Return dynamic identity DxD Jacobian for given SO(n)
template <int N_ = N, typename = IsDynamic<N_>>
static MatrixDD IdentityJacobian(size_t n) {
const size_t d = Dimension(n);
return MatrixDD::Identity(d, d);
}
/// @}
/// @name Lie Group
/// @{
2019-05-07 04:37:34 +08:00
/// Adjoint map
MatrixDD AdjointMap() const;
2019-04-17 11:46:08 +08:00
/**
* Exponential map at identity - create a rotation from canonical coordinates
2019-04-17 11:46:08 +08:00
*/
2019-05-07 04:37:34 +08:00
static SO Expmap(const TangentVector& omega, ChartJacobian H = boost::none);
/// Derivative of Expmap, currently only defined for SO3
static MatrixDD ExpmapDerivative(const TangentVector& omega);
2019-04-17 11:46:08 +08:00
/**
* Log map at identity - returns the canonical coordinates of this rotation
2019-04-17 11:46:08 +08:00
*/
2019-05-07 04:37:34 +08:00
static TangentVector Logmap(const SO& R, ChartJacobian H = boost::none);
2019-04-17 11:46:08 +08:00
2019-05-07 04:37:34 +08:00
/// Derivative of Logmap, currently only defined for SO3
static MatrixDD LogmapDerivative(const TangentVector& omega);
// inverse with optional derivative
using LieGroup<SO<N>, internal::DimensionSO(N)>::inverse;
2019-04-17 11:46:08 +08:00
/// @}
/// @name Other methods
/// @{
2019-04-30 20:47:34 +08:00
/**
* Return vectorized rotation matrix in column order.
* Will use dynamic matrices as intermediate results, but returns a fixed size
* X and fixed-size Jacobian if dimension is known at compile time.
* */
VectorN2 vec(OptionalJacobian<internal::NSquaredSO(N), dimension> H =
boost::none) const;
2019-04-17 11:46:08 +08:00
/// @}
2019-05-07 07:27:06 +08:00
template <class Archive>
2019-05-07 22:48:55 +08:00
friend void serialize(Archive&, SO&, const unsigned int);
2019-05-07 07:27:06 +08:00
friend class boost::serialization::access;
2019-05-07 22:48:55 +08:00
friend class Rot3; // for serialize
2019-04-17 11:46:08 +08:00
};
using SOn = SO<Eigen::Dynamic>;
/*
2019-05-07 06:45:16 +08:00
* Specialize dynamic Hat and Vee, because recursion depends on dynamic nature.
* The definition is in SOn.cpp. Fixed-size SO3 and SO4 have their own version,
* and implementation for other fixed N is in SOn-inl.h.
*/
template <>
Matrix SOn::Hat(const Vector& xi);
template <>
Vector SOn::Vee(const Matrix& X);
/*
* Specialize dynamic compose and between, because the derivative is unknowable
* by the LieGroup implementations, who return a fixed-size matrix for H2.
*/
2019-04-17 11:46:08 +08:00
using DynamicJacobian = OptionalJacobian<Eigen::Dynamic, Eigen::Dynamic>;
template <>
SOn LieGroup<SOn, Eigen::Dynamic>::compose(const SOn& g, DynamicJacobian H1,
DynamicJacobian H2) const;
template <>
SOn LieGroup<SOn, Eigen::Dynamic>::between(const SOn& g, DynamicJacobian H1,
DynamicJacobian H2) const;
2019-04-17 11:46:08 +08:00
/*
* Define the traits. internal::LieGroup provides both Lie group and Testable
*/
2019-04-17 11:46:08 +08:00
template <int N>
struct traits<SO<N>> : public internal::LieGroup<SO<N>> {};
2019-04-17 11:46:08 +08:00
template <int N>
struct traits<const SO<N>> : public internal::LieGroup<SO<N>> {};
2019-04-17 11:46:08 +08:00
} // namespace gtsam
2019-05-07 04:37:34 +08:00
#include "SOn-inl.h"