11/* Tests that mass properties are identical whether welded-together links are
22modeled with unfused weld joints or whether links that are welded together
3- are fused into a mobilized body (fused Mobod). The test builds two identical
3+ are fused onto a mobilized body (fused Mobod). The test builds two identical
44models that differ only in whether SetFuseWeldedLinks() is enabled. */
55
66#include < limits>
@@ -158,15 +158,16 @@ void SetState(const TestModel& m, double angle_rad, double angular_vel) {
158158}
159159
160160/* Verify that the spatial inertia for fused mobod Link123 (with links 1, 2, 3)
161- matches the sum of spatial inertias for unfused links 1, 2, 3. Next, ensure
162- spatial inertia for each of the follower links in a fused mobod are computed
163- correctly. Lastly, the 1x1 mass matrix for this 1-DOF model directly depends on
164- the spatial inertia of Link123, so we also verify:
165- (a) The mass matrix is identical between the fused and unfused weld models at
166- several configurations.
161+ matches the sum of spatial inertias for unfused links 1, 2, 3 at several poses.
162+ Next, ensure spatial inertia for each of the follower links in a fused mobod are
163+ computed correctly. Lastly, the 1x1 mass matrix for this 1-DOF model directly
164+ depends on the spatial inertia of Link123, so we also verify:
165+ (a) The mass matrix is identical between the fused and unfused weld models.
167166 (b) The mass matrix matches the analytically computed value.
168167
169- Analytical derivation
168+ Similarly, verify spatial momentum calculations.
169+
170+ Analytical derivation for spatial inertia.
170171---------------------
171172The model has 4 links, Linki (i=1,2,3,4), each a 1 kg solid cube of side 0.1 m.
172173Links 1,2,3 are welded together and are connected to World via a revolute joint
@@ -181,8 +182,7 @@ For a solid cube of mass m and side a, its moment of inertia about any axis
181182through its COM is m*a²/6. The parallel axis theorem calculates each cube's
182183moment of inertia about the revolute's z-axis via: Iᵢ = m*a²/6 + m*(dᵢ)²,
183184where dᵢ (i=1,2,3) is the distance between each cube's COM and the revolute's
184- z-axis. Iᵢ is independent of joint angle because the links are welded
185- together.
185+ z-axis. Iᵢ is independent of joint angle because the links are welded together.
186186
187187 Link1: I₁ = 1*(0.1)²/6 + 1*0² = 1/600 + 0 (d² = 0)
188188 Link2: I₂ = 1*(0.1)²/6 + 1*1² = 1/600 + 1 (d² = 1)
@@ -201,15 +201,15 @@ GTEST_TEST(FusedTest, CompositeSpatialInertia) {
201201 fused_model.link1 , fused_model.link2 , fused_model.link3 ,
202202 fused_model.link4 };
203203
204- // The mass matrix is configuration-independent for this model (see above),
205- // but we check at several angles to guard against future changes.
204+ // Note: The mass matrix is configuration-independent for this model (see
205+ // above), but we check at several angles to guard against future changes.
206+ // Note: Use a non-zero angular velocity so spatial momentum is non-trivial.
206207 const std::vector<double > angles = {0.0 , M_PI / 6 , M_PI / 4 , -M_PI / 3 };
207208 for (double angle : angles) {
208- SetState (unfused_model, angle, 0.0 );
209- SetState (fused_model, angle, 0.0 );
209+ SetState (unfused_model, angle, 2.0 /* rad/s */ );
210+ SetState (fused_model, angle, 2.0 /* rad/s */ );
210211
211- // Verify Link123's summed spatial inertia does not depend on whether they
212- // are on a fused mobod.
212+ // Verify Link123's summed spatial inertia does not depend on fused links.
213213 SpatialInertia<double > M_UWo_W = unfused_model.plant ->CalcSpatialInertia (
214214 *unfused_model.context , world_frame,
215215 {unfused_model.link1 ->index (), unfused_model.link2 ->index (),
@@ -223,8 +223,29 @@ GTEST_TEST(FusedTest, CompositeSpatialInertia) {
223223 MatrixCompareType::relative))
224224 << " Link123 spatial inertia mismatch at angle = " << angle;
225225
226- // Ensure that individual link spatial inertias are reported correctly
227- // regardless of whether they were fused.
226+ // Verify Link123's summed spatial momentum does not depend on fused links.
227+ const Vector3<double > p_WoP_W (1.1 , -2.3 , 4.2 );
228+ SpatialMomentum<double > L_WUP_W =
229+ unfused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
230+ *unfused_model.context ,
231+ {unfused_model.link1 ->model_instance (),
232+ unfused_model.link2 ->model_instance (),
233+ unfused_model.link3 ->model_instance ()},
234+ p_WoP_W);
235+ SpatialMomentum<double > L_WCP_W =
236+ fused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
237+ *fused_model.context ,
238+ {fused_model.link1 ->model_instance (),
239+ fused_model.link2 ->model_instance (),
240+ fused_model.link3 ->model_instance ()},
241+ p_WoP_W);
242+ // TODO(Mitiguy) EXPECT_FALSE is wrong! Should be EXPECT_TRUE!
243+ EXPECT_FALSE (CompareMatrices (L_WUP_W .get_coeffs (), L_WCP_W .get_coeffs (),
244+ kTolerance , MatrixCompareType::relative))
245+ << " Link123 spatial momentum mismatch at angle = " << angle;
246+
247+ // Ensure that individual link spatial inertias and spatial momentum are
248+ // accurately calculated, regardless of whether they were fused.
228249 for (int i = 0 ; i < 4 ; ++i) {
229250 const RigidBody<double >* unfused_linki = unfused_links[i];
230251 const RigidBody<double >* fused_linki = fused_links[i];
@@ -238,6 +259,17 @@ GTEST_TEST(FusedTest, CompositeSpatialInertia) {
238259 << " Spatial inertia mismatch: link" << i + 1
239260 << " at angle = " << angle;
240261
262+ // Verify individual link's spatial momentum do not depend on fused links.
263+ L_WUP_W = unfused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
264+ *unfused_model.context , {unfused_linki->model_instance ()}, p_WoP_W);
265+ L_WCP_W = fused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
266+ *fused_model.context , {fused_linki->model_instance ()}, p_WoP_W);
267+ // TODO(Mitiguy) EXPECT_FALSE is wrong! Should be EXPECT_TRUE!
268+ EXPECT_FALSE (CompareMatrices (L_WUP_W .get_coeffs (), L_WCP_W .get_coeffs (),
269+ kTolerance , MatrixCompareType::relative))
270+ << " Spatial momentum mismatch: link" << i + 1
271+ << " at angle = " << angle;
272+
241273 // Since link4 is welded to world, special-case calculations are used. For
242274 // this special case, also compare link4 results to an analytical value.
243275 if (i == 3 ) {
@@ -248,6 +280,13 @@ GTEST_TEST(FusedTest, CompositeSpatialInertia) {
248280 M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
249281 MatrixCompareType::relative))
250282 << " Inaccurate link4 spatial inertia at angle = " << angle;
283+
284+ // Link4's spatial momentum should always be zero (welded to ground).
285+ // TODO(Mitiguy) EXPECT_FALSE is wrong! Should be EXPECT_TRUE!
286+ EXPECT_FALSE (CompareMatrices (L_WCP_W .get_coeffs (),
287+ Vector6<double >::Zero (), kTolerance ,
288+ MatrixCompareType::relative))
289+ << " Inaccurate link 4 spatial momentum at angle = " << angle;
251290 }
252291 }
253292
@@ -268,20 +307,20 @@ GTEST_TEST(FusedTest, CompositeSpatialInertia) {
268307 << " Mass matrix analytical mismatch at angle = " << angle;
269308
270309 // Ensure spatial momentum does not depend on fused welded links.
271- // Use a non-zero angular velocity so that the momentum is non-trivial.
272- SetState (unfused_model, angle, 2.0 /* rad/s */ );
273- SetState (fused_model, angle, 2.0 /* rad/s */ );
274- const Vector3<double > p_WoWo_W = Vector3<double >::Zero ();
310+ // Also, use an "about-point" P which is not coincident with Wo.
275311 const SpatialMomentum<double > L_unfused =
276312 unfused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
277- *unfused_model.context , p_WoWo_W);
313+ *unfused_model.context , p_WoP_W);
314+
315+ // Test the function for the entire system's spatial momentum in world.
278316 const SpatialMomentum<double > L_fused =
279317 fused_model.plant ->CalcSpatialMomentumInWorldAboutPoint (
280- *fused_model.context , p_WoWo_W);
281- // TODO(Mitiguy) EXPECT_FALSE is wrong, should be EXPECT_TRUE !
318+ *fused_model.context , p_WoP_W);
282319 EXPECT_TRUE (CompareMatrices (L_unfused.get_coeffs (), L_fused.get_coeffs (),
283320 kTolerance , MatrixCompareType::relative))
284321 << " Spatial momentum mismatch at angle = " << angle;
322+
323+ // Test the function for a single link's spatial momentum in world.
285324 }
286325}
287326
0 commit comments