Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for Jacobian of PartialPriorFactor<Pose3> #721

Merged
merged 11 commits into from
Mar 29, 2021
3 changes: 3 additions & 0 deletions gtsam/base/ProductLieGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ class ProductLieGroup: public std::pair<G, H> {
}
return v;
}
static TangentVector LocalCoordinates(const ProductLieGroup& p, ChartJacobian Hp = boost::none) {
miloknowles marked this conversation as resolved.
Show resolved Hide resolved
return Logmap(p, Hp);
}
ProductLieGroup expmap(const TangentVector& v) const {
return compose(ProductLieGroup::Expmap(v));
}
Expand Down
54 changes: 19 additions & 35 deletions gtsam_unstable/dynamics/DynamicsPriors.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@

#pragma once

#include <gtsam_unstable/slam/PartialPriorFactor.h>

#include <gtsam_unstable/dynamics/PoseRTV.h>
#include <gtsam_unstable/slam/PartialPriorFactor.h>

namespace gtsam {

// Indices of relevant variables in the PoseRTV tangent vector:
// [ rx ry rz tx ty tz vx vy vz ]
static const size_t kRollIndex = 0;
static const size_t kPitchIndex = 1;
static const size_t kHeightIndex = 5;
static const size_t kVelocityZIndex = 8;
static const std::vector<size_t> kVelocityIndices = { 6, 7, 8 };

/**
* Forces the value of the height in a PoseRTV to a specific value
* Forces the value of the height (z) in a PoseRTV to a specific value.
* Dim: 1
*/
struct DHeightPrior : public gtsam::PartialPriorFactor<PoseRTV> {
typedef gtsam::PartialPriorFactor<PoseRTV> Base;

DHeightPrior(Key key, double height, const gtsam::SharedNoiseModel& model)
: Base(key, 5, height, model) { }
: Base(key, kHeightIndex, height, model) {}
};

/**
Expand All @@ -35,11 +43,11 @@ struct DRollPrior : public gtsam::PartialPriorFactor<PoseRTV> {

/** allows for explicit roll parameterization - uses canonical coordinate */
DRollPrior(Key key, double wx, const gtsam::SharedNoiseModel& model)
: Base(key, 0, wx, model) { }
: Base(key, kRollIndex, wx, model) { }

/** Forces roll to zero */
DRollPrior(Key key, const gtsam::SharedNoiseModel& model)
: Base(key, 0, 0.0, model) { }
: Base(key, kRollIndex, 0.0, model) { }
};

/**
Expand All @@ -49,17 +57,9 @@ struct DRollPrior : public gtsam::PartialPriorFactor<PoseRTV> {
*/
struct VelocityPrior : public gtsam::PartialPriorFactor<PoseRTV> {
typedef gtsam::PartialPriorFactor<PoseRTV> Base;

VelocityPrior(Key key, const gtsam::Vector& vel, const gtsam::SharedNoiseModel& model)
: Base(key, model) {
this->prior_ = vel;
assert(vel.size() == 3);
this->mask_.resize(3);
this->mask_[0] = 6;
this->mask_[1] = 7;
this->mask_[2] = 8;
this->H_ = Matrix::Zero(3, 9);
this->fillH();
}
: Base(key, kVelocityIndices, vel, model) {}
};

/**
Expand All @@ -74,31 +74,15 @@ struct DGroundConstraint : public gtsam::PartialPriorFactor<PoseRTV> {
* Primary constructor allows for variable height of the "floor"
*/
DGroundConstraint(Key key, double height, const gtsam::SharedNoiseModel& model)
: Base(key, model) {
this->prior_ = Vector::Unit(4,0)*height; // [z, vz, roll, pitch]
this->mask_.resize(4);
this->mask_[0] = 5; // z = height
this->mask_[1] = 8; // vz
this->mask_[2] = 0; // roll
this->mask_[3] = 1; // pitch
this->H_ = Matrix::Zero(3, 9);
this->fillH();
}
: Base(key, { kHeightIndex, kVelocityZIndex, kRollIndex, kPitchIndex },
Vector::Unit(4, 0)*height, model) {}

/**
* Fully specify vector - use only for debugging
*/
DGroundConstraint(Key key, const Vector& constraint, const gtsam::SharedNoiseModel& model)
: Base(key, model) {
: Base(key, { kHeightIndex, kVelocityZIndex, kRollIndex, kPitchIndex }, constraint, model) {
assert(constraint.size() == 4);
this->prior_ = constraint; // [z, vz, roll, pitch]
this->mask_.resize(4);
this->mask_[0] = 5; // z = height
this->mask_[1] = 8; // vz
this->mask_[2] = 0; // roll
this->mask_[3] = 1; // pitch
this->H_ = Matrix::Zero(3, 9);
this->fillH();
}
};

