-
Notifications
You must be signed in to change notification settings - Fork 0
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 new joint range limiter for hardware interfaces #5
base: add-simple-joint-limiter
Are you sure you want to change the base?
Changes from all commits
9162045
fb0e7d4
f8c6fcd
940ff31
0ed50bb
fe8394f
f291486
e87cad2
3a2ddc5
39d904a
a1ca050
a7e9852
dadc958
0563685
cc67fba
84fc1a1
4278adb
afade53
255be09
a47ba74
1a78beb
0f48afa
71df023
358e3bd
f26bd8d
7bc19c0
8a5267a
332d403
df6bef0
faba81d
97e18b8
0b476f3
e31f6a4
732dae6
d120117
f4741d1
bc97448
12973f6
a973d42
6d8d66e
415ea20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,9 +30,8 @@ | |
|
||
namespace joint_limits | ||
{ | ||
using JointLimitsStateDataType = trajectory_msgs::msg::JointTrajectoryPoint; | ||
|
||
template <typename LimitsType> | ||
template <typename LimitsType, typename JointLimitsStateDataType> | ||
class JointLimiterInterface | ||
{ | ||
public: | ||
|
@@ -70,55 +69,58 @@ class JointLimiterInterface | |
// TODO(destogl): get limits from URDF | ||
|
||
// Initialize and get joint limits from parameter server | ||
for (size_t i = 0; i < number_of_joints_; ++i) | ||
if (has_parameter_interface()) | ||
{ | ||
if (!declare_parameters(joint_names[i], node_param_itf_, node_logging_itf_)) | ||
{ | ||
RCLCPP_ERROR( | ||
node_logging_itf_->get_logger(), | ||
"JointLimiter: Joint '%s': parameter declaration has failed", joint_names[i].c_str()); | ||
result = false; | ||
break; | ||
} | ||
if (!get_joint_limits(joint_names[i], node_param_itf_, node_logging_itf_, joint_limits_[i])) | ||
for (size_t i = 0; i < number_of_joints_; ++i) | ||
{ | ||
RCLCPP_ERROR( | ||
node_logging_itf_->get_logger(), | ||
"JointLimiter: Joint '%s': getting parameters has failed", joint_names[i].c_str()); | ||
result = false; | ||
break; | ||
if (!declare_parameters(joint_names[i], node_param_itf_, node_logging_itf_)) | ||
{ | ||
RCLCPP_ERROR( | ||
node_logging_itf_->get_logger(), | ||
"JointLimiter: Joint '%s': parameter declaration has failed", joint_names[i].c_str()); | ||
result = false; | ||
break; | ||
} | ||
if (!get_joint_limits(joint_names[i], node_param_itf_, node_logging_itf_, joint_limits_[i])) | ||
{ | ||
RCLCPP_ERROR( | ||
node_logging_itf_->get_logger(), | ||
"JointLimiter: Joint '%s': getting parameters has failed", joint_names[i].c_str()); | ||
result = false; | ||
break; | ||
} | ||
RCLCPP_INFO( | ||
node_logging_itf_->get_logger(), "Limits for joint %zu (%s) are:\n%s", i, | ||
joint_names[i].c_str(), joint_limits_[i].to_string().c_str()); | ||
} | ||
RCLCPP_INFO( | ||
node_logging_itf_->get_logger(), "Limits for joint %zu (%s) are:\n%s", i, | ||
joint_names[i].c_str(), joint_limits_[i].to_string().c_str()); | ||
} | ||
updated_limits_.writeFromNonRT(joint_limits_); | ||
updated_limits_.writeFromNonRT(joint_limits_); | ||
|
||
auto on_parameter_event_callback = [this](const std::vector<rclcpp::Parameter> & parameters) | ||
{ | ||
rcl_interfaces::msg::SetParametersResult set_parameters_result; | ||
set_parameters_result.successful = true; | ||
auto on_parameter_event_callback = [this](const std::vector<rclcpp::Parameter> & parameters) | ||
{ | ||
rcl_interfaces::msg::SetParametersResult set_parameters_result; | ||
set_parameters_result.successful = true; | ||
|
||
std::vector<LimitsType> updated_joint_limits = joint_limits_; | ||
bool changed = false; | ||
std::vector<LimitsType> updated_joint_limits = joint_limits_; | ||
bool changed = false; | ||
|
||
for (size_t i = 0; i < number_of_joints_; ++i) | ||
{ | ||
changed |= joint_limits::check_for_limits_update( | ||
joint_names_[i], parameters, node_logging_itf_, updated_joint_limits[i]); | ||
} | ||
for (size_t i = 0; i < number_of_joints_; ++i) | ||
{ | ||
changed |= joint_limits::check_for_limits_update( | ||
joint_names_[i], parameters, node_logging_itf_, updated_joint_limits[i]); | ||
} | ||
|
||
if (changed) | ||
{ | ||
updated_limits_.writeFromNonRT(updated_joint_limits); | ||
RCLCPP_INFO(node_logging_itf_->get_logger(), "Limits are dynamically updated!"); | ||
} | ||
if (changed) | ||
{ | ||
updated_limits_.writeFromNonRT(updated_joint_limits); | ||
RCLCPP_INFO(node_logging_itf_->get_logger(), "Limits are dynamically updated!"); | ||
} | ||
|
||
return set_parameters_result; | ||
}; | ||
return set_parameters_result; | ||
}; | ||
|
||
parameter_callback_ = | ||
node_param_itf_->add_on_set_parameters_callback(on_parameter_event_callback); | ||
parameter_callback_ = | ||
node_param_itf_->add_on_set_parameters_callback(on_parameter_event_callback); | ||
} | ||
|
||
if (result) | ||
{ | ||
|
@@ -130,6 +132,31 @@ class JointLimiterInterface | |
return result; | ||
} | ||
|
||
/** | ||
* Wrapper init method that accepts the joint names and their limits directly | ||
*/ | ||
JOINT_LIMITS_PUBLIC virtual bool init( | ||
const std::vector<std::string> & joint_names, const std::vector<LimitsType> & joint_limits, | ||
const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & param_itf, | ||
const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf) | ||
{ | ||
number_of_joints_ = joint_names.size(); | ||
joint_names_ = joint_names; | ||
joint_limits_ = joint_limits; | ||
node_param_itf_ = param_itf; | ||
node_logging_itf_ = logging_itf; | ||
updated_limits_.writeFromNonRT(joint_limits_); | ||
|
||
if ((number_of_joints_ != joint_limits_.size()) && has_logging_interface()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should check if logger is provided differently. This is a bit strange way to do that. Maybe to add setter that returns error if value is not correct. Because we are checking function arguments using external method. I don't find this very nicely solved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And also you are doing check after everyting is assigned. Should't we do this at the begin of the method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can change it to use it properly. This is a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add the JointLimits into the SoftLimits |
||
{ | ||
RCLCPP_ERROR( | ||
node_logging_itf_->get_logger(), | ||
"JointLimiter: Number of joint names and limits do not match: %zu != %zu", | ||
number_of_joints_, joint_limits_.size()); | ||
} | ||
return (number_of_joints_ == joint_limits_.size()) && on_init(); | ||
} | ||
|
||
/** | ||
* Wrapper init method that accepts pointer to the Node. | ||
* For details see other init method. | ||
|
@@ -179,19 +206,6 @@ class JointLimiterInterface | |
return on_enforce(current_joint_states, desired_joint_states, dt); | ||
} | ||
|
||
/** \brief Enforce joint limits to desired joint state for single physical quantity. | ||
* | ||
* Generic enforce method that calls implementation-specific `on_enforce` method. | ||
* | ||
* \param[in,out] desired_joint_states joint state that should be adjusted to obey the limits. | ||
* \returns true if limits are enforced, otherwise false. | ||
*/ | ||
JOINT_LIMITS_PUBLIC virtual bool enforce(std::vector<double> & desired_joint_states) | ||
{ | ||
joint_limits_ = *(updated_limits_.readFromRT()); | ||
return on_enforce(desired_joint_states); | ||
} | ||
|
||
protected: | ||
/** \brief Method is realized by an implementation. | ||
* | ||
|
@@ -222,17 +236,21 @@ class JointLimiterInterface | |
JointLimitsStateDataType & current_joint_states, | ||
JointLimitsStateDataType & desired_joint_states, const rclcpp::Duration & dt) = 0; | ||
|
||
/** \brief Method is realized by an implementation. | ||
/** \brief Checks if the logging interface is set. | ||
* \returns true if the logging interface is available, otherwise false. | ||
* | ||
* Filter-specific implementation of the joint limits enforce algorithm for single physical | ||
* quantity. | ||
* This method might use "effort" limits since they are often used as wild-card. | ||
* Check the documentation of the exact implementation for more details. | ||
* \note this way of interfacing would be useful for instances where the logging interface is not | ||
* available, for example in the ResourceManager or ResourceStorage classes. | ||
*/ | ||
bool has_logging_interface() const { return node_logging_itf_ != nullptr; } | ||
|
||
/** \brief Checks if the parameter interface is set. | ||
* \returns true if the parameter interface is available, otherwise false. | ||
* | ||
* \param[in,out] desired_joint_states joint state that should be adjusted to obey the limits. | ||
* \returns true if limits are enforced, otherwise false. | ||
* * \note this way of interfacing would be useful for instances where the logging interface is | ||
* not available, for example in the ResourceManager or ResourceStorage classes. | ||
*/ | ||
JOINT_LIMITS_PUBLIC virtual bool on_enforce(std::vector<double> & desired_joint_states) = 0; | ||
bool has_parameter_interface() const { return node_param_itf_ != nullptr; } | ||
|
||
size_t number_of_joints_; | ||
std::vector<std::string> joint_names_; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2024 PAL Robotics S.L. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
/// \author Sai Kishor Kothakota | ||
|
||
#ifndef JOINT_LIMITS__JOINT_LIMITER_STRUCT_HPP_ | ||
#define JOINT_LIMITS__JOINT_LIMITER_STRUCT_HPP_ | ||
|
||
#include <memory> | ||
#include <optional> | ||
#include <string> | ||
|
||
namespace joint_limits | ||
{ | ||
|
||
struct JointControlInterfacesData | ||
{ | ||
std::string joint_name; | ||
std::optional<double> position = std::nullopt; | ||
std::optional<double> velocity = std::nullopt; | ||
std::optional<double> effort = std::nullopt; | ||
std::optional<double> acceleration = std::nullopt; | ||
std::optional<double> jerk = std::nullopt; | ||
|
||
bool has_data() const | ||
{ | ||
return has_position() || has_velocity() || has_effort() || has_acceleration() || has_jerk(); | ||
} | ||
|
||
bool has_position() const { return position.has_value(); } | ||
|
||
bool has_velocity() const { return velocity.has_value(); } | ||
|
||
bool has_effort() const { return effort.has_value(); } | ||
|
||
bool has_acceleration() const { return acceleration.has_value(); } | ||
|
||
bool has_jerk() const { return jerk.has_value(); } | ||
}; | ||
|
||
struct JointInterfacesCommandLimiterData | ||
{ | ||
std::string joint_name; | ||
JointControlInterfacesData actual; | ||
JointControlInterfacesData command; | ||
JointControlInterfacesData prev_command; | ||
JointControlInterfacesData limited; | ||
|
||
bool has_actual_data() const { return actual.has_data(); } | ||
|
||
bool has_command_data() const { return command.has_data(); } | ||
}; | ||
|
||
} // namespace joint_limits | ||
#endif // JOINT_LIMITS__JOINT_LIMITER_STRUCT_HPP_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,18 @@ | ||
<class_libraries> | ||
<library path="joint_saturation_limiter"> | ||
<class name="joint_limits/JointSaturationLimiter" | ||
type="joint_limits::JointSaturationLimiter<joint_limits::JointLimits>" | ||
base_class_type="joint_limits::JointLimiterInterface<joint_limits::JointLimits>"> | ||
<class name="joint_limits/JointTrajectoryPointSaturationLimiter" | ||
type="JointTrajectoryPointSaturationLimiter" | ||
base_class_type="joint_limits::JointLimiterInterface<joint_limits::JointLimits, trajectory_msgs::msg::JointTrajectoryPoint>"> | ||
<description> | ||
Simple joint limiter using clamping approach. Warning: clamping can cause discontinuities. | ||
</description> | ||
</class> | ||
<class name="joint_limits/JointInterfacesSaturationLimiter" | ||
type="JointInterfacesSaturationLimiter" | ||
base_class_type="joint_limits::JointLimiterInterface<joint_limits::JointLimits, joint_limits::JointControlInterfacesData>"> | ||
<description> | ||
Simple joint range limiter using clamping approach with the parsed limits. | ||
</description> | ||
</class> | ||
</library> | ||
</class_libraries> |
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.
Why to check this? is it possible that this happens? This is a mandatory paramter, so I would propose to check this differently, i.e., not add big if, but just exit the method with error if this argument is not correct.
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 we are integrating this inside the ResourceManager/ResourceStorage right now we don't have this, and may be we don't want to log things in realtime, that's one more reason for it to check if this interface is set or not. I think it is important.