Skip to content

Commit e871882

Browse files
author
Paul Mitiguy
committed
Partial update, responding to reviewer comments.
1 parent af76d64 commit e871882

File tree

2 files changed

+62
-39
lines changed

2 files changed

+62
-39
lines changed

multibody/tree/quaternion_floating_mobilizer.cc

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,9 @@ QuaternionFloatingMobilizer<T>::QuaternionRateToAngularVelocityMatrix(
314314
const Eigen::Matrix<T, 3, 4> NrPlus_q_unit = CalcTwoTimesQMatrixTranspose(
315315
{q_unit[0], q_unit[1], q_unit[2], q_unit[3]});
316316

317-
// Returns the matrix that when multiplied by q̇_FM produces
318-
// w_FM_F (M's angular velocity in F, expressed in F).
317+
// Returns the matrix that when multiplied by q̇_FM produces w_FM_F
318+
// (M's angular velocity in F, expressed in F). Note: When q_FM is a unit
319+
// quaternion (so q_norm = 1), the returned value is denoted Nᵣ⁺(q̂_FM).
319320
return NrPlus_q_unit * dqnorm_dq;
320321
}
321322

@@ -324,13 +325,11 @@ void QuaternionFloatingMobilizer<T>::DoCalcNMatrix(
324325
const systems::Context<T>& context, EigenPtr<MatrixX<T>> N) const {
325326
// Upper-left block (rotational part of the N matrix) is Nᵣ ≜ 0.5 Q_FM.
326327
// See QuaternionFloatingMobilizer::CalcQMatrix() for details.
328+
// The lower-right block (translational part of the N matrix) is [I₃₃].
327329
N->template block<4, 3>(0, 0) = CalcQMatrixOverTwo(get_quaternion(context));
328-
// Upper-right block
329-
N->template block<4, 3>(0, 3).setZero();
330-
// Lower-left block
331-
N->template block<3, 3>(4, 0).setZero();
332-
// Lower-right block (translational part of the N matrix).
333-
N->template block<3, 3>(4, 3).setIdentity();
330+
N->template block<4, 3>(0, 3).setZero(); // Upper-right block.
331+
N->template block<3, 3>(4, 0).setZero(); // Lower-left block.
332+
N->template block<3, 3>(4, 3).setIdentity(); // Lower-right block.
334333
}
335334

336335
template <typename T>
@@ -343,13 +342,10 @@ void QuaternionFloatingMobilizer<T>::DoCalcNplusMatrix(
343342
// in frame F, expressed in F. Hence, this accounts for a non-unit q_FM.
344343
const Quaternion<T> q_FM = get_quaternion(context);
345344
Nplus->template block<3, 4>(0, 0) =
346-
QuaternionRateToAngularVelocityMatrix(q_FM);
347-
// Upper-right block
348-
Nplus->template block<3, 3>(0, 4).setZero();
349-
// Lower-left block
350-
Nplus->template block<3, 4>(3, 0).setZero();
351-
// Lower-right block
352-
Nplus->template block<3, 3>(3, 4).setIdentity();
345+
QuaternionRateToAngularVelocityMatrix(q_FM); // Upper-left block.
346+
Nplus->template block<3, 3>(0, 4).setZero(); // Upper-right block.
347+
Nplus->template block<3, 4>(3, 0).setZero(); // Lower-left block.
348+
Nplus->template block<3, 3>(3, 4).setIdentity(); // Lower-right block.
353349
}
354350

