-
Notifications
You must be signed in to change notification settings - Fork 333
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
[JTC] Enable feed-forward effort trajectories #1200
base: master
Are you sure you want to change the base?
[JTC] Enable feed-forward effort trajectories #1200
Conversation
e6e3374
to
7b28c55
Compare
!(interface_types.size() == 1 || | ||
(interface_types.size() == 2 && | ||
rsl::contains<std::vector<std::string>>(interface_types, "position")))) |
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.
this is alright but I think we'll have to reimplement this function as it's getting a little chaotic in here :D
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 agree but I'm not volunteering to open this container of invertebrates just now. Happy to file it as an issue.
// If effort interface only, add desired effort as feed forward | ||
// If velocity interface, ignore desired effort | ||
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | ||
(has_effort_command_interface_ ? state_desired_.effort[i] : 0.0) + |
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.
how are we ignoring desired effort with velocity interface here?
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.
Yup! Same doubt
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.
// If effort interface only, add desired effort as feed forward | |
// If velocity interface, ignore desired effort | |
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | |
(has_effort_command_interface_ ? state_desired_.effort[i] : 0.0) + | |
// If effort interface only, add desired effort as feed-forward | |
// If velocity interface, ignore desired effort | |
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | |
(has_velocity_command_interface_ ? 0.0 : (has_effort_command_interface_ ? state_desired_.effort[i] : 0.0)) + |
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 admit this one is a not very readable.
It works because the whole block runs only if use_closed_loop_pid_adapter_ == true
(line 256, just above the displayed preview).
This is set here if has_velocity_command_interface_ || has_effort_command_interface_
as long as there is only one command interface. So checking for only for velocity command interface gives the correct result. It is probably an open door for all sorts of bugs though. I'll go with the suggestion.
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.
Just the one question
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.
Thanks for the nice add-on to JTC. I've left some comments from my side
|
||
This means that the joints can have one or more command interfaces, where the following control laws are applied at the same time: | ||
|
||
* For command interfaces ``position``, the desired positions are simply forwarded to the joints, | ||
* For command interfaces ``acceleration``, desired accelerations are simply forwarded to the joints. | ||
* For ``velocity`` (``effort``) command interfaces, the position+velocity trajectory following error is mapped to ``velocity`` (``effort``) commands through a PID loop if it is configured (:ref:`parameters`). | ||
* For ``effort`` command interface (without ``position`` command interface), if the trajectory contains effort, this will be added to the PID commands as a feed forward effort. | ||
* For ``position, effort`` command interface, if the trajectory contains effort, this will be passed directly to the ``effort`` interface (PID won't be used) while the positions will be passed to the ``position`` interface. |
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.
Can't the same be used with the combination of velocity, effort
and position, velocity, and effort
?. Ofcourse, given that there is no closed-loop option use_closed_loop_pid_adapter_
selected.
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 suppose it could work. The logic to switch what is calculated via PIDs and what comes from the desired effort would need a bit of thought. I don't have a use case for this though. Position+effort is very common for manipulation where you need to add that extra force to maintain contact. I've only used velocity control for an arm that had a very low control rate and for wheeled robots, neither of which needed feed forward effort.
// If effort interface only, add desired effort as feed forward | ||
// If velocity interface, ignore desired effort | ||
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | ||
(has_effort_command_interface_ ? state_desired_.effort[i] : 0.0) + |
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.
Yup! Same doubt
// No state interface for now, use command interface | ||
if (has_effort_command_interface_) | ||
{ | ||
assign_point_from_interface(state.effort, joint_command_interface_[3]); | ||
} |
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.
Do you think it would be interesting to support effort state interfaces in the future? it would help in introspect about tracking and all.
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.
isn't th read_state_from_command_interfaces
doing the same?
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 would be the convention for getting effort from the effort interface?
Some actuators expose torque or current measurements, for others it will be just returning back the last command. I would argue that both should be exposed when available, potentially needing two separate data fields in the message.
pids_[i]->computeCommand( | ||
state_error_.positions[i], state_error_.velocities[i], | ||
(uint64_t)period.nanoseconds()); | ||
} | ||
} | ||
else | ||
{ | ||
if (has_position_command_interface_ && has_effort_command_interface_) |
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.
if (has_position_command_interface_ && has_effort_command_interface_) | |
if (has_effort_command_interface_ && (has_position_command_interface_ || has_velocity_command_interface_)) |
If we support the combinations as commented as above, this would be sufficient I guess
// If effort interface only, add desired effort as feed forward | ||
// If velocity interface, ignore desired effort | ||
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | ||
(has_effort_command_interface_ ? state_desired_.effort[i] : 0.0) + |
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.
// If effort interface only, add desired effort as feed forward | |
// If velocity interface, ignore desired effort | |
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | |
(has_effort_command_interface_ ? state_desired_.effort[i] : 0.0) + | |
// If effort interface only, add desired effort as feed-forward | |
// If velocity interface, ignore desired effort | |
tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + | |
(has_velocity_command_interface_ ? 0.0 : (has_effort_command_interface_ ? state_desired_.effort[i] : 0.0)) + |
if (!has_effort_command_interface_ && !points[i].effort.empty()) | ||
{ | ||
RCLCPP_ERROR( | ||
get_node()->get_logger(), "Trajectories with effort fields are currently not supported."); | ||
get_node()->get_logger(), | ||
"Trajectories with effort fields are only supported for " | ||
"controllers using the 'effort' command interface."); | ||
return false; | ||
} |
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.
Should we do something if there is effort_command_interface and the points with empty effort?, should we at least print one time warning? or not needed?
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 would argue that for backward compatibility we shouldn't print warnings. Previously the effort command interface accepted position-only or position+velocity trajectories without warnings. It would start printing warnings for completely valid trajectories.
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.
Thanks for this contribution, including docs and tests.
However, the tests seem to fail deterministically with a segfault
2024-09-25T20:19:05.9980161Z [ RUN ] OnlyEffortTrajectoryControllers/TrajectoryControllerTestParameterized.compute_error_angle_wraparound_true/0
2024-09-25T20:19:05.9980772Z [INFO] [1727295318.864311285] [test_joint_trajectory_controller]: No specific joint names are used for command interfaces. Using 'joints' parameter.
2024-09-25T20:19:05.9981269Z [INFO] [1727295318.864359095] [test_joint_trajectory_controller]: Command interfaces are [effort] and state interfaces are [position velocity].
2024-09-25T20:19:05.9981683Z [INFO] [1727295318.864391335] [test_joint_trajectory_controller]: Using 'splines' interpolation method.
2024-09-25T20:19:05.9982066Z [INFO] [1727295318.864946880] [test_joint_trajectory_controller]: Action status changes will be monitored at 20.00 Hz.
2024-09-25T20:19:05.9982218Z -- run_test.py: return code -11
Can you please address this?
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1200 +/- ##
==========================================
+ Coverage 83.63% 83.68% +0.05%
==========================================
Files 122 122
Lines 10986 11059 +73
Branches 933 954 +21
==========================================
+ Hits 9188 9255 +67
- Misses 1488 1490 +2
- Partials 310 314 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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 fixed the segfault (no need for compute the error of the effort field) and fixed the tests. Now it looks fine for me.
effort
command interface (warning message is no longer displayed).effort
only controllers, the desired effort is added to the PID effort as a feed forward term.position, effort
command interfaces. The desired effort will be passed directly to the effort interface in this case (no PID is calculated). Positions are passed to directly to the position interface. This is useful when the harware runs a PID with a feed-forward term at a higher rate on the motor driver.