RobotLocomotion / drake

Model-based design and verification for robotics.
https://drake.mit.edu
Other
3.25k stars 1.26k forks source link

MbP introspection #10736

Closed amcastro-tri closed 1 year ago

amcastro-tri commented 5 years ago

A number of user requests regarding topology and state introspection for an MBP model have been made. These include:

cc'ing @mposa, @RussTedrake, @sammy-tri, @siyuanfeng-tri

amcastro-tri commented 5 years ago

11257 allows making some of our "physical representation" queries pre-finalize. Working on the design of model-specific queries with @sherm1, who has a draft in #10068.

amcastro-tri commented 5 years ago

First two bullets addressed in #11464

amcastro-tri commented 5 years ago

pumped priority to high go address ASAP given the high number of request for something like this.

EricCousineau-TRI commented 4 years ago

Is this still alive? (or high priority)

sherm1 commented 4 years ago

It's been a year, so not high priority. But still needed so I lowered the priority.

xuchenhan-tri commented 3 years ago

Working on this issue as a side project now, and I want to clarify the request a little bit. For the fourth item:

MBP::PrintStateNames(), MBP::PrintStateNames(ModelInstanceIndex) (or similar name)...

What information of the state is expected? I'm thinking the name of joint/free body and whether it's a position or velocity dof. Is this reasonable? Anything else? @hongkai-dai, I saw you recently posted a thread in slack that's related. Do you want to share what would be helpful to you?

sherm1 commented 3 years ago

I'm think "print" here is metaphorical -- we don't want methods that literally print anything. That brings up awkward issues of formatting and where to write the output. Whatever we decide is the right information it should come back as an std::vector<string> or something like that so we can let the caller figure out the tricky stuff. The value here is in the information returned.

xuchenhan-tri commented 3 years ago

Yes, I agree that we don't want to actually "print", and I was planning on returning a std::vector<string>. I'm not sure, however, what exactly is the information of interest. Same goes for the fifth item: anything else other the joint and actuator name?

joemasterjohn commented 3 years ago

I think the relevant information for each coordinate would be:

For joints:

<joint_name>_<local_joint_coordinate_name> and <joint_name>_<local_joint_coordinate_name_dot>

For example:

UniversalJoint3_theta1 UniversalJoint3_theta1_dot

And for a free body:

freebody1_q_FM_{w,x,y,z} freebody1_p_FM_{x,y,z} freebody1_w_FM_{x,y,z} freebody1_v_FM_{x,y,z}

Having _FM would follow the multibody quantity nomenclature, but currently for all freebodies F is the world frame and M is that body's BodyFrame.

For the joints you could add the method virtual void Joint::AddInStateNames(std::vector<std::string>*) and have each joint do its own thing. For the free bodies, you'll have to just iterate over them and grab their mobilizer to index into the coordinate name vector (MultibodyTree::GetFreeBodyMobilizerOrThrow and Mobilizer::position_start_in_q). You could probably encapsulate this all in a method like MultibodyTree::AddInFreeBodyStateNames(std::vector<std::string>*). What do you guys think?

sherm1 commented 3 years ago

Would be good to get some end-user input as to what would be most useful. Fishing: @ggould-tri @hongkai-dai @avalenzu @EricCousineau-TRI ...

jwnimmer-tri commented 3 years ago

See also #5857 for the framework angle -- dumping the context may be another way to debug, instead of MbP methods.

Or, if we're going to provide human-usable-name getters for vector indices, it might also make sense to define those accessors on the system framework (ports, state, etc.) so they are usable generically for more than just the MbP (e.g., the inverse dynamics controller vector index names would help a lot). I thought there was an issue filed for that already, but I didn't immediately find it.

RussTedrake commented 3 years ago

As discussed in this afternoon's meeting... I would like to propose a step towards this for a slightly simpler/streamlined version of @joemasterjohn 's proposal above.

This is based on an example I've been working with doing optimization for littledog. In particular, I have the following methods that proved super useful:

