Skip to content

Commit

Permalink
Added jacobian for sim2 action, added reference frame tests for Sim2
Browse files Browse the repository at this point in the history
  • Loading branch information
RachitB11 committed May 20, 2024
1 parent 0fb9544 commit 4358e94
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 10 deletions.
20 changes: 17 additions & 3 deletions gtsam/geometry/Similarity2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,23 @@ Similarity2 Similarity2::inverse() const {
return Similarity2(Rt, sRt, 1.0 / s_);
}

Point2 Similarity2::transformFrom(const Point2& p) const {
const Point2 q = R_ * p + t_;
return s_ * q;
Point2 Similarity2::transformFrom(const Point2 &p,
OptionalJacobian<2, 4> Hpose, OptionalJacobian<2, 2> Hpoint) const
{
OptionalJacobian<2, 2> Htranslation = Hpose.cols<2>(0);
OptionalJacobian<2, 1> Hrotation = Hpose.cols<1>(2);
OptionalJacobian<2, 1> Hscale = Hpose.cols<1>(3);
const Point2 q = R_.rotate(p, Hrotation, Hpoint);
Matrix22 scaledRot = s_ * R_.matrix();
if (Hpoint)
*Hpoint *= s_;
if (Hrotation)
*Hrotation *= s_;
if (Htranslation)
*Htranslation = (Hpoint ? *Hpoint : scaledRot);
if (Hscale)
*Hscale = s_ * q;
return s_ * (q + t_);
}

Pose2 Similarity2::transformFrom(const Pose2& T) const {
Expand Down
4 changes: 3 additions & 1 deletion gtsam/geometry/Similarity2.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ class GTSAM_EXPORT Similarity2 : public LieGroup<Similarity2, 4> {
/// @{

/// Action on a point p is s*(R*p+t)
Point2 transformFrom(const Point2& p) const;
GTSAM_EXPORT Point2 transformFrom(const Point2 &p,
OptionalJacobian<2, 4> Hpose = boost::none,
OptionalJacobian<2, 2> Hpoint = boost::none) const;

/**
* Action on a pose T.
Expand Down
7 changes: 3 additions & 4 deletions gtsam/slam/ReferenceFrameFactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,16 @@ P transform_point(
*/
template<class POINT, class TRANSFORM>
class ReferenceFrameFactor : public NoiseModelFactorN<POINT, TRANSFORM, POINT> {
protected:
/** default constructor for serialization only */
ReferenceFrameFactor() {}

public:
typedef NoiseModelFactorN<POINT, TRANSFORM, POINT> Base;
typedef ReferenceFrameFactor<POINT, TRANSFORM> This;

typedef POINT Point;
typedef TRANSFORM Transform;

/** default constructor for serialization only */
ReferenceFrameFactor() {}

/**
* General constructor with arbitrary noise model (constrained or otherwise)
*/
Expand Down
18 changes: 16 additions & 2 deletions gtsam/slam/slam.i
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,22 @@ virtual class PoseRotationPrior : gtsam::NoiseModelFactor {
typedef gtsam::PoseRotationPrior<gtsam::Pose2> PoseRotationPrior2D;
typedef gtsam::PoseRotationPrior<gtsam::Pose3> PoseRotationPrior3D;

#include <gtsam/slam/ReferenceFrameFactor.h>
template <POINT, TRANSFORM>
virtual class ReferenceFrameFactor : gtsam::NoiseModelFactor
{
ReferenceFrameFactor(size_t globalKey, size_t transKey, size_t localKey,
const gtsam::noiseModel::Base *model);
Vector evaluateError(const POINT &global, const TRANSFORM &trans, const POINT &local) const;
void print(string s = "",
const gtsam::KeyFormatter &keyFormatter = gtsam::DefaultKeyFormatter) const;
// enabling serialization functionality
void serialize() const;
};

typedef gtsam::ReferenceFrameFactor<gtsam::Point2, gtsam::Pose2> PointReferenceFrameFactorPose2;
typedef gtsam::ReferenceFrameFactor<gtsam::Point2, gtsam::Similarity2> PointReferenceFrameFactorSim2;

#include <gtsam/slam/EssentialMatrixFactor.h>
virtual class EssentialMatrixFactor : gtsam::NoiseModelFactor {
EssentialMatrixFactor(size_t key, const gtsam::Point2& pA,
Expand Down Expand Up @@ -330,11 +346,9 @@ virtual class FrobeniusBetweenFactor : gtsam::NoiseModelFactor {

Vector evaluateError(const T& R1, const T& R2);
};

#include <gtsam/slam/lago.h>
namespace lago {
gtsam::Values initialize(const gtsam::NonlinearFactorGraph& graph, bool useOdometricPath = true);
gtsam::Values initialize(const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& initialGuess);
}

} // namespace gtsam
248 changes: 248 additions & 0 deletions gtsam/slam/tests/testReferenceFrameFactorSim.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/* ----------------------------------------------------------------------------
* 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 testReferenceFrameFactor.cpp
* @author Alex Cunningham
*/

#include <iostream>

#include <CppUnitLite/TestHarness.h>

#include <gtsam/base/Testable.h>
#include <gtsam/base/numericalDerivative.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/geometry/Similarity2.h>
#include <gtsam/inference/Symbol.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/NonlinearEquality.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>

#include <gtsam/slam/ReferenceFrameFactor.h>

using namespace std;
using namespace boost;
using namespace std::placeholders;
using namespace gtsam;

typedef gtsam::ReferenceFrameFactor<gtsam::Point2, gtsam::Similarity2> PointReferenceFrameFactorSim;

Key lA1 = symbol_shorthand::L(1), lA2 = symbol_shorthand::L(2), lB1 = symbol_shorthand::L(11), lB2 = symbol_shorthand::L(12);
Key tA1 = symbol_shorthand::T(1), tB1 = symbol_shorthand::T(2);

/* ************************************************************************* */
TEST(ReferenceFrameFactor, equals)
{
PointReferenceFrameFactorSim
c1(lB1, tA1, lA1),
c2(lB1, tA1, lA1),
c3(lB1, tA1, lA2);

EXPECT(assert_equal(c1, c1));
EXPECT(assert_equal(c1, c2));
EXPECT(!c1.equals(c3));
}

/* ************************************************************************* */
Vector evaluateError_(const PointReferenceFrameFactorSim &c,
const Point2 &global, const Similarity2 &trans, const Point2 &local)
{
return Vector(c.evaluateError(global, trans, local));
}
TEST(ReferenceFrameFactor, jacobians)
{

// from examples below
Point2 local(2.0, 3.0), global(-1.0, 2.0);
static const Point2 P(1.5, 2.5);
static const Rot2 R = Rot2::fromAngle(0.3);
static const double s = 2;
Similarity2 trans(R, P, s);

PointReferenceFrameFactorSim tc(lA1, tA1, lB1);
Matrix actualDT, actualDL, actualDF;
tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);

Matrix numericalDT, numericalDL, numericalDF;
numericalDF = numericalDerivative31<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);
numericalDT = numericalDerivative32<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);
numericalDL = numericalDerivative33<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);

EXPECT(assert_equal(numericalDF, actualDF));
EXPECT(assert_equal(numericalDL, actualDL));
EXPECT(assert_equal(numericalDT, actualDT));
}

/* ************************************************************************* */
TEST(ReferenceFrameFactor, jacobians_zero)
{

// get values that are ideal
static const Point2 P(2.0, 3.0);
static const Rot2 R = Rot2::fromAngle(0.0);
static const double s = 3;
Similarity2 trans(R, P, s);
Point2 global(5.0, 6.0);
Point2 local = trans.transformFrom(global);

PointReferenceFrameFactorSim tc(lA1, tA1, lB1);
Vector actCost = tc.evaluateError(global, trans, local),
expCost = Z_2x1;
EXPECT(assert_equal(expCost, actCost, 1e-5));

Matrix actualDT, actualDL, actualDF;
tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);

Matrix numericalDT, numericalDL, numericalDF;
numericalDF = numericalDerivative31<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);
numericalDT = numericalDerivative32<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);
numericalDL = numericalDerivative33<Vector, Point2, Similarity2, Point2>(
std::bind(evaluateError_, tc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
global, trans, local, 1e-5);

EXPECT(assert_equal(numericalDF, actualDF));
EXPECT(assert_equal(numericalDL, actualDL));
EXPECT(assert_equal(numericalDT, actualDT));
}