Expand Down
1 change: 1 addition & 0 deletions gtsam_unstable/dynamics/PoseRTV.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class GTSAM_UNSTABLE_EXPORT PoseRTV : public ProductLieGroup<Pose3,Velocity3> {
using Base::Dim;
using Base::retract;
using Base::localCoordinates;
using Base::LocalCoordinates;
/// @}

/// @name measurement functions
Expand Down
82 changes: 43 additions & 39 deletions gtsam_unstable/slam/PartialPriorFactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ namespace gtsam {
*
* The prior vector used in this factor is stored in compressed form, such that
* it only contains values for measurements that are to be compared, and they are in
* the same order as VALUE::Logmap(). The mask will determine which components to extract
* in the error function.
* the same order as VALUE::Logmap(). The provided indices will determine which components to
* extract in the error function.
*
* For practical use, it would be good to subclass this factor and have the class type
* construct the mask.
* @tparam VALUE is the type of variable the prior effects
*/
template<class VALUE>
Expand All @@ -43,16 +41,14 @@ namespace gtsam {
typedef VALUE T;

protected:

// Concept checks on the variable type - currently requires Lie
GTSAM_CONCEPT_LIE_TYPE(VALUE)

typedef NoiseModelFactor1<VALUE> Base;
typedef PartialPriorFactor<VALUE> This;

Vector prior_; ///< measurement on tangent space parameters, in compressed form
std::vector<size_t> mask_; ///< indices of values to constrain in compressed prior vector
Matrix H_; ///< Constant Jacobian - computed at creation
Vector prior_; ///< Measurement on tangent space parameters, in compressed form.
std::vector<size_t> indices_; ///< Indices of the measured tangent space parameters.

/** default constructor - only use for serialization */
PartialPriorFactor() {}
Expand All @@ -68,20 +64,22 @@ namespace gtsam {

~PartialPriorFactor() override {}

/** Single Element Constructor: acts on a single parameter specified by idx */
/** Single Element Constructor: Prior on a single parameter at index 'idx' in the tangent vector.*/
PartialPriorFactor(Key key, size_t idx, double prior, const SharedNoiseModel& model) :
Base(model, key), prior_((Vector(1) << prior).finished()), mask_(1, idx), H_(Matrix::Zero(1, T::dimension)) {
Base(model, key),
prior_((Vector(1) << prior).finished()),
indices_(1, idx) {
assert(model->dim() == 1);
this->fillH();
}

/** Indices Constructor: specify the mask with a set of indices */
PartialPriorFactor(Key key, const std::vector<size_t>& mask, const Vector& prior,
/** Indices Constructor: Specify the relevant measured indices in the tangent vector.*/
PartialPriorFactor(Key key, const std::vector<size_t>& indices, const Vector& prior,
const SharedNoiseModel& model) :
Base(model, key), prior_(prior), mask_(mask), H_(Matrix::Zero(mask.size(), T::dimension)) {
assert((size_t)prior_.size() == mask.size());
assert(model->dim() == (size_t) prior.size());
this->fillH();
Base(model, key),
prior_(prior),
indices_(indices) {
assert((size_t)prior_.size() == indices_.size());
assert(model->dim() == (size_t)prior.size());
}

/// @return a deep copy of this factor
Expand All @@ -102,35 +100,41 @@ namespace gtsam {
const This *e = dynamic_cast<const This*> (&expected);
return e != nullptr && Base::equals(*e, tol) &&
gtsam::equal_with_abs_tol(this->prior_, e->prior_, tol) &&
this->mask_ == e->mask_;
this->indices_ == e->indices_;
}

/** implement functions needed to derive from Factor */

/** vector of errors */
/** Returns a vector of errors for the measured tangent parameters. */
Vector evaluateError(const T& p, boost::optional<Matrix&> H = boost::none) const override {
if (H) (*H) = H_;
// FIXME: this was originally the generic retraction - may not produce same results
Vector full_logmap = T::Logmap(p);
// Vector full_logmap = T::identity().localCoordinates(p); // Alternate implementation
Vector masked_logmap = Vector::Zero(this->dim());
for (size_t i=0; i<mask_.size(); ++i)
masked_logmap(i) = full_logmap(mask_[i]);
return masked_logmap - prior_;
Eigen::Matrix<double, T::dimension, T::dimension> H_local;

// If the Rot3 Cayley map is used, Rot3::LocalCoordinates will throw a runtime error
// when asked to compute the Jacobian matrix (see Rot3M.cpp).
#ifdef GTSAM_ROT3_EXPMAP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but why would we do this for every possible T?
But I'll let this slip, as (a) the fix is not straightforward, (b) this is not an often used factor, and (c) GTSAM_ROT3_EXPMAP is the default...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm not sure I follow. Do you mean we just shouldn't use this factor if GTSAM_ROT3_EXPMAP is turned off?

const Vector full_tangent = T::LocalCoordinates(p, H ? &H_local : nullptr);
#else
const Vector full_tangent = T::Logmap(p, H ? &H_local : nullptr);
#endif

if (H) {
(*H) = Matrix::Zero(indices_.size(), T::dimension);
for (size_t i = 0; i < indices_.size(); ++i) {
(*H).row(i) = H_local.row(indices_.at(i));
}
}
// Select relevant parameters from the tangent vector.
Vector partial_tangent = Vector::Zero(indices_.size());
for (size_t i = 0; i < indices_.size(); ++i) {
partial_tangent(i) = full_tangent(indices_.at(i));
}

return partial_tangent - prior_;
}

// access
const Vector& prior() const { return prior_; }
const std::vector<size_t>& mask() const { return mask_; }
const Matrix& H() const { return H_; }

protected:

/** Constructs the jacobian matrix in place */
void fillH() {
for (size_t i=0; i<mask_.size(); ++i)
H_(i, mask_[i]) = 1.0;
}
const std::vector<size_t>& indices() const { return indices_; }

private:
/** Serialization function */
Expand All @@ -140,8 +144,8 @@ namespace gtsam {
ar & boost::serialization::make_nvp("NoiseModelFactor1",
boost::serialization::base_object<Base>(*this));
ar & BOOST_SERIALIZATION_NVP(prior_);
ar & BOOST_SERIALIZATION_NVP(mask_);
ar & BOOST_SERIALIZATION_NVP(H_);
ar & BOOST_SERIALIZATION_NVP(indices_);
// ar & BOOST_SERIALIZATION_NVP(H_);
}
}; // \class PartialPriorFactor

Expand Down
Loading