Skip to content

Commit 5e529c1

Browse files
committed
Adds SpanningForest builder & modify MbP to use it.
Passes all tests (except space_xyz_floating_mobilizer is disabled)
1 parent 5165b07 commit 5e529c1

Some content is hidden

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

51 files changed

+5528
-1567
lines changed

multibody/plant/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ drake_cc_library(
149149
"//multibody/contact_solvers/sap",
150150
"//multibody/fem",
151151
"//multibody/hydroelastics:hydroelastic_engine",
152+
"//multibody/topology:multibody_topology",
152153
"//multibody/tree",
153154
"//systems/framework:diagram_builder",
154155
"//systems/framework:leaf_system",

multibody/plant/compliant_contact_manager.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ template <typename T>
3939
AccelerationsDueNonConstraintForcesCache<
4040
T>::AccelerationsDueNonConstraintForcesCache(const MultibodyTreeTopology&
4141
topology)
42-
: forces(topology.num_bodies(), topology.num_velocities()),
42+
: forces(topology.num_links(), topology.num_velocities()),
4343
abic(topology),
44-
Zb_Bo_W(topology.num_bodies()),
44+
Zb_Bo_W(topology.num_body_nodes()),
4545
aba_forces(topology),
4646
ac(topology) {}
4747

multibody/plant/multibody_plant.cc

+10-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "drake/multibody/plant/hydroelastic_traction_calculator.h"
2626
#include "drake/multibody/plant/make_discrete_update_manager.h"
2727
#include "drake/multibody/plant/slicing_and_indexing.h"
28+
#include "drake/multibody/topology/link_joint_graph.h"
2829
#include "drake/multibody/tree/prismatic_joint.h"
2930
#include "drake/multibody/tree/quaternion_floating_joint.h"
3031
#include "drake/multibody/tree/revolute_joint.h"
@@ -312,7 +313,6 @@ MultibodyPlant<T>::MultibodyPlant(
312313
// it less brittle.
313314
visual_geometries_.emplace_back(); // Entries for the "world" body.
314315
collision_geometries_.emplace_back();
315-
316316
DeclareSceneGraphPorts();
317317
}
318318

@@ -867,7 +867,7 @@ template <typename T>
867867
std::vector<const Body<T>*> MultibodyPlant<T>::GetBodiesWeldedTo(
868868
const Body<T>& body) const {
869869
const std::set<BodyIndex> island =
870-
internal_tree().multibody_graph().FindBodiesWeldedTo(body.index());
870+
internal_tree().link_joint_graph().FindLinksWeldedTo(body.index());
871871
// Map body indices to pointers.
872872
std::vector<const Body<T>*> sub_graph_bodies;
873873
for (BodyIndex body_index : island) {
@@ -967,7 +967,7 @@ void MultibodyPlant<T>::SetFreeBodyPoseInAnchoredFrame(
967967
DRAKE_MBP_THROW_IF_NOT_FINALIZED();
968968
this->ValidateContext(context);
969969

970-
if (!internal_tree().get_topology().IsBodyAnchored(frame_F.body().index())) {
970+
if (!internal_tree().get_topology().IsLinkAnchored(frame_F.body().index())) {
971971
throw std::logic_error("Frame '" + frame_F.name() +
972972
"' must be anchored to the world frame.");
973973
}
@@ -991,17 +991,17 @@ void MultibodyPlant<T>::CalcSpatialAccelerationsFromVdot(
991991
internal_tree().CalcSpatialAccelerationsFromVdot(
992992
context, internal_tree().EvalPositionKinematics(context),
993993
internal_tree().EvalVelocityKinematics(context), known_vdot, A_WB_array);
994-
// Permute BodyNodeIndex -> BodyIndex.
994+
// Permute BodyNodeIndex -> LinkIndex.
995995
// TODO(eric.cousineau): Remove dynamic allocations. Making this in-place
996996
// still required dynamic allocation for recording permutation indices.
997997
// Can change implementation once MultibodyTree becomes fully internal.
998998
std::vector<SpatialAcceleration<T>> A_WB_array_node = *A_WB_array;
999999
const internal::MultibodyTreeTopology& topology =
10001000
internal_tree().get_topology();
10011001
for (internal::BodyNodeIndex node_index(1);
1002-
node_index < topology.get_num_body_nodes(); ++node_index) {
1003-
const BodyIndex body_index = topology.get_body_node(node_index).body;
1004-
(*A_WB_array)[body_index] = A_WB_array_node[node_index];
1002+
node_index < topology.num_body_nodes(); ++node_index) {
1003+
const LinkIndex link_index = topology.get_body_node(node_index).link;
1004+
(*A_WB_array)[link_index] = A_WB_array_node[node_index];
10051005
}
10061006
}
10071007

@@ -1078,6 +1078,7 @@ void MultibodyPlant<T>::Finalize() {
10781078
if (geometry_source_is_registered()) {
10791079
ApplyDefaultCollisionFilters();
10801080
}
1081+
10811082
FinalizePlantOnly();
10821083

10831084
// Make the manager of discrete updates.
@@ -1316,7 +1317,7 @@ void MultibodyPlant<T>::ApplyDefaultCollisionFilters() {
13161317
}
13171318
// We explicitly exclude collisions within welded subgraphs.
13181319
std::vector<std::set<BodyIndex>> subgraphs =
1319-
internal_tree().multibody_graph().FindSubgraphsOfWeldedBodies();
1320+
internal_tree().link_joint_graph().FindSubgraphsOfWeldedLinks();
13201321
for (const auto& subgraph : subgraphs) {
13211322
// Only operate on non-trivial weld subgraphs.
13221323
if (subgraph.size() <= 1) {
@@ -3494,7 +3495,7 @@ void MultibodyPlant<T>::RemoveUnsupportedScalars(
34943495
template <typename T>
34953496
std::vector<std::set<BodyIndex>>
34963497
MultibodyPlant<T>::FindSubgraphsOfWeldedBodies() const {
3497-
return internal_tree().multibody_graph().FindSubgraphsOfWeldedBodies();
3498+
return internal_tree().link_joint_graph().FindSubgraphsOfWeldedLinks();
34983499
}
34993500

35003501
template <typename T>

multibody/plant/multibody_plant.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
#include "drake/multibody/plant/discrete_update_manager.h"
2727
#include "drake/multibody/plant/multibody_plant_config.h"
2828
#include "drake/multibody/plant/physical_model.h"
29-
#include "drake/multibody/topology/multibody_graph.h"
29+
#include "drake/multibody/topology/link_joint_graph.h"
30+
#include "drake/multibody/topology/spanning_forest.h"
3031
#include "drake/multibody/tree/force_element.h"
3132
#include "drake/multibody/tree/multibody_tree-inl.h"
3233
#include "drake/multibody/tree/multibody_tree_system.h"
@@ -4345,10 +4346,13 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
43454346
return internal_tree().world_frame();
43464347
}
43474348

4348-
/// Returns the number of bodies in the model, including the "world" body,
4349+
/// Returns the number of Links in the model, including the "world" Link,
43494350
/// which is always part of the model.
43504351
/// @see AddRigidBody().
4351-
int num_bodies() const { return internal_tree().num_bodies(); }
4352+
int num_links() const { return internal_tree().num_links(); }
4353+
4354+
/// Alternate spelling for num_links(). Prefer num_links().
4355+
int num_bodies() const { return num_links(); }
43524356

43534357
/// Returns a constant reference to the body with unique index `body_index`.
43544358
/// @throws std::exception if `body_index` does not correspond to a body in
@@ -4362,7 +4366,7 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
43624366
/// @throws std::exception if called pre-finalize.
43634367
bool IsAnchored(const Body<T>& body) const {
43644368
DRAKE_MBP_THROW_IF_NOT_FINALIZED();
4365-
return internal_tree().get_topology().IsBodyAnchored(body.index());
4369+
return internal_tree().get_topology().IsLinkAnchored(body.index());
43664370
}
43674371

43684372
/// @returns `true` if a body named `name` was added to the %MultibodyPlant.
@@ -4966,7 +4970,7 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
49664970
/// @} <!-- Introspection -->
49674971

49684972
#ifndef DRAKE_DOXYGEN_CXX
4969-
// Internal-only access to MultibodyGraph::FindSubgraphsOfWeldedBodies();
4973+
// Internal-only access to LinkJointGraph::FindSubgraphsOfWeldedBodies();
49704974
// TODO(calderpg-tri) Properly expose this method (docs/tests/bindings).
49714975
std::vector<std::set<BodyIndex>> FindSubgraphsOfWeldedBodies() const;
49724976
#endif

multibody/plant/test/multibody_plant_test.cc

+8-14
Original file line numberDiff line numberDiff line change
@@ -1689,8 +1689,11 @@ GTEST_TEST(MultibodyPlantTest, GetBodiesKinematicallyAffectedBy) {
16891689
".*No joint with index.*registered.");
16901690
}
16911691

1692-
// Regression test for unhelpful error message -- see #14641.
1693-
GTEST_TEST(MultibodyPlantTest, ReversedWeldError) {
1692+
// Weld a body to World but with the body as the parent and World as the
1693+
// child. This is fine but must be implemented with a reversed Weld
1694+
// mobilizer that specifies World as the inboard body. We're just verifying
1695+
// here that we can create the plant and finalize it with no trouble.
1696+
GTEST_TEST(MultibodyPlantTest, ReversedWeldJoint) {
16941697
// This test expects that the following model has a world body and a pair of
16951698
// welded-together bodies.
16961699
const std::string sdf_file =
@@ -1702,16 +1705,7 @@ GTEST_TEST(MultibodyPlantTest, ReversedWeldError) {
17021705
const Body<double>& extra = plant.AddRigidBody(
17031706
"extra", default_model_instance(), SpatialInertia<double>());
17041707
plant.WeldFrames(extra.body_frame(), plant.world_frame());
1705-
1706-
// The important property of this message is that it reports some identifier
1707-
// for the involved objects, so at least the developer can map those back to
1708-
// objects and deduce what API call was in error. If the details of the
1709-
// message change, update this check to match. If in future the error can be
1710-
// caught at the WeldFrames() step, so much the better. Modify this test to
1711-
// reflect that.
1712-
DRAKE_EXPECT_THROWS_MESSAGE(
1713-
plant.Finalize(),
1714-
".*already has a joint.*extra_welds_to_world.*joint.*not allowed.*");
1708+
EXPECT_NO_THROW(plant.Finalize());
17151709
}
17161710

17171711
// Utility to verify the subset of output ports we expect to be direct a
@@ -3504,8 +3498,8 @@ GTEST_TEST(SetRandomTest, SetDefaultWhenNoDistributionSpecified) {
35043498
// explicitly added).
35053499
const Body<double>& body1 =
35063500
plant.AddRigidBody("free body 1", SpatialInertia<double>::MakeUnitary());
3507-
plant.AddJoint<QuaternionFloatingJoint>("" + body1.name(), plant.world_body(),
3508-
{}, body1, {});
3501+
plant.AddJoint<QuaternionFloatingJoint>(body1.name(), plant.world_body(), {},
3502+
body1, {});
35093503
const std::string acrobot_file_name =
35103504
"drake/multibody/benchmarks/acrobot/acrobot.sdf";
35113505
const ModelInstanceIndex acrobot =

multibody/plant/test/sap_driver_multidof_joints_test.cc

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

88-
std::unique_ptr<typename Joint<T>::BluePrint> MakeImplementationBlueprint()
89-
const override {
88+
std::unique_ptr<typename Joint<T>::BluePrint> MakeImplementationBlueprint(
89+
bool use_reversed_mobilizer) const override {
9090
auto blue_print = std::make_unique<typename Joint<T>::BluePrint>();
91+
const auto [inboard_frame, outboard_frame] =
92+
this->tree_frames(use_reversed_mobilizer);
93+
// TODO(sherm1) The mobilizer needs to be reversed, not just the frames.
9194
// The only restriction here relevant for these tests is that we provide a
9295
// mobilizer with kNumDofs positions and velocities, so that indexes are
9396
// consistent during MultibodyPlant::Finalize().
9497
auto revolute_mobilizer = std::make_unique<internal::SpaceXYZMobilizer<T>>(
95-
this->frame_on_parent(), this->frame_on_child());
98+
*inboard_frame, *outboard_frame);
9699
blue_print->mobilizer = std::move(revolute_mobilizer);
97100
return blue_print;
98101
}

multibody/rational/rational_forward_kinematics.cc

+13-13
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ RationalForwardKinematics::RationalForwardKinematics(
6666
// Initialize map_mobilizer_to_s_index_ to -1, where -1 indicates "no s
6767
// variable".
6868
map_mobilizer_to_s_index_ = std::vector<int>(tree.num_mobilizers(), -1);
69-
for (BodyIndex body_index(1); body_index < plant_.num_bodies();
70-
++body_index) {
71-
const internal::BodyTopology& body_topology =
72-
tree.get_topology().get_body(body_index);
69+
for (LinkIndex link_index(1); link_index < plant_.num_bodies();
70+
++link_index) {
71+
const internal::LinkTopology& link_topology =
72+
tree.get_topology().get_link(link_index);
7373
const internal::Mobilizer<double>* mobilizer =
74-
&(tree.get_mobilizer(body_topology.inboard_mobilizer));
74+
&(tree.get_mobilizer(link_topology.inboard_mobilizer));
7575
if (IsRevolute(*mobilizer)) {
7676
const symbolic::Variable s_angle(fmt::format("s[{}]", s_.size()));
7777
s_.push_back(s_angle);
@@ -230,18 +230,18 @@ RationalForwardKinematics::CalcChildBodyPoseAsMultilinearPolynomial(
230230
// X_F'M' = X_FM.inverse()
231231
// X_M'C' = X_PF.inverse()
232232
const internal::MultibodyTree<double>& tree = GetInternalTree(plant_);
233-
const internal::BodyTopology& parent_topology =
234-
tree.get_topology().get_body(parent);
235-
const internal::BodyTopology& child_topology =
236-
tree.get_topology().get_body(child);
233+
const internal::LinkTopology& parent_topology =
234+
tree.get_topology().get_link(parent);
235+
const internal::LinkTopology& child_topology =
236+
tree.get_topology().get_link(child);
237237
internal::MobilizerIndex mobilizer_index;
238238
bool is_order_reversed;
239-
if (parent_topology.parent_body.is_valid() &&
240-
parent_topology.parent_body == child) {
239+
if (parent_topology.parent_link.is_valid() &&
240+
parent_topology.parent_link == child) {
241241
is_order_reversed = true;
242242
mobilizer_index = parent_topology.inboard_mobilizer;
243-
} else if (child_topology.parent_body.is_valid() &&
244-
child_topology.parent_body == parent) {
243+
} else if (child_topology.parent_link.is_valid() &&
244+
child_topology.parent_link == parent) {
245245
is_order_reversed = false;
246246
mobilizer_index = child_topology.inboard_mobilizer;
247247
} else {

multibody/rational/rational_forward_kinematics_internal.cc

+10-10
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,24 @@ std::vector<BodyIndex> FindPath(const MultibodyPlant<double>& plant,
3535
}
3636
};
3737
while (!worklist.empty()) {
38-
BodyIndex current = worklist.front();
38+
LinkIndex current = worklist.front();
3939
worklist.pop();
4040
if (current == end) {
4141
break;
4242
}
43-
const BodyTopology& current_node = topology.get_body(current);
43+
const LinkTopology& current_node = topology.get_link(current);
4444
if (current != world_index()) {
45-
const BodyIndex parent = current_node.parent_body;
45+
const LinkIndex parent = current_node.parent_link;
4646
visit_edge(current, parent);
4747
}
48-
for (BodyIndex child : current_node.child_bodies) {
48+
for (LinkIndex child : current_node.child_links) {
4949
visit_edge(current, child);
5050
}
5151
}
5252

5353
// Retrieve the path in reverse order.
54-
std::vector<BodyIndex> path;
55-
for (BodyIndex current = end; ; current = ancestors.at(current)) {
54+
std::vector<LinkIndex> path;
55+
for (LinkIndex current = end; ; current = ancestors.at(current)) {
5656
path.push_back(current);
5757
if (current == start) {
5858
break;
@@ -71,16 +71,16 @@ std::vector<MobilizerIndex> FindMobilizersOnPath(
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 BodyTopology& body_topology = tree.get_topology().get_body(path[i]);
75-
if (path[i] != world_index() && body_topology.parent_body == path[i + 1]) {
74+
const LinkTopology& link_topology = tree.get_topology().get_link(path[i]);
75+
if (path[i] != world_index() && link_topology.parent_link == path[i + 1]) {
7676
// path[i] is the child of path[i+1] in MultibodyTreeTopology, they are
7777
// connected by path[i]'s inboard mobilizer.
78-
mobilizers_on_path.push_back(body_topology.inboard_mobilizer);
78+
mobilizers_on_path.push_back(link_topology.inboard_mobilizer);
7979
} else {
8080
// path[i] is the parent of path[i+1] in MultibodyTreeTopology, they are
8181
// connected by path[i+1]'s inboard mobilizer.
8282
mobilizers_on_path.push_back(
83-
tree.get_topology().get_body(path[i + 1]).inboard_mobilizer);
83+
tree.get_topology().get_link(path[i + 1]).inboard_mobilizer);
8484
}
8585
}
8686
return mobilizers_on_path;

multibody/rational/test/rational_forward_kinematics_test_utilities.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ IiwaTest::IiwaTest()
8282
iiwa_link_[i] =
8383
iiwa_->GetBodyByName("iiwa_link_" + std::to_string(i)).index();
8484
iiwa_joint_[i] =
85-
iiwa_tree_.get_topology().get_body(iiwa_link_[i]).inboard_mobilizer;
85+
iiwa_tree_.get_topology().get_link(iiwa_link_[i]).inboard_mobilizer;
8686
}
8787
}
8888

@@ -94,7 +94,7 @@ FinalizedIiwaTest::FinalizedIiwaTest()
9494
iiwa_link_[i] =
9595
iiwa_->GetBodyByName("iiwa_link_" + std::to_string(i)).index();
9696
iiwa_joint_[i] =
97-
iiwa_tree_.get_topology().get_body(iiwa_link_[i]).inboard_mobilizer;
97+
iiwa_tree_.get_topology().get_link(iiwa_link_[i]).inboard_mobilizer;
9898
}
9999
}
100100

multibody/topology/BUILD.bazel

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,26 @@ package(
1111
)
1212

1313
drake_cc_library(
14-
name = "multibody_graph",
14+
name = "multibody_topology",
1515
srcs = [
16-
"multibody_graph.cc",
16+
"link_joint_graph.cc",
17+
"spanning_forest.cc",
1718
],
1819
hdrs = [
19-
"multibody_graph.h",
20+
"link_joint_graph.h",
21+
"spanning_forest.h",
2022
],
2123
deps = [
24+
"//common:copyable_unique_ptr",
2225
"//common:sorted_pair",
2326
"//multibody/tree:multibody_tree_indexes",
2427
],
2528
)
2629

2730
drake_cc_googletest(
28-
name = "multibody_graph_test",
31+
name = "link_joint_graph_test",
2932
deps = [
30-
":multibody_graph",
33+
":multibody_topology",
3134
"//common/test_utilities:expect_throws_message",
3235
],
3336
)

0 commit comments

Comments
 (0)