/* ************************************************************************* */
TEST(ReferenceFrameFactor, converge_trans)
{

// initial points
Point2 local1(4.0, 4.0), local2(8.0, 10.0),
global1(-1.0, 5.0), global2(2.0, 3.0);
static const Point2 P(7.0, 3.0);
static const Rot2 R = Rot2::fromAngle(M_PI / 2);
static const double s = 2;
Similarity2 transIdeal(R, P, s);
Similarity2 trans(Rot2::fromAngle(M_PI / 6), Point2(0, 0), 3);

// verify direction
EXPECT(assert_equal(local1, transIdeal.transformFrom(global1)));
EXPECT(assert_equal(local2, transIdeal.transformFrom(global2)));

NonlinearFactorGraph graph;
graph.emplace_shared<PointReferenceFrameFactorSim>(lB1, tA1, lA1);
graph.emplace_shared<PointReferenceFrameFactorSim>(lB2, tA1, lA2);

// hard constraints on points
double error_gain = 1000.0;
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lA1, local1, error_gain);
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lA2, local2, error_gain);
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lB1, global1, error_gain);
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lB2, global2, error_gain);

// create initial estimate
Values init;
init.insert(lA1, local1);
init.insert(lA2, local2);
init.insert(lB1, global1);
init.insert(lB2, global2);
init.insert(tA1, trans);

