Skip to content

Commit b1a0765

Browse files
author
Paul Mitiguy
committed
Add tests for new functions.
1 parent fd5cf6c commit b1a0765

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

multibody/tree/quaternion_floating_mobilizer.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,10 @@ void QuaternionFloatingMobilizer<T>::DoMapAccelerationToQDDot(
511511
}
512512

513513
template <typename T>
514-
void DoMapQDDotToAcceleration(const systems::Context<T>& context,
515-
const Eigen::Ref<const VectorX<T>>& qddot,
516-
EigenPtr<VectorX<T>> vdot) {
514+
void QuaternionFloatingMobilizer<T>::DoMapQDDotToAcceleration(
515+
const systems::Context<T>& context,
516+
const Eigen::Ref<const VectorX<T>>& qddot,
517+
EigenPtr<VectorX<T>> vdot) const {
517518
// This function maps qddot to vdot by calculating v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
518519
// It first calculates the rotational part, then the translational part.
519520
//

multibody/tree/test/quaternion_floating_mobilizer_test.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,68 @@ TEST_F(QuaternionFloatingMobilizerTest, MapUsesNplus) {
365365
MatrixCompareType::relative));
366366
}
367367

368+
TEST_F(QuaternionFloatingMobilizerTest, MapAccelerationToQDDotAndViceVersa) {
369+
// Set an arbitrary non-zero state.
370+
const math::RollPitchYaw<double> rpy(M_PI / 3, -M_PI / 4, M_PI / 5);
371+
const Quaternion<double> q_FM = rpy.ToQuaternion();
372+
const Vector3<double> wxyz(5.4, -9.8, 3.2);
373+
const Vector3<double> vxyz(0.2, 0.5, -0.87);
374+
const Vector6<double> v(wxyz[0], wxyz[1], wxyz[2], vxyz[0], vxyz[1], vxyz[2]);
375+
mobilizer_->SetQuaternion(context_.get(), q_FM);
376+
mobilizer_->SetAngularVelocity(context_.get(), wxyz);
377+
mobilizer_->SetTranslationalVelocity(context_.get(), vxyz);
378+
379+
// Set an arbitrary v̇ and use MapAccelerationToQDDot() to calculate q̈.
380+
// Reminder: v̇ = [ẇx, ẇy, ẇz, v̇x, v̇y, v̇z]ᵀ
381+
const Vector6<double> vdot(0.3, -0.2, 0.9, -1.2, 3.1, -2.3);
382+
Eigen::Matrix<double, 7, 1> qddot;
383+
mobilizer_->MapAccelerationToQDDot(*context_, vdot, &qddot);
384+
385+
// Starting with the previous q̈, use MapQDDotToAcceleration() to calculate v̇.
386+
// Verify MapQDDotToAcceleration() is the inverse of MapAccelerationToQDDot().
387+
Vector6<double> vdot_redo;
388+
mobilizer_->MapQDDotToAcceleration(*context_, qddot, &vdot_redo);
389+
EXPECT_TRUE(CompareMatrices(vdot_redo, vdot, 16 * kTolerance,
390+
MatrixCompareType::relative));
391+
392+
// Compute the 7x6 N(q) matrix and its time-derivative Ṅ(q,q̇) that appear in
393+
// q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇
394+
MatrixX<double> N(7, 6), Ndot(7, 6);
395+
mobilizer_->CalcNMatrix(*context_, &N);
396+
mobilizer_->CalcNDotMatrix(*context_, &Ndot);
397+
398+
// Verify equivalence of q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇ and MapAccelerationToQDDot().
399+
const Eigen::Matrix<double, 7, 1> qddot_expected = Ndot * v + N * vdot;
400+
EXPECT_TRUE(CompareMatrices(qddot, qddot_expected, kTolerance,
401+
MatrixCompareType::relative));
402+
403+
// Compute the 6x7 N⁺(q) matrix and its time-derivative Ṅ⁺(q,q̇) that appear in
404+
// v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈
405+
MatrixX<double> Nplus(6, 7), Nplusdot(6, 7);
406+
mobilizer_->CalcNplusMatrix(*context_, &Nplus);
407+
mobilizer_->CalcNplusDotMatrix(*context_, &Nplusdot);
408+
409+
// Verify equivalence of v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈ and MapQDDotToAcceleration().
410+
Eigen::Matrix<double, 7, 1> qdot;
411+
mobilizer_->MapVelocityToQDot(*context_, v, &qdot);
412+
const Vector6<double> vdot_expected = Nplusdot * qdot + Nplus * qddot;
413+
EXPECT_TRUE(CompareMatrices(vdot_expected, vdot, 16 * kTolerance,
414+
MatrixCompareType::relative));
415+
416+
// Ensure the N⁺(q) matrix is the left pseudoinverse of the N(q) matrix.
417+
// In other words, ensure Nplus * N = [I₆₆] (6x6 identity matrix).
418+
MatrixX<double> Nplus_x_N = Nplus * N;
419+
EXPECT_TRUE(CompareMatrices(Nplus_x_N, MatrixX<double>::Identity(6, 6),
420+
kTolerance, MatrixCompareType::relative));
421+
422+
// Ensure the rotation (upper-left block) part of the N(q) matrix is
423+
// 0.25 times the transpose of the rotation part of the N⁺(q) matrix.
424+
MatrixX<double> N_rotational = N.block<4, 3>(0, 0);
425+
MatrixX<double> Nplus_rotational = Nplus.block<3, 4>(0, 0);
426+
EXPECT_TRUE(CompareMatrices(N_rotational.transpose(), 0.25 * Nplus_rotational,
427+
kTolerance, MatrixCompareType::relative));
428+
}
429+
368430
} // namespace
369431
} // namespace internal
370432
} // namespace multibody

0 commit comments

Comments
 (0)