diff --git a/bindings/pydrake/multibody/BUILD.bazel b/bindings/pydrake/multibody/BUILD.bazel index f0048f766cbb..14d2d3d50f0b 100644 --- a/bindings/pydrake/multibody/BUILD.bazel +++ b/bindings/pydrake/multibody/BUILD.bazel @@ -220,6 +220,7 @@ drake_py_unittest( data = [ "//examples/atlas:models", "//multibody/benchmarks/acrobot:models", + "//multibody/parsing:process_model_directives_test_models", ], deps = [ ":parsing_py", diff --git a/bindings/pydrake/multibody/parsing_py.cc b/bindings/pydrake/multibody/parsing_py.cc index 6b149ecf9233..616fd9eb5078 100644 --- a/bindings/pydrake/multibody/parsing_py.cc +++ b/bindings/pydrake/multibody/parsing_py.cc @@ -5,6 +5,7 @@ #include "drake/bindings/pydrake/pydrake_pybind.h" #include "drake/multibody/parsing/package_map.h" #include "drake/multibody/parsing/parser.h" +#include "drake/multibody/parsing/process_model_directives.h" using drake::geometry::SceneGraph; using drake::multibody::MultibodyPlant; @@ -57,6 +58,38 @@ PYBIND11_MODULE(parsing, m) { py::arg("file_contents"), py::arg("file_type"), py::arg("model_name") = "", cls_doc.AddModelFromString.doc); } + + // Model Directives + { + constexpr auto& parsing_doc = doc.parsing; + py::class_( + m, "ModelDirectives", parsing_doc.ModelDirectives.doc); + m.def("LoadModelDirectives", &parsing::LoadModelDirectives, + py::arg("filename")); + py::class_(m, "ModelInstanceInfo") + .def_readonly("model_name", &parsing::ModelInstanceInfo::model_name) + .def_readonly("model_path", &parsing::ModelInstanceInfo::model_path) + .def_readonly( + "parent_frame_name", &parsing::ModelInstanceInfo::parent_frame_name) + .def_readonly( + "child_frame_name", &parsing::ModelInstanceInfo::child_frame_name) + .def_readonly("X_PC", &parsing::ModelInstanceInfo::X_PC) + .def_readonly( + "model_instance", &parsing::ModelInstanceInfo::model_instance); + m.def( + "ProcessModelDirectives", + [](const parsing::ModelDirectives& directives, + MultibodyPlant* plant, Parser* parser = nullptr) { + std::vector added_models; + parsing::ProcessModelDirectives( + directives, plant, &added_models, parser); + return added_models; + }, + py::arg("directives"), py::arg("plant"), py::arg("parser"), + (std::string(parsing_doc.ProcessModelDirectives.doc) + + "\nNOTE: pydrake does not support `ModelWeldErrorFunction`.") + .c_str()); + } } } // namespace pydrake diff --git a/bindings/pydrake/multibody/test/parsing_test.py b/bindings/pydrake/multibody/test/parsing_test.py index 204c62a75a3f..74ad644dad23 100644 --- a/bindings/pydrake/multibody/test/parsing_test.py +++ b/bindings/pydrake/multibody/test/parsing_test.py @@ -3,6 +3,8 @@ from pydrake.multibody.parsing import ( Parser, PackageMap, + LoadModelDirectives, + ProcessModelDirectives, ) import os @@ -82,3 +84,23 @@ def test_parser_string(self): result = parser.AddModelFromString( file_contents=sdf_contents, file_type="sdf") self.assertIsInstance(result, ModelInstanceIndex) + + def test_model_directives(self): + model_dir = os.path.dirname(FindResourceOrThrow( + "drake/multibody/parsing/test/" + "process_model_directives_test/package.xml")) + plant = MultibodyPlant(time_step=0.01) + parser = Parser(plant=plant) + parser.package_map().PopulateFromFolder(model_dir) + directives_file = model_dir + "/add_scoped_top.yaml" + directives = LoadModelDirectives(directives_file) + added_models = ProcessModelDirectives( + directives=directives, plant=plant, parser=parser) + # Check for an instance. + model_names = [model.model_name for model in added_models] + self.assertIn("extra_model", model_names) + plant.GetModelInstanceByName("extra_model") + + def test_model_directives_doc(self): + """Check that the warning note in the docstring was added.""" + self.assertIn("NOTE:", ProcessModelDirectives.__doc__) diff --git a/multibody/parsing/BUILD.bazel b/multibody/parsing/BUILD.bazel index 7d0be2ccb021..a616bf310729 100644 --- a/multibody/parsing/BUILD.bazel +++ b/multibody/parsing/BUILD.bazel @@ -268,6 +268,9 @@ filegroup( name = "process_model_directives_test_models", testonly = True, data = glob(["test/process_model_directives_test/**"]), + visibility = [ + "//visibility:public", + ], ) drake_cc_googletest(