14
14
namespace drake {
15
15
namespace multibody {
16
16
namespace internal {
17
+ // The following tolerance is used to test whether the cosine of the pitch angle
18
+ // is near zero, which occurs when the pitch angle ≈ π/2 ± n π (n=0, 1 2, …).
19
+ // An exception is thrown if a pitch angle is within ≈ 0.057° of a singularity.
20
+ constexpr double kToleranceCosPitchNearZero = 1.0e-3 ;
17
21
18
22
template <typename T>
19
23
RpyBallMobilizer<T>::~RpyBallMobilizer () = default ;
@@ -164,7 +168,7 @@ void RpyBallMobilizer<T>::DoCalcNMatrix(const systems::Context<T>& context,
164
168
using std::sin;
165
169
const Vector3<T> angles = get_angles (context);
166
170
const T cp = cos (angles[1 ]);
167
- if (abs (cp) < 1.0e-3 ) {
171
+ if (abs (cp) < kToleranceCosPitchNearZero ) {
168
172
const char * function_name_less_Do = __func__ + 2 ;
169
173
ThrowSinceCosPitchIsNearZero (angles[1 ], function_name_less_Do);
170
174
}
@@ -239,7 +243,7 @@ void RpyBallMobilizer<T>::DoCalcNDotMatrix(const systems::Context<T>& context,
239
243
const T sp = sin (angles[1 ]);
240
244
const T sy = sin (angles[2 ]);
241
245
const T cy = cos (angles[2 ]);
242
- if (abs (cp) < 1.0e-3 ) {
246
+ if (abs (cp) < kToleranceCosPitchNearZero ) {
243
247
const char * function_name_less_Do = __func__ + 2 ;
244
248
ThrowSinceCosPitchIsNearZero (angles[1 ], function_name_less_Do);
245
249
}
@@ -298,7 +302,7 @@ void RpyBallMobilizer<T>::DoCalcNplusDotMatrix(
298
302
299
303
// Throw an exception with the proper function name if a singularity would be
300
304
// encountered in DoMapVelocityToQDot().
301
- if (abs (cp) < 1.0e-3 ) {
305
+ if (abs (cp) < kToleranceCosPitchNearZero ) {
302
306
const char * function_name_less_Do = __func__ + 2 ;
303
307
ThrowSinceCosPitchIsNearZero (angles[1 ], function_name_less_Do);
304
308
}
@@ -361,7 +365,7 @@ void RpyBallMobilizer<T>::DoMapVelocityToQDot(
361
365
const T cp = cos (angles[1 ]);
362
366
const T sy = sin (angles[2 ]);
363
367
const T cy = cos (angles[2 ]);
364
- if (abs (cp) < 1.0e-3 ) {
368
+ if (abs (cp) < kToleranceCosPitchNearZero ) {
365
369
const char * function_name_less_Do = __func__ + 2 ;
366
370
ThrowSinceCosPitchIsNearZero (angles[1 ], function_name_less_Do);
367
371
}
@@ -456,13 +460,13 @@ Vector3<T> RpyBallMobilizer<T>::CalcAccelerationBiasForQDDot(
456
460
const T sp = sin (angles[1 ]);
457
461
const T sy = sin (angles[2 ]);
458
462
const T cy = cos (angles[2 ]);
459
- if (abs (cp) < 1.0e-3 ) {
463
+ if (abs (cp) < kToleranceCosPitchNearZero ) {
460
464
ThrowSinceCosPitchIsNearZero (angles[1 ], function_name);
461
465
}
462
- // The algorithm below efficiently calculates Ṅ⁺(q,q̇)⋅q̇. The algorithm was
463
- // verified with MotionGenesis. It can also be verified by-hand, e.g., with
464
- // the documentation in DoCalcNplusDotMatrix which differentiates N⁺(q) to
465
- // form Ṅ⁺(q,q̇). Thereafter, multiply by q̇ to form Ṅ⁺(q,q̇)⋅q̇ (and simplify).
466
+ // The algorithm below calculates Ṅ⁺(q,q̇)⋅q̇. The algorithm was verified with
467
+ // MotionGenesis. It can also be verified by-hand, e.g., with documentation
468
+ // in DoCalcNplusDotMatrix which directly differentiates N⁺(q) to form
469
+ // Ṅ⁺(q,q̇). Thereafter, multiply by q̇ to form Ṅ⁺(q,q̇)⋅q̇ (and simplify).
466
470
const Vector3<T> v = get_angular_velocity (context);
467
471
Vector3<T> qdot;
468
472
DoMapVelocityToQDot (context, v, &qdot);
@@ -484,7 +488,6 @@ void RpyBallMobilizer<T>::DoMapAccelerationToQDDot(
484
488
const systems::Context<T>& context,
485
489
const Eigen::Ref<const VectorX<T>>& vdot,
486
490
EigenPtr<VectorX<T>> qddot) const {
487
- // --------------------------------------------------------------------------
488
491
// As shown in DoMapVelocityToQDot(), the time-derivatives of generalized
489
492
// positions q̇ = [ṙ, ṗ, ẏ]ᵀ are related to the generalized velocities
490
493
// v = [ωx, ωy, ωz]ᵀ in the matrix form q̇ = N(q)⋅v, where N is a 3x3 matrix.
@@ -494,16 +497,16 @@ void RpyBallMobilizer<T>::DoMapAccelerationToQDDot(
494
497
// q = [r, p, y]ᵀ denote roll, pitch, yaw angles (generalized positions).
495
498
//
496
499
// There are various ways to get q̈ = [r̈, p̈, ÿ]ᵀ in terms of v̇ = [ω̇x, ω̇y, ω̇z]ᵀ.
497
- // A simple calculation directly solves for q̈ as q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇.
500
+ // One way to is differentiate q̇ = N(q)⋅v to form q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇.
498
501
// However, N⁺(q) is simpler than N(q) so Ṅ⁺(q,q̇) is much simpler than Ṅ(q,q̇).
499
- // Hence, a more efficient calculation of q̈ starts as v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈
500
- // and then solves as q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
501
- // --------------------------------------------------------------------------
502
+ // A calculation of q̈ that leverages the simplicity of Ṅ⁺(q,q̇) over Ṅ(q,q̇)
503
+ // and the available function CalcAccelerationBiasForQDDot() starts with
504
+ // v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈ and then solves as q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
502
505
const Vector3<T> vdot_minus_NplusDotTimesQDot =
503
506
vdot - CalcAccelerationBiasForQDDot (context, __func__);
504
507
505
- // Note: Although the function below was designed to efficiently calculate
506
- // q̇ = N(q)⋅v, it can also be used to calculate q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
508
+ // Note: Although the function below was designed to calculate q̇ = N(q)⋅v,
509
+ // it can also be used to calculate q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
507
510
DoMapVelocityToQDot (context, vdot_minus_NplusDotTimesQDot, qddot);
508
511
}
509
512
@@ -512,7 +515,6 @@ void RpyBallMobilizer<T>::DoMapQDDotToAcceleration(
512
515
const systems::Context<T>& context,
513
516
const Eigen::Ref<const VectorX<T>>& qddot,
514
517
EigenPtr<VectorX<T>> vdot) const {
515
- // --------------------------------------------------------------------------
516
518
// As seen in DoMapQDotToVelocity(), generalized velocities v = [ωx, ωy, ωz]ᵀ
517
519
// are related to q̇ = [ṙ, ṗ, ẏ]ᵀ (time-derivatives of generalized positions)
518
520
// in the matrix form v = N⁺(q)⋅q̇, where N⁺ is a 3x3 matrix.
@@ -524,7 +526,7 @@ void RpyBallMobilizer<T>::DoMapQDDotToAcceleration(
524
526
// There are various ways to calculate v̇ = [ω̇x, ω̇y, ω̇z]ᵀ (the time-derivatives
525
527
// of the generalized velocities). The calculation below is straighforward in
526
528
// that it simply differentiates v = N⁺(q)⋅q̇ to form v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
527
- // --------------------------------------------------------------------------
529
+
528
530
// Form the Ṅ⁺(q,q̇)⋅q̇ term of the result now (start of this function) so any
529
531
// singularity (if one exists) throws an exception referencing this function.
530
532
const Vector3<T> NplusDot_times_Qdot =
0 commit comments