@@ -131,6 +131,18 @@ void RpyBallMobilizer<T>::ProjectSpatialForce(
131
131
calc_tau (nullptr , F_BMo_F, tau.data ());
132
132
}
133
133
134
+ template <typename T>
135
+ void RpyBallMobilizer<T>::ThrowSinceCosPitchIsNearZero(const T& pitch) const {
136
+ throw std::runtime_error (fmt::format (
137
+ " The RpyBallMobilizer (implementing a BallRpyJoint) between "
138
+ " body {} and body {} has reached a singularity. This occurs when the "
139
+ " pitch angle takes values near π/2 + kπ, ∀ k ∈ ℤ. At the current "
140
+ " configuration, we have pitch = {}. Drake does not yet support a "
141
+ " comparable joint using quaternions, but the feature request is "
142
+ " tracked in https://github.com/RobotLocomotion/drake/issues/12404." ,
143
+ this ->inboard_body ().name (), this ->outboard_body ().name (), pitch));
144
+ }
145
+
134
146
template <typename T>
135
147
void RpyBallMobilizer<T>::DoCalcNMatrix(const systems::Context<T>& context,
136
148
EigenPtr<MatrixX<T>> N) const {
@@ -150,18 +162,7 @@ void RpyBallMobilizer<T>::DoCalcNMatrix(const systems::Context<T>& context,
150
162
using std::sin;
151
163
const Vector3<T> angles = get_angles (context);
152
164
const T cp = cos (angles[1 ]);
153
- // Ensure the calculation is not near a state in which N(q) is singular.
154
- if (abs (cp) < 1.0e-3 ) {
155
- throw std::runtime_error (fmt::format (
156
- " The RpyBallMobilizer (implementing a BallRpyJoint) between "
157
- " body {} and body {} has reached a singularity. This occurs when the "
158
- " pitch angle takes values near π/2 + kπ, ∀ k ∈ ℤ. At the current "
159
- " configuration, we have pitch = {}. Drake does not yet support a "
160
- " comparable joint using quaternions, but the feature request is "
161
- " tracked in https://github.com/RobotLocomotion/drake/issues/12404." ,
162
- this ->inboard_body ().name (), this ->outboard_body ().name (), angles[1 ]));
163
- }
164
-
165
+ if (abs (cp) < 1.0e-3 ) ThrowSinceCosPitchIsNearZero (angles[1 ]);
165
166
const T sp = sin (angles[1 ]);
166
167
const T sy = sin (angles[2 ]);
167
168
const T cy = cos (angles[2 ]);
@@ -200,6 +201,96 @@ void RpyBallMobilizer<T>::DoCalcNplusMatrix(const systems::Context<T>& context,
200
201
*Nplus << cy * cp, -sy, 0.0 , sy * cp, cy, 0.0 , -sp, 0.0 , 1.0 ;
201
202
}
202
203
204
+ template <typename T>
205
+ void RpyBallMobilizer<T>::DoCalcNDotMatrix(const systems::Context<T>& context,
206
+ EigenPtr<MatrixX<T>> Ndot) const {
207
+ // Computes the 3x3 matrix Ṅ(q,q̇) that helps relate q̈ = Ṅ(q,q̇)⋅v + N(q)⋅v̇,
208
+ // where q = [r, p, y]ᵀ contains the roll (r), pitch (p) and yaw (y) angles
209
+ // and v = [wx, wy, wz]ᵀ represents W_FM_F (the angular velocity of the
210
+ // mobilizer's M frame measured in its F frame, expressed in the F frame).
211
+ //
212
+ // The 3x3 matrix N(q) relates q̇ to v as q̇ = N(q)⋅v, where
213
+ //
214
+ // [ cos(y) / cos(p), sin(y) / cos(p), 0]
215
+ // N(q) = [ -sin(y), cos(y), 0]
216
+ // [ sin(p) * cos(y) / cos(p), sin(p) * sin(y) / cos(p), 1]
217
+ //
218
+ using std::cos;
219
+ using std::sin;
220
+ const Vector3<T> angles = get_angles (context);
221
+ const T cp = cos (angles[1 ]);
222
+ const T sp = sin (angles[1 ]);
223
+ const T sy = sin (angles[2 ]);
224
+ const T cy = cos (angles[2 ]);
225
+ if (abs (cp) < 1.0e-3 ) ThrowSinceCosPitchIsNearZero (angles[1 ]);
226
+ const T cpi = 1.0 / cp;
227
+ const T cpiSqr = cpi * cpi;
228
+
229
+ // Calculate time-derivative of roll, pitch, and yaw angles.
230
+ const Vector3<T> v = get_angular_velocity (context);
231
+ Vector3<T> qdot;
232
+ DoMapVelocityToQDot (context, v, &qdot);
233
+ const T& pdot = qdot (1 ); // time derivative of pitch angle.
234
+ const T& ydot = qdot (2 ); // time derivative of yaw angle.
235
+ const T sp_pdot = sp * pdot;
236
+ const T cp_ydot = cp * ydot;
237
+ const T cpiSqr_pdot = cpiSqr * pdot;
238
+ const T sp_cpi_ydot = sp * cpi * ydot;
239
+
240
+ Ndot->coeffRef (0 , 0 ) = (cy * sp_pdot - sy * cp_ydot) * cpiSqr;
241
+ Ndot->coeffRef (0 , 1 ) = (sy * sp_pdot + cy * cp_ydot) * cpiSqr;
242
+ Ndot->coeffRef (0 , 2 ) = 0 ;
243
+ Ndot->coeffRef (1 , 0 ) = -cy * ydot;
244
+ Ndot->coeffRef (1 , 1 ) = -sy * ydot;
245
+ Ndot->coeffRef (1 , 2 ) = 0 ;
246
+ Ndot->coeffRef (2 , 0 ) = cy * cpiSqr_pdot - sy * sp_cpi_ydot;
247
+ Ndot->coeffRef (2 , 1 ) = sy * cpiSqr_pdot + cy * sp_cpi_ydot;
248
+ Ndot->coeffRef (2 , 2 ) = 0 ;
249
+ }
250
+
251
+ template <typename T>
252
+ void RpyBallMobilizer<T>::DoCalcNplusDotMatrix(
253
+ const systems::Context<T>& context, EigenPtr<MatrixX<T>> NplusDot) const {
254
+ // Computes the matrix Ṅ⁺(q,q̇) that helps relate v̇ = Ṅ⁺(q,q̇)⋅q̇ + N⁺(q)⋅q̈,
255
+ // where q = [r, p, y]ᵀ contains the roll (r), pitch (p) and yaw (y) angles
256
+ // and v = [wx, wy, wz]ᵀ represents W_FM_F (the angular velocity of the
257
+ // mobilizer's M frame measured in its F frame, expressed in the F frame).
258
+ //
259
+ // The 3x3 matrix N⁺(q) relates v to q̇ as v = N⁺(q)⋅q̇, where
260
+ //
261
+ // [ cos(y) * cos(p), -sin(y), 0]
262
+ // N⁺(q) = [ sin(y) * cos(p), cos(y), 0]
263
+ // [ -sin(p), 0, 1]
264
+ //
265
+ using std::cos;
266
+ using std::sin;
267
+ const Vector3<T> angles = get_angles (context);
268
+ const T cp = cos (angles[1 ]);
269
+ const T sp = sin (angles[1 ]);
270
+ const T sy = sin (angles[2 ]);
271
+ const T cy = cos (angles[2 ]);
272
+
273
+ // Calculate time-derivative of roll, pitch, and yaw angles.
274
+ const Vector3<T> v = get_angular_velocity (context);
275
+ Vector3<T> qdot;
276
+ DoMapVelocityToQDot (context, v, &qdot);
277
+ const T& pdot = qdot (1 ); // time derivative of pitch angle.
278
+ const T& ydot = qdot (2 ); // time derivative of yaw angle.
279
+ const T sp_pdot = sp * pdot;
280
+ const T cy_ydot = cy * ydot;
281
+ const T sy_ydot = sy * ydot;
282
+
283
+ NplusDot->coeffRef (0 , 0 ) = -sy_ydot * cp - cy * sp_pdot;
284
+ NplusDot->coeffRef (0 , 1 ) = -cy_ydot;
285
+ NplusDot->coeffRef (0 , 2 ) = 0 ;
286
+ NplusDot->coeffRef (1 , 0 ) = cy_ydot * cp - sy * sp_pdot;
287
+ NplusDot->coeffRef (1 , 1 ) = -sy * ydot;
288
+ NplusDot->coeffRef (1 , 2 ) = 0 ;
289
+ NplusDot->coeffRef (2 , 0 ) = -cp * pdot;
290
+ NplusDot->coeffRef (2 , 1 ) = 0 ;
291
+ NplusDot->coeffRef (2 , 2 ) = 0 ;
292
+ }
293
+
203
294
template <typename T>
204
295
void RpyBallMobilizer<T>::DoMapVelocityToQDot(
205
296
const systems::Context<T>& context, const Eigen::Ref<const VectorX<T>>& v,
@@ -232,25 +323,11 @@ void RpyBallMobilizer<T>::DoMapVelocityToQDot(
232
323
using std::cos;
233
324
using std::sin;
234
325
const Vector3<T> angles = get_angles (context);
235
- const T cp = cos (angles[1 ]);
236
- if (abs (cp) < 1.0e-3 ) {
237
- throw std::runtime_error (fmt::format (
238
- " The RpyBallMobilizer (implementing a BallRpyJoint) between "
239
- " body {} and body {} has reached a singularity. This occurs when the "
240
- " pitch angle takes values near π/2 + kπ, ∀ k ∈ ℤ. At the current "
241
- " configuration, we have pitch = {}. Drake does not yet support a "
242
- " comparable joint using quaternions, but the feature request is "
243
- " tracked in https://github.com/RobotLocomotion/drake/issues/12404." ,
244
- this ->inboard_body ().name (), this ->outboard_body ().name (), angles[1 ]));
245
- }
246
-
247
- const T& w0 = v[0 ];
248
- const T& w1 = v[1 ];
249
- const T& w2 = v[2 ];
250
-
251
326
const T sp = sin (angles[1 ]);
327
+ const T cp = cos (angles[1 ]);
252
328
const T sy = sin (angles[2 ]);
253
329
const T cy = cos (angles[2 ]);
330
+ if (abs (cp) < 1.0e-3 ) ThrowSinceCosPitchIsNearZero (angles[1 ]);
254
331
const T cpi = 1.0 / cp;
255
332
256
333
// Although we can calculate q̇ = N(q) * v, it is more efficient to implicitly
@@ -265,6 +342,9 @@ void RpyBallMobilizer<T>::DoMapVelocityToQDot(
265
342
// ṙ = (cos(y) * w0 + sin(y) * w1) / cos(p)
266
343
// ṗ = -sin(y) * w0 + cos(y) * w1
267
344
// ẏ = sin(p) * ṙ + w2
345
+ const T& w0 = v[0 ];
346
+ const T& w1 = v[1 ];
347
+ const T& w2 = v[2 ];
268
348
const T rdot = (cy * w0 + sy * w1) * cpi;
269
349
*qdot = Vector3<T>(rdot, -sy * w0 + cy * w1, sp * rdot + w2);
270
350
}
0 commit comments