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 velocity feedforward term to velocity HardwareInterfaceAdapter #227

Merged

Conversation

miguelprada
Copy link
Contributor

In the current implementation of the VelocityJointInterface specialization for HardwareInterfaceAdapter, a velocity command for the joint will only be generated (by the PID) in the presence of tracking error, therefore perfect tracking can never be achieved.

Adding a velocity feedforward term unloads much of the burden from the PID, which only needs to correct for the position and velocity drift that may appear if (when) the robot does not perfectly follow the generated commands. This should allow to improve the controller's performance even decreasing the PID gains significantly, therefore improving also the controller's stability.

I'm not 100% sure of the effects of this change in configurations where the PIDs have been tuned to carry all the load of tracking the velocity reference. I'm inclined to believe that nothing bad will happen, although some tests or some sound theoretical proof would be nice to have in this respect.

@bmagyar
Copy link
Member

bmagyar commented Jun 16, 2016

Nice catch! First, could you please add some unit tests of expected outcomes? That is a good base to start a discussion on.

Also adding @ipa-fxm for further thoughts

@miguelprada
Copy link
Contributor Author

I'd be glad to, but I'm honestly not quite sure of what the tests should look like.

Only thing I can think of is making this feedforward term optional, using a parameter, and to include a test which compares the evolution of the velocity-based trajectory controller with and without the feedforward term. This would require adding a VelocityJointInterface to the RRbot used for testing, although it seems quite doable.

An additional benefit would be that adding a VelocityJointInterface to the RRbot would also allow to run the rest of the existing tests on the velocity-based controller, which, if I'm not wrong, are currently run only for the position-based trajectory controller.

What do you think? Any better ideas @bmagyar, @ipa-fxm, anyone?

@miguelprada
Copy link
Contributor Author

I just opened PR #228, duplicating the tests for the velocity-based joint trajectory controller, as a basis for testing this PR.

Any thoughts on how the tests for the (optional) feedforward term should look like?

And BTW, what's the nice thing to do with this PR? Rebase the only commit on top of #228 and reopen? Not quite comfortable with the rules of etiquette here yet 😅

@bmagyar
Copy link
Member

bmagyar commented Jul 1, 2016

Sorry Miguel for the delay.
First: PRs can be rebased without the need to reopen them. You can do this by rebasing the branch you made the pull request from, and doing a push --force on it, overwriting the history. Make sure you have a backup if you don't feel comfortable doing this.

Duplicating all that test code may not be the best solution. I mean 1000 lines of duplication are kinda hard to maintain :)

Any comments on this, @efernandez ?

I'm going to be quite busy this week but will try to get back next week, feel free to ping me.

@efernandez
Copy link
Contributor

I'm not an expert in control. My understanding is that with this the PID actually works on the state error, which is good.

Since I'm not sure of the side effects of this, I'd feel more comfortable if you could provide a param to enable/disable the feedforward term, and set it to false by default to keep previous behaviour; regarding the default value, I honestly don't have a strong opinion. Indeed, I really like that the PIDs can have smaller values and actually work on the state error, which I think it makes more sense.

@miguelprada
Copy link
Contributor Author

Sorry, guys, for the delay.

Duplicating all that test code may not be the best solution. I mean 1000 lines of duplication are kinda hard to maintain :)

Totally agree with this, but I was a bit confused by how the TEST_F macros have harcoded values for the test fixture and figured at first that it was important to make them different across tests.

I just commited to #228 with what I hope is a better solution for the tests, which duplicates no code at all, and just requires duplicating the controller configuration and test launchfile. Let me know what you think, @bmagyar.

Since I'm not sure of the side effects of this, I'd feel more comfortable if you could provide a param to enable/disable the feedforward term, and set it to false by default to keep previous behaviour; regarding the default value, I honestly don't have a strong opinion. Indeed, I really like that the PIDs can have smaller values and actually work on the state error, which I think it makes more sense.

I also thought about this and think it's a reasonable solution. Regarding the default value, I would be inclined to make it enabled by default at some point, but I understand if you want to leave that for future releases in order to avoid messing with working setups.

