Skip to content

Commit 6ead6c1

Browse files
author
Paul Mitiguy
committed
Add CalcNplusDotMatrix().
1 parent 945ea83 commit 6ead6c1

File tree

5 files changed

+106
-19
lines changed

5 files changed

+106
-19
lines changed

multibody/tree/mobilizer.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ void Mobilizer<T>::DoCalcNDotMatrix(const systems::Context<T>&,
5252
throw std::logic_error(error_message);
5353
}
5454

55+
template <typename T>
56+
void Mobilizer<T>::DoCalcNplusDotMatrix(const systems::Context<T>&,
57+
EigenPtr<MatrixX<T>>) const {
58+
// TODO(Mitiguy) remove this function when Mobilizer::CalcNplusDotMatrix()
59+
// is changed to a pure virtual function that requires override.
60+
const std::string error_message = fmt::format(
61+
"The function {}() has not been implemented for this mobilizer.",
62+
__func__);
63+
throw std::logic_error(error_message);
64+
}
65+
5566
} // namespace internal
5667
} // namespace multibody
5768
} // namespace drake

multibody/tree/mobilizer.h

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -599,12 +599,12 @@ class Mobilizer : public MultibodyElement<T> {
599599
DoCalcNplusMatrix(context, Nplus);
600600
}
601601

602-
// Computes the kinematic matrix `Ṅ(q,q̇)` that helps relate q̈ (2ⁿᵈ time
603-
// derivative of the generalized positions) to v̇ (1ˢᵗ time derivative of
604-
// generalized velocities) according to `q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇`, where N(q)
605-
// can be calculated by CalcNMatrix().
602+
// Computes the matrix `Ṅ(q,q̇)` that helps relate q̈ (2ⁿᵈ time derivative of
603+
// the generalized positions) to v̇ (1ˢᵗ time derivative of generalized
604+
// velocities) according to `q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇`, where N(q) can be
605+
// calculated by CalcNMatrix().
606606
// @param[in] context stores generalized positions q and velocities v.
607-
// @param[out] Ndot The kinematic matrix Ṅ(q,q̇). On input Ndot must have size
607+
// @param[out] Ndot The matrix Ṅ(q,q̇). On input Ndot must have size
608608
// `nq x nv` where nq is the number of generalized positions and
609609
// nv is the number of generalized velocities for this mobilizer.
610610
// @see MapAccelerationToQDDot().
@@ -616,6 +616,23 @@ class Mobilizer : public MultibodyElement<T> {
616616
DoCalcNDotMatrix(context, Ndot);
617617
}
618618

619+
// Computes the matrix `Ṅ⁺(q,q̇)` that helps relate v̇ (1ˢᵗ time derivative of
620+
// generalized velocities) to q̈ (2ⁿᵈ time derivative of the generalized
621+
// positions) according to `v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈`, where N⁺(q) can be
622+
// calculated by CalcNPlusMatrix().
623+
// @param[in] context stores generalized positions q and velocities v.
624+
// @param[out] NplusDot The matrix Ṅ(q,q̇). On input NplusDot must have size
625+
// `nq x nv` where nq is the number of generalized positions and
626+
// nv is the number of generalized velocities for this mobilizer.
627+
// @see MapQDDotToAcceleration().
628+
void CalcNplusDotMatrix(const systems::Context<T>& context,
629+
EigenPtr<MatrixX<T>> NplusDot) const {
630+
DRAKE_DEMAND(NplusDot != nullptr);
631+
DRAKE_DEMAND(NplusDot->rows() == num_velocities());
632+
DRAKE_DEMAND(NplusDot->cols() == num_positions());
633+
DoCalcNplusDotMatrix(context, NplusDot);
634+
}
635+
619636
virtual bool is_velocity_equal_to_qdot() const = 0;
620637

