-
Notifications
You must be signed in to change notification settings - Fork 99
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
Added dynamic reconfigure for PID gains #1
Conversation
I added this so that Gazebo users can easily tune their robot controllers to work in simulation |
@adolfo-rt can you review this PR and its real-time controller implications? |
ROS_ERROR("No p gain specified for pid. Namespace: %s", n.getNamespace().c_str()); | ||
return false; | ||
} | ||
n.param("i", i_gain_, 0.0); | ||
n.param("d", d_gain_, 0.0); | ||
if (!n.getParam("i", 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.
init should not fail when the derivative or integral gains are not specified. The previous behavior of defaulting them to zero without raising an error should be preserved. We should allow specifying P, PD or PI controllers without requiring the explicit setting of the unused gains. The proportional gain is error-checked because it is unlikely you'll find a controller without it.
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.
Ah, that makes a lot of sense. I thought it had just been coding laziness!
@adolfo-rt i have made your suggested fixes, can you double check the mutex usage here: https://github.com/ros-controls/control_toolbox/pull/1/files#L4R168 please? Thanks! |
|
||
getGains(config.p_gain, config.i_gain, config.d_gain, config.i_clamp_max, config.i_clamp_min); | ||
|
||
// Set starting values, using a shared mutex with dynamic reconfig |
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.
Does this look right?
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.
I think so, yes.
Who has commit access to control_toolbox? I seem to only have access to ros_controls and ros_controllers |
${Boost_INCLUDE_DIR} | ||
) | ||
|
||
find_package(Boost REQUIRED COMPONENTS system thread) |
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.
Doing find_package(Boost ...) after setting the include dirs to include boost seems like a bug.
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.
I've moved it above the include_directories
line.
I think the assignment operator needs an update; probably just an added call to updateDynamicReconfig(). |
I'm worried about locking around the p_gain_, i_gain_, d_gain_, i_max_ and i_min_ terms. Without locking, it's possible that the dynamic_reconfigure callback will update a few of the terms and get preempted while the realtime part of the controller runs, at which point it will see an inconsistent set of gains. With locking around these parameters, it's possible that the dynamic_reconfigure callback could block execution of the realtime part of the controller, which could cause the entire realtime loop to miss its deadline. I know some parts of this code operate in realtime and some parts do not; is there a clear delineation of which methods run as part of the realtime controller and which do not? |
From what I can tell, the only risk of breaking the realtime run loop is if you:
|
@ahendrix, @davetcoleman The recursive lock passed to dynamic_reconfigure would not be used inside the PID class. Instead, when new values arrive via a reconfigure update, one would safely make them effective by doing a writeFromNonRT(), and reading them would be done with readFromRT(). Lastly, I would advise to put the gains inside of a struct, so that only one |
I've seen the RealtimeBuffer used in ros_control[lers] in lots of places, and I'll implement it here, but is this really needed you think? |
If the alternative is using the recursive_mutex shared with dynamic_reconfigure, then yes; otherwise there is a risk of a priority inversion. In the end what you want is to read data using a fast, constant-time operation (ie. deterministic). The Notice that the |
+1; RealtimeBuffer sounds like the right solution. @davetcoleman see my reasoning above for why I think this is required. Realtime constraints don't seem to be a big problem in Gazebo, but they're a very real problem on a real robot where we're running the controllers at 1kHz, and a delay of more than a few hundred microseconds could halt the system. |
I've added the realtime buffer... it required a lot of changes and in order for me to thoroughly make sure I implemented it right I re-ordered some of the functions such that they are grouped by function (initialization, etc) and their ordering matches the header file. My apologies for the diff. I'd like to do some more runtime testing before merging this. |
I think this looks good overall. More testing is better. |
Is it still possible to set the gains through the old parameters? |
Thanks for taking on this Dave. It's a great usability improvement for the PID class. |
Done
Done
Yes, I haven't changed any of the old api There is one issue though... the ability to do In the old implementation (without a realtime buffer) it had an opertor= overload that did some custom stuff:
Now with the real time buffer, we can't do
But we also can't do any kind of accessing or copying of the The only solution I can think of is adding a new function to
But I'm not entirely sure if this will return the latest data/if its a good addition. I'll add a pull request do discuss this idea on the realtime_tools repo... |
… from a const PID class
I think I have resolved the overloaded assignment operator issue as well as removed the need for adding a new It should be pretty stable now. |
*/ | ||
void getCurrentPIDErrors(double *pe, double *ie, double *de); | ||
Gains getGainsConst() const; |
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.
getGains() should probably be const, which would make this redundant.
having const and non-const methods that do exactly the same thing is redundant and confusing.
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.
As of today's fixes, getGainsConst()
calls a different function in the runtime_buffer class - it calls a blocking readFromRTConst()
. I think providing this optional functionality is beneficial for certain applications.
I upgraded the PID tests to use the new |
@ahendrix can we merge and release this too? thanks! |
|
||
Pid::Gains Pid::getGains() const | ||
{ | ||
return *gains_buffer_.readFromNonRT(); |
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.
Having different versions of getGains that differ only in const-ness, one of which is realtime-safe and one of which is not seems like a good way to introduce bugs.
Is the non-const version useful for anything?
I suggest we either eliminate the non-const version or rename them to indicate realtime safety or lack thereof; perhaps getGainsRT and getGainsNonRT.
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.
I was using it for the printValues() helper function, but I have removed the const getGains() function.
|
||
/*! | ||
* \brief Get PID gains for the controller. | ||
* \return gains A struct of the PID gain values |
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.
Having const and non-const methods with the same name, one of which is realtime-safe and the other of which is not sounds like a recipe for bugs.
I suggest you rename them to 'getGainsNonRT' and 'getGainsRT' or something similar that reflects the realtime-safety of each method, or just remove one of them.
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.
Did you mean to post this comment again? I fixed this issue last Friday. There are no const getters anymore.
Added dynamic reconfigure for PID gains
Release to build farm? |
Doesn't build on my machine; copy constructors are broken:
The main problem here is that the copy constructor for control_toolbox::Pid does not take a const argument. I'll work on it... |
Fixed by making the copy constructor parameter const. Released control_toolbox 1.10.2 to Hydro: ros/rosdistro#1578 |
Thanks! |
* Update gh actions versions * Cleanup doc and destructor
You can now set the P, I, D and i_clam_min/max using dynamic reconfigure