Optimizing Your Own Types¶
Python and C++ code of this example can be found at optimize_custom_type.py and optimize_custom_type.cpp respectively.
miniSAM already has build-in support for optimizing various commonly used manifold types in C++ and Python, including Eigen::Vector types in C++, numpy.array in Python, and Lie groups \(SO(2)\), \(SE(2)\), \(SO(3)\), \(SE(3)\) and \(Sim(3)\), which are commonly used in SLAM and robotics problems, provided by Sophus library in both C++ and Python.
In this page we see how to customize manifold properties of any C++ or Python class for miniSAM. The example is given by defining a miniSAM-optimizable 2D vector space \(\mathbb{R}^2\) type.
Python example¶
In Python this is done by defining manifold-related member functions, including
dim(self), function returns local manifold dimensionality. For \(\mathbb{R}^2\) the dimensionality is just 2.local(self, other), function returns local coordinate ofotherdefined at originself. For \(\mathbb{R}^2\) since the local manifold is itself, this function returnsother - self.retract(self, vec), given local coordinatevecdefined at originself, this function returns point projected on manifold ofvec. For \(\mathbb{R}^2\) since the local manifold is itself, this function returnsself + vec.__repr__(self), function printselfto a string.
The example code:
# 2D point optimizable in miniSAM
class Point2D(object):
# constructor
def __init__(self, x, y):
self.x_ = float(x)
self.y_ = float(y)
# print function
def __repr__(self):
return 'custom 2D point [' + str(self.x_) + ', ' + str(self.y_) + ']\''
# local coordinate dimension
def dim(self):
return 2
# map manifold point other to local coordinate
def local(self, other):
return np.array([other.x_ - self.x_, other.y_ - self.y_], dtype=np.float)
# apply changes in local coordinate to manifold, \oplus operator
def retract(self, vec):
return Point2D(self.x_ + vec[0], self.y_ + vec[1])
C++ example¶
In C++ we use a non-intrusive technique called traits, which is a specialization of template minisam::traits<> for the type we are adding manifold properties.
Using traits to define manifold properties has two major advantages:
- Optimizing a class without modifying it, or even without knowing details of implementation (e.g. optimization support for third-party C/C++ types).
- Making optimizing primitive types (like
double) possible.
The specialization of template minisam::traits<> needs following typedefs/static functions defined.
- typedef
manifold_tag, usemanifold_tagfor regular manifold types andlie_group_tagfor a Lie group types. - typedef
TangentVector, fixed or dynamic sizeEigen::Vectorwhich has size of manifold dimensionality. static Dim(), function returns local manifold dimensionality.static Local(), function returns local coordinate of input defined at origin.static Retract(), given local coordinate input defined at origin, this function returns point projected on manifold.static Print(), function to print tostd::ostream.
Given a C++ 2D vector space class Point2D
class Point2D {
private:
double x_, y_;
public:
// ctor
Point2D(double x, double y) : x_(x), y_(y) {}
// access
double x() const { return x_; }
double y() const { return y_; }
};
The minisam::traits<Point2D> specialization is defined by
// speialization of minisam::traits<Point2D>
namespace minisam {
template <>
struct traits<Point2D> {
// minisam type category tag, used for static checking
// must be manifold_tag or lie_group_tag to be optimizable
typedef manifold_tag type_category;
// print
static void Print(const Point2D& m, std::ostream& out = std::cout) {
out << "custom 2D point [" << m.x() << ", " << m.y() << "]'";
}
// tangent vector type defs
typedef Eigen::Matrix<double, 2, 1> TangentVector;
// local coordinate dimension
static constexpr size_t Dim(const Point2D&) { return 2; }
// map manifold point s to local coordinate
static TangentVector Local(const Point2D& origin, const Point2D& s) {
return Eigen::Matrix<double, 2, 1>(s.x() - origin.x(), s.y() - origin.y());
}
// apply changes in local coordinate to manifold, \oplus operator
static Point2D Retract(const Point2D& origin, const TangentVector& v) {
return Point2D(origin.x() + v[0], origin.y() + v[1]);
}
};
} // namespace minisam