2009-08-29 09:24:26 +08:00
|
|
|
/**
|
|
|
|
* @file Pose2.cpp
|
|
|
|
* @brief 2D Pose
|
|
|
|
*/
|
|
|
|
|
2010-08-20 01:23:19 +08:00
|
|
|
#include <gtsam/geometry/Pose2.h>
|
|
|
|
#include <gtsam/base/Lie-inl.h>
|
2009-08-29 09:24:26 +08:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
namespace gtsam {
|
|
|
|
|
2010-01-10 07:15:06 +08:00
|
|
|
/** Explicit instantiation of base class to export members */
|
2010-01-16 09:16:59 +08:00
|
|
|
INSTANTIATE_LIE(Pose2);
|
2010-01-10 07:15:06 +08:00
|
|
|
|
2010-02-28 17:10:39 +08:00
|
|
|
static const Matrix I3 = eye(3), Z12 = zeros(1,2);
|
Rot2 "named constructors" (http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8):
/** Named constructor from angle == exponential map at identity */
static Rot2 fromAngle(double theta) { return Rot2(cos(theta),sin(theta));}
/** Named constructor from cos(theta),sin(theta) pair, will *not* normalize! */
static Rot2 fromCosSin(double c, double s);
/** Named constructor that behaves as atan2, i.e., y,x order (!) and normalizes */
static Rot2 atan2(double y, double x);
which are a bit more verbose, but at least won't cause hours of bug-tracking :-(
I also made most compose, inverse, and rotate functions into methods, with the global functions mostly calling the methods. Methods have privileged access to members and hence are typically much more readable.
2010-03-03 11:31:53 +08:00
|
|
|
static const Rot2 R_PI_2(Rot2::fromCosSin(0., 1.));
|
2010-02-24 14:14:02 +08:00
|
|
|
|
2010-02-19 00:27:01 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
Matrix Pose2::matrix() const {
|
|
|
|
Matrix R = r_.matrix();
|
2010-02-28 17:10:39 +08:00
|
|
|
R = stack(2, &R, &Z12);
|
2010-02-19 00:27:01 +08:00
|
|
|
Matrix T = Matrix_(3,1, t_.x(), t_.y(), 1.0);
|
|
|
|
return collect(2, &R, &T);
|
|
|
|
}
|
|
|
|
|
2009-12-15 08:00:02 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
void Pose2::print(const string& s) const {
|
|
|
|
cout << s << "(" << t_.x() << ", " << t_.y() << ", " << r_.theta() << ")" << endl;
|
|
|
|
}
|
2009-08-29 09:24:26 +08:00
|
|
|
|
2009-12-15 08:00:02 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
bool Pose2::equals(const Pose2& q, double tol) const {
|
|
|
|
return t_.equals(q.t_, tol) && r_.equals(q.r_, tol);
|
|
|
|
}
|
2009-08-29 09:24:26 +08:00
|
|
|
|
2010-02-28 17:10:39 +08:00
|
|
|
/* ************************************************************************* */
|
Rot2 "named constructors" (http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8):
/** Named constructor from angle == exponential map at identity */
static Rot2 fromAngle(double theta) { return Rot2(cos(theta),sin(theta));}
/** Named constructor from cos(theta),sin(theta) pair, will *not* normalize! */
static Rot2 fromCosSin(double c, double s);
/** Named constructor that behaves as atan2, i.e., y,x order (!) and normalizes */
static Rot2 atan2(double y, double x);
which are a bit more verbose, but at least won't cause hours of bug-tracking :-(
I also made most compose, inverse, and rotate functions into methods, with the global functions mostly calling the methods. Methods have privileged access to members and hence are typically much more readable.
2010-03-03 11:31:53 +08:00
|
|
|
|
|
|
|
#ifdef SLOW_BUT_CORRECT_EXPMAP
|
|
|
|
|
|
|
|
template<> Pose2 expmap(const Vector& xi) {
|
|
|
|
Point2 v(xi(0),xi(1));
|
|
|
|
double w = xi(2);
|
|
|
|
if (fabs(w) < 1e-5)
|
|
|
|
return Pose2(xi[0], xi[1], xi[2]);
|
|
|
|
else {
|
|
|
|
Rot2 R(Rot2::fromAngle(w));
|
|
|
|
Point2 v_ortho = R_PI_2 * v; // points towards rot center
|
|
|
|
Point2 t = (v_ortho - rotate(R,v_ortho)) / w;
|
|
|
|
return Pose2(R, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector logmap(const Pose2& p) {
|
|
|
|
const Rot2& R = p.r();
|
|
|
|
const Point2& t = p.t();
|
|
|
|
double w = R.theta();
|
|
|
|
if (fabs(w) < 1e-5)
|
|
|
|
return Vector_(3, t.x(), t.y(), w);
|
|
|
|
else {
|
|
|
|
double c_1 = R.c()-1.0, s = R.s();
|
|
|
|
double det = c_1*c_1 + s*s;
|
|
|
|
Point2 p = R_PI_2 * (unrotate(R, t) - t);
|
|
|
|
Point2 v = (w/det) * p;
|
|
|
|
return Vector_(3, v.x(), v.y(), w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
template<> Pose2 expmap(const Vector& v) {
|
|
|
|
return Pose2(v[0], v[1], v[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector logmap(const Pose2& p) {
|
|
|
|
return Vector_(3, p.x(), p.y(), p.theta());
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* ************************************************************************* */
|
2010-02-28 17:10:39 +08:00
|
|
|
// Calculate Adjoint map
|
|
|
|
// Ad_pose is 3*3 matrix that when applied to twist xi, returns Ad_pose(xi)
|
|
|
|
Matrix AdjointMap(const Pose2& p) {
|
|
|
|
const Rot2 R = p.r();
|
|
|
|
const Point2 t = p.t();
|
|
|
|
double c = R.c(), s = R.s(), x = t.x(), y = t.y();
|
|
|
|
return Matrix_(3,3,
|
|
|
|
c, -s, y,
|
|
|
|
s, c, -x,
|
|
|
|
0.0, 0.0, 1.0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
Pose2 inverse(const Pose2& pose) {
|
|
|
|
const Rot2& R = pose.r();
|
|
|
|
const Point2& t = pose.t();
|
|
|
|
return Pose2(inverse(R), R.unrotate(Point2(-t.x(), -t.y())));
|
|
|
|
}
|
|
|
|
|
|
|
|
Matrix Dinverse(const Pose2& pose) {
|
|
|
|
return -AdjointMap(pose);
|
|
|
|
}
|
|
|
|
|
2009-12-15 08:00:02 +08:00
|
|
|
/* ************************************************************************* */
|
2010-03-02 10:27:09 +08:00
|
|
|
// see doc/math.lyx, SE(2) section
|
2010-01-12 10:08:41 +08:00
|
|
|
Point2 transform_to(const Pose2& pose, const Point2& point, boost::optional<
|
|
|
|
Matrix&> H1, boost::optional<Matrix&> H2) {
|
|
|
|
const Rot2& R = pose.r();
|
|
|
|
Point2 d = point - pose.t();
|
|
|
|
Point2 q = R.unrotate(d);
|
|
|
|
if (!H1 && !H2) return q;
|
|
|
|
if (H1) *H1 = Matrix_(2, 3,
|
|
|
|
-1.0, 0.0, q.y(),
|
|
|
|
0.0, -1.0, -q.x());
|
|
|
|
if (H2) *H2 = R.transpose();
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
2010-03-02 10:27:09 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
// see doc/math.lyx, SE(2) section
|
2009-12-09 03:12:20 +08:00
|
|
|
|
2010-05-24 13:40:59 +08:00
|
|
|
Pose2 compose(const Pose2& p1, const Pose2& p2, boost::optional<Matrix&> H1,
|
|
|
|
boost::optional<Matrix&> H2) {
|
|
|
|
// TODO: inline and reuse?
|
|
|
|
if(H1) *H1 = AdjointMap(inverse(p2));
|
|
|
|
if(H2) *H2 = I3;
|
|
|
|
return p1*p2;
|
|
|
|
}
|
|
|
|
|
2010-02-26 18:55:41 +08:00
|
|
|
Matrix Dcompose1(const Pose2& p1, const Pose2& p2) {
|
2010-02-28 17:10:39 +08:00
|
|
|
return AdjointMap(inverse(p2));
|
2010-02-26 18:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Matrix Dcompose2(const Pose2& p1, const Pose2& p2) {
|
2010-02-28 17:10:39 +08:00
|
|
|
return I3;
|
2010-02-26 18:55:41 +08:00
|
|
|
}
|
|
|
|
|
2010-02-28 16:51:43 +08:00
|
|
|
/* ************************************************************************* */
|
2010-03-01 09:33:45 +08:00
|
|
|
// see doc/math.lyx, SE(2) section
|
|
|
|
Point2 transform_from(const Pose2& pose, const Point2& p,
|
2010-02-28 16:51:43 +08:00
|
|
|
boost::optional<Matrix&> H1, boost::optional<Matrix&> H2) {
|
2010-03-01 09:33:45 +08:00
|
|
|
const Rot2& rot = pose.r();
|
|
|
|
const Point2 q = rot * p;
|
|
|
|
if (H1 || H2) {
|
|
|
|
const Matrix R = rot.matrix();
|
|
|
|
const Matrix Drotate1 = Matrix_(2, 1, -q.y(), q.x());
|
|
|
|
if (H1) *H1 = collect(2, &R, &Drotate1); // [R R_{pi/2}q]
|
|
|
|
if (H2) *H2 = R; // R
|
|
|
|
}
|
|
|
|
return q + pose.t();
|
2010-02-28 16:51:43 +08:00
|
|
|
}
|
|
|
|
|
2009-12-15 08:00:02 +08:00
|
|
|
/* ************************************************************************* */
|
2010-01-16 09:16:59 +08:00
|
|
|
Pose2 between(const Pose2& p1, const Pose2& p2, boost::optional<Matrix&> H1,
|
|
|
|
boost::optional<Matrix&> H2) {
|
2010-02-24 14:14:02 +08:00
|
|
|
// get cosines and sines from rotation matrices
|
|
|
|
const Rot2& R1 = p1.r(), R2 = p2.r();
|
|
|
|
double c1=R1.c(), s1=R1.s(), c2=R2.c(), s2=R2.s();
|
|
|
|
|
|
|
|
// Calculate delta rotation = between(R1,R2)
|
|
|
|
double c = c1 * c2 + s1 * s2, s = -s1 * c2 + c1 * s2;
|
2010-03-03 13:34:08 +08:00
|
|
|
Rot2 R(Rot2::atan2(s,c)); // normalizes
|
2010-02-24 14:14:02 +08:00
|
|
|
|
|
|
|
// Calculate delta translation = unrotate(R1, dt);
|
2010-01-16 09:16:59 +08:00
|
|
|
Point2 dt = p2.t() - p1.t();
|
2010-02-24 14:14:02 +08:00
|
|
|
double x = dt.x(), y = dt.y();
|
|
|
|
Point2 t(c1 * x + s1 * y, -s1 * x + c1 * y);
|
|
|
|
|
2010-02-28 17:10:39 +08:00
|
|
|
// FD: This is just -AdjointMap(between(p2,p1)) inlined and re-using above
|
2010-01-16 09:16:59 +08:00
|
|
|
if (H1) {
|
2010-02-24 14:14:02 +08:00
|
|
|
double dt1 = -s2 * x + c2 * y;
|
|
|
|
double dt2 = -c2 * x - s2 * y;
|
2010-02-26 12:03:31 +08:00
|
|
|
H1->resize(3,3);
|
|
|
|
double data[9] = {
|
2010-02-24 14:14:02 +08:00
|
|
|
-c, -s, dt1,
|
|
|
|
s, -c, dt2,
|
2010-02-26 12:03:31 +08:00
|
|
|
0.0, 0.0, -1.0};
|
|
|
|
copy(data, data+9, H1->data().begin());
|
2010-01-16 09:16:59 +08:00
|
|
|
}
|
2010-01-17 00:46:57 +08:00
|
|
|
if (H2) *H2 = I3;
|
2010-02-24 14:14:02 +08:00
|
|
|
|
|
|
|
return Pose2(R,t);
|
2010-01-16 09:16:59 +08:00
|
|
|
}
|
2009-12-09 03:12:20 +08:00
|
|
|
|
2010-01-16 09:16:59 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
Rot2 bearing(const Pose2& pose, const Point2& point) {
|
|
|
|
Point2 d = transform_to(pose, point);
|
|
|
|
return relativeBearing(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
Rot2 bearing(const Pose2& pose, const Point2& point,
|
|
|
|
boost::optional<Matrix&> H1, boost::optional<Matrix&> H2) {
|
|
|
|
if (!H1 && !H2) return bearing(pose, point);
|
2010-03-02 10:27:09 +08:00
|
|
|
Point2 d = transform_to(pose, point, H1, H2);
|
2010-01-16 09:16:59 +08:00
|
|
|
Matrix D_result_d;
|
|
|
|
Rot2 result = relativeBearing(d, D_result_d);
|
2010-03-02 10:27:09 +08:00
|
|
|
if (H1) *H1 = D_result_d * (*H1);
|
|
|
|
if (H2) *H2 = D_result_d * (*H2);
|
2010-01-16 09:16:59 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
double range(const Pose2& pose, const Point2& point) {
|
|
|
|
Point2 d = transform_to(pose, point);
|
|
|
|
return d.norm();
|
|
|
|
}
|
|
|
|
|
|
|
|
double range(const Pose2& pose, const Point2& point,
|
|
|
|
boost::optional<Matrix&> H1, boost::optional<Matrix&> H2) {
|
|
|
|
if (!H1 && !H2) return range(pose, point);
|
2010-03-02 10:27:09 +08:00
|
|
|
Point2 d = transform_to(pose, point, H1, H2);
|
2010-01-16 09:16:59 +08:00
|
|
|
double x = d.x(), y = d.y(), d2 = x * x + y * y, n = sqrt(d2);
|
|
|
|
Matrix D_result_d = Matrix_(1, 2, x / n, y / n);
|
2010-03-02 10:27:09 +08:00
|
|
|
if (H1) *H1 = D_result_d * (*H1);
|
|
|
|
if (H2) *H2 = D_result_d * (*H2);
|
2010-01-16 09:16:59 +08:00
|
|
|
return n;
|
|
|
|
}
|
2009-12-09 03:12:20 +08:00
|
|
|
|
2009-12-15 08:00:02 +08:00
|
|
|
/* ************************************************************************* */
|
2009-08-29 09:24:26 +08:00
|
|
|
} // namespace gtsam
|