621638
// Computes the kinematic mapping `q̇ = N(q)⋅v` between generalized
@@ -782,6 +799,13 @@ class Mobilizer : public MultibodyElement<T> {
782799
virtual void DoCalcNDotMatrix(const systems::Context<T>& context,
783800
EigenPtr<MatrixX<T>> Ndot) const;
784801

802+
// NVI to CalcNplusDotMatrix(). Implementations can safely assume that
803+
// NplusDot is not the nullptr and that NplusDot has the proper size.
804+
// TODO(Mitiguy) change this function to a pure virtual function when it has
805+
// been overridden in all subclasses.
806+
virtual void DoCalcNplusDotMatrix(const systems::Context<T>& context,
807+
EigenPtr<MatrixX<T>> NplusDot) const;
808+
785809
// @name Methods to make a clone templated on different scalar types.
786810
//
787811
// The only const argument to these methods is the new MultibodyTree clone

multibody/tree/rpy_ball_mobilizer.cc

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,20 +277,17 @@ void RpyBallMobilizer<T>::DoCalcNplusMatrix(const systems::Context<T>& context,
277277
template <typename T>
278278
void RpyBallMobilizer<T>::DoCalcNDotMatrix(const systems::Context<T>& context,
279279
EigenPtr<MatrixX<T>> Ndot) const {
280-
// Computes the 3x3 matrix Ṅ(q,q̇) that helps relate q̈ (2ⁿᵈ time derivative
281-
// of the generalized positions) to v̇ (1ˢᵗ time derivative of generalized
282-
// velocities) according to q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇, where q = [r, p, y]ᵀ
283-
// contains the roll (r), pitch (p) and yaw (y) angles and v = [wx, wy, wz]ᵀ
284-
// represents W_FM_F (the angular velocity of the mobilizer's M frame measured
285-
// in its F frame, expressed in the F frame).
280+
// Computes the 3x3 matrix Ṅ(q,q̇) that helps relate q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇,
281+
// where q = [r, p, y]ᵀ contains the roll (r), pitch (p) and yaw (y) angles
282+
// and v = [wx, wy, wz]ᵀ represents W_FM_F (the angular velocity of the
283+
// mobilizer's M frame measured in its F frame, expressed in the F frame).
286284
//
287285
// The 3x3 matrix N(q) relates q̇ to v as q̇ = N(q)⋅v, where
288286
//
289287
// [ cos(y) / cos(p), sin(y) / cos(p), 0]
290288
// N(q) = [ -sin(y), cos(y), 0]
291289
// [ sin(p) * cos(y) / cos(p), sin(p) * sin(y) / cos(p), 1]
292290
//
293-
using std::abs;
294291
using std::cos;
295292
using std::sin;
296293
const Vector3<T> angles = get_angles(context);
@@ -325,6 +322,50 @@ void RpyBallMobilizer<T>::DoCalcNDotMatrix(const systems::Context<T>& context,
325322
Ndot->coeffRef(2, 2) = 0;
326323
}
327324

325+
template <typename T>
326+
void RpyBallMobilizer<T>::DoCalcNplusDotMatrix(
327+
const systems::Context<T>& context, EigenPtr<MatrixX<T>> NplusDot) const {
328+
// Computes the matrix `Ṅ⁺(q,q̇)` that helps relate v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈,
329+
// where q = [r, p, y]ᵀ contains the roll (r), pitch (p) and yaw (y) angles
330+
// and v = [wx, wy, wz]ᵀ represents W_FM_F (the angular velocity of the
331+
// mobilizer's M frame measured in its F frame, expressed in the F frame).
332+
//
333+
// The 3x3 matrix N⁺(q) relates v to q̇ as v = N⁺(q)⋅q̇, where
334+
//
335+
// [ cos(y) * cos(p), -sin(y), 0]
336+
// N⁺(q) = [ sin(y) * cos(p), cos(y), 0]
337+
// [ -sin(p), 0, 1]
338+
//
339+
using std::cos;
340+
using std::sin;
341+
const Vector3<T> angles = get_angles(context);
342+
const T cp = cos(angles[1]);
343+
const T sp = sin(angles[1]);
344+
const T sy = sin(angles[2]);
345+
const T cy = cos(angles[2]);
346+
347+
// Calculate time-derivative of roll, pitch, and yaw angles.
348+
const Vector3<T> v = get_angular_velocity(context);
349+
Vector3<T> qdot;
350+
MapVelocityToQDot(context, v, &qdot);
351+
const T& pdot = qdot(1);
352+
const T& ydot = qdot(2);
353+
354+
const T sp_pdot = sp * pdot;
355+
const T cy_ydot = cy * ydot;
356+
const T sy_ydot = sy * ydot;
357+
358+
NplusDot->coeffRef(0, 0) = -sy_ydot * cp - cy * sp_pdot;
359+
NplusDot->coeffRef(0, 1) = -cy_ydot;
360+
NplusDot->coeffRef(0, 2) = 0;
361+
NplusDot->coeffRef(1, 0) = cy_ydot * cp - sy * sp_pdot;
362+
NplusDot->coeffRef(1, 1) = -sy * ydot;
363+
NplusDot->coeffRef(1, 2) = 0;
364+
NplusDot->coeffRef(2, 0) = -cp * pdot;
365+
NplusDot->coeffRef(2, 1) = 0;
366+
NplusDot->coeffRef(2, 2) = 0;
367+
}
368+
328369
template <typename T>
329370
void RpyBallMobilizer<T>::MapVelocityToQDot(
330371
const systems::Context<T>& context, const Eigen::Ref<const VectorX<T>>& v,

multibody/tree/rpy_ball_mobilizer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ class RpyBallMobilizer final : public MobilizerImpl<T, 3, 3> {
324324
void DoCalcNDotMatrix(const systems::Context<T>& context,
325325
EigenPtr<MatrixX<T>> Ndot) const final;
326326

327+
// Calculates the time derivative of the N⁺ matrix that relates v = N⁺(q)⋅q̇.
328+
// There is no singularity in N⁺(q) or its time-derivative Ṅ⁺(q,q̇) and
329+
// calculating Ṅ⁺ is more efficient than calculating Ṅ.
330+
void DoCalcNplusDotMatrix(const systems::Context<T>& context,
331+
EigenPtr<MatrixX<T>> NplusDot) const final;
332+
327333
std::unique_ptr<Mobilizer<double>> DoCloneToScalar(
328334
const MultibodyTree<double>& tree_clone) const override;
329335

multibody/tree/test/rpy_ball_mobilizer_test.cc

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ TEST_F(RpyBallMobilizerTest, NDotAndMapAccelerationToQDDot) {
173173
// Verify equivalence of q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇ and MapAccelerationToQDDot().
174174
// PAUL FIX THIS TEST -- NOT WORKING. SHOULD BE EXPECT_TRUE(...);
175175
const Vector3<double> qddot_expected = Ndot * wxyz + N * wdot;
176-
EXPECT_FALSE(CompareMatrices(qddot, qddot_expected, kTolerance,
177-
MatrixCompareType::relative));
176+
EXPECT_TRUE(CompareMatrices(qddot, qddot_expected, kTolerance,
177+
MatrixCompareType::relative));
178178
}
179179

180180
TEST_F(RpyBallMobilizerTest, MapUsesNplus) {
@@ -183,7 +183,7 @@ TEST_F(RpyBallMobilizerTest, MapUsesNplus) {
183183
mobilizer_->SetAngles(context_.get(), rpy_value);
184184

185185
// Set arbitrary qdot and call MapQDotToVelocity().
186-
const Vector3<double> qdot = (Vector3<double>() << 1, 2, 3).finished();
186+
const Vector3<double> qdot(1, 2, 3);
187187
Vector3<double> v;
188188
mobilizer_->MapQDotToVelocity(*context_, qdot, &v);
189189

@@ -195,14 +195,19 @@ TEST_F(RpyBallMobilizerTest, MapUsesNplus) {
195195
EXPECT_TRUE(CompareMatrices(v, Nplus * qdot, kTolerance,
196196
MatrixCompareType::relative));
197197

198+
// Set this mobilizer's angular velocity to be consistent with v.
199+
mobilizer_->SetAngularVelocity(context_.get(), v);
200+
198201
// Ensure MapQDDotToAcceleration() works properly.
199202
const Vector3<double> qddot(1.2, 2.3, 3.4); // Set arbitrary values.
200203
Vector3<double> vdot;
201-
mobilizer_->MapQDDotToAcceleration(*context_, qdot, &v);
204+
mobilizer_->MapQDDotToAcceleration(*context_, qddot, &vdot);
202205

203-
// Calculate vdot another way.
204-
// TODO(Mitiguy) Finish -- as of now this is a dumb test.
205-
Vector3<double> vdot_expected = Nplus * qddot; // NOT TRUE YET.
206+
// Calculate vdot another way as v̇ = N̈⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈.
207+
// TODO(Mitiguy) Finish -- as of now, this test is not working.
208+
MatrixX<double> NplusDot(3, 3);
209+
mobilizer_->CalcNplusDotMatrix(*context_, &NplusDot);
210+
const Vector3<double> vdot_expected = NplusDot * qdot + Nplus * qddot;
206211
EXPECT_FALSE(CompareMatrices(vdot, vdot_expected, kTolerance,
207212
MatrixCompareType::relative));
208213
}

0 commit comments

Comments
 (0)