| Directory: | ./ |
|---|---|
| File: | src/explicit/relative-transformation.cc |
| Date: | 2025-05-05 12:19:30 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 63 | 133 | 47.4% |
| Branches: | 82 | 436 | 18.8% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2017 - 2018, Joseph Mirabel | ||
| 2 | // Authors: Joseph Mirabel (joseph.mirabel@laas.fr) | ||
| 3 | // | ||
| 4 | |||
| 5 | // Redistribution and use in source and binary forms, with or without | ||
| 6 | // modification, are permitted provided that the following conditions are | ||
| 7 | // met: | ||
| 8 | // | ||
| 9 | // 1. Redistributions of source code must retain the above copyright | ||
| 10 | // notice, this list of conditions and the following disclaimer. | ||
| 11 | // | ||
| 12 | // 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | // notice, this list of conditions and the following disclaimer in the | ||
| 14 | // documentation and/or other materials provided with the distribution. | ||
| 15 | // | ||
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 20 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
| 27 | // DAMAGE. | ||
| 28 | |||
| 29 | #include <boost/serialization/utility.hpp> | ||
| 30 | #include <boost/serialization/vector.hpp> | ||
| 31 | #include <boost/serialization/weak_ptr.hpp> | ||
| 32 | #include <hpp/constraints/explicit/relative-transformation.hh> | ||
| 33 | #include <hpp/constraints/serialization.hh> | ||
| 34 | #include <hpp/pinocchio/device.hh> | ||
| 35 | #include <hpp/util/serialization.hh> | ||
| 36 | #include <pinocchio/serialization/se3.hpp> | ||
| 37 | #include <pinocchio/spatial/explog.hpp> | ||
| 38 | #include <pinocchio/spatial/skew.hpp> | ||
| 39 | |||
| 40 | #include "../serialization.hh" | ||
| 41 | |||
| 42 | namespace hpp { | ||
| 43 | namespace constraints { | ||
| 44 | namespace explicit_ { | ||
| 45 | namespace { | ||
| 46 | typedef JointJacobian_t::ConstNRowsBlockXpr<3>::Type ConstHalfJacobian_t; | ||
| 47 | ✗ | inline ConstHalfJacobian_t omega(const JointJacobian_t& j) { | |
| 48 | ✗ | return j.bottomRows<3>(); | |
| 49 | } | ||
| 50 | ✗ | inline ConstHalfJacobian_t trans(const JointJacobian_t& j) { | |
| 51 | ✗ | return j.topRows<3>(); | |
| 52 | } | ||
| 53 | |||
| 54 | 16 | void inputVariable(JointConstPtr_t joint, std::vector<bool>& conf, | |
| 55 | std::vector<bool>& vel) { | ||
| 56 |
5/6✓ Branch 1 taken 6 times.
✓ Branch 2 taken 16 times.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 16 times.
|
22 | while (joint && joint->index() != 0) { |
| 57 |
2/2✓ Branch 2 taken 42 times.
✓ Branch 3 taken 6 times.
|
48 | for (size_type i = 0; i < joint->configSize(); ++i) |
| 58 |
2/4✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
|
42 | conf[joint->rankInConfiguration() + i] = |
| 59 | 84 | !conf[joint->rankInConfiguration() + i]; | |
| 60 |
2/2✓ Branch 2 taken 36 times.
✓ Branch 3 taken 6 times.
|
42 | for (size_type i = 0; i < joint->numberDof(); ++i) |
| 61 |
2/4✓ Branch 6 taken 36 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 36 times.
✗ Branch 10 not taken.
|
36 | vel[joint->rankInVelocity() + i] = !vel[joint->rankInVelocity() + i]; |
| 62 | hppDout(info, "Adding joint " << joint->name() << " as input variable."); | ||
| 63 | 6 | joint = joint->parentJoint(); | |
| 64 | } | ||
| 65 | 16 | } | |
| 66 | |||
| 67 | 16 | BlockIndex::segments_t vectorOfBoolToIntervals(std::vector<bool>& v) { | |
| 68 | 16 | BlockIndex::segments_t ret; | |
| 69 |
2/2✓ Branch 1 taken 286 times.
✓ Branch 2 taken 16 times.
|
302 | for (std::size_t i = 0; i < v.size(); ++i) |
| 70 |
4/6✓ Branch 1 taken 286 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✓ Branch 5 taken 208 times.
✓ Branch 8 taken 78 times.
✗ Branch 9 not taken.
|
286 | if (v[i]) ret.push_back(BlockIndex::segment_t(i, 1)); |
| 71 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | BlockIndex::shrink(ret); |
| 72 | 16 | return ret; | |
| 73 | } | ||
| 74 | |||
| 75 | 8 | BlockIndex::segments_t jointConfInterval(JointConstPtr_t j) { | |
| 76 | return BlockIndex::segments_t( | ||
| 77 |
3/6✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
|
16 | 1, BlockIndex::segment_t(j->rankInConfiguration(), j->configSize())); |
| 78 | } | ||
| 79 | 8 | BlockIndex::segments_t jointVelInterval(JointConstPtr_t j) { | |
| 80 | return BlockIndex::segments_t( | ||
| 81 |
3/6✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
|
16 | 1, BlockIndex::segment_t(j->rankInVelocity(), j->numberDof())); |
| 82 | } | ||
| 83 | } // namespace | ||
| 84 | |||
| 85 | 8 | RelativeTransformationPtr_t RelativeTransformation::create( | |
| 86 | const std::string& name, const DevicePtr_t& robot, | ||
| 87 | const JointConstPtr_t& joint1, const JointConstPtr_t& joint2, | ||
| 88 | const Transform3s& frame1, const Transform3s& frame2) { | ||
| 89 |
2/4✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
|
8 | std::vector<bool> conf(robot->configSize(), false); |
| 90 |
2/4✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
|
8 | std::vector<bool> vel(robot->numberDof(), false); |
| 91 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | inputVariable(joint1, conf, vel); |
| 92 |
2/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
|
8 | inputVariable(joint2->parentJoint(), conf, vel); |
| 93 | |||
| 94 | RelativeTransformation* ptr = new RelativeTransformation( | ||
| 95 | name, robot, joint1, joint2, frame1, frame2, | ||
| 96 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
24 | vectorOfBoolToIntervals(conf), jointConfInterval(joint2), |
| 97 |
5/10✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
|
16 | vectorOfBoolToIntervals(vel), jointVelInterval(joint2)); |
| 98 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | RelativeTransformationPtr_t shPtr(ptr); |
| 99 | 8 | ptr->init(shPtr); | |
| 100 | 16 | return shPtr; | |
| 101 | 8 | } | |
| 102 | |||
| 103 | 8 | RelativeTransformation::RelativeTransformation( | |
| 104 | const std::string& name, const DevicePtr_t& robot, | ||
| 105 | const JointConstPtr_t& joint1, const JointConstPtr_t& joint2, | ||
| 106 | const Transform3s& frame1, const Transform3s& frame2, | ||
| 107 | const segments_t inConf, const segments_t outConf, const segments_t inVel, | ||
| 108 | 8 | const segments_t outVel, std::vector<bool> /*mask*/) | |
| 109 | : DifferentiableFunction(BlockIndex::cardinal(inConf), | ||
| 110 | BlockIndex::cardinal(inVel), | ||
| 111 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 | pinocchio::LiegroupSpace::SE3(), name), |
| 112 | 8 | robot_(robot), | |
| 113 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | parentJoint_(joint2->parentJoint()), |
| 114 | 8 | joint1_(joint1), | |
| 115 | 8 | joint2_(joint2), | |
| 116 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | frame1_(frame1), |
| 117 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | frame2_(frame2), |
| 118 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | inConf_(inConf), |
| 119 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | inVel_(inVel), |
| 120 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | outConf_(outConf), |
| 121 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | outVel_(outVel), |
| 122 |
9/18✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 8 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 8 times.
✗ Branch 24 not taken.
✓ Branch 26 taken 8 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 8 times.
✗ Branch 30 not taken.
|
32 | F1inJ1_invF2inJ2_(frame1 * frame2.inverse()) {} |
| 123 | |||
| 124 | 6800 | void RelativeTransformation::forwardKinematics(vectorIn_t arg) const { | |
| 125 |
1/2✓ Branch 4 taken 6800 times.
✗ Branch 5 not taken.
|
6800 | qsmall_ = inConf_.rview(robot_->currentConfiguration()); |
| 126 |
2/2✓ Branch 1 taken 199 times.
✓ Branch 2 taken 6601 times.
|
6800 | if (qsmall_ != arg) { |
| 127 | 199 | q_ = robot_->currentConfiguration(); | |
| 128 |
1/2✓ Branch 2 taken 199 times.
✗ Branch 3 not taken.
|
199 | inConf_.lview(q_) = arg; |
| 129 |
1/2✓ Branch 3 taken 199 times.
✗ Branch 4 not taken.
|
199 | robot_->currentConfiguration(q_); |
| 130 | } | ||
| 131 | 6800 | robot_->computeForwardKinematics(pinocchio::JOINT_POSITION); | |
| 132 | 6800 | } | |
| 133 | |||
| 134 | 6800 | void RelativeTransformation::impl_compute(LiegroupElementRef result, | |
| 135 | vectorIn_t argument) const { | ||
| 136 |
2/4✓ Branch 1 taken 6800 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6800 times.
✗ Branch 5 not taken.
|
6800 | forwardKinematics(argument); |
| 137 | |||
| 138 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 6800 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
6800 | bool hasParent = (parentJoint_ && parentJoint_->index() > 0); |
| 139 | |||
| 140 | // J1 * M1/J1 = J2 * M2/J2 | ||
| 141 | // J2 = J1 * M1/J1 * M2/J2^{-1} | ||
| 142 | // J2 = J2_{parent} * T | ||
| 143 | // T = J2_{parent}^{-1} * J2 | ||
| 144 | // T = J2_{parent}^{-1} * J1 * F1/J1 * F2/J2^{-1} | ||
| 145 |
1/2✓ Branch 1 taken 6800 times.
✗ Branch 2 not taken.
|
6800 | Transform3s freeflyerPose; |
| 146 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6800 times.
|
6800 | if (!joint1_) |
| 147 | ✗ | freeflyerPose = F1inJ1_invF2inJ2_; | |
| 148 | else | ||
| 149 |
3/6✓ Branch 2 taken 6800 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6800 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6800 times.
✗ Branch 9 not taken.
|
6800 | freeflyerPose = joint1_->currentTransformation() * F1inJ1_invF2inJ2_; |
| 150 | |||
| 151 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6800 times.
|
6800 | if (hasParent) |
| 152 | ✗ | freeflyerPose = parentJoint_->currentTransformation().actInv(freeflyerPose); | |
| 153 | |||
| 154 |
3/6✓ Branch 2 taken 6800 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6800 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6800 times.
✗ Branch 9 not taken.
|
6800 | freeflyerPose = joint2_->positionInParentFrame().actInv(freeflyerPose); |
| 155 | |||
| 156 | typedef Transform3s::Quaternion Q_t; | ||
| 157 |
3/6✓ Branch 1 taken 6800 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6800 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6800 times.
✗ Branch 9 not taken.
|
6800 | result.vector().head<3>() = freeflyerPose.translation(); |
| 158 |
4/8✓ Branch 1 taken 6800 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6800 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 6800 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 6800 times.
✗ Branch 13 not taken.
|
6800 | result.vector().tail<4>() = Q_t(freeflyerPose.rotation()).coeffs(); |
| 159 | 6800 | } | |
| 160 | |||
| 161 | ✗ | void RelativeTransformation::impl_jacobian(matrixOut_t jacobian, | |
| 162 | vectorIn_t arg) const { | ||
| 163 | ✗ | LiegroupElement result(outputSpace()); | |
| 164 | ✗ | impl_compute(result, arg); | |
| 165 | ✗ | Configuration_t q(robot_->currentConfiguration()); | |
| 166 | ✗ | outConf_.lview(q) = result.vector(); | |
| 167 | ✗ | robot_->currentConfiguration(q); | |
| 168 | ✗ | robot_->computeForwardKinematics(pinocchio::JOINT_POSITION | | |
| 169 | pinocchio::JACOBIAN); | ||
| 170 | |||
| 171 | ✗ | bool absolute = !joint1_; | |
| 172 | ✗ | bool hasParent = (parentJoint_ && parentJoint_->index() > 0); | |
| 173 | |||
| 174 | ✗ | static const JointJacobian_t Jabs; | |
| 175 | ✗ | const JointJacobian_t& J1(absolute ? Jabs : joint1_->jacobian()); | |
| 176 | // const JointJacobian_t& J2_parent (parentJoint_->jacobian()); | ||
| 177 | |||
| 178 | ✗ | const matrix3_t& R1(absolute ? matrix3_t::Identity().eval() | |
| 179 | ✗ | : joint1_->currentTransformation().rotation()); | |
| 180 | ✗ | const matrix3_t& R2(joint2_->currentTransformation().rotation()); | |
| 181 | const matrix3_t& R2_inParentFrame( | ||
| 182 | ✗ | joint2_->positionInParentFrame().rotation()); | |
| 183 | |||
| 184 | const vector3_t& t1(absolute | ||
| 185 | ✗ | ? vector3_t::Zero().eval() | |
| 186 | ✗ | : joint1_->currentTransformation().translation()); | |
| 187 | |||
| 188 | matrix3_t cross1 = ::pinocchio::skew( | ||
| 189 | ✗ | (R1 * F1inJ1_invF2inJ2_.translation()).eval()), | |
| 190 | ✗ | cross2; | |
| 191 | ✗ | if (hasParent) { | |
| 192 | const vector3_t& t2_parent( | ||
| 193 | ✗ | parentJoint_->currentTransformation().translation()); | |
| 194 | ✗ | cross2 = ::pinocchio::skew((t2_parent - t1).eval()); | |
| 195 | |||
| 196 | ✗ | if (absolute) | |
| 197 | ✗ | J2_parent_minus_J1_.noalias() = parentJoint_->jacobian(); | |
| 198 | else | ||
| 199 | ✗ | J2_parent_minus_J1_.noalias() = parentJoint_->jacobian() - J1; | |
| 200 | } else { | ||
| 201 | ✗ | cross2 = -::pinocchio::skew(t1); | |
| 202 | // J2_parent_minus_J1_ = - J1; | ||
| 203 | } | ||
| 204 | |||
| 205 | // Express velocity of J1 * M1/J1 * M2/J2^{-1} in J2_{parent}. | ||
| 206 | ✗ | if (hasParent) { | |
| 207 | const matrix3_t& R2_parent( | ||
| 208 | ✗ | parentJoint_->currentTransformation().rotation()); | |
| 209 | ✗ | const JointJacobian_t& J2_parent(parentJoint_->jacobian()); | |
| 210 | |||
| 211 | ✗ | tmpJac_.noalias() = | |
| 212 | ✗ | (R2_inParentFrame.transpose() * R2_parent.transpose()) * | |
| 213 | ✗ | (cross1 * (omega(J2_parent_minus_J1_))-cross2 * omega(J2_parent) - | |
| 214 | ✗ | trans(J2_parent_minus_J1_)); | |
| 215 | ✗ | jacobian.topRows<3>() = inVel_.rview(tmpJac_); | |
| 216 | } else { | ||
| 217 | ✗ | if (absolute) | |
| 218 | ✗ | jacobian.topRows<3>().setZero(); | |
| 219 | else { | ||
| 220 | ✗ | tmpJac_.noalias() = | |
| 221 | ✗ | R2.transpose() * ((-cross1 * R1) * omega(J1) + R1 * trans(J1)); | |
| 222 | ✗ | jacobian.topRows<3>() = inVel_.rview(tmpJac_); | |
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | ✗ | if (hasParent) { | |
| 227 | const matrix3_t& R2_parent( | ||
| 228 | ✗ | parentJoint_->currentTransformation().rotation()); | |
| 229 | ✗ | const JointJacobian_t& J2_parent(parentJoint_->jacobian()); | |
| 230 | |||
| 231 | // J = p2RT2 * 0RTp2 * [ p2 | ||
| 232 | ✗ | tmpJac_.noalias() = (R2.transpose() * R2_parent) * omega(J2_parent); | |
| 233 | ✗ | if (!absolute) tmpJac_.noalias() -= (R2.transpose() * R1) * omega(J1); | |
| 234 | ✗ | jacobian.bottomRows<3>() = inVel_.rview(tmpJac_); | |
| 235 | } else { | ||
| 236 | ✗ | if (absolute) | |
| 237 | ✗ | jacobian.bottomRows<3>().setZero(); | |
| 238 | else { | ||
| 239 | ✗ | tmpJac_.noalias() = (R2.transpose() * R1) * omega(J1); | |
| 240 | ✗ | jacobian.bottomRows<3>() = inVel_.rview(tmpJac_); | |
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | template <class Archive> | ||
| 246 | ✗ | void RelativeTransformation::serialize(Archive& ar, | |
| 247 | const unsigned int version) { | ||
| 248 | (void)version; | ||
| 249 | ✗ | ar& boost::serialization::make_nvp( | |
| 250 | ✗ | "base", boost::serialization::base_object<DifferentiableFunction>(*this)); | |
| 251 | ✗ | ar& BOOST_SERIALIZATION_NVP(robot_); | |
| 252 | ✗ | internal::serialize_joint(ar, "parentJoint_", parentJoint_); | |
| 253 | ✗ | internal::serialize_joint(ar, "joint1_", joint1_); | |
| 254 | ✗ | internal::serialize_joint(ar, "joint2_", joint2_); | |
| 255 | ✗ | ar& BOOST_SERIALIZATION_NVP(frame1_); | |
| 256 | ✗ | ar& BOOST_SERIALIZATION_NVP(frame2_); | |
| 257 | |||
| 258 | ✗ | ar& BOOST_SERIALIZATION_NVP(inConf_); | |
| 259 | ✗ | ar& BOOST_SERIALIZATION_NVP(inVel_); | |
| 260 | ✗ | ar& BOOST_SERIALIZATION_NVP(outConf_); | |
| 261 | ✗ | ar& BOOST_SERIALIZATION_NVP(outVel_); | |
| 262 | ✗ | ar& BOOST_SERIALIZATION_NVP(F1inJ1_invF2inJ2_); | |
| 263 | ✗ | ar& BOOST_SERIALIZATION_NVP(weak_); | |
| 264 | } | ||
| 265 | |||
| 266 | HPP_SERIALIZATION_IMPLEMENT(RelativeTransformation); | ||
| 267 | } // namespace explicit_ | ||
| 268 | } // namespace constraints | ||
| 269 | } // namespace hpp | ||
| 270 | |||
| 271 | BOOST_CLASS_EXPORT_IMPLEMENT( | ||
| 272 | hpp::constraints::explicit_::RelativeTransformation) | ||
| 273 |