I'll give this a go, @efernandez.

@miguelprada
Copy link
Contributor Author

Hi guys, just changed the velocity feedforward term to be enabled or disabled based on a parameter named use_velocity_ff. The default case if the parameter is not found is to disable the feedforward term, although this is straightforward to change if it is decided to enable it by default.

I also rebased on top of #228, even though it hasn't been merged yet. I'm sorry if this causes any inconveniences.

Since #228 does not contain the velocity feedforward term, though, there's no specific test to validate it. Can you think of any additional test that can be added to validate this specific addition?

@bmagyar
Copy link
Member

bmagyar commented Aug 2, 2016

Our common practice is to have one (perhaps one set of) explicit test(s) for each parameter we add. This makes sure that branch testing is somewhat achieved.

@miguelprada
Copy link
Contributor Author

miguelprada commented May 14, 2017

I finally managed to add a dedicated test for the velocity feedforward optional term!

What I did is:

  • drastically lower the PID gains for the VelocityJointInterface-based controller and enable the feedforward term in rrbot_vel_controllers.yaml, which does not affect the outcome of the existing tests
  • add a new test1 which executes the same trajectory twice, one with and one withouth use_velocity_ff, showing that the first one succeeds while the second one aborts due to a path tolerance violation
  • disable the test for PositionJointInterface
  • fix a typo

Thoughts?

1 If you wonder about the place where I inserted the new test, I put it there because I initially added it at the end and this assertion was always failing. Moving it before the tolerance checking tests made it work flawlessly, so I guess there's some side effect from the latter two tests. I haven't had time to figure out what happens, though.

@miguelprada
Copy link
Contributor Author

miguelprada commented May 14, 2017

I don't understand what's going on with the tests. They:

  • just failed when triggered by the PR, but for the PositionJointInterface controller test here
  • failed as well (in different places) for my fork when I rebased the branch before the PR, a test whose results I didn't wait for because it...
  • passed for the very same commit (c3e0988) when I was testing on another branch before rebasing add_velocity_ff
  • they also consistently pass for me locally

Any ideas? Can anyone else please try?

@mathias-luedtke
Copy link
Contributor

mathias-luedtke commented May 15, 2017

The current test set is very sensitive to timing and processor load. That's why we have configured travis to run only one test at a time. However, this approach does not fix the timing issues, but reduces the number of occurrences. In addition I am not 100% sure that we really limit the number of jobs properly.

Right now I am runnign the tests of the current master and your PR locally with run_ci.

Update: Tests passed locally for multiple times.

@miguelprada
Copy link
Contributor Author

miguelprada commented May 15, 2017

ROS_DISTRO=kinetic ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu rosrun industrial_ci run_ci failed for me 2 out of 10 times locally. 😕

@miguelprada
Copy link
Contributor Author

Also, if this happens to be finally merged, is there any chance the changes are backported to indigo? Not sure about the compatibility policy that's followed around here.

