diff --git a/include/core/state_block/state_homogeneous_3d.h b/include/core/state_block/state_homogeneous_3d.h index 99509303d95573811c52c9158b4ff7f5dcafcc3b..84011a2b8cce60be106c564abf75ad4ee593851c 100644 --- a/include/core/state_block/state_homogeneous_3d.h +++ b/include/core/state_block/state_homogeneous_3d.h @@ -37,6 +37,9 @@ class StateHomogeneous3d : public StateBlock void setIdentity(bool _notify = true) override; Eigen::VectorXd identity() const override; + Eigen::Vector3d point() const; + void setStatePoint(const Eigen::Vector3d&); + virtual void transform(const VectorComposite& _transformation) override; }; @@ -74,11 +77,20 @@ inline Eigen::VectorXd StateHomogeneous3d::Identity() return Eigen::Quaterniond::Identity().coeffs(); } +inline Eigen::Vector3d StateHomogeneous3d::point() const +{ + return state_.head<3>() / state_(3); +} + +inline void StateHomogeneous3d::setStatePoint(const Eigen::Vector3d& _p) +{ + state_ << _p, 1; +} + inline void StateHomogeneous3d::transform(const VectorComposite& _transformation) { if (transformable_) - setState((Eigen::Quaterniond(_transformation.at('O').data()) * Eigen::Quaterniond(getState().data())) - .coeffs()); // TODO is this correct?????? probably not!!! + setStatePoint(_transformation.at('P') + Eigen::Quaterniond(_transformation.at('O').data()) * point()); } } // namespace wolf diff --git a/include/core/state_block/state_quaternion.h b/include/core/state_block/state_quaternion.h index f5fb41a75ca1aa9091896abe1eb088f2d45a77c7..40f6ab0a4427003381aed3864094a74eab465906 100644 --- a/include/core/state_block/state_quaternion.h +++ b/include/core/state_block/state_quaternion.h @@ -45,7 +45,7 @@ inline StateQuaternion::StateQuaternion(const Eigen::Quaterniond& _quaternion, b : StateBlock("StateQuaternion", _quaternion.coeffs(), _fixed, - std::make_shared<LocalParametrizationQuaternion<DQ_LOCAL>>(), + std::make_shared<LocalParametrizationQuaternionLocal>(), _transformable) { state_.normalize(); diff --git a/test/gtest_state_block.cpp b/test/gtest_state_block.cpp index cf1aa928b6f351a6283e65fe70527e75ead23706..9f657911b7d5b729e1add83e62a2bbc097e17619 100644 --- a/test/gtest_state_block.cpp +++ b/test/gtest_state_block.cpp @@ -22,6 +22,7 @@ #include "core/state_block/state_block.h" #include "core/state_block/state_quaternion.h" +#include "core/state_block/state_homogeneous_3d.h" #include "core/state_block/state_angle.h" #include "core/state_block/state_block_derived.h" #include "core/composite/vector_composite.h" @@ -32,7 +33,8 @@ using namespace Eigen; using namespace std; using namespace wolf; -TEST(StateBlock, point_perturb) +// PERTURB ---------------------------------------------- +TEST(StatePoint3d, perturb) { Vector3d x(10, 20, 30); StatePoint3d sb(x); @@ -45,7 +47,7 @@ TEST(StateBlock, point_perturb) ASSERT_MATRIX_APPROX(x, sb.getState(), 0.5 * 4); // 4-sigma test... } -TEST(StateBlock, angle_perturb) +TEST(StateAngle, perturb) { Vector1d x(1.0); StateAngle sb(x(0)); @@ -58,7 +60,7 @@ TEST(StateBlock, angle_perturb) ASSERT_MATRIX_APPROX(x, sb.getState(), 0.1 * 4); // 4-sigma test... } -TEST(StateBlock, quaternion_perturb) +TEST(StateQuaternion, perturb) { Vector4d x(0.5, 0.5, 0.5, 0.5); StateQuaternion sb(x); @@ -71,6 +73,20 @@ TEST(StateBlock, quaternion_perturb) ASSERT_MATRIX_APPROX(x, sb.getState(), 0.01 * 4); // 4-sigma test... } +TEST(StateHomogeneous, perturb) +{ + Vector4d x(0.5, 0.5, 0.5, 0.5); + StateHomogeneous3d sb(x); + + sb.perturb(0.01); + + WOLF_INFO("original ", x.transpose(), ", perturbed = ", sb.getState().transpose()); + + ASSERT_LT((sb.getState().transpose() * x).norm(), 1.0); // quaternions are not parallel ==> not equal + ASSERT_MATRIX_APPROX(x, sb.getState(), 0.01 * 4); // 4-sigma test... +} + +// TRANSFORMABLE ---------------------------------------------- TEST(StatePoint3d, transformable) { Vector3d x(1, 2, 3); @@ -88,6 +104,7 @@ TEST(StatePoint3d, transformable) ASSERT_TRUE(sb_tr2.isTransformable()); } +// TRANSFORM ---------------------------------------------- TEST(StatePoint3d, transform_identity) { Vector3d x0(1, 2, 3); @@ -151,6 +168,104 @@ TEST(StatePoint3d, non_transformable_translation_rotation) ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15); } +TEST(StateQuaternion, transform_identity) +{ + Vector4d x0(0.5, 0.5, 0.5, 0.5); // Euler XYZ : 90º, 90º, 0º + StateQuaternion sb(x0); + + VectorComposite trf({{'P', Vector3d::Zero()}, {'O', Quaterniond::Identity().coeffs()}}); + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState(), x0, 1e-15); +} + +TEST(StateQuaternion, transform_translation) +{ + Vector4d x0(0.5, 0.5, 0.5, 0.5); // Euler XYZ : 90º, 90º, 0º + StateQuaternion sb(x0); + + VectorComposite trf({{'P', Vector3d(4, 5, 6)}, {'O', Quaterniond::Identity().coeffs()}}); + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState(), x0, 1e-15); +} + +TEST(StateQuaternion, transform_rotation_90x) +{ + Vector4d x0(0.5, 0.5, 0.5, 0.5); // Euler XYZ : 90º, 90º, 0º + StateQuaternion sb(x0); + + VectorComposite trf({{'P', Vector3d::Zero()}, {'O', (Vector4d() << sqrt(2) / 2, 0, 0, sqrt(2) / 2).finished()}}); + + Vector4d x(sqrt(2) / 2, 0, sqrt(2) / 2, 0); // x0 rotated (90,0,0): XYZ : 180º, 90º, 0º + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15); +} + +TEST(StateQuaternion, transform_translation_1y_rotation_90x) +{ + Vector4d x0(0.5, 0.5, 0.5, 0.5); // Euler XYZ : 90º, 90º, 0º + StateQuaternion sb(x0); + + VectorComposite trf({{'P', Vector3d(0, 1, 0)}, {'O', (Vector4d() << sqrt(2) / 2, 0, 0, sqrt(2) / 2).finished()}}); + + Vector4d x(sqrt(2) / 2, 0, sqrt(2) / 2, 0); // x0 rotated (90,0,0): XYZ : 180º, 90º, 0º + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15); +} + +TEST(StateHomogeneous3d, transform_identity) +{ + Vector4d x0(1, 2, 3, 1); + StateHomogeneous3d sb(x0); + + VectorComposite trf({{'P', Vector3d::Zero()}, {'O', Quaterniond::Identity().coeffs()}}); + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState().head<3>() / sb.getState()(3), x0.head<3>(), 1e-15); +} + +TEST(StateHomogeneous3d, transform_translation_4x5y6z) +{ + Vector4d x0(1, 2, 3, 1); + StateHomogeneous3d sb(x0); + + VectorComposite trf({{'P', Vector3d(4, 5, 6)}, {'O', Quaterniond::Identity().coeffs()}}); + + Vector3d x(5, 7, 9); // x0 translated (4,5,6) + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState().head<3>() / sb.getState()(3), x, 1e-15); +} + +TEST(StateHomogeneous3d, transform_rotation_90x) +{ + Vector4d x0(1, 2, 3, 1); + StateHomogeneous3d sb(x0); + + VectorComposite trf({{'P', Vector3d::Zero()}, {'O', (Vector4d() << sqrt(2) / 2, 0, 0, sqrt(2) / 2).finished()}}); + + Vector3d x(1, -3, 2); // x0 rotated (90,0,0) + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState().head<3>() / sb.getState()(3), x, 1e-15); +} + +TEST(StateHomogeneous3d, transform_translation_1y_rotation_90x) +{ + Vector4d x0(1, 2, 3, 1); + StateHomogeneous3d sb(x0); + + VectorComposite trf({{'P', Vector3d(0, 1, 0)}, {'O', (Vector4d() << sqrt(2) / 2, 0, 0, sqrt(2) / 2).finished()}}); + + Vector3d x(1, -2, 2); // x0 translated (0,1,0) and rotated (90,0,0) + + sb.transform(trf); + ASSERT_MATRIX_APPROX(sb.getState().head<3>() / sb.getState()(3), x, 1e-12); +} + TEST(StateVector3d, transform_translation_1y_rotation_90x) { Vector3d x0(1, 2, 3); @@ -203,6 +318,19 @@ TEST(StateParams4, transform_translation_1y_rotation_90x) ASSERT_MATRIX_APPROX(sb.getState(), x, 1e-15); } +TEST(StateHomogeneous3d, point) +{ + Vector4d x0(1, 2, 3, 1); + StateHomogeneous3d sb(x0); + + ASSERT_MATRIX_APPROX(sb.point(), x0.head<3>() / x0(3), 1e-15); + + sb.setStatePoint(Vector3d(3, 2, 1)); + + ASSERT_MATRIX_APPROX(sb.getState(), Vector4d(3, 2, 1, 1), 1e-15); + ASSERT_MATRIX_APPROX(sb.point(), Vector3d(3, 2, 1), 1e-15); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);