Merge pull request #520 from Eskilade/add_jacobians_Rot3_ypr
Added Jacobians for Rot3::xyz and related conversions to euler anglesrelease/4.3a0
commit
c45ba00a83
|
@ -158,21 +158,73 @@ Point3 Rot3::column(int index) const{
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector3 Rot3::xyz() const {
|
Vector3 Rot3::xyz(OptionalJacobian<3, 3> H) const {
|
||||||
Matrix3 I;Vector3 q;
|
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);
|
||||||
|
|
||||||
|
// TODO : Explore whether this expression can be optimized as both
|
||||||
|
// qHm and mH are super-sparse
|
||||||
|
*H = qHm * mH;
|
||||||
|
} else
|
||||||
|
boost::tie(I, q) = RQ(matrix());
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector3 Rot3::ypr() const {
|
Vector3 Rot3::ypr(OptionalJacobian<3, 3> H) const {
|
||||||
Vector3 q = xyz();
|
Vector3 q = xyz(H);
|
||||||
|
if (H) H->row(0).swap(H->row(2));
|
||||||
|
|
||||||
return Vector3(q(2),q(1),q(0));
|
return Vector3(q(2),q(1),q(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector3 Rot3::rpy() const {
|
Vector3 Rot3::rpy(OptionalJacobian<3, 3> H) const { return xyz(H); }
|
||||||
return xyz();
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
@ -203,21 +255,62 @@ Matrix3 Rot3::LogmapDerivative(const Vector3& x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
pair<Matrix3, Vector3> RQ(const Matrix3& A) {
|
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;
|
||||||
|
|
||||||
double x = -atan2(-A(2, 1), A(2, 2));
|
const double y = -atan2(B(2, 0), B(2, 2));
|
||||||
Rot3 Qx = Rot3::Rx(-x);
|
const auto Qy = Rot3::Ry(-y).matrix();
|
||||||
Matrix3 B = A * Qx.matrix();
|
const Matrix3 C = B * Qy;
|
||||||
|
|
||||||
double y = -atan2(B(2, 0), B(2, 2));
|
const double z = -atan2(-C(1, 0), C(1, 1));
|
||||||
Rot3 Qy = Rot3::Ry(-y);
|
const auto Qz = Rot3::Rz(-z).matrix();
|
||||||
Matrix3 C = B * Qy.matrix();
|
const Matrix3 R = C * Qz;
|
||||||
|
|
||||||
double z = -atan2(-C(1, 0), C(1, 1));
|
if (H) {
|
||||||
Rot3 Qz = Rot3::Rz(-z);
|
if (std::abs(y - M_PI / 2) < 1e-2)
|
||||||
Matrix3 R = C * Qz.matrix();
|
throw std::runtime_error(
|
||||||
|
"Rot3::RQ : Derivative undefined at singularity (gimbal lock)");
|
||||||
|
|
||||||
Vector xyz = Vector3(x, y, z);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto xyz = Vector3(x, y, z);
|
||||||
return make_pair(R, xyz);
|
return make_pair(R, xyz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -441,19 +441,19 @@ namespace gtsam {
|
||||||
* Use RQ to calculate xyz angle representation
|
* Use RQ to calculate xyz angle representation
|
||||||
* @return a vector containing x,y,z s.t. R = Rot3::RzRyRx(x,y,z)
|
* @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
|
* Use RQ to calculate yaw-pitch-roll angle representation
|
||||||
* @return a vector containing ypr s.t. R = Rot3::Ypr(y,p,r)
|
* @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
|
* Use RQ to calculate roll-pitch-yaw angle representation
|
||||||
* @return a vector containing rpy s.t. R = Rot3::Ypr(y,p,r)
|
* @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
|
* Accessor to get to component of angle representations
|
||||||
|
@ -461,7 +461,7 @@ namespace gtsam {
|
||||||
* you should instead use xyz() or ypr()
|
* you should instead use xyz() or ypr()
|
||||||
* TODO: make this more efficient
|
* 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
|
* Accessor to get to component of angle representations
|
||||||
|
@ -469,7 +469,7 @@ namespace gtsam {
|
||||||
* you should instead use xyz() or ypr()
|
* you should instead use xyz() or ypr()
|
||||||
* TODO: make this more efficient
|
* 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
|
* Accessor to get to component of angle representations
|
||||||
|
@ -477,7 +477,7 @@ namespace gtsam {
|
||||||
* you should instead use xyz() or ypr()
|
* you should instead use xyz() or ypr()
|
||||||
* TODO: make this more efficient
|
* TODO: make this more efficient
|
||||||
*/
|
*/
|
||||||
inline double yaw() const { return xyz()(2); }
|
double yaw(OptionalJacobian<1, 3> H = boost::none) const;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Advanced Interface
|
/// @name Advanced Interface
|
||||||
|
@ -557,7 +557,8 @@ namespace gtsam {
|
||||||
* @return an upper triangular matrix R
|
* @return an upper triangular matrix R
|
||||||
* @return a vector [thetax, thetay, thetaz] in radians.
|
* @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<>
|
template<>
|
||||||
struct traits<Rot3> : public internal::LieGroup<Rot3> {};
|
struct traits<Rot3> : public internal::LieGroup<Rot3> {};
|
||||||
|
|
|
@ -794,6 +794,122 @@ TEST(Rot3, Ypr_derivative) {
|
||||||
CHECK(assert_equal(num_r, act_r));
|
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) {
|
||||||
|
using VecAndErr = std::pair<Vector3, double>;
|
||||||
|
std::vector<VecAndErr> test_xyz;
|
||||||
|
// Test zeros and a couple of random values
|
||||||
|
test_xyz.push_back(VecAndErr{{0, 0, 0}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{0, 0.5, -0.5}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{0.3, 0, 0.2}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{-0.6, 1.3, 0}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{1.0, 0.7, 0.8}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{3.0, 0.7, -0.6}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{M_PI / 2, 0, 0}, error});
|
||||||
|
test_xyz.push_back(VecAndErr{{0, 0, M_PI / 2}, error});
|
||||||
|
|
||||||
|
// Test close to singularity
|
||||||
|
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1e-1, 0}, 1e-8});
|
||||||
|
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1e-1, 0}, 1e-8});
|
||||||
|
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1.1e-2, 0}, 1e-4});
|
||||||
|
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1.1e-2, 0}, 1e-4});
|
||||||
|
|
||||||
|
for (auto const& vec_err : test_xyz) {
|
||||||
|
auto const& xyz = vec_err.first;
|
||||||
|
|
||||||
|
const auto R = Rot3::RzRyRx(xyz).matrix();
|
||||||
|
const auto num = numericalDerivative11(RQ_proxy, R);
|
||||||
|
Matrix39 calc;
|
||||||
|
RQ(R, calc).second;
|
||||||
|
|
||||||
|
const auto err = vec_err.second;
|
||||||
|
CHECK(assert_equal(num, calc, err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
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() {
|
int main() {
|
||||||
TestResult tr;
|
TestResult tr;
|
||||||
|
|
Loading…
Reference in New Issue