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

Added Jacobians for Rot3::xyz and related conversions to euler angles #520

Merged
merged 2 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 106 additions & 20 deletions gtsam/geometry/Rot3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,21 +158,70 @@ Point3 Rot3::column(int index) const{
}

/* ************************************************************************* */
Vector3 Rot3::xyz() const {
Vector3 Rot3::xyz(OptionalJacobian<3, 3> H) const {
Matrix3 I;Vector3 q;
boost::tie(I,q)=RQ(matrix());
if (H) {
Matrix93 mH;
const auto m = matrix();
#ifdef GTSAM_USE_QUATERNIONS
SO3{m}.vec(mH);
#else
rot_.vec(mH);
#endif

Matrix39 qHm;
boost::tie(I, q) = RQ(m, qHm);
*H = qHm * mH;
Copy link
Member

Choose a reason for hiding this comment

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

So, this is of course perfectly fine. However, if you care about performance, this is (a) 81 mults, and (b) we could use of the fact that mH is super-sparse. In particular, SO3.cpp says

    mH << R * P3.block<3, 3>(0, 0), R * P3.block<3, 3>(3, 0),
        R * P3.block<3, 3>(6, 0);

Maybe add as a TODO. In fact I suspect the 3*3 matrix of derivatives might yield not too complicated symbolic expressions, and we do a lot of compute here (including multiplying a lot of things with zero). Might be worth some mathematica exploration.

} else
boost::tie(I, q) = RQ(matrix());
return q;
}

/* ************************************************************************* */
Vector3 Rot3::ypr() const {
Vector3 q = xyz();
Vector3 Rot3::ypr(OptionalJacobian<3, 3> H) const {
Vector3 q = xyz(H);
if (H) H->row(0).swap(H->row(2));
Copy link
Member

Choose a reason for hiding this comment

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

nice !


return Vector3(q(2),q(1),q(0));
}

/* ************************************************************************* */
Vector3 Rot3::rpy() const {
return xyz();
Vector3 Rot3::rpy(OptionalJacobian<3, 3> H) const { return xyz(H); }

/* ************************************************************************* */
double Rot3::roll(OptionalJacobian<1, 3> H) const {
double r;
if (H) {
Matrix3 xyzH;
r = xyz(xyzH)(0);
*H = xyzH.row(0);
} else
r = xyz()(0);
return r;
}

/* ************************************************************************* */
double Rot3::pitch(OptionalJacobian<1, 3> H) const {
double p;
if (H) {
Matrix3 xyzH;
p = xyz(xyzH)(1);
*H = xyzH.row(1);
} else
p = xyz()(1);
return p;
}

/* ************************************************************************* */
double Rot3::yaw(OptionalJacobian<1, 3> H) const {
double y;
if (H) {
Matrix3 xyzH;
y = xyz(xyzH)(2);
*H = xyzH.row(2);
} else
y = xyz()(2);
return y;
}

/* ************************************************************************* */
Expand Down Expand Up @@ -203,21 +252,58 @@ Matrix3 Rot3::LogmapDerivative(const Vector3& x) {
}

/* ************************************************************************* */
pair<Matrix3, Vector3> RQ(const Matrix3& A) {

double x = -atan2(-A(2, 1), A(2, 2));
Rot3 Qx = Rot3::Rx(-x);
Matrix3 B = A * Qx.matrix();

double y = -atan2(B(2, 0), B(2, 2));
Rot3 Qy = Rot3::Ry(-y);
Matrix3 C = B * Qy.matrix();

double z = -atan2(-C(1, 0), C(1, 1));
Rot3 Qz = Rot3::Rz(-z);
Matrix3 R = C * Qz.matrix();
pair<Matrix3, Vector3> RQ(const Matrix3& A, OptionalJacobian<3, 9> H) {
const double x = -atan2(-A(2, 1), A(2, 2));
const auto Qx = Rot3::Rx(-x).matrix();
const Matrix3 B = A * Qx;

const double y = -atan2(B(2, 0), B(2, 2));
const auto Qy = Rot3::Ry(-y).matrix();
const Matrix3 C = B * Qy;

const double z = -atan2(-C(1, 0), C(1, 1));
const auto Qz = Rot3::Rz(-z).matrix();
const Matrix3 R = C * Qz;

if (H) {
auto atan_d1 = [](double y, double x) { return x / (x * x + y * y); };
auto atan_d2 = [](double y, double x) { return -y / (x * x + y * y); };

const auto sx = -Qx(2, 1), cx = Qx(1, 1);
const auto sy = -Qy(0, 2), cy = Qy(0, 0);

*H = Matrix39::Zero();
// First, calculate the derivate of x
(*H)(0, 5) = atan_d1(A(2, 1), A(2, 2));
(*H)(0, 8) = atan_d2(A(2, 1), A(2, 2));

// Next, calculate the derivate of y. We have
// b20 = a20 and b22 = a21 * sx + a22 * cx
(*H)(1, 2) = -atan_d1(B(2, 0), B(2, 2));
const auto yHb22 = -atan_d2(B(2, 0), B(2, 2));
(*H)(1, 5) = yHb22 * sx;
(*H)(1, 8) = yHb22 * cx;

// Next, calculate the derivate of z. We have
// c20 = a10 * cy + a11 * sx * sy + a12 * cx * sy
// c22 = a11 * cx - a12 * sx
const auto c10Hx = (A(1, 1) * cx - A(1, 2) * sx) * sy;
const auto c10Hy = A(1, 2) * cx * cy + A(1, 1) * cy * sx - A(1, 0) * sy;
Vector9 c10HA = c10Hx * H->row(0) + c10Hy * H->row(1);
c10HA[1] = cy;
c10HA[4] = sx * sy;
c10HA[7] = cx * sy;

const auto c11Hx = -A(1, 2) * cx - A(1, 1) * sx;
Vector9 c11HA = c11Hx * H->row(0);
c11HA[4] = cx;
c11HA[7] = -sx;

H->block<1, 9>(2, 0) =
atan_d1(C(1, 0), C(1, 1)) * c10HA + atan_d2(C(1, 0), C(1, 1)) * c11HA;
}

Vector xyz = Vector3(x, y, z);
const auto xyz = Vector3(x, y, z);
return make_pair(R, xyz);
}

Expand Down
15 changes: 8 additions & 7 deletions gtsam/geometry/Rot3.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,43 +441,43 @@ namespace gtsam {
* Use RQ to calculate xyz angle representation
* @return a vector containing x,y,z s.t. R = Rot3::RzRyRx(x,y,z)
*/
Vector3 xyz() const;
Vector3 xyz(OptionalJacobian<3, 3> H = boost::none) const;

/**
* Use RQ to calculate yaw-pitch-roll angle representation
* @return a vector containing ypr s.t. R = Rot3::Ypr(y,p,r)
*/
Vector3 ypr() const;
Vector3 ypr(OptionalJacobian<3, 3> H = boost::none) const;

/**
* Use RQ to calculate roll-pitch-yaw angle representation
* @return a vector containing rpy s.t. R = Rot3::Ypr(y,p,r)
*/
Vector3 rpy() const;
Vector3 rpy(OptionalJacobian<3, 3> H = boost::none) const;

/**
* Accessor to get to component of angle representations
* NOTE: these are not efficient to get to multiple separate parts,
* you should instead use xyz() or ypr()
* TODO: make this more efficient
*/
inline double roll() const { return xyz()(0); }
double roll(OptionalJacobian<1, 3> H = boost::none) const;

/**
* Accessor to get to component of angle representations
* NOTE: these are not efficient to get to multiple separate parts,
* you should instead use xyz() or ypr()
* TODO: make this more efficient
*/
inline double pitch() const { return xyz()(1); }
double pitch(OptionalJacobian<1, 3> H = boost::none) const;

/**
* Accessor to get to component of angle representations
* NOTE: these are not efficient to get to multiple separate parts,
* you should instead use xyz() or ypr()
* TODO: make this more efficient
*/
inline double yaw() const { return xyz()(2); }
double yaw(OptionalJacobian<1, 3> H = boost::none) const;

/// @}
/// @name Advanced Interface
Expand Down Expand Up @@ -557,7 +557,8 @@ namespace gtsam {
* @return an upper triangular matrix R
* @return a vector [thetax, thetay, thetaz] in radians.
*/
GTSAM_EXPORT std::pair<Matrix3,Vector3> RQ(const Matrix3& A);
GTSAM_EXPORT std::pair<Matrix3, Vector3> RQ(
const Matrix3& A, OptionalJacobian<3, 9> H = boost::none);

template<>
struct traits<Rot3> : public internal::LieGroup<Rot3> {};
Expand Down
94 changes: 94 additions & 0 deletions gtsam/geometry/tests/testRot3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,100 @@ TEST(Rot3, Ypr_derivative) {
CHECK(assert_equal(num_r, act_r));
}

/* ************************************************************************* */
Vector3 RQ_proxy(Matrix3 const& R) {
const auto RQ_ypr = RQ(R);
return RQ_ypr.second;
}

TEST(Rot3, RQ_derivative) {
const auto aa = Vector3{1.0, 0.7, 0.8};
const auto R = Rot3::Expmap(aa).matrix();
const auto num = numericalDerivative11(RQ_proxy, R);
Matrix39 calc;
RQ(R, calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
Vector3 xyz_proxy(Rot3 const& R) { return R.xyz(); }

TEST(Rot3, xyz_derivative) {
const auto aa = Vector3{-0.6, 0.3, 0.2};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(xyz_proxy, R);
Matrix3 calc;
R.xyz(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
Vector3 ypr_proxy(Rot3 const& R) { return R.ypr(); }

TEST(Rot3, ypr_derivative) {
const auto aa = Vector3{0.1, -0.3, -0.2};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(ypr_proxy, R);
Matrix3 calc;
R.ypr(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
Vector3 rpy_proxy(Rot3 const& R) { return R.rpy(); }

TEST(Rot3, rpy_derivative) {
const auto aa = Vector3{1.2, 0.3, -0.9};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(rpy_proxy, R);
Matrix3 calc;
R.rpy(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
double roll_proxy(Rot3 const& R) { return R.roll(); }

TEST(Rot3, roll_derivative) {
const auto aa = Vector3{0.8, -0.8, 0.8};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(roll_proxy, R);
Matrix13 calc;
R.roll(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
double pitch_proxy(Rot3 const& R) { return R.pitch(); }

TEST(Rot3, pitch_derivative) {
const auto aa = Vector3{0.01, 0.1, 0.0};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(pitch_proxy, R);
Matrix13 calc;
R.pitch(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
double yaw_proxy(Rot3 const& R) { return R.yaw(); }

TEST(Rot3, yaw_derivative) {
const auto aa = Vector3{0.0, 0.1, 0.6};
const auto R = Rot3::Expmap(aa);
const auto num = numericalDerivative11(yaw_proxy, R);
Matrix13 calc;
R.yaw(calc);

CHECK(assert_equal(num, calc));
}

/* ************************************************************************* */
int main() {
TestResult tr;
Expand Down