Skip to content

Commit 8bb80dd

Browse files
author
Paul Mitiguy
committed
Add more tests.
1 parent 462c2a6 commit 8bb80dd

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

multibody/tree/quaternion_floating_mobilizer.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,15 +346,14 @@ class QuaternionFloatingMobilizer final : public MobilizerImpl<T, 7, 6> {
346346
// Many uses of Q_FM are associated with an angular velocity expressed in
347347
// a particular frame. The examples below show Q_FM used in conjunction
348348
// with w_FM_F (frame M's angular velocity in frame F, expressed in F).
349+
// Two other uses of Q_FM are for the rotational parts of this mobilizer's
350+
// N and Nplus matrices, namely as Nᵣ ≜ 0.5 Q_FM and Nᵣ⁺ ≜ 2 (Q_FM)ᵀ.
349351
//
350352
// q̇_FM = 0.5 * Q_FM * w_FM_F
351353
// q̈_FM = 0.5 * Q_FM * ẇ_FM_F - 0.25 ω² q_FM Note: ω² = |w_FM_F|²
352354
// w_FM_F = 2 * (Q_FM)ᵀ * q̇_FM
353355
// ẇ_FM_F = 2 * (Q_FM)ᵀ * q̈_FM
354356
//
355-
// Two other uses of Q_FM are Nᵣ ≜ 0.5 Q_FM and Nᵣ⁺ ≜ 2 (Q_FM)ᵀ, the
356-
// rotational parts of this mobilizer's N and Nplus matrices.
357-
//
358357
// @note Since the elements of the matrix returned by Q(q) depend linearly on
359358
// qw, qx, qy, qz, s * Q(q) = Q(s * q), where s is a scalar (e.g., 0.5 or 2).
360359
//

multibody/tree/test/quaternion_floating_mobilizer_test.cc

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,40 @@ TEST_F(QuaternionFloatingMobilizerTest, MapUsesNplus) {
375375
EXPECT_FALSE(std::abs(is_zero_if_proper_qdot) < 0.1);
376376
}
377377

378+
TEST_F(QuaternionFloatingMobilizerTest, MapVelocityToQDotAndViceVersa) {
379+
// Set an arbitrary non-zero state, but with a non-unit quaternion.
380+
const Quaternion<double> q_FM(0.7, 0.8, 0.9, -1.2); // Unnormalized.
381+
const Vector3<double> wxyz(5.4, -9.8, 3.2);
382+
const Vector3<double> vxyz(0.2, 0.5, -0.87);
383+
const Vector6<double> v(wxyz[0], wxyz[1], wxyz[2], vxyz[0], vxyz[1], vxyz[2]);
384+
mobilizer_->SetQuaternion(context_.get(), q_FM);
385+
mobilizer_->SetAngularVelocity(context_.get(), wxyz);
386+
mobilizer_->SetTranslationalVelocity(context_.get(), vxyz);
387+
388+
// Calculate the qdot associated with angular and translational velocity.
389+
Eigen::Matrix<double, 7, 1> qdot;
390+
mobilizer_->MapVelocityToQDot(*context_, v, &qdot);
391+
392+
// Check if q̇_FM (the rotational part of the calculated qdot) satisfies the
393+
// time-derivative of the quaternion constraint (q_FM)ᵀ * q_FM = constant, so
394+
// (q̇_FM)ᵀ * q_FM + (q_FM)ᵀ * q̇_FM = 2 * (q_FM)ᵀ * q̇_FM = 0. However, since
395+
// the qdot above is arbitrary, this is not true.
396+
const Vector4<double> qdot_FM(qdot[0], qdot[1], qdot[2], qdot[3]);
397+
const double is_zero_if_proper_qdot =
398+
Vector4<double>(q_FM.w(), q_FM.x(), q_FM.y(), q_FM.z()).dot(qdot_FM);
399+
EXPECT_TRUE(std::abs(is_zero_if_proper_qdot) < 16 * kTolerance);
400+
401+
// Use qdot to calculate its associated angular and translational velocity.
402+
Vector6<double> v_from_qdot;
403+
mobilizer_->MapQDotToVelocity(*context_, qdot, &v_from_qdot);
404+
EXPECT_TRUE(CompareMatrices(v, v_from_qdot, 16 * kTolerance,
405+
MatrixCompareType::relative));
406+
}
407+
378408
TEST_F(QuaternionFloatingMobilizerTest, MapAccelerationToQDDotAndViceVersa) {
379409
// Set an arbitrary non-zero state.
380410
const math::RollPitchYaw<double> rpy(M_PI / 3, -M_PI / 4, M_PI / 5);
381-
const Quaternion<double> q_FM = rpy.ToQuaternion();
411+
Quaternion<double> q_FM = rpy.ToQuaternion();
382412
const Vector3<double> wxyz(5.4, -9.8, 3.2);
383413
const Vector3<double> vxyz(0.2, 0.5, -0.87);
384414
const Vector6<double> v(wxyz[0], wxyz[1], wxyz[2], vxyz[0], vxyz[1], vxyz[2]);
@@ -435,6 +465,17 @@ TEST_F(QuaternionFloatingMobilizerTest, MapAccelerationToQDDotAndViceVersa) {
435465
MatrixX<double> Nplus_rotational = Nplus.block<3, 4>(0, 0);
436466
EXPECT_TRUE(CompareMatrices(N_rotational.transpose(), 0.25 * Nplus_rotational,
437467
kTolerance, MatrixCompareType::relative));
468+
469+
// Repeat these experiments with an unnormalized (non-unit) quaternion.
470+
// Use MapAccelerationToQDDot() to calculate q̈ and then use this calculated q̈
471+
// with MapQDDotToAcceleration() to calculate v̇.
472+
// Verify MapQDDotToAcceleration() is the inverse of MapAccelerationToQDDot().
473+
q_FM = Quaternion<double>(0.7, 0.8, 0.9, -1.2); // Unnormalized.
474+
mobilizer_->SetQuaternion(context_.get(), q_FM);
475+
mobilizer_->MapAccelerationToQDDot(*context_, vdot, &qddot);
476+
mobilizer_->MapQDDotToAcceleration(*context_, qddot, &vdot_redo);
477+
EXPECT_TRUE(CompareMatrices(vdot_redo, vdot, 16 * kTolerance,
478+
MatrixCompareType::relative));
438479
}
439480

440481
} // namespace

0 commit comments

Comments
 (0)