@@ -446,6 +446,36 @@ RpyBallMobilizer<T>::TemplatedDoCloneToScalar(
446
446
outboard_frame_clone);
447
447
}
448
448
449
+ template <typename T>
450
+ Vector3<T> RpyBallMobilizer<T>::CalcNplusDotTimesQdot(
451
+ const systems::Context<T>& context, const char * function_name) const {
452
+ using std::cos;
453
+ using std::sin;
454
+ const Vector3<T> angles = get_angles (context);
455
+ const T cp = cos (angles[1 ]);
456
+ const T sp = sin (angles[1 ]);
457
+ const T sy = sin (angles[2 ]);
458
+ const T cy = cos (angles[2 ]);
459
+ if (abs (cp) < 1.0e-3 ) {
460
+ ThrowSinceCosPitchIsNearZero (angles[1 ], function_name);
461
+ }
462
+ // The algorithm below efficiently calculates Ṅ⁺(q,q̇)⋅q̇.
463
+ const Vector3<T> v = get_angular_velocity (context);
464
+ Vector3<T> qdot;
465
+ DoMapVelocityToQDot (context, v, &qdot);
466
+ const T& rdot = qdot[0 ];
467
+ const T& pdot = qdot[1 ];
468
+ const T& ydot = qdot[2 ];
469
+ const T pdot_ydot = pdot * ydot;
470
+ const T rdot_pdot = rdot * pdot;
471
+ const T rdot_ydot = rdot * ydot;
472
+ const T sp_rdot_pdot = sp * rdot_pdot;
473
+ const T cp_rdot_ydot = cp * rdot_ydot;
474
+ return Vector3<T>(-cy * pdot_ydot - cy * sp_rdot_pdot - sy * cp_rdot_ydot,
475
+ -sy * pdot_ydot - sy * sp_rdot_pdot + cy * cp_rdot_ydot,
476
+ -cp * rdot_pdot);
477
+ }
478
+
449
479
template <typename T>
450
480
void RpyBallMobilizer<T>::DoMapAccelerationToQDDot(
451
481
const systems::Context<T>& context,
@@ -466,47 +496,49 @@ void RpyBallMobilizer<T>::DoMapAccelerationToQDDot(
466
496
//
467
497
// There are various ways to get q̈ = [r̈, p̈, ÿ]ᵀ in terms of v̇ = [ω̇x, ω̇y, ω̇z]ᵀ.
468
498
// A simple calculation directly solves for q̈ as q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇.
469
- // Since N⁺(q) is simpler than N(q), then Ṅ⁺(q,q̇) is much simpler than Ṅ(q,q̇),
470
- // so a more efficient calculation of q̈ starts with v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈
471
- // and then solves for q̈ = N⁺(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}. Since N(q)⋅N⁺(q) = I₃₃
472
- // (the 3x3 identity matrix), Ṅ⋅N⁺ + N Ṅ⁺ = 0₃₃, which leads to Ṅ⁺ = N⁺⋅Ṅ⋅N⁺.
473
- // Substituting this gives q̈ = N⁺(q) {v̇ - N⁺⋅Ṅ⋅N⁺⋅q̇} = N⁺(q) {v̇ - N⁺⋅Ṅ⋅N⁺⋅q̇}
499
+ // However, N⁺(q) is simpler than N(q) so Ṅ⁺(q,q̇) is much simpler than Ṅ(q,q̇).
500
+ // Hence, a more efficient calculation of q̈ starts as v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈
501
+ // and then solves as q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
474
502
// --------------------------------------------------------------------------
475
- using std::cos;
476
- using std::sin;
477
- const Vector3<T> angles = get_angles (context);
478
- const T cp = cos (angles[1 ]);
479
- const T sp = sin (angles[1 ]);
480
- const T sy = sin (angles[2 ]);
481
- const T cy = cos (angles[2 ]);
482
- if (abs (cp) < 1.0e-3 ) {
483
- const char * function_name_less_Do = __func__ + 2 ;
484
- ThrowSinceCosPitchIsNearZero (angles[1 ], function_name_less_Do);
485
- }
503
+ const Vector3<T> vdotEtc = vdot - CalcNplusDotTimesQdot (context, __func__);
486
504
487
- const Vector3<T> v = get_angular_velocity (context);
488
- Vector3<T> qdot;
489
- DoMapVelocityToQDot (context, v, &qdot);
490
- const T& rdot = qdot[0 ];
491
- const T& pdot = qdot[1 ];
492
- const T& ydot = qdot[2 ];
493
- const T pdot_ydot = pdot * ydot;
494
- const T rdot_pdot = rdot * pdot;
495
- const T rdot_ydot = rdot * ydot;
496
- const T sp_rdot_pdot = sp * rdot_pdot;
497
- const T cp_rdot_ydot = cp * rdot_ydot;
505
+ // Mathematical trick: Although the function below was designed to efficiently
506
+ // calculate q̇ = N(q)⋅v, it can be used to calculate q̈ = N(q) {v̇ - Ṅ⁺(q,q̇)⋅q̇}.
507
+ DoMapVelocityToQDot (context, vdotEtc, qddot);
508
+ }
498
509
499
- const T& wxdot = vdot[0 ];
500
- const T& wydot = vdot[1 ];
501
- const T& wzdot = vdot[2 ];
510
+ template <typename T>
511
+ void RpyBallMobilizer<T>::DoMapQDDotToAcceleration(
512
+ const systems::Context<T>& context,
513
+ const Eigen::Ref<const VectorX<T>>& qddot,
514
+ EigenPtr<VectorX<T>> vdot) const {
515
+ // --------------------------------------------------------------------------
516
+ // Seen in MapQDotToVelocity(), the generalized velocities v = [ωx, ωy, ωz]ᵀ
517
+ // are related to q̇ = [ṙ, ṗ, ẏ]ᵀ (time-derivatives of generalized positions)
518
+ // as shown below, whose matrix form is v = N⁺(q)⋅q̇, where N⁺ is a 3x3 matrix.
519
+ //
520
+ // ⌈ ωx ⌉ ⌈cos(y) cos(p) -sin(y) 0⌉ ⌈ ṙ ⌉
521
+ // | ωy | = |sin(y) cos(p) cos(y) 0| | ṗ |
522
+ // ⌊ ωz ⌋ ⌊ -sin(p) 0 1⌋ ⌊ ẏ ⌋
523
+ //
524
+ // For this mobilizer v = w_FM_F = [ωx, ωy, ωz]ᵀ is the mobilizer M frame's
525
+ // angular velocity in the mobilizer F frame, expressed in frame F and
526
+ // q = [r, p, y]ᵀ denote roll, pitch, yaw angles (generalized positions).
527
+ //
528
+ // There are various ways to calculate v̇ = [ω̇x, ω̇y, ω̇z]ᵀ (the time-derivatives
529
+ // of the generalized velocities). A simple one is v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
530
+ // --------------------------------------------------------------------------
531
+ // Calculate Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
532
+ const Vector3<T> NplusDotTimesQdot = CalcNplusDotTimesQdot (context, __func__);
502
533
503
- Vector3<T> rhs;
504
- rhs[0 ] = wxdot + cy * pdot_ydot + cy * sp_rdot_pdot + sy * cp_rdot_ydot;
505
- rhs[1 ] = wydot + sy * pdot_ydot + sy * sp_rdot_pdot - cy * cp_rdot_ydot;
506
- rhs[2 ] = wzdot + cp * rdot_pdot;
534
+ // Mathematical trick: Although the function below was designed to efficiently
535
+ // calculate v = N⁺(q)⋅q̇, it can be used to calculate N⁺(q)⋅q̈.
536
+ DoMapQDotToVelocity (context, qddot, vdot);
507
537
508
- // Mathematical trick to efficiently calculate the result.
509
- DoMapVelocityToQDot (context, rhs, qddot);
538
+ // Calculate v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
539
+ vdot->coeffRef (0 ) += NplusDotTimesQdot[0 ];
540
+ vdot->coeffRef (1 ) += NplusDotTimesQdot[1 ];
541
+ vdot->coeffRef (2 ) += NplusDotTimesQdot[2 ];
510
542
}
511
543
512
544
template <typename T>
0 commit comments