Skip to content

Commit 4746323

Browse files
authored
Modified MultibodyTree to replace its internal topology with SpanningForest. (#21820)
Harmonized BodyNode & Mobilizer with Mobilized Body numbering.
1 parent 0741876 commit 4746323

File tree

76 files changed

+1047
-2175
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1047
-2175
lines changed

geometry/optimization/cspace_free_internal.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct BodyPairHash {
3838
[[nodiscard]] bool ChainIsWelded(const multibody::MultibodyPlant<double>& plant,
3939
multibody::BodyIndex start,
4040
multibody::BodyIndex end) {
41-
const std::vector<multibody::internal::MobilizerIndex> mobilizers =
41+
const std::vector<multibody::internal::MobodIndex> mobilizers =
4242
multibody::internal::FindMobilizersOnPath(plant, start, end);
4343
if (mobilizers.size() == 0) {
4444
return true;

geometry/optimization/cspace_free_polytope_base.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ void CspaceFreePolytopeBase::CalcSBoundsPolynomial(
208208
void CspaceFreePolytopeBase::SetIndicesOfSOnChainForBodyPair(
209209
const SortedPair<multibody::BodyIndex>& body_pair) {
210210
if (!map_body_pair_to_s_on_chain_.contains(body_pair)) {
211-
const std::vector<multibody::internal::MobilizerIndex> mobilizer_indices =
211+
const std::vector<multibody::internal::MobodIndex> mobilizer_indices =
212212
multibody::internal::FindMobilizersOnPath(rational_forward_kin_.plant(),
213213
body_pair.first(),
214214
body_pair.second());

multibody/parsing/test/detail_dmd_parser_test.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ TEST_F(DmdParserTest, DefaultFreeBodyPoseAddsDuplicateJoints) {
304304

305305
DRAKE_EXPECT_THROWS_MESSAGE(
306306
ParseModelDirectives(directives),
307-
".*already has a joint .* connecting 'dummy' to 'ball'.*");
307+
".*already has.*joint.*connecting.*dummy.*ball.*");
308308
}
309309

310310
} // namespace

multibody/plant/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ drake_cc_library(
147147
"//multibody/contact_solvers/sap",
148148
"//multibody/fem",
149149
"//multibody/hydroelastics:hydroelastic_engine",
150+
"//multibody/topology:multibody_topology",
150151
"//multibody/tree",
151152
"//systems/framework:diagram_builder",
152153
"//systems/framework:leaf_system",

multibody/plant/compliant_contact_manager.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ AccelerationsDueNonConstraintForcesCache<
4040
topology)
4141
: forces(topology.num_rigid_bodies(), topology.num_velocities()),
4242
abic(topology),
43-
Zb_Bo_W(topology.num_rigid_bodies()),
43+
Zb_Bo_W(topology.num_mobods()),
4444
aba_forces(topology),
4545
ac(topology) {}
4646

multibody/plant/multibody_plant.cc

+16-8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "drake/multibody/plant/hydroelastic_traction_calculator.h"
2828
#include "drake/multibody/plant/make_discrete_update_manager.h"
2929
#include "drake/multibody/plant/slicing_and_indexing.h"
30+
#include "drake/multibody/topology/graph.h"
3031
#include "drake/multibody/tree/door_hinge.h"
3132
#include "drake/multibody/tree/prismatic_joint.h"
3233
#include "drake/multibody/tree/prismatic_spring.h"
@@ -1053,8 +1054,10 @@ const SceneGraphInspector<T>& MultibodyPlant<T>::EvalSceneGraphInspector(
10531054
template <typename T>
10541055
std::vector<const RigidBody<T>*> MultibodyPlant<T>::GetBodiesWeldedTo(
10551056
const RigidBody<T>& body) const {
1057+
const internal::LinkJointGraph& graph = internal_tree().graph();
10561058
const std::set<BodyIndex> island =
1057-
internal_tree().multibody_graph().FindBodiesWeldedTo(body.index());
1059+
graph.forest_is_valid() ? graph.GetLinksWeldedTo(body.index())
1060+
: graph.CalcLinksWeldedTo(body.index());
10581061
// Map body indices to pointers.
10591062
std::vector<const RigidBody<T>*> sub_graph_bodies;
10601063
for (BodyIndex body_index : island) {
@@ -1079,7 +1082,10 @@ std::vector<BodyIndex> MultibodyPlant<T>::GetBodiesKinematicallyAffectedBy(
10791082
fmt::format("{}: joint with index {} is welded.", __func__, joint));
10801083
}
10811084
}
1082-
return internal_tree().GetBodiesKinematicallyAffectedBy(joint_indexes);
1085+
const std::set<BodyIndex> links =
1086+
internal_tree().GetBodiesKinematicallyAffectedBy(joint_indexes);
1087+
// TODO(sherm1) Change the return type to set to avoid this copy.
1088+
return std::vector<BodyIndex>(links.cbegin(), links.cend());
10831089
}
10841090

10851091
template <typename T>
@@ -1157,7 +1163,10 @@ void MultibodyPlant<T>::SetFreeBodyPoseInAnchoredFrame(
11571163
DRAKE_MBP_THROW_IF_NOT_FINALIZED();
11581164
this->ValidateContext(context);
11591165

1160-
if (!internal_tree().get_topology().IsBodyAnchored(frame_F.body().index())) {
1166+
if (!internal_tree()
1167+
.graph()
1168+
.link_by_index(frame_F.body().index())
1169+
.is_anchored()) {
11611170
throw std::logic_error("Frame '" + frame_F.name() +
11621171
"' must be anchored to the world frame.");
11631172
}
@@ -1527,7 +1536,7 @@ void MultibodyPlant<T>::ApplyDefaultCollisionFilters() {
15271536
}
15281537
// We explicitly exclude collisions within welded subgraphs.
15291538
std::vector<std::set<BodyIndex>> subgraphs =
1530-
internal_tree().multibody_graph().FindSubgraphsOfWeldedBodies();
1539+
internal_tree().graph().GetSubgraphsOfWeldedLinks();
15311540
for (const auto& subgraph : subgraphs) {
15321541
// Only operate on non-trivial weld subgraphs.
15331542
if (subgraph.size() <= 1) {
@@ -3911,12 +3920,11 @@ void MultibodyPlant<T>::CalcReactionForces(
39113920
// necessary frame conversions.
39123921
for (JointIndex joint_index : GetJointIndices()) {
39133922
const Joint<T>& joint = get_joint(joint_index);
3914-
const internal::MobilizerIndex mobilizer_index =
3923+
const internal::MobodIndex mobilizer_index =
39153924
internal_tree().get_joint_mobilizer(joint_index);
39163925
const internal::Mobilizer<T>& mobilizer =
39173926
internal_tree().get_mobilizer(mobilizer_index);
3918-
const internal::MobodIndex mobod_index =
3919-
mobilizer.get_topology().mobod_index;
3927+
const internal::MobodIndex mobod_index = mobilizer.mobod().index();
39203928

39213929
// Force on mobilized body B at mobilized frame's origin Mo, expressed in
39223930
// world frame.
@@ -4051,7 +4059,7 @@ void MultibodyPlant<T>::RemoveUnsupportedScalars(
40514059
template <typename T>
40524060
std::vector<std::set<BodyIndex>>
40534061
MultibodyPlant<T>::FindSubgraphsOfWeldedBodies() const {
4054-
return internal_tree().multibody_graph().FindSubgraphsOfWeldedBodies();
4062+
return internal_tree().graph().GetSubgraphsOfWeldedLinks();
40554063
}
40564064

40574065
template <typename T>

multibody/plant/multibody_plant.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "drake/multibody/plant/dummy_physical_model.h"
2828
#include "drake/multibody/plant/multibody_plant_config.h"
2929
#include "drake/multibody/plant/physical_model_collection.h"
30+
#include "drake/multibody/topology/graph.h"
3031
#include "drake/multibody/tree/force_element.h"
3132
#include "drake/multibody/tree/frame.h"
3233
#include "drake/multibody/tree/joint.h"
@@ -4640,8 +4641,8 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
46404641
return internal_tree().world_frame();
46414642
}
46424643

4643-
/// Returns the number of bodies in the model, including the "world" body,
4644-
/// which is always part of the model.
4644+
/// Returns the number of RigidBody elements in the model, including the
4645+
/// "world" RigidBody, which is always part of the model.
46454646
/// @see AddRigidBody().
46464647
int num_bodies() const { return internal_tree().num_bodies(); }
46474648

@@ -4657,7 +4658,7 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
46574658
/// @throws std::exception if called pre-finalize.
46584659
bool IsAnchored(const RigidBody<T>& body) const {
46594660
DRAKE_MBP_THROW_IF_NOT_FINALIZED();
4660-
return internal_tree().get_topology().IsBodyAnchored(body.index());
4661+
return internal_tree().graph().link_by_index(body.index()).is_anchored();
46614662
}
46624663

46634664
/// @returns `true` if a body named `name` was added to the %MultibodyPlant.
@@ -4771,7 +4772,7 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
47714772
const RigidBody<T>& body) const;
47724773

47734774
/// Returns all bodies whose kinematics are transitively affected by the given
4774-
/// vector of joints. The affected bodies are returned in increasing order of
4775+
/// vector of Joints. The affected bodies are returned in increasing order of
47754776
/// body indexes. Note that this is a kinematic relationship rather than a
47764777
/// dynamic one. For example, if one of the inboard joints is a free (6dof)
47774778
/// joint, the kinematic influence is still felt even though dynamically
@@ -5289,7 +5290,7 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
52895290
/// @} <!-- Introspection -->
52905291

52915292
#ifndef DRAKE_DOXYGEN_CXX
5292-
// Internal-only access to MultibodyGraph::FindSubgraphsOfWeldedBodies();
5293+
// Internal-only access to LinkJointGraph::FindSubgraphsOfWeldedBodies();
52935294
// TODO(calderpg-tri) Properly expose this method (docs/tests/bindings).
52945295
std::vector<std::set<BodyIndex>> FindSubgraphsOfWeldedBodies() const;
52955296
#endif

multibody/plant/test/multibody_plant_test.cc

+12-13
Original file line numberDiff line numberDiff line change
@@ -1711,8 +1711,12 @@ GTEST_TEST(MultibodyPlantTest, GetBodiesKinematicallyAffectedBy) {
17111711
".*No joint with index.*registered.*removed.");
17121712
}
17131713

1714-
// Regression test for unhelpful error message -- see #14641.
1715-
GTEST_TEST(MultibodyPlantTest, ReversedWeldError) {
1714+
// Weld a body to World but with the body as the parent and World as the
1715+
// child. This is fine but must be implemented with a reversed Weld
1716+
// mobilizer that specifies World as the inboard body. Drake doesn't support
1717+
// that yet.
1718+
// TODO(sherm1) Remove this restriction and fix the test.
1719+
GTEST_TEST(MultibodyPlantTest, ReversedWeldJoint) {
17161720
// This test expects that the following model has a world body and a pair of
17171721
// welded-together bodies.
17181722
const std::string sdf_url =
@@ -1725,15 +1729,10 @@ GTEST_TEST(MultibodyPlantTest, ReversedWeldError) {
17251729
"extra", default_model_instance(), SpatialInertia<double>::NaN());
17261730
plant.WeldFrames(extra.body_frame(), plant.world_frame());
17271731

1728-
// The important property of this message is that it reports some identifier
1729-
// for the involved objects, so at least the developer can map those back to
1730-
// objects and deduce what API call was in error. If the details of the
1731-
// message change, update this check to match. If in future the error can be
1732-
// caught at the WeldFrames() step, so much the better. Modify this test to
1733-
// reflect that.
1734-
DRAKE_EXPECT_THROWS_MESSAGE(
1735-
plant.Finalize(),
1736-
".*already has a joint.*extra_welds_to_world.*joint.*not allowed.*");
1732+
DRAKE_EXPECT_THROWS_MESSAGE(plant.Finalize(),
1733+
".*Finalize.*: parent/child ordering.*"
1734+
"Joint extra_welds_to_world.*WorldModelInstance.*"
1735+
"would have to be reversed.*");
17371736
}
17381737

17391738
// Verifies exact set of output ports we expect to be a direct feedthrough of
@@ -3848,8 +3847,8 @@ GTEST_TEST(SetRandomTest, SetDefaultWhenNoDistributionSpecified) {
38483847
// explicitly added).
38493848
const RigidBody<double>& body1 =
38503849
plant.AddRigidBody("free body 1", SpatialInertia<double>::MakeUnitary());
3851-
plant.AddJoint<QuaternionFloatingJoint>("" + body1.name(), plant.world_body(),
3852-
{}, body1, {});
3850+
plant.AddJoint<QuaternionFloatingJoint>(body1.name(), plant.world_body(), {},
3851+
body1, {});
38533852
const std::string acrobot_url =
38543853
"package://drake/multibody/benchmarks/acrobot/acrobot.sdf";
38553854
const ModelInstanceIndex acrobot =

multibody/plant/test/sap_driver_multidof_joints_test.cc

+6-3
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,17 @@ class MultiDofJointWithLimits final : public Joint<T> {
9191
int do_get_velocity_start() const override { return 0; }
9292
int do_get_position_start() const override { return 0; }
9393

94-
std::unique_ptr<typename Joint<T>::BluePrint> MakeImplementationBlueprint()
95-
const override {
94+
std::unique_ptr<typename Joint<T>::BluePrint> MakeImplementationBlueprint(
95+
const SpanningForest::Mobod& mobod) const override {
9696
auto blue_print = std::make_unique<typename Joint<T>::BluePrint>();
97+
const auto [inboard_frame, outboard_frame] =
98+
this->tree_frames(mobod.is_reversed());
99+
// TODO(sherm1) The mobilizer needs to be reversed, not just the frames.
97100
// The only restriction here relevant for these tests is that we provide a
98101
// mobilizer with kNumDofs positions and velocities, so that indexes are
99102
// consistent during MultibodyPlant::Finalize().
100103
auto revolute_mobilizer = std::make_unique<internal::RpyBallMobilizer<T>>(
101-
this->frame_on_parent(), this->frame_on_child());
104+
mobod, *inboard_frame, *outboard_frame);
102105
blue_print->mobilizer = std::move(revolute_mobilizer);
103106
return blue_print;
104107
}

multibody/rational/rational_forward_kinematics.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ RationalForwardKinematics::RationalForwardKinematics(
6666
map_mobilizer_to_s_index_ = std::vector<int>(tree.num_mobilizers(), -1);
6767
for (BodyIndex body_index(1); body_index < plant_.num_bodies();
6868
++body_index) {
69-
const internal::RigidBodyTopology& body_topology =
69+
const internal::RigidBodyTopology& rigid_body_topology =
7070
tree.get_topology().get_rigid_body(body_index);
7171
const internal::Mobilizer<double>* mobilizer =
72-
&(tree.get_mobilizer(body_topology.inboard_mobilizer));
72+
&(tree.get_mobilizer(rigid_body_topology.inboard_mobilizer));
7373
if (IsRevolute(*mobilizer)) {
7474
const symbolic::Variable s_angle(fmt::format("s[{}]", s_.size()));
7575
s_.push_back(s_angle);
@@ -229,8 +229,8 @@ RationalForwardKinematics::CalcChildBodyPoseAsMultilinearPolynomial(
229229
tree.get_topology().get_rigid_body(parent);
230230
const internal::RigidBodyTopology& child_topology =
231231
tree.get_topology().get_rigid_body(child);
232-
internal::MobilizerIndex mobilizer_index;
233-
bool is_order_reversed;
232+
internal::MobodIndex mobilizer_index;
233+
bool is_order_reversed{};
234234
if (parent_topology.parent_body.is_valid() &&
235235
parent_topology.parent_body == child) {
236236
is_order_reversed = true;

multibody/rational/rational_forward_kinematics.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class RationalForwardKinematics {
285285
// indeterminates in the rational functions.
286286
std::vector<symbolic::Variable> s_;
287287
// Each s(i) is associated with a mobilizer.
288-
std::unordered_map<symbolic::Variable::Id, internal::MobilizerIndex>
288+
std::unordered_map<symbolic::Variable::Id, internal::MobodIndex>
289289
map_s_to_mobilizer_;
290290
// map_mobilizer_to_s_index_[mobilizer_index] returns the starting index of
291291
// the mobilizer's variable in s_ (the variable will be contiguous in s for

multibody/rational/rational_forward_kinematics_internal.cc

+8-7
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,20 @@ std::vector<BodyIndex> FindPath(const MultibodyPlant<double>& plant,
6464
return path;
6565
}
6666

67-
std::vector<MobilizerIndex> FindMobilizersOnPath(
67+
std::vector<MobodIndex> FindMobilizersOnPath(
6868
const MultibodyPlant<double>& plant, BodyIndex start, BodyIndex end) {
6969
const std::vector<BodyIndex> path = FindPath(plant, start, end);
70-
std::vector<MobilizerIndex> mobilizers_on_path;
70+
std::vector<MobodIndex> mobilizers_on_path;
7171
mobilizers_on_path.reserve(path.size() - 1);
7272
const MultibodyTree<double>& tree = GetInternalTree(plant);
7373
for (int i = 0; i < static_cast<int>(path.size()) - 1; ++i) {
74-
const RigidBodyTopology& body_topology =
74+
const RigidBodyTopology& rigid_body_topology =
7575
tree.get_topology().get_rigid_body(path[i]);
76-
if (path[i] != world_index() && body_topology.parent_body == path[i + 1]) {
76+
if (path[i] != world_index() &&
77+
rigid_body_topology.parent_body == path[i + 1]) {
7778
// path[i] is the child of path[i+1] in MultibodyTreeTopology, they are
7879
// connected by path[i]'s inboard mobilizer.
79-
mobilizers_on_path.push_back(body_topology.inboard_mobilizer);
80+
mobilizers_on_path.push_back(rigid_body_topology.inboard_mobilizer);
8081
} else {
8182
// path[i] is the parent of path[i+1] in MultibodyTreeTopology, they are
8283
// connected by path[i+1]'s inboard mobilizer.
@@ -97,10 +98,10 @@ BodyIndex FindBodyInTheMiddleOfChain(const MultibodyPlant<double>& plant,
9798
path_not_weld.reserve(path.size());
9899
path_not_weld.push_back(start);
99100
const MultibodyTree<double>& tree = GetInternalTree(plant);
100-
const std::vector<MobilizerIndex> mobilizer_indices =
101+
const std::vector<MobodIndex> mobilizer_indices =
101102
FindMobilizersOnPath(plant, path[0], path.back());
102103
for (int i = 0; i < static_cast<int>(mobilizer_indices.size()); ++i) {
103-
const MobilizerIndex mobilizer_index = mobilizer_indices[i];
104+
const MobodIndex mobilizer_index = mobilizer_indices[i];
104105
const Mobilizer<double>& mobilizer = tree.get_mobilizer(mobilizer_index);
105106
if (mobilizer.num_positions() != 0) {
106107
path_not_weld.push_back(path[i + 1]);

multibody/rational/rational_forward_kinematics_internal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ std::vector<BodyIndex> FindPath(const MultibodyPlant<double>& plant,
3030
/*
3131
* Finds all the mobilizer on the path from start to the end.
3232
*/
33-
std::vector<internal::MobilizerIndex> FindMobilizersOnPath(
33+
std::vector<internal::MobodIndex> FindMobilizersOnPath(
3434
const MultibodyPlant<double>& plant, BodyIndex start, BodyIndex end);
3535

3636
/*

multibody/rational/test/rational_forward_kinematics_test_utilities.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class IiwaTest : public ::testing::Test {
5151
const internal::MultibodyTree<double>& iiwa_tree_;
5252
const BodyIndex world_;
5353
std::array<BodyIndex, 8> iiwa_link_;
54-
std::array<internal::MobilizerIndex, 8> iiwa_joint_;
54+
std::array<internal::MobodIndex, 8> iiwa_joint_;
5555
};
5656

5757
/*
@@ -66,7 +66,7 @@ class FinalizedIiwaTest : public ::testing::Test {
6666
const internal::MultibodyTree<double>& iiwa_tree_;
6767
const BodyIndex world_;
6868
std::array<BodyIndex, 8> iiwa_link_;
69-
std::array<internal::MobilizerIndex, 8> iiwa_joint_;
69+
std::array<internal::MobodIndex, 8> iiwa_joint_;
7070
};
7171

7272
/*

multibody/topology/BUILD.bazel

-22
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,6 @@ package(
1010
default_visibility = ["//visibility:public"],
1111
)
1212

13-
drake_cc_library(
14-
name = "multibody_graph",
15-
srcs = [
16-
"multibody_graph.cc",
17-
],
18-
hdrs = [
19-
"multibody_graph.h",
20-
],
21-
deps = [
22-
"//common:sorted_pair",
23-
"//multibody/tree:multibody_tree_indexes",
24-
],
25-
)
26-
27-
drake_cc_googletest(
28-
name = "multibody_graph_test",
29-
deps = [
30-
":multibody_graph",
31-
"//common/test_utilities:expect_throws_message",
32-
],
33-
)
34-
3513
drake_cc_library(
3614
name = "multibody_topology",
3715
srcs = [

multibody/topology/graph.h

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ This is the file to #include to use LinkJointGraph. It includes
55
subsidiary headers for nested class definitions to keep file sizes
66
reasonable. */
77

8-
// TODO(sherm1) Not used yet by MultibodyPlant; see PR #20225.
9-
108
#define DRAKE_MULTIBODY_TOPOLOGY_GRAPH_INCLUDED
119

1210
// Don't alpha-sort these internal includes; the order matters.

0 commit comments

Comments
 (0)