// Copyright (c) 2017, Joseph Mirabel // Authors: Joseph Mirabel (joseph.mirabel@laas.fr) // // This file is part of hpp-core. // hpp-core is free software: you can redistribute it // and/or modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation, either version // 3 of the License, or (at your option) any later version. // // hpp-core is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied warranty // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Lesser Public License for more details. You should have // received a copy of the GNU Lesser General Public License along with // hpp-core. If not, see . #define BOOST_TEST_MODULE ExplicitRelativeTransformationTest #include #include #include #include <../tests/util.hh> // Force benchmark output #define HPP_ENABLE_BENCHMARK 1 #include #include #include #include #include #include #include #include #include #include #include #include using namespace hpp::pinocchio; using namespace hpp::constraints; using namespace hpp::core; #define NB_RANDOM_CONF 2 const value_type test_precision = 1e-8; DevicePtr_t createRobot () { //DevicePtr_t robot = unittest::makeDevice(unittest::HumanoidRomeo, "romeo"); DevicePtr_t robot (Device::create ("2-objects")); urdf::loadModel (robot, 0, "obj1/", "freeflyer", "file://" DATA_DIR "/empty.urdf", ""); robot->controlComputation((Computation_t) (JOINT_POSITION | JACOBIAN)); robot->rootJoint()->lowerBound (0, -10); robot->rootJoint()->lowerBound (1, -10); robot->rootJoint()->lowerBound (2, -10); robot->rootJoint()->upperBound (0, 10); robot->rootJoint()->upperBound (1, 10); robot->rootJoint()->upperBound (2, 10); /// Add a freeflyer at the end. urdf::loadModel (robot, 0, "obj2/", "freeflyer", "file://" DATA_DIR "/empty.urdf", ""); JointPtr_t rj = robot->getJointByName("obj2/root_joint"); rj->lowerBound (0, -10); rj->lowerBound (1, -10); rj->lowerBound (2, -10); rj->upperBound (0, 10); rj->upperBound (1, 10); rj->upperBound (2, 10); return robot; } template inline typename T::template NRowsBlockXpr<3>::Type trans(const Eigen::MatrixBase& j) { return const_cast&>(j).derived().template topRows <3>(); } template inline typename T::template NRowsBlockXpr<3>::Type omega(const Eigen::MatrixBase& j) { return const_cast&>(j).derived().template bottomRows<3>(); } BOOST_AUTO_TEST_CASE (two_freeflyers) { DevicePtr_t robot = createRobot(); BOOST_REQUIRE (robot); ConfigurationShooterPtr_t cs = configurationShooter::Uniform::create(robot); JointPtr_t object2 = robot->getJointByName("obj2/root_joint"); JointPtr_t object1 = robot->getJointByName("obj1/root_joint"); Transform3f M2inO2 (Transform3f::Identity()); Transform3f M1inO1 (Transform3f::Identity()); ExplicitPtr_t enm (explicit_::RelativePose::create ("explicit_relative_transformation", robot, object1, object2, M1inO1, M2inO2)); DifferentiableFunctionPtr_t ert (enm->explicitFunction ()); Configuration_t q = robot->currentConfiguration (), qrand = *cs->shoot(), qout = qrand; // Check the output value Eigen::RowBlockIndices outConf (enm->outputConf()); Eigen::RowBlockIndices inConf (enm-> inputConf()); // Compute position of object2 by solving explicit constraints LiegroupElement q_obj2 (ert->outputSpace ()); vector_t q_obj1 (inConf.rview(qout).eval()); ert->value (q_obj2, q_obj1); outConf.lview(qout) = q_obj2.vector (); // Test that at solution configuration, object2 and robot frames coincide. robot->currentConfiguration(qout); robot->computeForwardKinematics(); Transform3f diff = M1inO1.inverse() * object1->currentTransformation().inverse() * object2->currentTransformation() * M2inO2; BOOST_CHECK (diff.isIdentity()); // Check Jacobian of implicit numerical constraints by finite difference // value_type dt (1e-5); Configuration_t q0 (qout); vector_t v (robot->numberDof ()); matrix_t J (6, enm->function ().inputDerivativeSize()); LiegroupElement value0 (enm->function ().outputSpace ()), value (enm->function ().outputSpace ()); enm->function ().value (value0, q0); enm->function ().jacobian (J, q0); std::cout << "J=" << std::endl << J << std::endl; std::cout << "q0=" << q0.transpose () << std::endl; // First at solution configuration for (size_type i=0; i<12; ++i) { // test canonical basis vectors v.setZero (); v [i] = 1; integrate (robot, q0, dt * v, q); enm->function ().value (value, q); vector_t df ((value - value0)/dt); vector_t Jdq (J * v); std::cout << "v=" << v.transpose () << std::endl; std::cout << "q=" << q.transpose () << std::endl; std::cout << "df=" << df.transpose () << std::endl; std::cout << "Jdq=" << Jdq.transpose () << std::endl; std::cout << "||Jdq - df ||=" << (df - Jdq).norm () << std::endl << std::endl; BOOST_CHECK ((df - Jdq).norm () < 1e-4); } // Second at random configurations for (size_type i=0; ishoot(); enm->function ().jacobian (J, q0); std::cout << "J=" << std::endl << J << std::endl; std::cout << "q0=" << q0.transpose () << std::endl; enm->function ().value (value0, q0); enm->function ().jacobian (J, q0); for (size_type j=0; j<12; ++j) { v.setZero (); v [j] = 1; std::cout << "v=" << v.transpose () << std::endl; integrate (robot, q0, dt * v, q); std::cout << "q=" << q.transpose () << std::endl; enm->function ().value (value, q); vector_t df ((value - value0)/dt); vector_t Jdq (J * v); std::cout << "df=" << df.transpose () << std::endl; std::cout << "Jdq=" << Jdq.transpose () << std::endl; std::cout << "||Jdq - df ||=" << (df - Jdq).norm () << std::endl << std::endl; BOOST_CHECK ((df - Jdq).norm () < 1e-4); } std::cout << std::endl; } } BOOST_AUTO_TEST_CASE (two_frames_on_freeflyer) { DevicePtr_t robot = createRobot(); BOOST_REQUIRE (robot); ConfigurationShooterPtr_t cs = configurationShooter::Uniform::create(robot); JointPtr_t object2 = robot->getJointByName("obj2/root_joint"); JointPtr_t object1 = robot->getJointByName("obj1/root_joint"); Transform3f M2inO2 (Transform3f::Random ()); Transform3f M1inO1 (Transform3f::Random ()); std::cout << "M2inO2=" << M2inO2 << std::endl; std::cout << "M1inO1=" << M1inO1 << std::endl; ExplicitPtr_t enm (explicit_::RelativePose::create ("explicit_relative_transformation", robot, object1, object2, M1inO1, M2inO2)); DifferentiableFunctionPtr_t ert (enm->explicitFunction ()); Configuration_t q = robot->currentConfiguration (), qrand = *cs->shoot(), qout = qrand; // Check the output value Eigen::RowBlockIndices outConf (enm->outputConf()); Eigen::RowBlockIndices inConf (enm-> inputConf()); // Compute position of object2 by solving explicit constraints LiegroupElement q_obj2 (ert->outputSpace ()); vector_t q_obj1 (inConf.rview(qout).eval()); ert->value (q_obj2, q_obj1); outConf.lview(qout) = q_obj2.vector (); // Test that at solution configuration, object2 and robot frames coincide. robot->currentConfiguration(qout); robot->computeForwardKinematics(); Transform3f diff = M1inO1.inverse() * object1->currentTransformation().inverse() * object2->currentTransformation() * M2inO2; BOOST_CHECK (diff.isIdentity()); // Check Jacobian of implicit numerical constraints by finite difference // value_type dt (1e-5); Configuration_t q0 (qout); vector_t v (robot->numberDof ()); matrix_t J (6, enm->function ().inputDerivativeSize()); LiegroupElement value0 (enm->function ().outputSpace ()), value (enm->function ().outputSpace ()); enm->function ().value (value0, q0); enm->function ().jacobian (J, q0); std::cout << "J=" << std::endl << J << std::endl; std::cout << "q0=" << q0.transpose () << std::endl; // First at solution configuration for (size_type i=0; i<12; ++i) { // test canonical basis vectors v.setZero (); v [i] = 1; integrate (robot, q0, dt * v, q); enm->function ().value (value, q); vector_t df ((value - value0)/dt); vector_t Jdq (J * v); std::cout << "v=" << v.transpose () << std::endl; std::cout << "q=" << q.transpose () << std::endl; std::cout << "df=" << df.transpose () << std::endl; std::cout << "Jdq=" << Jdq.transpose () << std::endl; std::cout << "||Jdq - df ||=" << (df - Jdq).norm () << std::endl << std::endl; BOOST_CHECK ((df - Jdq).norm () < 1e-4); } // Second at random configurations for (size_type i=0; ishoot(); enm->function ().jacobian (J, q0); std::cout << "J=" << std::endl << J << std::endl; std::cout << "q0=" << q0.transpose () << std::endl; enm->function ().value (value0, q0); enm->function ().jacobian (J, q0); for (size_type j=0; j<12; ++j) { v.setZero (); v [j] = 1; std::cout << "v=" << v.transpose () << std::endl; integrate (robot, q0, dt * v, q); std::cout << "q=" << q.transpose () << std::endl; enm->function ().value (value, q); vector_t df ((value - value0)/dt); vector_t Jdq (J * v); std::cout << "df=" << df.transpose () << std::endl; std::cout << "Jdq=" << Jdq.transpose () << std::endl; std::cout << "||Jdq - df ||=" << (df - Jdq).norm () << std::endl << std::endl; BOOST_CHECK ((df - Jdq).norm () < 1e-4); } std::cout << std::endl; } } BOOST_AUTO_TEST_CASE (compare_to_relative_transform) { DevicePtr_t robot = createRobot(); BOOST_REQUIRE (robot); ConfigurationShooterPtr_t cs = configurationShooter::Uniform::create(robot); JointPtr_t object2 = robot->getJointByName("obj2/root_joint"); JointPtr_t object1 = robot->getJointByName("obj1/root_joint"); Transform3f M2inO2 (Transform3f::Random ()); Transform3f M1inO1 (Transform3f::Random ()); BOOST_TEST_MESSAGE("M2inO2=" << M2inO2); BOOST_TEST_MESSAGE("M1inO1=" << M1inO1); ExplicitPtr_t enm (explicit_::RelativePose::create ("explicit_relative_transformation", robot, object1, object2, M1inO1, M2inO2)); DifferentiableFunctionPtr_t ert (enm->explicitFunction ()); DifferentiableFunctionPtr_t irt = enm->functionPtr(); RelativeTransformation::Ptr_t rt = RelativeTransformation::create ( "relative_transformation", robot, object1, object2, M1inO1, M2inO2); Configuration_t q = robot->currentConfiguration (), qrand = *cs->shoot(), qout = qrand; // Check the output value Eigen::RowBlockIndices outConf (enm->outputConf()); Eigen::RowBlockIndices inConf (enm-> inputConf()); // Compute position of object2 by solving explicit constraints LiegroupElement q_obj2 (ert->outputSpace ()); vector_t q_obj1 (inConf.rview(qout).eval()); ert->value (q_obj2, q_obj1); outConf.lview(qout) = q_obj2.vector (); // Test that at solution configuration, object2 and robot frames coincide. robot->currentConfiguration(qout); robot->computeForwardKinematics(); Transform3f diff = M1inO1.inverse() * object1->currentTransformation().inverse() * object2->currentTransformation() * M2inO2; BOOST_CHECK (diff.isIdentity()); // Check that value and Jacobian of relative transform from GenericTransformation // and from ExplicitRelativeTransformation are equal BOOST_CHECK_EQUAL (irt->inputSize(), rt->inputSize()); BOOST_CHECK_EQUAL (irt->inputDerivativeSize(), rt->inputDerivativeSize()); BOOST_CHECK_EQUAL (irt->outputSize(), rt->outputSize()); BOOST_CHECK_EQUAL (irt->outputDerivativeSize(), rt->outputDerivativeSize()); BOOST_CHECK_EQUAL (*irt->outputSpace(), *rt->outputSpace()); Configuration_t q0 (qout); LiegroupElement v0 (rt->outputSpace ()), v1 (v0), q_out (q_obj2), q_in (q_obj2); matrix_t J0 (rt->outputSpace()->nv(), rt->inputDerivativeSize()), J1 (J0); irt->value (v0, q0); irt->jacobian (J0, q0); rt ->value (v1, q0); rt ->jacobian (J1, q0); BOOST_CHECK (v0.vector().isZero(test_precision)); BOOST_CHECK (v1.vector().isZero(test_precision)); EIGEN_IS_APPROX (J0, J1, test_precision); // Second at random configurations for (size_type i=0; ishoot(); irt->value (v0, q0); irt->jacobian (J0, q0); rt ->value (v1, q0); rt ->jacobian (J1, q0); EIGEN_VECTOR_IS_APPROX (v0.vector(), v1.vector(), test_precision); EIGEN_IS_APPROX (J0, J1, test_precision); } }