def MakeNamedViewPositions(mbp, view_name):
    names = [None]*mbp.num_positions()
    for ind in range(mbp.num_joints()): 
        joint = mbp.get_joint(JointIndex(ind))
        # TODO: Handle planar joints, etc.
        assert(joint.num_positions() == 1)
        names[joint.position_start()] = joint.name()
    for ind in mbp.GetFloatingBaseBodies():
        body = mbp.get_body(ind)
        start = body.floating_positions_start()
        body_name = body.name()
        names[start] = body_name+'_qw'
        names[start+1] = body_name+'_qx'
        names[start+2] = body_name+'_qy'
        names[start+3] = body_name+'_qz'
        names[start+4] = body_name+'_x'
        names[start+5] = body_name+'_y'
        names[start+6] = body_name+'_z'
    return namedview(view_name, names)

def MakeNamedViewVelocities(mbp, view_name):
    names = [None]*mbp.num_velocities()
    for ind in range(mbp.num_joints()): 
        joint = mbp.get_joint(JointIndex(ind))
        # TODO: Handle planar joints, etc.
        assert(joint.num_velocities() == 1)
        names[joint.velocity_start()] = joint.name()
    for ind in mbp.GetFloatingBaseBodies():
        body = mbp.get_body(ind)
        start = body.floating_velocities_start() - mbp.num_positions()
        body_name = body.name()
        names[start] = body_name+'_wx'
        names[start+1] = body_name+'_wy'
        names[start+2] = body_name+'_wz'
        names[start+3] = body_name+'_vx'
        names[start+4] = body_name+'_vy'
        names[start+5] = body_name+'_vz'
    return namedview(view_name, names)

So specifically:

Proposal: Add the following new methods to Mobilizer:

virtual string position_suffix(int position_index_in_mobilizer) {
  DRAKE_DEMAND(num_positions==1);
  return "";
}

virtual string velocity_suffix(int velocity_index_in_mobilizer) {
  DRAKE_DEMAND(num_velocities==1);
  return "";
}

with overloads in mobilizers with num_positions>1, so that, e.g.

virtual string QuaternionMobilizer::position_suffix(int position_index_in_mobilizer) {
  switch(position_index_in_mobilizer) {
    case 0: return "qw";
    case 1: return "qx";
    ...
  }
}

@sherm1 , @amcastro-tri , @joemasterjohn (et al) - WDYT?

sherm1 commented 3 years ago

Thanks for this proposal! A few thoughts:

RussTedrake commented 3 years ago

Thanks @sherm1

Another alternative:

Maybe that's better.

sherm1 commented 3 years ago

I do like the alternative better since the names don't change in context. Still some issues:

As a multibody dynamicist I don't like treating 1-dof and n-dof joints differently and would use the same conventions for both. I can see that a roboticist may feel differently!

RussTedrake commented 3 years ago

To be clear, the proposal is that the default suffix for positions is "" and the default suffix for velocities is "dot" (which I had changed from "_dot" on your request?). That default will throw if num_positions != 1. But even a single DOF joint can overload it.

I'm happy to hear other opionons about "elbow" and "elbowdot" (or "elbow_dot") vs "elbow_q" and "elbow_v". But it seems like the basic setup is ok, so I will open a PR and we can easily change the default strings in the PR discususion.

amcastro-tri commented 3 years ago

This looks great @RussTedrake. My 2 cents:

EricCousineau-TRI commented 3 years ago

Minor concern: Using _ as a delimiter is understandable for simplicity, and as a valid variable name, but perhaps there's another delim, like a period .? e.g. my_spherical_joint.rx, my_spherical_joint.rx_dot, etc?

amcastro-tri commented 3 years ago

nice!, I like @EricCousineau-TRI's idea. It has the advantage of allowing the _ to be used in the variable name!

RussTedrake commented 3 years ago

we discussed that in the dynamics meeting. doesn't it interfere with other syntactical structure? like in python, i would have my namedview class accessing an element by a string with a '.' in the middle?

RussTedrake commented 3 years ago

and of course you can use _ in the variable name either way.

EricCousineau-TRI commented 3 years ago

While it'd be a complication in namedview, I think it'd be a minor complication; it should be kinda trivial to quickly teach it how to nest fields in a Pythonic way; just some meta-programming hand-waving that Python makes easy ;)

RussTedrake commented 3 years ago

not anywhere as easy as just sticking with _?

EricCousineau-TRI commented 3 years ago

Yeah, but would be a flattening of hierarchy where you wouldn't necessarily need flattening. But yeah, I'm fine either way.