355351
template <typename T>
@@ -366,11 +362,6 @@ void QuaternionFloatingMobilizer<T>::DoCalcNDotMatrix(
366362
// | q̇y | | -qz qw qx | ⌊ ωz ⌋
367363
// ⌊ q̇z ⌋ ⌊ qy -qx qw ⌋
368364
//
369-
// For the translational part of this mobilizer, the time-derivative of the
370-
// generalized positions q̇ₜ = [ẋ, ẏ, ż]ᵀ are related to the translational
371-
// generalized velocities v_FM_F = vₜ = [vx, vy, vz]ᵀ as q̇ₜ = Nₜ(q)⋅vₜ, where
372-
// Nₜ(q) = [I₃₃] (3x3 identity matrix). Hence, Ṅₜ(q,q̇ᵣ) = [0₃₃] (zero matrix).
373-
374365
// Calculate the time-derivative of the quaternion, i.e., q̇ᵣ = Nᵣ(q)⋅vᵣ.
375366
const Quaternion<T> q_FM = get_quaternion(context);
376367
const Vector3<T> w_FM_F = get_angular_velocity(context);
@@ -381,6 +372,11 @@ void QuaternionFloatingMobilizer<T>::DoCalcNDotMatrix(
381372
// q_FM = [qw, qx, qy, qz]ᵀ, so Ṅᵣ(qᵣ,q̇ᵣ) = 0.5 * Q(q̇_FM).
382373
const Eigen::Matrix<T, 4, 3> NrDotMatrix = CalcQMatrixOverTwo(qdot_FM);
383374

375+
// For the translational part of this mobilizer, the time-derivative of the
376+
// generalized positions q̇ₜ = [ẋ, ẏ, ż]ᵀ are related to the translational
377+
// generalized velocities v_FM_F = vₜ = [vx, vy, vz]ᵀ as q̇ₜ = Nₜ(q)⋅vₜ, where
378+
// Nₜ(q) = [I₃₃] (3x3 identity matrix). Hence, Ṅₜ(q,q̇ᵣ) = [0₃₃] (zero matrix).
379+
384380
// Form the Ṅ(q,q̇) matrix associated with q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇.
385381
Ndot->template block<4, 3>(0, 0) = NrDotMatrix; // Upper-left block.
386382
Ndot->template block<4, 3>(0, 3).setZero(); // Upper-right block.
@@ -402,11 +398,6 @@ void QuaternionFloatingMobilizer<T>::DoCalcNplusDotMatrix(
402398
// ⌊ ωz ⌋ | -qz -qy qx qw | | q̇y |
403399
// ⌊ q̇z ⌋
404400
//
405-
// For the translational part of this mobilizer, the translational generalized
406-
// velocities v_FM_F = vₜ = [vx, vy, vz]ᵀ are related to the time-derivatives
407-
// of the generalized positions q̇ₜ = [ẋ, ẏ, ż]ᵀ as vₜ = N⁺ₜ(q)⋅q̇ₜ, where
408-
// Nₜ(q) = [I₃₃] (3x3 identity matrix). Thus, Ṅ⁺ₜ(q,q̇ₜ) = [0₃₃] (zero matrix).
409-
410401
// Calculate the time-derivative of the quaternion, i.e., q̇ᵣ = Nᵣ(q)⋅vᵣ.
411402
const Quaternion<T> q_FM = get_quaternion(context);
412403
const Vector3<T> w_FM_F = get_angular_velocity(context);
@@ -416,11 +407,16 @@ void QuaternionFloatingMobilizer<T>::DoCalcNplusDotMatrix(
416407
// In view of the documentation in CalcQMatrix(), since
417408
// N⁺ᵣ(q_FM) = 2 * (Q_FM)ᵀ, where Q_FM is linear in the elements of
418409
// q_FM = [qw, qx, qy, qz]ᵀ, hence Ṅ⁺ᵣ(q̇_FM) = 2 * (Q̇_FM)ᵀ.
419-
// TODO(Mitiguy) Ensure this calculation is consistent with the precise
420-
// definition of this function (the definition is also a TODO).
410+
// TODO(Mitiguy) Ensure this calculation provides the time derivative of the
411+
// calculation in DoCalcNplusMatrix().
421412
const Eigen::Matrix<T, 3, 4> NrPlusDot =
422413
CalcTwoTimesQMatrixTranspose(qdot_FM);
423414

415+
// For the translational part of this mobilizer, the translational generalized
416+
// velocities v_FM_F = vₜ = [vx, vy, vz]ᵀ are related to the time-derivatives
417+
// of the generalized positions q̇ₜ = [ẋ, ẏ, ż]ᵀ as vₜ = N⁺ₜ(q)⋅q̇ₜ, where
418+
// Nₜ(q) = [I₃₃] (3x3 identity matrix). Thus, Ṅ⁺ₜ(q,q̇ₜ) = [0₃₃] (zero matrix).
419+
424420
// Form the Ṅ⁺(q,q̇) matrix associated with v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
425421
NplusDot->template block<3, 4>(0, 0) = NrPlusDot; // Upper-left block.
426422
NplusDot->template block<3, 3>(0, 4).setZero(); // Upper-right block.

multibody/tree/quaternion_floating_mobilizer.h

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -293,22 +293,49 @@ class QuaternionFloatingMobilizer final : public MobilizerImpl<T, 7, 6> {
293293
return velocity.get_coeffs();
294294
}
295295

296-
// This function calculates this mobilizers N matrix using the quaternion in
297-
// context, _without_ normalization -- differs from DoCalcNplusMatrix().
296+
// This function calculates this mobilizer's N matrix using the quaternion in
297+
// context, _without_ normalization, which differs from DoCalcNplusMatrix().
298+
// This mobilizer's N(q) matrix relates q̇ (time derivatives of 7 generalized
299+
// positions) to v (6 generalized velocities) as q̇ = N(q)⋅v, where
300+
// N(q) = ⎡ Nᵣ(q) 0₄₃ ⎤ 0₄₃ is the 4x3 zero matrix.
301+
// ⎣ 0₃₃ I₃₃ ⎦ I₃₃ is the 3x3 identity matrix.
302+
// Nᵣ(q) = 0.5 * QuaternionFloatingMobilizer::CalcQMatrix() is a 4x3 matrix.
303+
// Note: The time-derivative of the quaternion qᵣ in context can be calculated
304+
// q̇ᵣ = Nᵣ(q) w_FM_F, where w_FM_F is frame M's angular velocity in frame F,
305+
// expressed in F. For a unit quaternion q̂ᵣ, we prove d/dt(q̂ᵣ) satisfies the
306+
// "orthogonality constraint" i.e., d/dt(q̂ᵣ ⋅ q̂ᵣ = 1) => q̂ᵣ ⋅ d/dt(q̂ᵣ) = 0
307+
// via q̂ᵣ ⋅ d/dt(q̂ᵣ) = q̂ᵣ ⋅ Nᵣ(q̂ᵣ) w_FM_F = [0 0 0] w_FM_F = 0.
308+
// For a non-unit quaternion qᵣ, the orthogonality constraint is proved via
309+
// qᵣ ⋅ d/dt(qᵣ) = qᵣ ⋅ Nᵣ(qᵣ) w_FM_F = |qᵣ| q̂ᵣ ⋅ |qᵣ| Nᵣ(q̂ᵣ) w_FM_F =
310+
// |qᵣ|² q̂ᵣ ⋅ Nᵣ(q̂ᵣ) w_FM_F = |qᵣ|² [0 0 0] w_FM_F = 0, where this proof
311+
// uses the fact Nᵣ(qᵣ) is linear in qᵣ and qᵣ = |qᵣ| q̂ᵣ.
312+
// Summary: If the quaternion in context is a unit quaternion q̂ᵣ, then its
313+
// time-derivative can be calculated as d/dt(q̂ᵣ) = Nᵣ(q̂ᵣ) w_FM_F.
314+
// If the quaternion is context is a non-unit quaternion, then its time-
315+
// derivative can be calculated d/dt(qᵣ) = Nᵣ(qᵣ) w_FM_F = |qᵣ| Nᵣ(q̂ᵣ) w_FM_F.
298316
void DoCalcNMatrix(const systems::Context<T>& context,
299317
EigenPtr<MatrixX<T>> N) const final;
300318

301-
// This function calculates this mobilizers N matrix using the quaternion in
302-
// context, _with_ normalization -- differs from DoCalcNMatrix().
319+
// This function calculates this mobilizer's N⁺ matrix using the quaternion in
320+
// context, _with_ normalization, which differs from DoCalcNMatrix().
321+
// This mobilizer's N⁺(q) matrix relates v (6 generalized velocities) to q̇
322+
// (time derivatives of 7 generalized positions) as v = N⁺(q)⋅q̇, where
323+
// N⁺(q) = ⎡ Nᵣ(q)⁺ 0₃₄ ⎤ 0₃₄ is the 4x3 zero matrix.
324+
// ⎣ 0₃₃ I₃₃ ⎦ I₃₃ is the 3x3 identity matrix.
325+
// Nᵣ⁺(q) is a 3x4 matrix formed by QuaternionRateToAngularVelocityMatrix().
326+
// Note: Contextual definition of Nᵣ⁺ -- denoting q̂_FM = q_FM / |q_FM|,
327+
// w_FM_F = Nᵣ⁺ * d/dt(q̂_FM), where w_FM_F is frame M's angular velocity
328+
// in frame F, expressed in F. Hence, this accounts for a non-unit q_FM.
303329
void DoCalcNplusMatrix(const systems::Context<T>& context,
304330
EigenPtr<MatrixX<T>> Nplus) const final;
305331

306-
// This function calculates this mobilizers Ṅ matrix using the quaternion in
307-
// context, _without_ normalization.
332+
// This function calculates this mobilizer's Ṅ matrix using the quaternion
333+
// and angular velocity in the context.
308334
void DoCalcNDotMatrix(const systems::Context<T>& context,
309335
EigenPtr<MatrixX<T>> Ndot) const final;
310336

311-
// TODO(Mitiguy) Provide a precise definition of this function.
337+
// This function calculates this mobilizer's Ṅ⁺ matrix using the quaternion
338+
// and angular velocity in the context.
312339
void DoCalcNplusDotMatrix(const systems::Context<T>& context,
313340
EigenPtr<MatrixX<T>> NplusDot) const final;
314341

@@ -350,17 +377,17 @@ class QuaternionFloatingMobilizer final : public MobilizerImpl<T, 7, 6> {
350377
// @note Herein, we denote the function that forms this matrix as Q(q).
351378
// When q is the quaternion q_FM that relates the orientation of frames F
352379
// F and M, we define the matrix Q_FM ≜ Q(q_FM). When q_FM is a unit
353-
// quaternion, we denote it as q̂_FM and Q̂_FM ≜ Q(q̂_FM).
380+
// quaternion, we denote it as q̂_FM. Similarly, Q̂_FM ≜ Q(q̂_FM).
354381
// Many uses of Q_FM and Q̂_FM are associated with angular velocity expressed
355382
// in a particular frame. The examples below show them used in conjunction
356383
// with w_FM_F (frame M's angular velocity in frame F, expressed in F).
357384
// Another use of Q_FM and Q̂_FM are for rotational parts of this mobilizer's
358-
// N and Nplus matrices, namely as Nᵣ ≜ 0.5 Q_FM and Nᵣ⁺ 2 (Q̂_FM)ᵀ.
385+
// N and Nplus matrices, e.g., as Nᵣ ≜ 0.5 Q_FM and Nᵣ⁺ = 2 (Q̂_FM)ᵀ.
359386
//
360387
// q̇_FM = 0.5 * Q_FM * w_FM_F
361388
// q̈_FM = 0.5 * Q_FM * ẇ_FM_F - 0.25 ω² q_FM Note: ω² = |w_FM_F|²
362-
// w_FM_F = 2 * (Q̂_FM)ᵀ * q̇_FM
363-
// ẇ_FM_F = 2 * (Q̂_FM)ᵀ * q̈_FM
389+
// w_FM_F = 2 * (Q̂_FM)ᵀ * d/dt(q̂_FM)
390+
// ẇ_FM_F = 2 * (Q̂_FM)ᵀ * d²/dt²(q̂_FM)
364391
//
365392
// @note Since the elements of the matrix returned by Q(q) depend linearly on
366393
// qw, qx, qy, qz, s * Q(q) = Q(s * q), where s is a scalar (e.g., 0.5 or 2).
@@ -394,7 +421,7 @@ class QuaternionFloatingMobilizer final : public MobilizerImpl<T, 7, 6> {
394421

395422
// Helper to compute this mobilizer's rotational kinematic map Nᵣ⁺(q̂_FM) from
396423
// quaternion time derivative to angular velocity as w_FM_F = Nᵣ⁺ * d/dt(q̂_FM)
397-
// where q̂_FM ≜ q_FM / |q_FM|, where w_FM_F is frame M's angular velocity
424+
// where q̂_FM ≜ q_FM / |q_FM| and w_FM_F is frame M's angular velocity
398425
// in frame F, expressed in F. Hence, this accounts for a non-unit q_FM.
399426
// param[in] q_FM quaternion describing the orientation of frames F and M.
400427
// @note The argument q_FM can be a non-unit quaternion as this function

0 commit comments

Comments
 (0)