At present, there are significant bits of the MultibodyTree API that are manually forwarded into the MultibodyPlant API as sugar. However, this introduces coupling and some confusion as to what really belongs where, and what the concrete responsibilities are for each API. (Is MBT really just for kinematics and dynamics? What if an external contributor decides to PR unique functionality to MBP?)
Per f2f with @amcastro-tri, this coupling was initially offered as sugar methods; however, as I've been binding stuff in Python, it's been a bit confusing to know what duplicates I should bind and test.
While it's true that copy and pasting APIs and docstrings are kind of low-effort, it makes testing (for proper code coverage, which we really should go for?), maintenance, and in general high-level explanations much more annoying and boilerplate-y.
My vote is to deprecate all of MultibodyPlants forwarding of the MultibodyTree API, and simplify things once and for all.
\cc @amcastro-tri @sherm1 @RussTedrake @sammy-tri @jwnimmer-tri
Given my current travel, I'd like to suggest these updated steps / timelines:
MultibodyTree to internal::MultibodyTree, make aliasinternal:MultibodyTreeSystem, all cache objects, BodyNodeIndex, Mobilizer stuff, MultibodyTreeTopology, MultibodyTreeContextinternal/~* We have a TRI-only document (search for "Drake Issue 9366") for methods that we want to migrate; I can share this publicly if anyone outside of TRI requests it.
We had been heading in the direction of supporting only MBPlant as the user-facing API because MBTree necessarily requires a Context for efficiency, and MBPlant provides user-friendly concepts like "Joints" rather than dynamicist-friendly concepts like "Mobilizers" (which are subtly different). That would suggest wrapping _only_ the MBPlant methods. @amcastro-tri, are you still moving that way?
Not quite, since it depends on the use case.
This is what I had in mind:
CalcMassMatrix or CalcAnalyticalJacobian() etc. Examples of this are #9372 For the second case, you might argue that all multibody queries, including things like Jacobians, should go through ports.
It would be nice to draw a little finer line.
If there is any redundancy, I'd prefer that we have that functionality live in one and only one place, minimizing any sugary interface forwarding; we should avoid sugary forwarding as it introduces needless coupling, that makes it more annoying for bindings and other things, which should ultimately be tested in as concise a fashion as possible.
Can I ask if you'd be OK with moving tree-specific queries (such as sizes) to the tree? e.g. things like num_positions(), etc., would live in the tree, and only be accessible from the plant via something like plant.tree().num_positions().
For mutation, this does mean that we should expose a mutable_tree() from the MultibodyPlant for adding things like frames, and/or permit constructing a MultibodyPlant using a MultibodyTree (which is currently not possible). We should also make it clear when something like MultibodyPlant::AddBody is needed vs. MultibodyTree::AddBody (e.g. when there is plant-specific code that is needed for MultibodyTree).
Regarding having everything route through the ports, it would be nice to defer that until we can figure out a generic to make that less painful in the Systems framework when you start to have multiple small call sites (e.g. hiding the deal of allocating contexts / ports if / when you don't care about it, but avoid needing to write a whole bunch of slow wrapping code).
To make things more concrete, we should make a MBT vs. MBP matrix for methods for things like:
SetFreeBodyPose)That's all I can think of at the moment; will see if I can draft up a quick table.
Here's a draft spreadsheet
Thanks for doing this @EricCousineau-TRI.
The reason I introduced the sugary forwarding methods is because then they'd allow you to do:
const auto& link1 = plant.AddRigidBody("link1", M_B); // Someone defined M_B for link1.
plant.RegisterCollisionGeometry(
link1, X_BG /* geometry frame G in the body frame B*/,
geometry::Box(sizes), "link1_geometry", friction_coefficients, &scene_graph);
all with the plant's provided API. How would this look if we move AddRigidBody() out from the plant into MBT?
let me add that @SeanCurtis-TRI is thinking (correct me if I'm wrong) to provide a way for the SceneGraph to book-keep thinks like friction coefficients (and visual properties for visual geometry). I wonder if that would change the way we could do things. But for now RegisterCollisionGeometry lives in MBP for us to book keep properties for that geometry. The suggar is there to keep things more or less homogeneuos. I'm super open to ideas though
Welcome! For the above code, why would you have aversion to this version if we mutate MutlibodyTree?
auto& tree = plant.mutable_tree();
const auto& link1 = tree.AddRigidBody("link1", M_B); // Someone defined M_B for link1.
plant.RegisterCollisionGeometry(...);
I can understand why MBP has its own version of AddRigidBody, since it wants its num_collision and num_visuals to grow in lock-step with its bodies (but only the rigid ones???).
However, that can be resolved with other things:
*_geometries_ be a vector, just make it be a map<BodyIndex, Ptr>, then you don't have to worry about synchronization. If performance is a worry, you could always move this data to the finalized vector which Finalize() is called.OnBodyAdded callback, which can handle internal queries to enable consistency.Either one of these may lend themselves to enabling people to construct MultibodyTrees outside of MultibodyPlant, which would permit the development of APIs to use only MultibodyTree (which seems ideal if you don't need too much fanciness, as is what we're kind of doing with MBT in Anzu at present?).
@amcastro-tri is right. It is my intent to revisit how much of this works. In general, MBP shouldn't be saving geometry ids -- only frame ids. However, that's largely dependent on a general parsing interface. However, in the spirit of expediency, @amcastro-tri has made this API that effectively gets the job done (taking some of the pressure off of me....yay!) But it's around the corner.
The idea of using MBT without MBP is a bit of an historical anachronism. People could do that in the past because it was a one-stop shop. More particularly, someone could use the MBT to pose and test for collisions. In MBP that's no longer possible. You need an SG to do geometry queries. And SG updates its internals based on what's coming in on its input ports. The simplest way to not reproduce the MBP functionality that feeds to SG is to just use an MBP.
@SeanCurtis-TRI From what you've written, I'm assuming that you're advocating that MBT shouldn't really be used at all, in which case we should have MBP cover all the bases?
~If this is the case, should we make a concrete decision soon in light of other features, such as @hongkai-dai's #9372? Might EOD tomorrow be feasible?~
If that's not the case, can I ask what your recommendation is in terms of delineating the APIs? (specifically guidelines that we write for users and developers about what goes where)
@hongkai-dai Having worked with the current API, and considering what'd be necessary for incorporating collision-based constraints in IK, can I ask if you have any insight into what would be optimal for features like what you're developing?
EDIT: Just read the PR and saw that you switched over to MultibodyPlant. Still, I'd like to ask, do you see value in (a) keeping the APIs separate, (b) just using MultibodyPlant, ~(c) keeping things as-is,~ or something else?
My preference: do as much as possible through MBPlant. Provide read-only MBPlant::tree() method for querying as-built information about how we took the original (possibly cyclic) specifications and modeled them using a spanning tree and constraints.
My concern is that this problem is still present: there's no hard distinction between the two, so it's ambiguous to know what should be used / developed upon /etc., and there's two ways to do the exact same thing.
To resolve this issue, I would really like there to be two choices that minimize ambiguity during development and consumption:
Either MBT is a public API (and we should decouple things as much as possible) or it's not (and we should deprecate its public API and promote everything to MultibodyPlant and never suggest that anyone use MultibodyTree outside of internal Drake, period).
The timeline can be relatively flexible (e.g. how it affects the current binding development, etc.), but I'd like to know what direction to go towards so that way we can write succinct tests, examples, tutorials, etc.
If there's no value to keeping MultibodyTree as a separate API (e.g. we always parse SDFs into MBP, all IK and other usable interfaces, etc., all go through MBP), then we should make it private, so that there's a consistent story.
I see what you mean. SG has something akin to MBT, but it's, by design, completely internal and all queries on that data come through the QueryObject (for queries that depend on input state) and SceneGraphInspector (for queries about the contents and topology of the scene graph). It's probably worth sitting down and making sure if this direction will satisfy use cases.
If we can make MBTree private and satisfy all uses through MBPlant, I'd be very happy with that.
We had a little f2f with @EricCousineau-TRI last night. IMO we should do is to make sure something like IK works with whatever scheme we decide (hide/not-hide MBT, add MBP ports, have a bigger class wrapping MBP+SG... blah...). If we can successfully go through that exercise I believe we solved the problem (and assuming all caching magic can happen of course).
The reason I say this is because I don't feel comfortable just yet hiding/not-hiding APIs until we are absolutely sure we can get our basics to work.
We could prototype IK (starting off from @hongkai-dai's work in #9372) with only two constraints: 1) position constraint, 2) collision constraints. The prototype should successfully show how we wire all this together.
I like it. Proof of concept.
Sounds good to me!
@sherm1 @amcastro-tri The story of the separation between MBT and MBP, to me at least, seems to be getting murkier with the merge of #9489. The requirement of MultibodyTreeSystem for using MultibodyTree is only documented in MBT (which admittedly can be fixed), but seems to further indicate that MBT functionality should really only be accessible through MBP?
Might y'all have time at the on-site for us to make a concrete action item on this?
Thanks, @EricCousineau-TRI -- this is a great topic to discuss f2f at the offsite. Let's do that.
From meeting, we've deemed that we should be able to subsume the public MultibodyTree API into MultibodyPlant, and move MBT to be a private implementation detail.
Will post more details about plan of change for (a) public user interface (e.g. deprecation timeline) and (b) ensuring that things are convenient for developers.
Suggested plan:
internal::MultibodyTree, and have MultibodyPlant use this. Target date: Nov. 10MultibodyPlant (Calc* methods, SetFreeBody* methods, indexing, etc.). Target date: Nov. 15pydrake after the conclusion of 6.881; implement it by putting the definition of MBT in internal, make the public symbol an alias, and deprecate the alias. Target date: Dec. 20Any objections?
This is great @EricCousineau-TRI. Are you thinking to do this migration yourself (I can help of course)? or who are you thinking the owner would be?
Yuppers. (Also, updated the above plan to include your question about docs.)
Thanks @EricCousineau-TRI! this is awesome.
@EricCousineau-TRI, before the new MBPlant API is set in stone I'd like to discuss the abstractions that we want it to support. Here is an outline of what is really happening (most users won't need to understand all of this):
I'm mentioning these various abstractions because as we consolidate the API into MBPlant, users will still need to deal with it at the levels of abstraction best-suited to the work to be done. Using different classes for that is one way to keep the API size manageable; if we don't do that we'll need to provide the same capabilities, perhaps in doxygen groupings in the flattened API.
Per issue scrubbing with Sherm, since this affect user experience quite a bit, we're marking it high priority. Next step would be for Eric and Sherm to have a conversation to discuss API design
For upcoming meeting, just did a brief pass of capturing the difference in APIs between MBP and MBT, just local to those classes:
https://gist.github.com/EricCousineau-TRI/1183ece65e199958f2f2967a0eea758a#file-diff-patch
EDIT: Moved items up to top-level for checkbox-ness.
Per discussion in #10380, resolved!
Most helpful comment
We had a little f2f with @EricCousineau-TRI last night. IMO we should do is to make sure something like IK works with whatever scheme we decide (hide/not-hide MBT, add MBP ports, have a bigger class wrapping MBP+SG... blah...). If we can successfully go through that exercise I believe we solved the problem (and assuming all caching magic can happen of course).
The reason I say this is because I don't feel comfortable just yet hiding/not-hiding APIs until we are absolutely sure we can get our basics to work.
We could prototype IK (starting off from @hongkai-dai's work in #9372) with only two constraints: 1) position constraint, 2) collision constraints. The prototype should successfully show how we wire all this together.