Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for floating, planar joint types #281

Merged
merged 3 commits into from
Aug 28, 2024

Conversation

EzraBrooks
Copy link
Contributor

@EzraBrooks EzraBrooks commented Feb 16, 2024

Closes #8

WIP, need to add an example (and debug any issues said example ends up having)

Comment on lines 90 to 97
this.jointValue = new Array(2).fill(0);
// Planar joints rotate about their Z axis
this.axis = new Vector3(0, 0, 1);
this.jointValue = new Array(3).fill(0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize planar joints could rotate? Is there documentation on this? The specification is pretty sparse when it comes to the descriptions on joint behavior but it makes it sound like planar movement only? If the ROS implementation differs from the spec then it might be worth getting the wiki page updated.

planar — this joint allows motion in a plane perpendicular to the axis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to dig into this more. I was pointed to some code in MoveIt and ROS Control that treats them as X,Y,Rz - I was also previously under the impression that it was only X,Y.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Info seems pretty scattered but from these urdfpy docs it looks like a "planar" joint is described as a joint that only supports planar movement on x and y in the plane described by the axis vector. It's not clear how you're supposed to derive the local x and y axes from the axis vector, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, here's the code in MoveIt that clearly treats it as having a rotational component.

But you're definitely right that that's not how it's implemented in urdfpy. I'm trying to dig into how this is implemented in RViz, because I assume whatever MoveIt is doing is visualized fine in RViz.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll discuss this more internally with the MoveIt & MoveIt Pro team and determine whether we actually think this is part of the standard or whether it's some incorrect assumption made long ago. git blame says that code is 12 years old!

If it's a MoveIt-specific thing then I'll have the implementation here be 2dof and we'll fix it on the MoveIt end.

Copy link
Owner

@gkjohnson gkjohnson Mar 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec does not read so ambiguously to me and the URDF py project explicitly does not support a rotational axis. URDF is a defined specification and I'm generally not keen on adding new features outside of the spec (for this or other formats) just because it's convenient. It inevitably leads to situations like this where users will produce files that are designed with a single, spec-breaking implementation in mind but which will not work in applications that have followed it, ultimately diluting the purpose of the spec.

If this is something that's important or is more or less becoming spec-by-convention I recommend making suggestions to edit the URDF specification. I understand that ROS is already a bit of a wild west in terms of software but the least we can do is not continue to proliferate inconsistencies with the few formats that are in place to enable interoperability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately every attempt to update the URDF specification that I'm aware of has stalled for years at a time :(

My understanding is that URDFpy is the outlier here and other URDF parsers (i.e. urdf-rs) do support a rotational axis.

Copy link
Contributor Author

@EzraBrooks EzraBrooks Mar 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll reopen the conversation internally about removing the rotation from planar joints in MoveIt. If you're certain we don't want to support a rotation component here, I can remove it from this PR and move just that addition to another branch I'll use on my project for now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mostly asking for a good faith effort to be made to improve the specification if it seems that it's out of date or not clearly representative of the common interpretation, as you are suggesting, so this kind of confusion does not happen in the future. It is not difficult to change the wiki page - I made a change myself years ago at afford more geometry formats to be allowed in the specification. If all but urdf-py is interpreting planar joints as "two translation axes and one rotation axis" then the documentation should be updated to reflect that more clearly and at least an issue should be made for urdf-py to bring it in line with other tools. You can request edit access for your wiki account here.

Unfortunately every attempt to update the URDF specification that I'm aware of has stalled for years at a time :(

If you're referring to URDF 2.0, then yeah it's been stalled forever and I'll be surprised if it goes anywhere because of how large and breaking the the scope is. But making clarifying, non breaking changes to the URDF spec is not the same as trying to replace it completely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the clarification. We'll start a conversation on the ROS wiki side!

This has been a huge can of worms across the board, honestly - we went to polish up mobile base support in our application and found issues all across the ROS stack (even to the point of straight-up segfaults when you pass a planar joint to things). As always, I appreciate your engagement with my issues and PRs here. That's more than can be said for a lot of the packages we use!

javascript/src/URDFClasses.js Outdated Show resolved Hide resolved
javascript/src/URDFClasses.js Outdated Show resolved Hide resolved
javascript/src/URDFClasses.js Outdated Show resolved Hide resolved
Copy link
Owner

@gkjohnson gkjohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks! I know it's still WIP but I added a few comments. An example would be awesome, as well, and we'll want to update the README for the joint value function.

@EzraBrooks EzraBrooks changed the title Add planar joint support Add support for floating, planar joint types Feb 19, 2024
@gkjohnson gkjohnson added this to the v0.12.2 milestone Feb 20, 2024
@EzraBrooks
Copy link
Contributor Author

I'm headed out on vacation for a week but handing this branch over to my team for testing with mobile bases, so hopefully there'll be a good example case soon to iron out any bugs and showcase the capability here :)

@gkjohnson
Copy link
Owner

I'm headed out on vacation for a week but handing this branch over to my team for testing with mobile bases, so hopefully there'll be a good example case soon to iron out any bugs and showcase the capability here :)

Sounds good - once the PR is marked as "ready for review" I can provide comments!

@EzraBrooks
Copy link
Contributor Author

sorry for the wait here; we're still working out some issues with the backend we were going to use this with! will get this finished up as soon as I have it working for us :)