@@ -213,7 +216,7 @@ class HardwareInterfaceAdapter<hardware_interface::VelocityJointInterface, State
// Update PIDs
for (unsigned int i = 0; i < n_joints; ++i)
{
const double command = pids_[i]->computeCommand(state_error.position[i], state_error.velocity[i], period);
const double command = (desired_state.velocity[i] * use_velocity_ff_) + pids_[i]->computeCommand(state_error.position[i], state_error.velocity[i], period);
Copy link
Contributor

Choose a reason for hiding this comment

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

This line multiplies a boolean with a double. value.
I would prefer a velocity_ffparam, which defaults to 0.0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it the unconventional double * bool multiplication which you don't like or is it restricting the range of values to effectively 0 and 1? If its the multiplication I can always reformulate in a conditional statement.

I ask because I can't think of any reasonable case where feedforward gains other than 0 or 1 make sense. Can you?

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure if that really made sense, but we used 0.9 for the Care-O-bot 3 arms (pre-ros_control stack).
I don't see a reason to restrict it to either 0.0 or 1.0, but I am no contoller expert.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Me neither. I had always assumed that the feedforward was either there or not, but let's assume whoever put the 0.9 in the COB knew what he was doing and make the parameter a double.

Then, what would you suggest for values greater than 1.0? Should we raise some warning and clamp, just raise a warning or do nothing altogether?

@mathias-luedtke
Copy link
Contributor

Travis runs

 rosrun industrial_ci run_ci ROS_DISTRO=kinetic ROS_REPOSITORY_PATH=http://packages.ros.org/ros-shadow-fixed/ubuntu CATKIN_PARALLEL_TEST_JOBS=-p1 ROS_PARALLEL_TEST_JOBS=-j1

@bmagyar
Copy link
Member

bmagyar commented May 15, 2017

Thanks for your work on this @miguelprada . I can't join in the review discussion at the moment but I'd like to comment on the note about Indigo. Personally I'd like to eventually freeze Indigo and only allow bugfixes but we can discuss small and controlled features to be added if there's a big demand.

@miguelprada
Copy link
Contributor Author

Personally I'd like to eventually freeze Indigo and only allow bugfixes but we can discuss small and controlled features to be added if there's a big demand.

I'm not sure about the demand, but there's a specific reason I think it would be nice to have this in indigo.

The ur_modern_driver's implementation performs much better when using the VelocityJointInterface than when using the PositionJointInterface (due to a non-negligible delay introduced by the latter). However, the behavior of the speedj URScript command (used by the VelocityJointInterface) has changed a couple of times in past UR software updates, introducing delays and making some users of this driver experience stability issues (myself included). As a last note, ur_modern_driver is not yet available for Kinetic.

Adding this patch to the indigo branch would allow reducing the gains in the default configuration for the joint_trajectory_controller in that driver, potentially removing/reducing the instability issues.

@miguelprada
Copy link
Contributor Author

miguelprada commented Nov 21, 2017

Just added a new commit making the parameter a double, as @ipa-mdl suggested.

As I was on it, I also added the optional feedforward available for the EffortJointInterface. In fact, it is in the latter case where I can see values different than 0 or 1 make any sense (e.g. to compensate for friction, based on velocity), but I guess it's better to have some consistency in the parameter API for the different interfaces.

Also, I thought it made more sense to be able to set a different value of the feedforward gain for each joint, so I changed how the parameter is read as well.

One missing thing would be to add tests for the EffortJointInterface, similarly as #228, but it would be much more complex than that. I think it's beyond the scope of this PR, but let me know if you think otherwise.

Thoughts, anyone?

EDIT: I also rebased on top of current kinetic-devel. Hope that's fine.

@miguelprada
Copy link
Contributor Author

Friendly ping, @bmagyar, @ipa-mdl.

Not sure why job #528.3 is failing. It passes for me locally. Can anyone try to relaunch?

@bmagyar
Copy link
Member

bmagyar commented Dec 5, 2017

Thumbs up for the rebase!
I've also relaunched the travis job.
Disregard the failed test on four_wheel_steering_controller, we have to revise those numbers to get more stable tests.

@miguelprada
Copy link
Contributor Author

And yet another failed test which I'm unable to reproduce locally 😓

The failed test case seems to be different each time, though. Not sure what to do about this, to be honest.

@mathias-luedtke
Copy link
Contributor

And yet another failed test which I'm unable to reproduce locally 😓

It's all about timing, hard to reproduce..

Not sure what to do about this, to be honest.

I would suggest to ignore it for now, unless all jobs fail..

@miguelprada
Copy link
Contributor Author

Are you waiting for any additional input from me? Maybe I'm missing something, but I think all existing comments have been addressed.

It would be a nice Xmas present to finally get this merged so I can stop including ros_controllers source in all my workspaces 😉

@@ -277,6 +286,13 @@ class HardwareInterfaceAdapter<hardware_interface::EffortJointInterface, State>
}
}

// Load velocity feedforward gains from parameter server
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be reused somehow?
It's the same as in line 185..
FF-enabled base class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In fact, most code is duplicated between the VelocityJointInterface and the EffortJointInterface adapters. I'll refactor into a common base class for these specializations.

