@@ -124,10 +124,14 @@ TestModel MakeModel(bool combine_welded_bodies) {
124124 // Sanity check: Some information in the SpanningForest should be the same,
125125 // whether or not the welded links are combined.
126126 const internal::SpanningForest& forest = tree.forest ();
127+ const internal::SpanningForest::Mobod& mobod_0 =
128+ forest.mobods (internal::MobodIndex (0 ));
127129 const internal::SpanningForest::Mobod& mobod_1 =
128130 forest.mobods (internal::MobodIndex (1 ));
129- EXPECT_EQ (mobod_1. active_link_ordinal (), LinkOrdinal (1 ));
131+ EXPECT_TRUE (mobod_0. is_world ()); // World is mobod(0) and LinkOrdinal(0).
130132 EXPECT_TRUE (mobod_1.is_base_body ()); // Connects to World via revolute joint.
133+ EXPECT_EQ (mobod_0.active_link_ordinal (), LinkOrdinal (0 ));
134+ EXPECT_EQ (mobod_1.active_link_ordinal (), LinkOrdinal (1 ));
131135
132136 // If the welded links are combined, verify that the composite mobilized body
133137 // has the proper follower link ordinals (the active link ordinal which
@@ -175,96 +179,68 @@ GTEST_TEST(CompositeTest, CompositeSpatialInertia) {
175179 const TestModel explicit_model = MakeModel (false /* no combining */ );
176180 const TestModel composite_model = MakeModel (true /* combine welds */ );
177181
182+ const double m = 1.0 , a = 0.1 ; // mass m and side-length a of solid cubes.
183+ const Frame<double >& world_frame = explicit_model.plant ->world_frame ();
184+ const RigidBody<double >* explicit_links[] = {
185+ explicit_model.link1 , explicit_model.link2 , explicit_model.link3 ,
186+ explicit_model.link4 };
187+ const RigidBody<double >* composite_links[] = {
188+ composite_model.link1 , composite_model.link2 , composite_model.link3 ,
189+ composite_model.link4 };
190+
178191 // The mass matrix is configuration-independent for this model (see above),
179192 // but we check at several angles to guard against future changes.
180193 const std::vector<double > angles = {0.0 , M_PI / 6 , M_PI / 4 , -M_PI / 3 };
181194 for (double angle : angles) {
182195 SetState (explicit_model, angle, 0.0 );
183196 SetState (composite_model, angle, 0.0 );
184197
185- // Verify Link123's spatial inertia not depend on combined welded links
186- const Frame< double >& world_frame = explicit_model. plant -> world_frame ();
198+ // Verify Link123's spatial inertia not depend on combined welded links.
199+ // Note: Due to Bug 1 above, the following test used to fail (now fixed)
187200 SpatialInertia<double > M_EWo_W = explicit_model.plant ->CalcSpatialInertia (
188201 *explicit_model.context , world_frame,
189202 {explicit_model.link1 ->index (), explicit_model.link2 ->index (),
190203 explicit_model.link3 ->index ()});
191-
192- // TODO(Mitiguy) This next tests should be EXPECT_TRUE, hence a bug in
193- // MultibodyPlant::CalcSpatialInertia(), maybe something like poses
194- // using MoBod index rather linkIndex or cache not updated or ...
195204 SpatialInertia<double > M_CWo_W = composite_model.plant ->CalcSpatialInertia (
196205 *composite_model.context , world_frame,
197206 {composite_model.link1 ->index (), composite_model.link2 ->index (),
198- composite_model.link3 ->index ()});
199- EXPECT_FALSE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
207+ composite_model.link3 ->index ()});
208+ EXPECT_TRUE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
200209 M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
201210 MatrixCompareType::relative))
202- << " Spatial inertia mismatch at angle = " << angle;
203-
204- // TODO(Mitiguy) The next three tests show that there is a bug in
205- // MultibodyPlant::CalcSpatialInertia() that incorrectly associates the
206- // Link123 composite spatial inertia with a single link (Link 1 or 2 or 3).
207- M_CWo_W = composite_model.plant ->CalcSpatialInertia (
208- *composite_model.context , world_frame,
209- {composite_model.link1 ->index ()});
210- EXPECT_FALSE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
211- M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
212- MatrixCompareType::relative))
213- << " Spatial inertia mismatch at angle = " << angle;
214-
215- // TODO(Mitiguy) This test and the previous and next test should NOT pass.
216- M_CWo_W = composite_model.plant ->CalcSpatialInertia (
217- *composite_model.context , world_frame,
218- {composite_model.link2 ->index ()});
219- EXPECT_FALSE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
220- M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
221- MatrixCompareType::relative))
222- << " Spatial inertia mismatch at angle = " << angle;
223-
224- // TODO(Mitiguy) This test and the previous two test should NOT pass.
225- M_CWo_W = composite_model.plant ->CalcSpatialInertia (
226- *composite_model.context , world_frame,
227- {composite_model.link3 ->index ()});
228- EXPECT_FALSE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
229- M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
230- MatrixCompareType::relative))
231- << " Spatial inertia mismatch at angle = " << angle;
232-
233- // Calculate Link4's spatial inertia about Wo (world origin) expressed in W.
234- const double m = 1.0 , a = 0.1 ; // Cube's mass (kg) and side-length (meter).
235- const Vector3<double > p_WoL4o_W (4.0 , 0.0 , 0.0 );
236- SpatialInertia<double > M_L4Wo_W_expected =
237- SpatialInertia<double >::SolidCubeWithMass (m, a).Shift (-p_WoL4o_W);
238-
239- // Ensure the spatial inertia for Link 4 is correct in the explicit model.
240- SpatialInertia<double > M_L4Wo_W = explicit_model.plant ->CalcSpatialInertia (
241- *explicit_model.context , world_frame, {explicit_model.link4 ->index ()});
242- EXPECT_TRUE (CompareMatrices (M_L4Wo_W.CopyToFullMatrix6 (),
243- M_L4Wo_W_expected.CopyToFullMatrix6 (),
244- kTolerance , MatrixCompareType::relative))
245- << " Spatial inertia mismatch for Link4 at angle = " << angle;
246-
247- // Ensure the spatial inertia for Link 4 is correct in the composite model.
248- // TODO(Mitiguy) There is a bug in MultibodyPlant::CalcSpatialInertia() when
249- // composite mobilized bodies are used. The spatial inertia for the World
250- // body (which is nan) replaces that of Link 4 (which is welded to world).
251- // TODO(Mitiguy) Remove dead code after we are sure the bug is fixed.
252- if (!kDrakeAssertIsArmed ) {
253- M_L4Wo_W = composite_model.plant ->CalcSpatialInertia (
254- *composite_model.context , world_frame,
255- {composite_model.link4 ->index ()});
256- // TODO(Mitiguy) This test should be EXPECT_TRUE not EXPECT_FALSE.
257- EXPECT_TRUE (CompareMatrices (M_L4Wo_W.CopyToFullMatrix6 (),
258- M_L4Wo_W_expected.CopyToFullMatrix6 (),
259- kTolerance , MatrixCompareType::relative))
260- << " Spatial inertia mismatch for Link4 at angle = " << angle;
261- } else {
262- // TODO(Mitiguy) Fix as an exception should not be thrown here.
263- DRAKE_EXPECT_THROWS_MESSAGE_IF_ARMED (
264- composite_model.plant ->CalcSpatialInertia (
265- *composite_model.context , world_frame,
266- {composite_model.link4 ->index ()}),
267- " [\\ s\\ S]*did not pass the test CouldBePhysicallyValid[\\ s\\ S]*" );
211+ << " Link123 spatial inertia mismatch at angle = " << angle;
212+
213+ // Tests that uncovered old bugs in MultibodyPlant::CalcSpatialInertia().
214+ // Bug 1: When CalcSpatialInertia() was called with composite_model on a
215+ // single link (Link 1 or 2 or 3), the returned spatial inertia was for the
216+ // entire Link123 composite mobod.
217+ // Bug 2: When CalcSpatialInertia() was called with composite_model on link4
218+ // (which is welded to world), an exception was thrown (debug mode) or NaN
219+ // were returned (release mode).
220+ for (int i = 0 ; i < 3 ; ++i) {
221+ const RigidBody<double >* explicit_linki = explicit_links[i];
222+ const RigidBody<double >* composite_linki = composite_links[i];
223+ M_EWo_W = explicit_model.plant ->CalcSpatialInertia (
224+ *explicit_model.context , world_frame, {explicit_linki->index ()});
225+ M_CWo_W = composite_model.plant ->CalcSpatialInertia (
226+ *composite_model.context , world_frame, {composite_linki->index ()});
227+ EXPECT_TRUE (CompareMatrices (M_EWo_W.CopyToFullMatrix6 (),
228+ M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
229+ MatrixCompareType::relative))
230+ << " Spatial inertia mismatch: link" << i + 1
231+ << " at angle = " << angle;
232+
233+ // Due to special case of link4 being welded to world, compare link4's
234+ // spatial inertia to its expected value.
235+ if (i == 3 ) {
236+ const Vector3<double > p_WoL4o_W (4.0 , 0.0 , 0.0 );
237+ SpatialInertia<double > M_L4Wo_W_expected =
238+ SpatialInertia<double >::SolidCubeWithMass (m, a).Shift (-p_WoL4o_W);
239+ EXPECT_TRUE (CompareMatrices (M_L4Wo_W_expected.CopyToFullMatrix6 (),
240+ M_CWo_W.CopyToFullMatrix6 (), kTolerance ,
241+ MatrixCompareType::relative))
242+ << " Inaccurate link4 spatial inertia at angle = " << angle;
243+ }
268244 }
269245
270246 // Ensure the mass matrix does not depend on welded links being combined.
0 commit comments