@Abishalini
Copy link

Abishalini commented Mar 28, 2024

@gkjohnson Would it be okay to add the rotational component for the planar joint?
Adding this here for more visibility - #281 (comment)

Comment on lines 250 to 252
if (this.jointValue.every((value, index) => values[index] === value || values[index] === null)) return didUpdate;
// anonymous block scope to prevent variable name clobbering with other cases in the switch
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we follow the code formatting in the rest of the and wrap the whole case in braces:

case 'floating': {

  // ...

}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. worth noting this is not just styling: this is creating an anonymous block scope.

Comment on lines 255 to 271
this.position.copy(this.origPosition);
// Respect origin RPY when setting position
if (posX !== this.jointValue[0] && posX !== null) {
_tempAxis.set(1, 0, 0).applyEuler(this.rotation);
this.position.addScaledVector(_tempAxis, posX);
didUpdate = true;
}
if (posY !== this.jointValue[1] && posY !== null) {
_tempAxis.set(0, 1, 0).applyEuler(this.rotation);
this.position.addScaledVector(_tempAxis, posY);
didUpdate = true;
}
if (posZ !== this.jointValue[2] && posZ !== null) {
_tempAxis.set(0, 0, 1).applyEuler(this.rotation);
this.position.addScaledVector(_tempAxis, posZ);
didUpdate = true;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the if conditions here. Right now you're always resetting the object position but then you're only shifting along one of the axes if it's changed. That means the position of the joint will only reflect the values that have changed in the recent function call.

At this point in the function we can really just return true indicating that the joint changed since we've already checked if the joint values have changed above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, you're right - I totally blanked on the fact that the initial position set was outside the if clauses.

didUpdate = true;
}
if (posY !== this.jointValue[1] && posY !== null) {
_tempAxis.set(0, 1, 0).applyEuler(this.rotation);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to make sure we use the "origQuaternion" here so we're operating in the frame defined by the original joint state otherwise the rotation of these axes will be dependent on the previous state of the rotation based on prior joint angles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me, but does that mean the identical code in the prismatic joint has the same issue?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prismatic joint will never have the rotation changed so the orientation inthis.rotation will never be different from this.origQuaternion in that case.

Comment on lines 303 to 305
_tempAxis.set(1, 0, 0).applyEuler(this.rotation);
this.position.addScaledVector(_tempAxis, posX);
this.jointValue[0] = posX;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the specification the plane that the joint is supposed to move on should be based on the axis specified in the joint - this is just using the local x axis:

planar — this joint allows motion in a plane perpendicular to the axis.

One additional issue with the spec is that it does not clearly define how to derive the X and Y plane axes from the single "axis" vector. It could just be from rotating the frame such that the z axis the shortest direction to the axis direction with no twist rotation - but this leaves us with an undefined case when the axis is 0 0 - 1. If there is a consistent and well-defined approach to this in MoveIt or other applications we should use that one. It would also be good to update the specification document to be more clear here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point. This is something we waffled on internally due to the vagueness of the specification. @Abishalini @henningkayser how does MoveIt/OMPL/etc. handle this?

Fix test that expects 2 dofs for planar which has 3

use isNaN on individual DoFs instead of `== null` on all of them together

Go back to checking for null, parsing should be fixed as of gkjohnson#282

Return early if all values are identical to current state _or null_

note to self

Avoid allocating new memory for vectors during joint setting

Revert added Rz DoF for planar joint type

as far as I can tell, the URDF spec does not allow multi-dof limits

add first cut at floating joint support

formatting

re-add Rz component to planar joints

update setJointValue type in typescript definition

test update

fix block scoping

cleanup
@EzraBrooks
Copy link
Contributor Author

EzraBrooks commented Aug 21, 2024

Hi Garrett, we've decided to stay the course on 3DoF planar joints, which is what ROS expects (even though I agree with you that it's kind of a silly expectation). I'm closing this PR in favor of our fork. Thanks so much for your work on this library and for your help along the way!

@EzraBrooks EzraBrooks closed this Aug 21, 2024
@gkjohnson
Copy link
Owner

gkjohnson commented Aug 21, 2024

@EzraBrooks was there a blocker on updating the URDF spec? From some links you've sent before and other references I've seen since it does look like planar joints do generally (for some reason) include a third twist component and I think I've come around a bit since previous discussions. I'm just trying to reconcile the differences in the spec with how people are using things (which is the more important piece). It's unfortunate that parts of interchange documentation get left behind while software changes are made to get something working but it is looking like the documentation is the "wrong" thing here.

@gkjohnson
Copy link
Owner

I'm going to go ahead and merge this rather than contribute to the fragmentation I'm seeing in the community - I'm seeing that the spec is likely not the "source of truth" in these cases but rather an afterthought, unfortunately. Likewise the current implementation is structured so that the X, Y translation are the first arguments so the function will ignore the rotation component meaning it will work in either case.

Sorry for the delay in coming around on this.

@gkjohnson gkjohnson reopened this Aug 28, 2024
@gkjohnson gkjohnson marked this pull request as ready for review August 28, 2024 03:03
@gkjohnson gkjohnson merged commit 0c442aa into gkjohnson:master Aug 28, 2024
5 checks passed
@gkjohnson
Copy link
Owner

Just published in v0.12.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Floating, Planar joint types
4 participants