The great Lie group heist: LieGroup CRTP class that defines both Manifold and Lie Group methods for a class, using AdjointMap as its main weapon. For the retract/localCoordinates, a ChartAtOrigin struct needs to be defined that can be switched at compile time (example Pose2). Rot3, Pose3 soon to follow.

release/4.3a0
dellaert 2014-12-24 01:37:30 +01:00
parent 6ddd37a0e2
commit f22c922600
5 changed files with 106 additions and 192 deletions

View File

@ -24,6 +24,67 @@
namespace gtsam {
/// A CRTP helper class that implements Lie group methods
/// Prerequisites: methods operator*, inverse, and AdjointMap, as well as a
/// ChartAtOrigin struct that will be used to define the manifold Chart
/// To use, simply derive, but also say "using LieGroup<Class,N>::inverse"
/// For derivative math, see doc/math.pdf
template <class Class, int N>
struct LieGroup {
BOOST_STATIC_ASSERT_MSG(N != Eigen::Dynamic,
"LieGroup not yet specialized for dynamically sized types.");
enum { dimension = N };
typedef OptionalJacobian<N, N> ChartJacobian;
typedef Eigen::Matrix<double, N, 1> TangentVector;
const Class & derived() const {
return static_cast<const Class&>(*this);
}
Class compose(const Class& g) const {
return derived() * g;
}
Class between(const Class& g) const {
return derived().inverse() * g;
}
Class compose(const Class& g, ChartJacobian H1,
ChartJacobian H2 = boost::none) const {
if (H1) *H1 = g.inverse().AdjointMap();
if (H2) *H2 = Eigen::Matrix<double, N, N>::Identity();
return derived() * g;
}
Class between(const Class& g, ChartJacobian H1,
ChartJacobian H2 = boost::none) const {
Class result = derived().inverse() * g;
if (H1) *H1 = - result.inverse().AdjointMap();
if (H2) *H2 = Eigen::Matrix<double, N, N>::Identity();
return result;
}
Class inverse(ChartJacobian H) const {
if (H) *H = - derived().AdjointMap();
return derived().inverse();
}
Class retract(const TangentVector& v, //
ChartJacobian H1 = boost::none, //
ChartJacobian H2 = boost::none) const {
return compose(Class::ChartAtOrigin::Retract(v),H1,H2);
}
TangentVector localCoordinates(const Class& g, //
ChartJacobian H1 = boost::none, //
ChartJacobian H2 = boost::none) const {
return Class::ChartAtOrigin::Local(between(g,H1,H2));
}
};
/// tag to assert a type is a Lie group
struct lie_group_tag: public manifold_tag, public group_tag {};

View File

@ -92,25 +92,20 @@ Vector3 Pose2::Logmap(const Pose2& p, OptionalJacobian<3, 3> H) {
}
/* ************************************************************************* */
Pose2 Pose2::retract(const Vector& v, OptionalJacobian<3, 3> Hthis,
OptionalJacobian<3, 3> Hv) const {
if (Hthis || Hv) CONCEPT_NOT_IMPLEMENTED;
Pose2 Pose2::ChartAtOrigin::Retract(const Vector3& v, ChartJacobian H) {
#ifdef SLOW_BUT_CORRECT_EXPMAP
return compose(Expmap(v));
return Expmap(v, H);
#else
assert(v.size() == 3);
return compose(Pose2(v[0], v[1], v[2]));
if (H) *H = Matrix3::Identity();
return Pose2(v[0], v[1], v[2]);
#endif
}
/* ************************************************************************* */
Vector Pose2::localCoordinates(const Pose2& p2, OptionalJacobian<3, 3> Hthis,
OptionalJacobian<3, 3> Hother) const {
if (Hthis || Hother) CONCEPT_NOT_IMPLEMENTED;
Vector3 Pose2::ChartAtOrigin::Local(const Pose2& r, ChartJacobian H) {
#ifdef SLOW_BUT_CORRECT_EXPMAP
return Logmap(between(p2));
return Logmap(r, H);
#else
Pose2 r = between(p2);
if (H) *H = Matrix3::Identity();
return Vector3(r.x(), r.y(), r.theta());
#endif
}
@ -189,8 +184,7 @@ Matrix3 Pose2::dexpInvL(const Vector3& v) {
}
/* ************************************************************************* */
Pose2 Pose2::inverse(OptionalJacobian<3,3> H1) const {
if (H1) *H1 = -AdjointMap();
Pose2 Pose2::inverse() const {
return Pose2(r_.inverse(), r_.unrotate(Point2(-t_.x(), -t_.y())));
}
@ -208,16 +202,6 @@ Point2 Pose2::transform_to(const Point2& point,
return q;
}
/* ************************************************************************* */
// see doc/math.lyx, SE(2) section
Pose2 Pose2::compose(const Pose2& p2, OptionalJacobian<3,3> H1,
OptionalJacobian<3,3> H2) const {
// TODO: inline and reuse?
if(H1) *H1 = p2.inverse().AdjointMap();
if(H2) *H2 = I_3x3;
return (*this)*p2;
}
/* ************************************************************************* */
// see doc/math.lyx, SE(2) section
Point2 Pose2::transform_from(const Point2& p,
@ -233,40 +217,6 @@ Point2 Pose2::transform_from(const Point2& p,
return q + t_;
}
/* ************************************************************************* */
Pose2 Pose2::between(const Pose2& p2, OptionalJacobian<3,3> H1,
OptionalJacobian<3,3> H2) const {
// get cosines and sines from rotation matrices
const Rot2& R1 = r_, R2 = p2.r();
double c1=R1.c(), s1=R1.s(), c2=R2.c(), s2=R2.s();
// Assert that R1 and R2 are normalized
assert(std::abs(c1*c1 + s1*s1 - 1.0) < 1e-5 && std::abs(c2*c2 + s2*s2 - 1.0) < 1e-5);
// Calculate delta rotation = between(R1,R2)
double c = c1 * c2 + s1 * s2, s = -s1 * c2 + c1 * s2;
Rot2 R(Rot2::atan2(s,c)); // normalizes
// Calculate delta translation = unrotate(R1, dt);
Point2 dt = p2.t() - t_;
double x = dt.x(), y = dt.y();
// t = R1' * (t2-t1)
Point2 t(c1 * x + s1 * y, -s1 * x + c1 * y);
// FD: This is just -AdjointMap(between(p2,p1)) inlined and re-using above
if (H1) {
double dt1 = -s2 * x + c2 * y;
double dt2 = -c2 * x - s2 * y;
*H1 <<
-c, -s, dt1,
s, -c, dt2,
0.0, 0.0,-1.0;
}
if (H2) *H2 = I_3x3;
return Pose2(R,t);
}
/* ************************************************************************* */
Rot2 Pose2::bearing(const Point2& point,
OptionalJacobian<1, 3> H1, OptionalJacobian<1, 2> H2) const {

View File

@ -31,7 +31,7 @@ namespace gtsam {
* @addtogroup geometry
* \nosubgrouping
*/
class GTSAM_EXPORT Pose2 {
class GTSAM_EXPORT Pose2: public LieGroup<Pose2, 3> {
public:
@ -40,13 +40,12 @@ public:
typedef Point2 Translation;
private:
Rot2 r_;
Point2 t_;
public:
enum { dimension = 3 };
/// @name Standard Constructors
/// @{
@ -106,52 +105,23 @@ public:
/// identity for group operation
inline static Pose2 identity() { return Pose2(); }
/// inverse transformation with derivatives
Pose2 inverse(OptionalJacobian<3, 3> H1=boost::none) const;
/// compose this transformation onto another (first *this and then p2)
Pose2 compose(const Pose2& p2,
OptionalJacobian<3, 3> H1 = boost::none,
OptionalJacobian<3, 3> H2 = boost::none) const;
/// inverse
Pose2 inverse() const;
/// compose syntactic sugar
inline Pose2 operator*(const Pose2& p2) const {
return Pose2(r_*p2.r(), t_ + r_*p2.t());
}
/**
* Return relative pose between p1 and p2, in p1 coordinate frame
*/
Pose2 between(const Pose2& p2, OptionalJacobian<3,3> H1 = boost::none,
OptionalJacobian<3,3> H = boost::none) const;
/// @}
/// @name Manifold
/// @{
/// Dimensionality of tangent space = 3 DOF - used to autodetect sizes
inline static size_t Dim() { return 3; }
/// Dimensionality of tangent space = 3 DOF
inline size_t dim() const { return 3; }
/// Retraction from R^3 \f$ [T_x,T_y,\theta] \f$ to Pose2 manifold neighborhood around current pose
Pose2 retract(const Vector& v, OptionalJacobian<3, 3> Hthis =
boost::none, OptionalJacobian<3, 3> Hv = boost::none) const;
/// Local 3D coordinates \f$ [T_x,T_y,\theta] \f$ of Pose2 manifold neighborhood around current pose
Vector localCoordinates(const Pose2& p2, OptionalJacobian<3, 3> Hthis =
boost::none, OptionalJacobian<3, 3> Hother = boost::none) const;
/// @}
/// @name Lie Group
/// @{
///Exponential map at identity - create a rotation from canonical coordinates \f$ [T_x,T_y,\theta] \f$
static Pose2 Expmap(const Vector& xi, OptionalJacobian<3, 3> H = boost::none);
static Pose2 Expmap(const Vector& xi, ChartJacobian H = boost::none);
///Log map at identity - return the canonical coordinates \f$ [T_x,T_y,\theta] \f$ of this rotation
static Vector3 Logmap(const Pose2& p, OptionalJacobian<3, 3> H = boost::none);
static Vector3 Logmap(const Pose2& p, ChartJacobian H = boost::none);
/**
* Calculate Adjoint map
@ -189,6 +159,13 @@ public:
/// Left-trivialized derivative inverse of the exponential map
static Matrix3 dexpInvL(const Vector3& v);
// Chart at origin, depends on compile-time flag SLOW_BUT_CORRECT_EXPMAP
struct ChartAtOrigin {
static Pose2 Retract(const Vector3& v, ChartJacobian H = boost::none);
static Vector3 Local(const Pose2& r, ChartJacobian H = boost::none);
};
using LieGroup<Pose2, 3>::inverse; // version with derivative
/// @}
/// @name Group Action on Point2

View File

@ -60,47 +60,7 @@ Rot2& Rot2::normalize() {
}
/* ************************************************************************* */
Rot2 Rot2::compose(const Rot2& R, OptionalJacobian<1, 1> H1,
OptionalJacobian<1, 1> H2) const {
if (H1)
*H1 = I_1x1;
if (H2)
*H2 = I_1x1;
return fromCosSin(c_ * R.c_ - s_ * R.s_, s_ * R.c_ + c_ * R.s_);
}
/* ************************************************************************* */
Rot2 Rot2::between(const Rot2& R, OptionalJacobian<1, 1> H1,
OptionalJacobian<1, 1> H2) const {
if (H1)
*H1 = -I_1x1;
if (H2)
*H2 = I_1x1;
return fromCosSin(c_ * R.c_ + s_ * R.s_, -s_ * R.c_ + c_ * R.s_);
}
/* ************************************************************************* */
Rot2 Rot2::retract(const Vector& v, OptionalJacobian<1, 1> H1,
OptionalJacobian<1, 1> H2) const {
if (H1)
*H1 = I_1x1;
if (H2)
*H2 = I_1x1;
return *this * Expmap(v);
}
/* ************************************************************************* */
Vector1 Rot2::localCoordinates(const Rot2& t2, OptionalJacobian<1, 1> H1,
OptionalJacobian<1, 1> H2) const {
if (H1)
*H1 = -I_1x1;
if (H2)
*H2 = I_1x1;
return Logmap(between(t2));
}
/* ************************************************************************* */
Rot2 Rot2::Expmap(const Vector& v, OptionalJacobian<1, 1> H) {
Rot2 Rot2::Expmap(const Vector1& v, OptionalJacobian<1, 1> H) {
if (H)
*H = I_1x1;
if (zero(v))

View File

@ -30,25 +30,16 @@ namespace gtsam {
* @addtogroup geometry
* \nosubgrouping
*/
class GTSAM_EXPORT Rot2 {
public:
/** get the dimension by the type */
static const size_t dimension = 1;
private:
class GTSAM_EXPORT Rot2 : public LieGroup<Rot2, 1> {
/** we store cos(theta) and sin(theta) */
double c_, s_;
/** normalize to make sure cos and sin form unit vector */
Rot2& normalize();
/** private constructor from cos/sin */
inline Rot2(double c, double s) :
c_(c), s_(s) {
}
inline Rot2(double c, double s) : c_(c), s_(s) {}
public:
@ -56,14 +47,10 @@ namespace gtsam {
/// @{
/** default constructor, zero rotation */
Rot2() :
c_(1.0), s_(0.0) {
}
Rot2() : c_(1.0), s_(0.0) {}
/// Constructor from angle in radians == exponential map at identity
Rot2(double theta) :
c_(cos(theta)), s_(sin(theta)) {
}
Rot2(double theta) : c_(cos(theta)), s_(sin(theta)) {}
/// Named constructor from angle in radians
static Rot2 fromAngle(double theta) {
@ -110,59 +97,26 @@ namespace gtsam {
inline static Rot2 identity() { return Rot2(); }
/** The inverse rotation - negative angle */
Rot2 inverse(OptionalJacobian<1,1> H = boost::none) const {
if (H) *H = -I_1x1;
return Rot2(c_, -s_);
}
Rot2 inverse() const { return Rot2(c_, -s_);}
/** Compose - make a new rotation by adding angles */
Rot2 operator*(const Rot2& R) const {
return fromCosSin(c_ * R.c_ - s_ * R.s_, s_ * R.c_ + c_ * R.s_);
}
/** Compose - make a new rotation by adding angles */
Rot2 compose(const Rot2& R, OptionalJacobian<1,1> H1 =
boost::none, OptionalJacobian<1,1> H2 = boost::none) const;
/** Between using the default implementation */
Rot2 between(const Rot2& R, OptionalJacobian<1,1> H1 =
boost::none, OptionalJacobian<1,1> H2 = boost::none) const;
/// @}
/// @name Manifold
/// @{
/// dimension of the variable - used to autodetect sizes
inline static size_t Dim() {
return dimension;
}
/// Dimensionality of the tangent space, DOF = 1
inline size_t dim() const {
return dimension;
}
/// Updates a with tangent space delta
Rot2 retract(const Vector& v, OptionalJacobian<1, 1> H1 = boost::none,
OptionalJacobian<1, 1> H2 = boost::none) const;
/// Returns inverse retraction
Vector1 localCoordinates(const Rot2& t2, OptionalJacobian<1, 1> H1 =
boost::none, OptionalJacobian<1, 1> H2 = boost::none) const;
/// @}
/// @name Lie Group
/// @{
/// Exponential map at identity - create a rotation from canonical coordinates
static Rot2 Expmap(const Vector1& v, ChartJacobian H = boost::none);
/// Log map at identity - return the canonical coordinates of this rotation
static Vector1 Logmap(const Rot2& r, ChartJacobian H = boost::none);
/** Calculate Adjoint map */
Matrix1 AdjointMap() const { return I_1x1; }
///Exponential map at identity - create a rotation from canonical coordinates
static Rot2 Expmap(const Vector& v, OptionalJacobian<1, 1> H = boost::none);
///Log map at identity - return the canonical coordinates of this rotation
static Vector1 Logmap(const Rot2& r, OptionalJacobian<1, 1> H = boost::none);
/// Left-trivialized derivative of the exponential map
static Matrix dexpL(const Vector& v) {
return ones(1);
@ -173,6 +127,18 @@ namespace gtsam {
return ones(1);
}
// Chart at origin simply uses exponential map and its inverse
struct ChartAtOrigin {
static Rot2 Retract(const Vector1& v, ChartJacobian H = boost::none) {
return Expmap(v, H);
}
static Vector1 Local(const Rot2& r, ChartJacobian H = boost::none) {
return Logmap(r, H);
}
};
using LieGroup<Rot2, 1>::inverse; // version with derivative
/// @}
/// @name Group Action on Point2
/// @{