@@ -35,5 +35,6 @@
<test test-name="joint_trajectory_controller_test"
pkg="joint_trajectory_controller"
type="joint_trajectory_controller_test"
time-limit="85.0"/>
time-limit="85.0"
args="--gtest_filter=-JointTrajectoryControllerTest.jointVelocityFeedForward"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you exclude this test?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this test file is for the position interface only?
Since the code gets built twice anyway, It could be an option to add macro switch to emphasize that this test won't get run for the

At least a comment should be added.

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 guess this test file is for the position interface only?

Exactly.

Since the code gets built twice anyway

Yeah, not very happy about this either. I'll see if I can come up with some way to only build them once.

And I'll add some comment anyways.

Copy link
Contributor

@mathias-luedtke mathias-luedtke Dec 15, 2017

Choose a reason for hiding this comment

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

I'll see if I can come up with some way to only build them once.

Something like
target_compile_definitions(joint_trajectory_controller_vel_test PRIVATE TEST_VELOCITY_FF=1) would justify somehow to build it twice ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO building it twice is not too bad.

Building it just once would require (?) to:

  1. move tests (without FF) into a library
  2. expose text fixture class as header (in test directory)
  3. add source file for the main function
  4. add_rostest_gtest with main, linked to lib
  5. add_rostest_gtest with main + ff-test, linked to lib

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Finally went for the #ifdef TEST_VELOCITY_FF check. Is it more or less what you had in mind, @ipa-mdl ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Finally went for the #ifdef TEST_VELOCITY_FF check. Is it more or less what you had in mind,

It's exactly what I had in mind :D

@bmagyar
Copy link
Member

bmagyar commented Dec 15, 2017 via email

Most code in Velocity and EffortJointInterface specializations of
HardwareInterfaceAdapter was shared and has been refactored into a common
base class.
@@ -981,6 +981,9 @@ TEST_F(JointTrajectoryControllerTest, ignorePartiallyOldActionTraj)
}

// Velocity FF parameter ///////////////////////////////////////////////////////////////////////////////////////////////
// This test will only be built and run for the VelocityJointInterface-based version of the JointTrajectoryController

#ifdef TEST_VELOCITY_FF
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider #if TEST_VELOCITY_FF to handle TEST_VELOCITY_FF=0 as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just noticed this comment. Updated.

@mathias-luedtke
Copy link
Contributor

Thanks for the refactoring!
Why did you name it ClosedLoopHardwareInterfaceAdapter? Can't position controllers be closed-loop as well?

Somehow the joint trajectory controller have become unstable with the last commit.

@miguelprada
Copy link
Contributor Author

Why did you name it ClosedLoopHardwareInterfaceAdapter? Can't position controllers be closed-loop as well?

In those cases the adapter closes a loop by computing their commands based on the desired state and the state error. This is not the case for the rest of the adapter specializations.

Somehow the joint trajectory controller have become unstable with the last commit.

Which one? The one modifying the tests or the refactor? I must admit I just run the tests locally and pushed when I saw that they passed 😳

I'll play with it locally a bit.

@mathias-luedtke
Copy link
Contributor

In those cases the adapter closes a loop by computing their commands based on the desired state and the state error. This is not the case for the rest of the adapter specializations.

I see, this makes sense!

Which one? The one modifying the tests or the refactor?

79bf8d1. I have restarted 537.2.

537.3 failed as well, but with a different errror

@mathias-luedtke
Copy link
Contributor

mathias-luedtke commented Dec 18, 2017

Thanks for the update!

i have restarted all test jobs, which failed for unrelated errors :-/.

Copy link
Contributor

@mathias-luedtke mathias-luedtke left a comment

Choose a reason for hiding this comment

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

Thanks a lot for your patience!
This is definitely ready for merge now.

I will wait some time (a couple of days?) for others to review it as well.

@miguelprada
Copy link
Contributor Author

No hurries. What's a couple of days compared with a year an a half I've been dragging this along 😉

@mathias-luedtke mathias-luedtke merged commit 6591a56 into ros-controls:kinetic-devel Dec 24, 2017
@mathias-luedtke
Copy link
Contributor

ho ho ho 🎅

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.

None yet

4 participants