-
Notifications
You must be signed in to change notification settings - Fork 100
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 antiwindup to PID controller #38
Add antiwindup to PID controller #38
Conversation
Please add some documentation and tests for the new functionality. From reading the existing tests, it seems that More specifically, I would expect from reading the code API docs to understand the features and usage of the class. I would further expect to find reasonable usage examples from inspecting the test suite. The code might be there (except for the tests), but doc-wise I don't think the point is getting across. |
Hi, I'm not sure this is actually implementing an anti-windup method, and in fact, I don't think it is changing anything at all from the previous PID implementation, but correct me if I'm wrong at something. I'm going to go through the added feature, that is, when
Same happens if you go through the upper-saturated case, you end up with This is because the clamping is done on the If you don't really need an anti-windup method, and just want to get rid of the clamping, I would change the name |
@carlosjoserg, clamping command output is certainly a feasible augmentation to the PID implementation, however it's distinct from antiwindup. The existing controller behaviour is to clamp the integral output ( There are many flavours of antiwindup, however the one I'm going with is "Preventing the integral term from accumulating above or below pre-determined bounds", which is done in this pull by limiting the controller's state ( |
Tests added, depends on #44 |
Or #42 which does the same thing, as well as adding tests. |
Ok, now I see! It is because of the clamping on the new Still I have a suggestion: Why not to use And perhaps the flag name could be like |
@carlosjoserg, because we are reusing gains.i_max from the previous implementation, where it is in the scale of i_term (and hence cmd_) and not i_error. This is reasonable because a user would want to parametrize the windup limit in the context of the controller's output cmd_ value. |
@paulbovbel thanks for adding this antiwindup. We are highly interested in this feature. Maybe a test should be added to consider negative gains too unless this is never allowed / unsupported. Please also add the antiwindup to the second computeCommand, Shadow uses this one: hope this can be fixed and merged soon since it solves one issue on our Shadow hands controllers. |
@guihomework, I've only added the code to one computeCommand implementation, waiting on #44 to chain them together. Thanks for testing these changes out - can you the fabs fix to https://github.com/clearpathrobotics/control_toolbox/tree/antiwindup-fix please? I am happy to write some tests, but I know @adolfo-rt is very busy, and I want to make sure we settle on the implementation before I spend time writing the tests. |
clearpathrobotics#1 with the fix only on the current implementation of the antiwindup |
I'm now in the process of merging pending PRs. I can take another look at this one once the negative gains issue is fixed and some tests are in place. |
5633b61
to
c8e5133
Compare
Negative gains issue has been fixed and tests have been added, ping for review. |
9192c5c
to
1272ac2
Compare
dear @paulbovbel we are using your branch + merged pid_state in our fork (waiting for this to be merged upstream, and this was fine until we wanted to rebuild today. I updated to the latest rebased and fixed branch but you added some parameters to the set function that is not covered yet by ros_controllers. Do you have a branch that already has some code fixes of ros_controller to handle these ?
thanks in advance |
@guihomework, thanks for picking up on that. also required: ros-controls/ros_controllers#195 @adolfo-rt, I have propagated the changes to add an antiwindup bool to control_msgs/JointControllerState. This probably wouldn't fly for indigo, but would you consider merging this change set into jade instead? |
@paulbovbel thanks for quick reacting on that. |
@paulbovbel I found another bug in the handling of the antiwindup read from the parameter server. Due to a python software converting by error a True into a 1, I got antiwindup: 1 in my pid controller configuration yaml. If a param is not a True/False boolean, param reading to a bool will fail and hence false is initialized in the pid controller as the default false value. Fine. But why can I read my 1 converted into True on the parameter server after the pid controller has initialized where I should see 1 or false at least ? The Gains struct does not get its member antiwindup initialized to the real value read from the parameter server when init with a nodehandle is used https://github.com/clearpathrobotics/control_toolbox/blob/antiwindup-fix/src/pid.cpp#L93. This is the only init where setGains passes the Gains struct and not individual values. The solution is to initialize the Gains.antiwindup_ and then at least the 1 is converted into false, which should help the user to see the problem. If you want I can provide a patch, but this is a rather straight forward one liner add I think However, because the internal variable antiwindup_ is used in the computeCommand, this means that the dynamicReconfiguration of antiwindup will not be effective since it only changes the gains struct and not the private antiwindup_. |
@@ -45,6 +45,7 @@ def generate(gen): | |||
gen.add( "d" , double_t, 1,"Derivative gain.", 1.0 , 0 , 1000) | |||
gen.add( "i_clamp_min" , double_t, 1,"Min bounds for the integral windup", -10.0 , -1000 , 0) | |||
gen.add( "i_clamp_max" , double_t, 1,"Max bounds for the integral windup", 10.0 , 0 , 1000) | |||
gen.add( "antiwindup" , bool_t, 1,"Antiwindup.", True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the default for the antiwindup
parameter? It seems to be true
in this dynamic_reconfigure config here, but false
in the constructor.
Is there a real need for adding the Actually both settings clamp the integrator output to In my opinion the On the other side I would suggest to introduce an |
if(antiwindup_) | ||
{ | ||
// Prevent i_error_ from climbing higher than permitted by i_max_/i_min_ | ||
i_error_ = std::max(gains.i_min_ / std::fabs(gains.i_gain_), std::min(i_error_, gains.i_max_ / std::fabs(gains.i_gain_))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: factor out the clamp implementation to improve readability. The function could live in an anonymous namespace in this file. If it's not used outside the file, you can inline it as well (in the cpp).
namespace
{
inline double clamp(val, min, max) {return std::max(min, std::min(val, max));}
}
@meyerj regarding your suggested If the antiwindup parameter is maintained, the default values should indeed be consistent everywhere. |
That's exactly the point: No. In almost all practical use cases the limitation is the hardware (or the hardware interface), which only sees the overall output (sum of the proportional, integral and differential terms). Whenever this limit is reached, the anti-windup mechanism should stop the integrator, because the control error cannot be further reduced by "applying more force". Otherwise, if the internal integrator state continues to "charge" up, the control system eventually cannot recover fast enough once the set point would come back to a feasible region. So the limitation of the integral term only like in the current implementation looks quite weird to me, because the output is still unbounded and if only the user of the Pid class would apply some saturation limits, there is no way to prevent internal wind-up. In practice, limiting the integral term alone might be enough because the other terms are usually somehow bounded by the inputs, but where should the Here is how the Simulink PID controller block handles anti-windup, just for reference: What I would propose is to apply the |
@meyerj the simulink approach is one solution. If you have a branch that does anti-windup regarding global output saturation, I would be glad to test it, and possibly contradict my intuition. The Shadow controllers have limitations for absolute hardware maximum (or soft maximum let's say), to not send values above what the hardware can do. The PID output is not sent directly to the joint_effort command as there is a feed-forward term too or a friction compensation term (that do not exist yet in PID controller) added to that. I need a second output limitation anyway. I suppose the imin/imax parameter should be kept also for backward compatibility. |
I think the general approach with a package like this one is that the default functionality/implemenation must be respected, since it's used downstream by many unaware users. @guihomework thanks for spotting that. I'll have to re-evaluate how I've used the Gains struct and make sure everything works well. |
Any updates here? Not likely to make it into the first Kinetic release but better to start pushing for it now. |
I was hoping it could be even integrated in indigo at some point... |
I'm open to merge things for Kinetic. Merging to Indigo and Jade will need more attention that I do not wish to decide on alone. @paulbovbel any updates? Could you please do a rebase & a new PR against kinetic-devel? |
Will have time to work on this on March 27th. Is this blocking release for you @bmagyar? |
I'm rolling everything out as soon as the anti windup stuff is merged to I would prefer to not to start with a planned API update but perhaps that's Will have time to work on this on March 27th. Is this blocking release for — |
The core stuff works, I just have to examine the whole chain of components that depend on control_toolbox and make sure I don't break anything, it seems like a deep rabbit hole with ros_controllers. Not particularly difficult but time consuming and I want to do it right. Wednesday should be good. |
I think I may be able to release |
@paulbovbel , I cut the dependency of |
1272ac2
to
6690092
Compare
6c5c945
to
c05d03c
Compare
@guihomework, thanks for your patience and testing, I've corrected the issues you highlighted. |
6bdaabb
to
ad8ae2a
Compare
Resolve #34, adding a clamp on the internal integral error state rather than the output integral term.