Cartesian linear move can "wind up" the wrist

We have an application where a UR5 is moving items from a box to another location (either palletizing or simply placing the products at a fixed location). We use linear cartesian motions for most moves. Quite regularly, the required orientation for picking up the products causes the last wrist3 joint to wind up and finally cause a protective stop when the robot would have to move outside of the allowed joint range.

It occurred to me that for any cartesian linear motion there are in fact two distinct linear paths: one rotating clockwise around some axis, the other rotating the other way around the same axis. I imagine the UR selects the rotation direction with the smallest angle of the two. This makes sense, but it would be nice if the UR could detect cases where that path causes a joint limit violation and try the other path.

This would imply checking the path before executing it. Currently, the protective stop occurs after the robot started executing the path when it hits the joint limits. So checking the two possible paths for joint limits before executing (and rejecting the move if it isn’t possible) has the additional advantage that it leaves the robot in a more predictable state when it truly can’t execute the linear motion.

Is there any chance of seeing a feature like this in a future version of the controller?

You are correct in observing, that a MoveL command will always use the “shortest route” to the given pose.

You could consider using MoveJ, as this will move to the specified unique joint positions, and not the pose given.
If you have a pose, that you would like to convert to a unique set of joint positions, the script code get_inverse_kin() can be used, while specifying the qnear parameter.

You can also use the is_within_safety_limits() script code, which will return whether a position is within the safety limits. If the parameter given is a joint vector, the joint position limits are also taken into account, which I believe is what you are aiming for.

Combining get_inverse_kin() and is_within_safety_limits() could perform the check that you mention above.

We did indeed look at using MoveJ to avoid hitting joint limits, but it comes with its own drawbacks. For starters your motion isn’t linear in cartesian space anymore. I suppose we might be able to interpolate in cartesian space ourselves, but I doubt we can get the same performance out of the robot as a MoveL command.

Using MoveJ without interpolating small steps in cartesian space complicates avoiding collisions, since movements become less predictable. This is especially true when pick-up and place locations aren’t fixed. You also have to be very careful to check for configuration changes before executing the MoveJ.

So with or without interpolating small steps, the program becomes a lot more complex then just putting a few cartesian waypoints in sequence. That’s why I still think it would be great if MoveL could try to fallback to the path with the longer rotation if the first one causes joint limits to be violated.

You can also send joint positions to a movel, see the script manual. :slight_smile:

Yes, thanks :slight_smile: But the robot will not necessarily move to that joint configuration. The controller will perform forward kinematics and treat it as any other MoveL, so that doesn’t prevent winding up joints.

@de-vri-es You only really need one MoveJ in a cycle to return the joints to the starting angles and prevent cumulative rotations (spin wrist 3 back to the default angle while moving back to pick up the next product), the rest of the movements can still be MoveL.

I think that depends on the pick and place poses (both of which are dynamic in our case) and the pose of the robot when a cycle starts. In general I think you would need one MoveJ per dynamic move.

However, I would really like to do the unwinding of the wrist during a long cartesian motion to save time. A small MoveJ after pick-up adds more than a second to the cycle time. And making the long movement a MoveJ makes collision prevention difficult.

To clarify: we’re already working around this issue by using MoveJ. I appreciate all the suggested workarounds, but I would like to focus on the suggested behaviour for MoveL rather than workarounds with MoveJ.

2 Likes

Are your dynamic picking and placing positions very different each cycle? I was thinking along the lines of making a standard predictable movej between the pick “zone” and place “zone” assuming they’re distinct areas of the workspace. Hard to say without knowing more about the task.

If you want to stick with MoveL I’d go with Jacob’s suggestion of using the get_inverse_kin() and qnear function together with is_within_safety_limits(). You could put this check in a subprogram/custom script function to avoid adding complexity to your main program.

Like I said, we’re already working around this with a MoveJ. But I did put this in the suggestion category. I’m not looking for help coming up with workarounds, as much as I appreciate the readiness of everyone here to help out with that. I would like to discuss whether or not there is any merit in the behaviour for MoveL suggested in the original post.

I think there is because all of the workarounds are either slower or make collision prevention much more difficult. And duplicating the suggested behaviour with manual interpolation is a lot of complexity and probably a performance hit compared to the robot controller doing it itself.

Ok fair enough, hopefully the suggestions will help others encountering the same issue in future.

So yes I can see the benefits of your suggestion :slight_smile: but whether it will be implemented when it can be avoided with a couple of lines of script, I am not sure. @jbm What do you think?

@ajp
I believe the biggest concern on implementing this, is that it might break the functionality of old programs, that would then no longer work as expected/previously.

That is a very valid concern. What about an optional boolean argument (defaulting to False) to allow MoveL to take the longer rotation if the shorter one isn’t possible? And of course a checkbox in that case in PolyScope. I’d think per waypoint, but it could also be on the MoveL node.

After some more working around the problem, I’m not so sure this is actually a very useful approach.

My assumption was that the long rotation would prevent hitting joint limits. However, in some cases we actually had to rotate in the same direction as the short path, but an additional 2*pi. I’m not so sure that this can easily be fixed in movel() without making the robot movement rather unpredictable.

So, we did manage to detect what rotation should prevent hitting the wrist3 limit in our setup, but there are a number of assumptions it makes which don’t hold in general. However, once we detect which way we want to rotate, we have to inject multiple movel() commands to force the robot to take a longer path (either alpha - 2*pi or even alpha + 2*pi). This is less than ideal because it is not trivial to come up with a a good blend radius for the waypoints.

So I think a different command might be useful, a command that takes a pose relative to the current TCP pose (moveLR?). Basically instead of describing the desired end pose, you describe the desired motion. Then you can easily make the robot perform a linear rotation in cartesian space of more than 180 degrees without having to worry about blend factors.

2 Likes

You are right in that the performance of an interpolated movej is not the same as a MoveL, I’ve tried it and, although motion is smooth and linear, the speed is significantly reduced. Changing acceleration and velocity parameters only makes it jerky, so not very practical…

I think this is the best idea, a good compromise between backwards compatibility and usability.

We are working on a camera-controlled robot motion, so all our positions are dynamic. We teach the robot to grasp and drop objects in coordinate frames (UR features) generated by the camera. As we use teaching, the motion code is auto-generated, and we cannot add extra checks there, so it would be great, if the robot could do it on its own.

The workaround with movej works, but it’s not optimal - the robot makes unnecessary wrist rotations.