diff --git a/multibody/parsing/BUILD.bazel b/multibody/parsing/BUILD.bazel index 866dfe7ad1aa..a596c82b1350 100644 --- a/multibody/parsing/BUILD.bazel +++ b/multibody/parsing/BUILD.bazel @@ -652,10 +652,13 @@ drake_cc_googletest( ":test_models", "//geometry:test_obj_files", "//geometry:test_stl_files", + "@mujoco_menagerie_internal//:google_robot", + "@mujoco_menagerie_internal//:kuka_iiwa_14", ] + _DM_CONTROL_MUJOCO_FILES, deps = [ ":detail_mujoco_parser", "//common:find_resource", + "//common:find_runfiles", "//common/test_utilities:diagnostic_policy_test_base", "//common/test_utilities:eigen_matrix_compare", "//common/test_utilities:expect_throws_message", diff --git a/multibody/parsing/test/detail_mujoco_parser_test.cc b/multibody/parsing/test/detail_mujoco_parser_test.cc index 6a2feb4647d8..3991694456ac 100644 --- a/multibody/parsing/test/detail_mujoco_parser_test.cc +++ b/multibody/parsing/test/detail_mujoco_parser_test.cc @@ -6,6 +6,7 @@ #include #include "drake/common/find_resource.h" +#include "drake/common/find_runfiles.h" #include "drake/common/test_utilities/diagnostic_policy_test_base.h" #include "drake/common/test_utilities/eigen_matrix_compare.h" #include "drake/common/test_utilities/expect_throws_message.h" @@ -117,10 +118,10 @@ class MujocoParserTest : public test::DiagnosticPolicyTestBase { "drake/multibody/parsing/test/box_package/urdfs/box.urdf"))}; }; -class GymModelTest : public MujocoParserTest, - public testing::WithParamInterface {}; +class DeepMindControlTest : public MujocoParserTest, + public testing::WithParamInterface {}; -TEST_P(GymModelTest, GymModel) { +TEST_P(DeepMindControlTest, DeepMindControl) { // Confirm successful parsing of the MuJoCo models in the DeepMind control // suite. std::string model{GetParam()}; @@ -134,13 +135,39 @@ TEST_P(GymModelTest, GymModel) { warning_records_.clear(); } -const char* gym_models[] = { +const char* dm_control_models[] = { "acrobot", "cartpole", "cheetah", "finger", "fish", "hopper", "humanoid", "humanoid_CMU", "lqr", "manipulator", "pendulum", "point_mass", "quadruped", "reacher", "stacker", "swimmer", "walker"}; -INSTANTIATE_TEST_SUITE_P(GymModels, GymModelTest, - testing::ValuesIn(gym_models)); +INSTANTIATE_TEST_SUITE_P(DeepMindControl, DeepMindControlTest, + testing::ValuesIn(dm_control_models)); + +class MujocoMenagerieTest : public MujocoParserTest, + public testing::WithParamInterface {}; + +TEST_P(MujocoMenagerieTest, MujocoMenagerie) { + // Confirm successful parsing of the MuJoCo models in the DeepMind control + // suite. + std::string model{GetParam()}; + const RlocationOrError rlocation = FindRunfile( + fmt::format("mujoco_menagerie_internal/{}.xml", model)); + ASSERT_EQ(rlocation.error, ""); + AddModelFromFile(rlocation.abspath, model); + + EXPECT_TRUE(plant_.HasModelInstanceNamed(model)); + + // For this test, ignore all warnings. + warning_records_.clear(); +} + +const char* mujoco_menagerie_models[] = {"google_robot/robot", + "kuka_iiwa_14/iiwa14"}; +// TODO(russt): Add the remaining models, once they can be parsed correctly, as +// tracked in #20444. + +INSTANTIATE_TEST_SUITE_P(MujocoMenagerie, MujocoMenagerieTest, + testing::ValuesIn(mujoco_menagerie_models)); // In addition to confirming that the parser can successfully parse the model, // this test can be used to manually inspect the resulting visualization. diff --git a/multibody/tree/geometry_spatial_inertia.h b/multibody/tree/geometry_spatial_inertia.h index cee79901180c..503760fa66cc 100644 --- a/multibody/tree/geometry_spatial_inertia.h +++ b/multibody/tree/geometry_spatial_inertia.h @@ -10,7 +10,7 @@ namespace multibody { /** Computes the SpatialInertia of a body made up of a homogeneous material (of given `density` in kg/m³) uniformly distributed in the volume of the given `shape`. - + The `shape` is defined in its canonical frame S and the body in frame B. The two frames are coincident and aligned (i.e., X_SB = I). @@ -31,6 +31,10 @@ namespace multibody { given `shape`. @throws std::exception if `shape` is an instance of geometry::HalfSpace or geometry::MeshcatCone. + @throws std::exception if the resulting spatial inertia computation does not + result in a physically meaningful value. See + SpatialInertia::IsPhysicallyValid() for more + information. @pydrake_mkdoc_identifier{shape} */ SpatialInertia CalcSpatialInertia(const geometry::Shape& shape, double density); diff --git a/tools/workspace/default.bzl b/tools/workspace/default.bzl index 6c54dae6d2d4..bf11f2757993 100644 --- a/tools/workspace/default.bzl +++ b/tools/workspace/default.bzl @@ -51,6 +51,7 @@ load("//tools/workspace/meshcat:repository.bzl", "meshcat_repository") load("//tools/workspace/mosek:repository.bzl", "mosek_repository") load("//tools/workspace/mpmath_py_internal:repository.bzl", "mpmath_py_internal_repository") # noqa load("//tools/workspace/msgpack_internal:repository.bzl", "msgpack_internal_repository") # noqa +load("//tools/workspace/mujoco_menagerie_internal:repository.bzl", "mujoco_menagerie_internal_repository") # noqa load("//tools/workspace/mumps_internal:repository.bzl", "mumps_internal_repository") # noqa load("//tools/workspace/mypy_extensions_internal:repository.bzl", "mypy_extensions_internal_repository") # noqa load("//tools/workspace/mypy_internal:repository.bzl", "mypy_internal_repository") # noqa @@ -219,6 +220,8 @@ def add_default_repositories(excludes = [], mirrors = DEFAULT_MIRRORS): mpmath_py_internal_repository(name = "mpmath_py_internal", mirrors = mirrors) # noqa if "msgpack_internal" not in excludes: msgpack_internal_repository(name = "msgpack_internal", mirrors = mirrors) # noqa + if "mujoco_menagerie_internal" not in excludes: + mujoco_menagerie_internal_repository(name = "mujoco_menagerie_internal", mirrors = mirrors) # noqa if "mumps_internal" not in excludes: mumps_internal_repository(name = "mumps_internal") if "mypy_extensions_internal" not in excludes: diff --git a/tools/workspace/mujoco_menagerie_internal/BUILD.bazel b/tools/workspace/mujoco_menagerie_internal/BUILD.bazel new file mode 100644 index 000000000000..67914ea7e0a0 --- /dev/null +++ b/tools/workspace/mujoco_menagerie_internal/BUILD.bazel @@ -0,0 +1,3 @@ +load("//tools/lint:lint.bzl", "add_lint_tests") + +add_lint_tests() diff --git a/tools/workspace/mujoco_menagerie_internal/package.BUILD.bazel b/tools/workspace/mujoco_menagerie_internal/package.BUILD.bazel new file mode 100644 index 000000000000..7b84de9832a2 --- /dev/null +++ b/tools/workspace/mujoco_menagerie_internal/package.BUILD.bazel @@ -0,0 +1,28 @@ +# -*- bazel -*- + +package(default_visibility = ["//visibility:public"]) + +# We maintain a different filegroup for each subdirectory of the repository, +# and only once we have vetted the license (in the main LICENSE file). +# +# DO NOT ADD new subdirectories here unless and until you vet their LICENSE. + +filegroup( + name = "google_robot", + srcs = [ + "google_robot/robot.xml", + "google_robot/LICENSE", + ] + glob([ + "google_robot/assets/*.obj", + "google_robot/assets/*.png", + "google_robot/assets/*.stl", + ]), +) + +filegroup( + name = "kuka_iiwa_14", + srcs = [ + "kuka_iiwa_14/iiwa14.xml", + "kuka_iiwa_14/LICENSE", + ] + glob(["kuka_iiwa_14/assets/*.obj"]), +) diff --git a/tools/workspace/mujoco_menagerie_internal/repository.bzl b/tools/workspace/mujoco_menagerie_internal/repository.bzl new file mode 100644 index 000000000000..adbc593d7c00 --- /dev/null +++ b/tools/workspace/mujoco_menagerie_internal/repository.bzl @@ -0,0 +1,13 @@ +load("//tools/workspace:github.bzl", "github_archive") + +def mujoco_menagerie_internal_repository( + name, + mirrors = None): + github_archive( + name = name, + repository = "google-deepmind/mujoco_menagerie", + commit = "af493511dbdfce2e046858a4d2f2955e063e17fd", + sha256 = "2975d9a03728bf1b8a2b7d2b14e62bc8d411031b73264e797dcce649bbf4811c", # noqa + build_file = ":package.BUILD.bazel", + mirrors = mirrors, + )