Hat/Vee etc, working Random for SOn
parent
cc2f0f242c
commit
be83a6bad7
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
#include <gtsam/base/Lie.h>
|
#include <gtsam/base/Lie.h>
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/geometry/Unit3.h>
|
||||||
|
|
||||||
|
#include <boost/random.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
@ -25,16 +28,10 @@
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
// Calculate dimensionality of SO<N> manifold, or return Dynamic if so
|
/// Calculate dimensionality of SO<N> manifold, or return Dynamic if so
|
||||||
constexpr int DimensionSO(int N) {
|
constexpr int DimensionSO(int N) {
|
||||||
return (N < 0) ? Eigen::Dynamic : N * (N - 1) / 2;
|
return (N < 0) ? Eigen::Dynamic : N * (N - 1) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return dynamic identity matrix for given SO(n) dimensionality d
|
|
||||||
Matrix IdentitySO(size_t n) {
|
|
||||||
const size_t d = n * (n - 1) / 2;
|
|
||||||
return Matrix::Identity(d, d);
|
|
||||||
}
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,21 +42,30 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
public:
|
public:
|
||||||
enum { dimension = internal::DimensionSO(N) };
|
enum { dimension = internal::DimensionSO(N) };
|
||||||
using MatrixNN = Eigen::Matrix<double, N, N>;
|
using MatrixNN = Eigen::Matrix<double, N, N>;
|
||||||
using VectorD = Eigen::Matrix<double, dimension, 1>;
|
|
||||||
using MatrixDD = Eigen::Matrix<double, dimension, dimension>;
|
using MatrixDD = Eigen::Matrix<double, dimension, dimension>;
|
||||||
|
|
||||||
|
protected:
|
||||||
MatrixNN matrix_; ///< Rotation matrix
|
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 = boost::enable_if_t<N_ == Eigen::Dynamic, void>;
|
||||||
|
template <int N_>
|
||||||
|
using IsFixed = boost::enable_if_t<N_ >= 2, void>;
|
||||||
|
template <int N_>
|
||||||
|
using IsSO3 = boost::enable_if_t<N_ == 3, void>;
|
||||||
|
|
||||||
|
public:
|
||||||
/// @name Constructors
|
/// @name Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Construct SO<N> identity for N >= 2
|
/// Construct SO<N> identity for N >= 2
|
||||||
template <int N_ = N, typename = boost::enable_if_t<N_ >= 2, void>>
|
template <int N_ = N, typename = IsFixed<N_>>
|
||||||
SO() : matrix_(MatrixNN::Identity()) {}
|
SO() : matrix_(MatrixNN::Identity()) {}
|
||||||
|
|
||||||
/// Construct SO<N> identity for N == Eigen::Dynamic
|
/// Construct SO<N> identity for N == Eigen::Dynamic
|
||||||
template <int N_ = N,
|
template <int N_ = N, typename = IsDynamic<N_>>
|
||||||
typename = boost::enable_if_t<N_ == Eigen::Dynamic, void>>
|
|
||||||
explicit SO(size_t n = 0) {
|
explicit SO(size_t n = 0) {
|
||||||
if (n == 0) throw std::runtime_error("SO: Dimensionality not known.");
|
if (n == 0) throw std::runtime_error("SO: Dimensionality not known.");
|
||||||
matrix_ = Eigen::MatrixXd::Identity(n, n);
|
matrix_ = Eigen::MatrixXd::Identity(n, n);
|
||||||
|
|
@ -69,6 +75,31 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
explicit SO(const Eigen::MatrixBase<Derived>& R) : matrix_(R.eval()) {}
|
explicit SO(const Eigen::MatrixBase<Derived>& R) : matrix_(R.eval()) {}
|
||||||
|
|
||||||
|
/// Constructor from AngleAxisd
|
||||||
|
template <int N_ = N, typename = IsSO3<N_>>
|
||||||
|
SO(const Eigen::AngleAxisd& angleAxis) : matrix_(angleAxis) {}
|
||||||
|
|
||||||
|
/// 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.");
|
||||||
|
// This needs to be re-thought!
|
||||||
|
static boost::uniform_real<double> randomAngle(-M_PI, M_PI);
|
||||||
|
const size_t d = SO::Dimension(n);
|
||||||
|
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
|
/// @name Standard methods
|
||||||
/// @{
|
/// @{
|
||||||
|
|
@ -96,14 +127,13 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
SO operator*(const SO& other) const { return SO(matrix_ * other.matrix_); }
|
SO operator*(const SO& other) const { return SO(matrix_ * other.matrix_); }
|
||||||
|
|
||||||
/// SO<N> identity for N >= 2
|
/// SO<N> identity for N >= 2
|
||||||
template <int N_ = N, typename = boost::enable_if_t<N_ >= 2, void>>
|
template <int N_ = N, typename = IsFixed<N_>>
|
||||||
static SO identity() {
|
static SO identity() {
|
||||||
return SO();
|
return SO();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SO<N> identity for N == Eigen::Dynamic
|
/// SO<N> identity for N == Eigen::Dynamic
|
||||||
template <int N_ = N,
|
template <int N_ = N, typename = IsDynamic<N_>>
|
||||||
typename = boost::enable_if_t<N_ == Eigen::Dynamic, void>>
|
|
||||||
static SO identity(size_t n = 0) {
|
static SO identity(size_t n = 0) {
|
||||||
return SO(n);
|
return SO(n);
|
||||||
}
|
}
|
||||||
|
|
@ -115,18 +145,127 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
/// @name Manifold
|
/// @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()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* element corresponds to so(2), the last 3 to so(3), the last 6 to so(4)
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
static Matrix Hat(const Vector& xi) {
|
||||||
|
size_t n = AmbientDim(xi.size());
|
||||||
|
if (n < 2) throw std::invalid_argument("SOn::Hat: n<2 not supported");
|
||||||
|
|
||||||
|
Matrix X(n, n); // allocate space for n*n skew-symmetric matrix
|
||||||
|
X.setZero();
|
||||||
|
if (n == 2) {
|
||||||
|
// Handle SO(2) case as recursion bottom
|
||||||
|
assert(xi.size() == 1);
|
||||||
|
X << 0, -xi(0), xi(0), 0;
|
||||||
|
} else {
|
||||||
|
// Recursively call SO(n-1) call for top-left block
|
||||||
|
const size_t dmin = (n - 1) * (n - 2) / 2;
|
||||||
|
X.topLeftCorner(n - 1, n - 1) = Hat(xi.tail(dmin));
|
||||||
|
|
||||||
|
// Now fill last row and column
|
||||||
|
double sign = 1.0;
|
||||||
|
for (size_t i = 0; i < n - 1; i++) {
|
||||||
|
const size_t j = n - 2 - i;
|
||||||
|
X(n - 1, j) = sign * xi(i);
|
||||||
|
X(j, n - 1) = -X(n - 1, j);
|
||||||
|
sign = -sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return X;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverse of Hat. See note about xi element order in Hat.
|
||||||
|
*/
|
||||||
|
static Vector Vee(const Matrix& X) {
|
||||||
|
const size_t n = X.rows();
|
||||||
|
if (n < 2) throw std::invalid_argument("SOn::Hat: n<2 not supported");
|
||||||
|
|
||||||
|
if (n == 2) {
|
||||||
|
// Handle SO(2) case as recursion bottom
|
||||||
|
Vector xi(1);
|
||||||
|
xi(0) = X(1, 0);
|
||||||
|
return xi;
|
||||||
|
} else {
|
||||||
|
// Calculate dimension and allocate space
|
||||||
|
const size_t d = n * (n - 1) / 2;
|
||||||
|
Vector xi(d);
|
||||||
|
|
||||||
|
// Fill first n-1 spots from last row of X
|
||||||
|
double sign = 1.0;
|
||||||
|
for (size_t i = 0; i < n - 1; i++) {
|
||||||
|
const size_t j = n - 2 - i;
|
||||||
|
xi(i) = sign * X(n - 1, j);
|
||||||
|
sign = -sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively call Vee to fill remainder of x
|
||||||
|
const size_t dmin = (n - 1) * (n - 2) / 2;
|
||||||
|
xi.tail(dmin) = Vee(X.topLeftCorner(n - 1, n - 1));
|
||||||
|
return xi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Chart at origin
|
// Chart at origin
|
||||||
struct ChartAtOrigin {
|
struct ChartAtOrigin {
|
||||||
static SO Retract(const VectorD& xi,
|
/**
|
||||||
OptionalJacobian<dimension, dimension> H = boost::none) {
|
* Retract uses Cayley map. See note about xi element order in Hat.
|
||||||
return SO(); // TODO(frank): Expmap(xi, H);
|
* Deafault implementation has no Jacobian implemented
|
||||||
|
*/
|
||||||
|
static SO Retract(const TangentVector& xi, ChartJacobian H = boost::none) {
|
||||||
|
const Matrix X = Hat(xi / 2.0);
|
||||||
|
size_t n = AmbientDim(xi.size());
|
||||||
|
const auto I = Eigen::MatrixXd::Identity(n, n);
|
||||||
|
return SO((I + X) * (I - X).inverse());
|
||||||
}
|
}
|
||||||
static VectorD Local(
|
/**
|
||||||
const SO& R, OptionalJacobian<dimension, dimension> H = boost::none) {
|
* Inverse of Retract. See note about xi element order in Hat.
|
||||||
return VectorD(); // TODO(frank): Logmap(R, H);
|
*/
|
||||||
|
static TangentVector Local(const SO& R, ChartJacobian H = boost::none) {
|
||||||
|
const size_t n = R.rows();
|
||||||
|
const auto I = Eigen::MatrixXd::Identity(n, n);
|
||||||
|
const Matrix X = (I - R.matrix_) * (I + R.matrix_).inverse();
|
||||||
|
return -2 * Vee(X);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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
|
/// @name Lie Group
|
||||||
/// @{
|
/// @{
|
||||||
|
|
@ -136,14 +275,12 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
/**
|
/**
|
||||||
* Exponential map at identity - create a rotation from canonical coordinates
|
* Exponential map at identity - create a rotation from canonical coordinates
|
||||||
*/
|
*/
|
||||||
static SO Expmap(const VectorD& omega,
|
static SO Expmap(const TangentVector& omega, ChartJacobian H = boost::none);
|
||||||
OptionalJacobian<dimension, dimension> H = boost::none);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log map at identity - returns the canonical coordinates of this rotation
|
* Log map at identity - returns the canonical coordinates of this rotation
|
||||||
*/
|
*/
|
||||||
static VectorD Logmap(const SO& R,
|
static TangentVector Logmap(const SO& R, ChartJacobian H = boost::none);
|
||||||
OptionalJacobian<dimension, dimension> H = boost::none);
|
|
||||||
|
|
||||||
// inverse with optional derivative
|
// inverse with optional derivative
|
||||||
using LieGroup<SO<N>, internal::DimensionSO(N)>::inverse;
|
using LieGroup<SO<N>, internal::DimensionSO(N)>::inverse;
|
||||||
|
|
@ -151,22 +288,22 @@ class SO : public LieGroup<SO<N>, internal::DimensionSO(N)> {
|
||||||
/// @}
|
/// @}
|
||||||
};
|
};
|
||||||
|
|
||||||
using SOn = SO<Eigen::Dynamic>;
|
|
||||||
using SO3 = SO<3>;
|
|
||||||
using SO4 = SO<4>;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fully specialize compose and between, because the derivative is unknowable by
|
* Fully specialize compose and between, because the derivative is unknowable by
|
||||||
* the LieGroup implementations, who return a fixed-size matrix for H2.
|
* the LieGroup implementations, who return a fixed-size matrix for H2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using SO3 = SO<3>;
|
||||||
|
using SO4 = SO<4>;
|
||||||
|
using SOn = SO<Eigen::Dynamic>;
|
||||||
|
|
||||||
using DynamicJacobian = OptionalJacobian<Eigen::Dynamic, Eigen::Dynamic>;
|
using DynamicJacobian = OptionalJacobian<Eigen::Dynamic, Eigen::Dynamic>;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
SOn LieGroup<SOn, Eigen::Dynamic>::compose(const SOn& g, DynamicJacobian H1,
|
SOn LieGroup<SOn, Eigen::Dynamic>::compose(const SOn& g, DynamicJacobian H1,
|
||||||
DynamicJacobian H2) const {
|
DynamicJacobian H2) const {
|
||||||
if (H1) *H1 = g.inverse().AdjointMap();
|
if (H1) *H1 = g.inverse().AdjointMap();
|
||||||
if (H2) *H2 = internal::IdentitySO(g.rows());
|
if (H2) *H2 = SOn::IdentityJacobian(g.rows());
|
||||||
return derived() * g;
|
return derived() * g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,7 +312,7 @@ SOn LieGroup<SOn, Eigen::Dynamic>::between(const SOn& g, DynamicJacobian H1,
|
||||||
DynamicJacobian H2) const {
|
DynamicJacobian H2) const {
|
||||||
SOn result = derived().inverse() * g;
|
SOn result = derived().inverse() * g;
|
||||||
if (H1) *H1 = -result.inverse().AdjointMap();
|
if (H1) *H1 = -result.inverse().AdjointMap();
|
||||||
if (H2) *H2 = internal::IdentitySO(g.rows());
|
if (H2) *H2 = SOn::IdentityJacobian(g.rows());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,10 +343,13 @@ using namespace std;
|
||||||
using namespace gtsam;
|
using namespace gtsam;
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
// TEST(SOn, SO5) {
|
TEST(SOn, SO5) {
|
||||||
// const auto R = SOn(5);
|
const auto R = SOn(5);
|
||||||
// EXPECT_LONGS_EQUAL(5, R.rows());
|
EXPECT_LONGS_EQUAL(5, R.rows());
|
||||||
// }
|
EXPECT_LONGS_EQUAL(Eigen::Dynamic, SOn::dimension);
|
||||||
|
EXPECT_LONGS_EQUAL(Eigen::Dynamic, SOn::Dim());
|
||||||
|
EXPECT_LONGS_EQUAL(10, R.dim());
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST(SOn, Concept) {
|
TEST(SOn, Concept) {
|
||||||
|
|
@ -218,23 +358,23 @@ TEST(SOn, Concept) {
|
||||||
BOOST_CONCEPT_ASSERT((IsLieGroup<SOn>));
|
BOOST_CONCEPT_ASSERT((IsLieGroup<SOn>));
|
||||||
}
|
}
|
||||||
|
|
||||||
// /* *************************************************************************
|
/* ************************************************************************* */
|
||||||
// */ TEST(SOn, Values) {
|
TEST(SOn, Values) {
|
||||||
// const auto R = SOn(5);
|
const auto R = SOn(5);
|
||||||
// Values values;
|
Values values;
|
||||||
// const Key key(0);
|
const Key key(0);
|
||||||
// values.insert(key, R);
|
values.insert(key, R);
|
||||||
// const auto B = values.at<SOn>(key);
|
const auto B = values.at<SOn>(key);
|
||||||
// EXPECT_LONGS_EQUAL(5, B.rows());
|
EXPECT_LONGS_EQUAL(5, B.rows());
|
||||||
// }
|
}
|
||||||
|
|
||||||
// /* *************************************************************************
|
/* ************************************************************************* */
|
||||||
// */ TEST(SOn, Random) {
|
TEST(SOn, Random) {
|
||||||
// static boost::mt19937 rng(42);
|
static boost::mt19937 rng(42);
|
||||||
// EXPECT_LONGS_EQUAL(3, SOn::Random(rng, 3).rows());
|
EXPECT_LONGS_EQUAL(3, SOn::Random(rng, 3).rows());
|
||||||
// EXPECT_LONGS_EQUAL(4, SOn::Random(rng, 4).rows());
|
EXPECT_LONGS_EQUAL(4, SOn::Random(rng, 4).rows());
|
||||||
// EXPECT_LONGS_EQUAL(5, SOn::Random(rng, 5).rows());
|
EXPECT_LONGS_EQUAL(5, SOn::Random(rng, 5).rows());
|
||||||
// }
|
}
|
||||||
|
|
||||||
// /* *************************************************************************
|
// /* *************************************************************************
|
||||||
// */ TEST(SOn, HatVee) {
|
// */ TEST(SOn, HatVee) {
|
||||||
|
|
@ -296,6 +436,9 @@ TEST(SOn, Concept) {
|
||||||
TEST(SO4, Identity) {
|
TEST(SO4, Identity) {
|
||||||
const SO4 R;
|
const SO4 R;
|
||||||
EXPECT_LONGS_EQUAL(4, R.rows());
|
EXPECT_LONGS_EQUAL(4, R.rows());
|
||||||
|
EXPECT_LONGS_EQUAL(6, SO4::dimension);
|
||||||
|
EXPECT_LONGS_EQUAL(6, SO4::Dim());
|
||||||
|
EXPECT_LONGS_EQUAL(6, R.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
@ -439,6 +582,9 @@ TEST(SO4, Concept) {
|
||||||
TEST(SO3, Identity) {
|
TEST(SO3, Identity) {
|
||||||
const SO3 R;
|
const SO3 R;
|
||||||
EXPECT_LONGS_EQUAL(3, R.rows());
|
EXPECT_LONGS_EQUAL(3, R.rows());
|
||||||
|
EXPECT_LONGS_EQUAL(3, SO3::dimension);
|
||||||
|
EXPECT_LONGS_EQUAL(3, SO3::Dim());
|
||||||
|
EXPECT_LONGS_EQUAL(3, R.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
|
|
@ -448,14 +594,14 @@ TEST(SO3, Concept) {
|
||||||
BOOST_CONCEPT_ASSERT((IsLieGroup<SO3>));
|
BOOST_CONCEPT_ASSERT((IsLieGroup<SO3>));
|
||||||
}
|
}
|
||||||
|
|
||||||
// //******************************************************************************
|
//******************************************************************************
|
||||||
// TEST(SO3, Constructor) { SO3 q(Eigen::AngleAxisd(1, Vector3(0, 0, 1))); }
|
TEST(SO3, Constructor) { SO3 q(Eigen::AngleAxisd(1, Vector3(0, 0, 1))); }
|
||||||
|
|
||||||
// //******************************************************************************
|
//******************************************************************************
|
||||||
// SO3 id;
|
SO3 id;
|
||||||
// Vector3 z_axis(0, 0, 1);
|
Vector3 z_axis(0, 0, 1);
|
||||||
// SO3 R1(Eigen::AngleAxisd(0.1, z_axis));
|
SO3 R1(Eigen::AngleAxisd(0.1, z_axis));
|
||||||
// SO3 R2(Eigen::AngleAxisd(0.2, z_axis));
|
SO3 R2(Eigen::AngleAxisd(0.2, z_axis));
|
||||||
|
|
||||||
// //******************************************************************************
|
// //******************************************************************************
|
||||||
// TEST(SO3, Local) {
|
// TEST(SO3, Local) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue