Closed amcastro-tri closed 1 year ago
First two bullets addressed in #11464
pumped priority to high go address ASAP given the high number of request for something like this.
Is this still alive? (or high priority)
It's been a year, so not high priority. But still needed so I lowered the priority.
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?
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.
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?
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?
Would be good to get some end-user input as to what would be most useful. Fishing: @ggould-tri @hongkai-dai @avalenzu @EricCousineau-TRI ...
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.
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:
num_positions==1
joint.name()
joint.name()
joint.name()
/ joint.name() + "dot"
.num_positions>=1
joint.name() + "_" + joint.position_suffix(k)
joint.name() + "_" + joint.velocity_suffix(k)
joint.name() + "_" + joint.position_suffix()
/ joint.name() + "_" + joint.velocity_suffix()
. 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?
Thanks for this proposal! A few thoughts:
Thanks @sherm1
dot
. I agree it was clunky.Another alternative:
num_positions==1
joint.name()
joint.name() + "dot"
num_positions > 1
joint.name() + "_" + joint.position_suffix(k)
joint.name() + "_" + joint.velocity_suffix(k)
joint.velocity_suffix(k) != joint.position_suffix(k)
Maybe that's better.
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!
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.
This looks great @RussTedrake. My 2 cents:
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?
nice!, I like @EricCousineau-TRI's idea. It has the advantage of allowing the _
to be used in the variable name!
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?
and of course you can use _
in the variable name either way.
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 ;)
not anywhere as easy as just sticking with _
?
Yeah, but would be a flattening of hierarchy where you wouldn't necessarily need flattening. But yeah, I'm fine either way.
A number of user requests regarding topology and state introspection for an MBP model have been made. These include:
get_foo_index()
methods would throw if the body is not a floating base.u
, also index vs. actuator name (it could also include actuated joint name).std::string MBP::get_state_name(int i), to retrieve the name of the i-th entry in a bare state vector.cc'ing @mposa, @RussTedrake, @sammy-tri, @siyuanfeng-tri