// optimize
LevenbergMarquardtOptimizer solver(graph, init);
Values actual = solver.optimize();

Values expected;
expected.insert(lA1, local1);
expected.insert(lA2, local2);
expected.insert(lB1, global1);
expected.insert(lB2, global2);
expected.insert(tA1, transIdeal);

EXPECT(assert_equal(expected, actual, 1e-4));
}

/* ************************************************************************* */
TEST(ReferenceFrameFactor, converge_local)
{

// initial points
Point2 global(-1.0, 2.0);
Similarity2 trans(Rot2::fromAngle(3.1), Point2(1.5, 2.5), 4);
Point2 idealLocal = trans.transformFrom(global);

// perturb the initial estimate
Point2 local = idealLocal + Point2(-10.0, 10.0); // works

NonlinearFactorGraph graph;
double error_gain = 1000.0;
graph.emplace_shared<PointReferenceFrameFactorSim>(lB1, tA1, lA1);
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lB1, global, error_gain);
graph.emplace_shared<NonlinearEquality<gtsam::Similarity2>>(tA1, trans, error_gain);

// create initial estimate
Values init;
init.insert(lA1, local);
init.insert(lB1, global);
init.insert(tA1, trans);

// optimize
LevenbergMarquardtOptimizer solver(graph, init);
Values actual = solver.optimize();

CHECK(actual.exists(lA1));
EXPECT(assert_equal(idealLocal, actual.at<Point2>(lA1), 1e-5));
}

/* ************************************************************************* */
TEST(ReferenceFrameFactor, converge_global)
{

// initial points
Point2 local(2.0, 3.0);
Similarity2 trans(Rot2::fromAngle(3.1), Point2(1.5, 2.5), 3);
Point2 idealForeign = trans.inverse().transformFrom(local);

// perturb the initial estimate
Point2 global = idealForeign + Point2(10.0, -10.0); // larger - works

NonlinearFactorGraph graph;
double error_gain = 1000.0;
graph.emplace_shared<PointReferenceFrameFactorSim>(lB1, tA1, lA1);
graph.emplace_shared<NonlinearEquality<gtsam::Point2>>(lA1, local, error_gain);
graph.emplace_shared<NonlinearEquality<gtsam::Similarity2>>(tA1, trans, error_gain);

// create initial estimate
Values init;
init.insert(lA1, local);
init.insert(lB1, global);
init.insert(tA1, trans);

// optimize
LevenbergMarquardtOptimizer solver(graph, init);
Values actual = solver.optimize();

// verify
CHECK(actual.exists(lB1));
EXPECT(assert_equal(idealForeign, actual.at<Point2>(lB1), 1e-5));
}

/* ************************************************************************* */
int main()
{
TestResult tr;
return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */

0 comments on commit 4358e94

Please sign in to comment.