-
Notifications
You must be signed in to change notification settings - Fork 524
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
[effort_controllers] JointGroupPositionController: Check output of angles::shortest_angular_distance_with_large_limits #509
base: noetic-devel
Are you sure you want to change the base?
Conversation
This change has no dependence on your last 2 PRs, could you please trim the branch down to just the relevant commit to make it easier to review and merge. |
Ping @francofusco as the one who knows most about this. I'll need to give this a proper review tomorrow but my initial impression is that clipping/saturating the current position seems like the wrong approach. |
…gles::shortest_angular_distance_with_large_limits The current implementation does not check the output of angles::shortest_angular_distance_with_large_limits which indicates if the returned distance is an unspecified (and potentially wrong) case. This commit checks the return statement and adds a detailed warning. Secondly, the implementation in angles::shortest_angular_distance_with_large_limits is only valid if the `from` argument is within the range given by the upper and lower joint limit. As thus, the current_position is now enforced/clamped to the joint limits.
edc35b3
to
d732920
Compare
I've separated out the commit. Very interested to hear about alternatives to enforcing the joint limits - the important factor is right now it's broken by using undefined, known to be incorrect results for the error when the return value is false. I've seen this lead to joint run-offs when they are outside the URDF-defined position limits. We need to check the return result for sure, and then happy to consider other options/logic to handle those cases. |
Hi @wxmerkt!
You are totally right, when I updated the code I simply changed calls to
Looks good 👍
This choice does not fully convince me. If I am not mistaken,
The second option is almost "for free". In fact, even when
If I understand correctly, you mean that the error was left undefined after the function call, leading to some motor misbehaving. If this is the case, do you have more information related to the issue? To sum up: your Regarding the issue with the desired position being exactly the joint limit: as you said this is perhaps better addressed in ros-angles, I'll answer you there 😃 |
Another important point: when I introduced the
Once we have decided how to solve the issues you pointed out, we should remember to update all three controllers! |
Hi @francofusco, I am happy to remove the enforcing of joint limits. I noticed that for some cases with very, very large limits we had the opposite direction being returned as intended - an error is returned and defined in any case, but it may be pointing in the wrong direction. For those cases, I initially had a version which if the return value was false, just used Also happy to apply it to all three controllers once a consensus is reached. Many thanks, |
I'll think about this, it might be that in some cases the error is "swapped". Clearly this should not happen, I was expecting something closer to your
This might be a good way to handle some of the issues. What about doing something similar to the following? bool is_valid = angles::shortest_angular_distance_with_large_limits(
current_position,
command_position,
joint_urdfs_[i]->limits->lower,
joint_urdfs_[i]->limits->upper,
error);
if(!is_valid) {
double new_target = current_position + error;
enforceJointLimits(new_target, i);
error = new_target - current_position;
} |
Thanks for jumping on this @francofusco and for the awesome breakdown.
Thanks for articulating this more clearly that I did. I agree wholeheartedly, I don't want to clip the real joint position and hide out-of-bounds conditions. They can and do happen, and should be safely handled.
I think the confusion here might be that the documentation of
👍, I imagine sol 1 would be quite problematic unless we added some option that allowed the "freeze on out-of-bounds" to be toggled (Which might be worth considering anyways, might be a handy feature).
As long as we mean "Closest limit" in the whole revolution space, rather than just "closest" around a 2pi range, I agree with the concept but I don't think we need to override the command position. The command position is guaranteed to be within the valid range, so an attempt to get to the command would also be trying to bring us back in range. I think the bigger concern here is, as you @wxmerkt mentioned, the possibility of getting an error that further violates the limits in an attempt to get back in range. If you're out of range by more than 2pi, I like the idea of your |
Yes, that's pretty much it: the distance should still be set to the shortest angle. I'll consider updating the documentation at ros/angles so that it is clear without having to look at the code.
I did not think about it 😥 Now that you say that, I get the situation described by @wxmerkt in which the error is pointing in the wrong direction. Sorry, I was a little slow!
Yes, you are probably right, I was just overthinking it! However, the more I reason about this issue, the more I realize that perhaps the whole idea of using angle wrapping leads to more issues than benefits? I'll try to elaborate here. If no limits are used (continuous joints) then we can happily use With short limits, we are sure that the command will be shifted within the valid range, and thus the "raw error" Coming now to the large limits, let's assume that the bounds are ridiculously large, like [-1e10,1e10]. The cool thing of using wrapping is that if my joint is currently at 350deg and I ask to move it to 0deg, it will simply make a short motion of 10deg and reach 360deg. The only downside I see is that, if I actually wanted it to go to 0deg, I'd have to "bring it there" gradually, but let's forget about it for a moment. The biggest benefit I see in wrapping is that it would allow to avoid sudden jumps in certain cases. As an example, the commands might be produced from functions like "atan2(y,x)", which is discontinuous when x<0 and y changes sign. In this case, wrapping would make sure that commands are "smooth". If wrapping was not available, the issue would be that the user would be required to monitor the current position of the joint and to properly shift the command accordingly. Now consider the limits to be [-360PI,360PI]. Say that the joint was at 350deg. Say that I send a command that changes from -10deg to 10deg. During the first 10deg, the command would be "shifted" so that the joint moves from 350deg to 360deg. However, as soon as the command becomes positive, a jump happens. As an example, if the command was 1deg, the shortest wrapped motion would lead the joint to go at 361deg. However, this violates the upper limit, and thus To sum up:
With these considerations in mind, I argue that it would be easier to just use What do you think? Unfortunately, when I proposed #466 I did not think about it, but perhaps the "easy" solution is the best. I also think that this would be a minor issue in terms of compatibility, as robots with short limits (arguably the majority?) would not be affected. Finally, I can move the discussion somewhere else if you think this has become off-topic. |
On top of #504
The current implementation does not check the output of angles::shortest_angular_distance_with_large_limits which indicates if the returned distance is an unspecified (and potentially wrong) case.
Firstly, this commit checks the return statement and adds a detailed warning. If
false
is returned, e.g. the direction of the error might be wrong and the PID would command further against the joint limit.Secondly, the implementation in angles::shortest_angular_distance_with_large_limits is only valid if the
from
argument is within the range given by the upper and lower joint limit. This is highlighted in the doxygen of the function. As thus, the current_position is now enforced/clamped to the joint limits.There are further cases which are not yet treated in this PR: When the current or command positions are directly on the joint limit due to floating point precision. I've opened an issue to get insights into where to best handle this upstream: ros/angles#25. A quick-fix would be to modify the limits in
enforceJointLimits
by e.g.1e-6
.