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 new joint range limiter for hardware interfaces #5

Open
wants to merge 41 commits into
base: add-simple-joint-limiter
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9162045
make JointTrajectoryPoint as an input through the template argument
saikishor Apr 6, 2024
fb0e7d4
check for node parameter interface before declaring and getting the p…
saikishor Apr 7, 2024
f8c6fcd
Added methods to check is the parameter and logging interfaces are se…
saikishor Apr 7, 2024
940ff31
add an init method parsing the limits directly in the init method
saikishor Apr 7, 2024
0ed50bb
update the joint state limiter to use template typename in the header…
saikishor Apr 7, 2024
fe8394f
check the size of the joint limits and the joint names in the init me…
saikishor Apr 7, 2024
f291486
added joint range limiter library for individual joints control
saikishor Apr 7, 2024
e87cad2
added compute_effort_limits method
saikishor Apr 7, 2024
3a2ddc5
update the velocity limits based on the position of the joint
saikishor Apr 7, 2024
39d904a
fix the limits clamping in the methods
saikishor Apr 7, 2024
a1ca050
Add proper acceleration limits bounding
saikishor Apr 7, 2024
a7e9852
remove the enforce methods of the double vector as it can be handled …
saikishor Apr 16, 2024
dadc958
Added comments to the has_logging_interface and has_parameter_interfa…
saikishor Apr 16, 2024
0563685
remove the joint range limiter header and reuse it from the joint sat…
saikishor Apr 16, 2024
cc67fba
rename the plugin names to be more consistent with the types the use …
saikishor Apr 16, 2024
84fc1a1
Add first version of tests into the joint range limiter
saikishor Apr 17, 2024
4278adb
fix the init check bug
saikishor Apr 17, 2024
afade53
Apply formatting changes
saikishor Apr 17, 2024
255be09
update the joint range limiter test with case of only desired positio…
saikishor Apr 17, 2024
a47ba74
Added some minor fixes in the joint range limiter
saikishor Apr 17, 2024
1a78beb
Fill the data of actual or desired fields into the previous commands
saikishor Apr 17, 2024
0f48afa
reset the prev_data_ on initialization
saikishor Apr 17, 2024
71df023
add test cases of the actual and desired position cases
saikishor Apr 17, 2024
358e3bd
Add tests for the case of no position limits
saikishor Apr 18, 2024
f26bd8d
add initial test cases for desired velocity only cases
saikishor Apr 18, 2024
7bc19c0
Enforce wrt to the position limits only when the actual position valu…
saikishor Apr 18, 2024
8a5267a
change some asserts to expects
saikishor Apr 18, 2024
332d403
Use lambdas for better testing
saikishor Apr 18, 2024
df6bef0
If the joint is outside the position range, then don't let the joint …
saikishor Apr 18, 2024
faba81d
extend tests of acceleration limits also with and without actual posi…
saikishor Apr 18, 2024
97e18b8
Add tests for the case of actuators with actual position state and ac…
saikishor Apr 18, 2024
0b476f3
remove unused test cases
saikishor Apr 18, 2024
e31f6a4
pass by const reference and parse the optional actual position and ve…
saikishor Apr 18, 2024
732dae6
Added tests for the effort case
saikishor Apr 18, 2024
d120117
better conditioning for the acceleration limits enforcement
saikishor Apr 18, 2024
f4741d1
Added tests for the desired acceleration case
saikishor Apr 18, 2024
bc97448
Add tests for the desired jerk test cases
saikishor Apr 18, 2024
12973f6
consider also the acceleration limits when computing the position lim…
saikishor Apr 18, 2024
a973d42
Fix minor bug in the limit enforcement
saikishor Apr 18, 2024
6d8d66e
better computation of the position limits
saikishor Apr 18, 2024
415ea20
better combined desired references test
saikishor Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions joint_limits/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ target_compile_definitions(joint_limiter_interface PRIVATE "JOINT_LIMITS_BUILDIN

add_library(joint_saturation_limiter SHARED
src/joint_saturation_limiter.cpp
src/joint_range_limiter.cpp
)
target_compile_features(joint_saturation_limiter PUBLIC cxx_std_17)
target_include_directories(joint_saturation_limiter PUBLIC
Expand Down Expand Up @@ -93,6 +94,15 @@ if(BUILD_TESTING)
rclcpp
)

ament_add_gtest(test_joint_range_limiter test/test_joint_range_limiter.cpp)
target_include_directories(test_joint_range_limiter PRIVATE include)
target_link_libraries(test_joint_range_limiter joint_limiter_interface)
ament_target_dependencies(
test_joint_saturation_limiter
pluginlib
rclcpp
)

endif()

install(
Expand Down
144 changes: 81 additions & 63 deletions joint_limits/include/joint_limits/joint_limiter_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@

namespace joint_limits
{
using JointLimitsStateDataType = trajectory_msgs::msg::JointTrajectoryPoint;

template <typename LimitsType>
template <typename LimitsType, typename JointLimitsStateDataType>
class JointLimiterInterface
{
public:
Expand Down Expand Up @@ -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())
Copy link

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.

Copy link
Author

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.

{
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)
{
Expand All @@ -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())
Copy link

Choose a reason for hiding this comment

The 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.

Copy link

Choose a reason for hiding this comment

The 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?

Copy link
Author

Choose a reason for hiding this comment

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

I can change it to use it properly. This is a new init method and this will take a vector of LimitsType and you will need to check if this size is good or not w.r.t joint_names size

Copy link
Author

Choose a reason for hiding this comment

The 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.
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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_;
Expand Down
66 changes: 66 additions & 0 deletions joint_limits/include/joint_limits/joint_limiter_struct.hpp
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_
33 changes: 12 additions & 21 deletions joint_limits/include/joint_limits/joint_saturation_limiter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ namespace joint_limits
* limit. For example, if a joint is close to its position limit, velocity and acceleration will be
* reduced accordingly.
*/
template <typename LimitsType>
class JointSaturationLimiter : public JointLimiterInterface<LimitsType>
template <typename LimitsType, typename JointLimitsStateDataType>
class JointSaturationLimiter : public JointLimiterInterface<LimitsType, JointLimitsStateDataType>
{
public:
/** \brief Constructor */
Expand All @@ -49,8 +49,9 @@ class JointSaturationLimiter : public JointLimiterInterface<LimitsType>
JOINT_LIMITS_PUBLIC bool on_init() override { return true; }

JOINT_LIMITS_PUBLIC bool on_configure(
const trajectory_msgs::msg::JointTrajectoryPoint & /*current_joint_states*/) override
const JointLimitsStateDataType & current_joint_states) override
{
prev_command_ = current_joint_states;
return true;
}

Expand All @@ -70,33 +71,23 @@ class JointSaturationLimiter : public JointLimiterInterface<LimitsType>
* \returns true if limits are enforced, otherwise false.
*/
JOINT_LIMITS_PUBLIC bool on_enforce(
trajectory_msgs::msg::JointTrajectoryPoint & current_joint_states,
trajectory_msgs::msg::JointTrajectoryPoint & desired_joint_states,
const rclcpp::Duration & dt) override;

/** \brief Enforce joint limits to desired arbitrary physical quantity.
* Additional, commonly used method for enforcing limits by clamping desired input value.
* The limit is defined in effort limits of the `joint::limits/JointLimit` structure.
*
* If `has_effort_limits` is set to false, the limits will be not enforced to a joint.
*
* \param[in,out] desired_joint_states physical quantity that should be adjusted to obey the
* limits. \returns true if limits are enforced, otherwise false.
*/
JOINT_LIMITS_PUBLIC bool on_enforce(std::vector<double> & desired_joint_states) override;
JointLimitsStateDataType & current_joint_states,
JointLimitsStateDataType & desired_joint_states, const rclcpp::Duration & dt) override;

private:
rclcpp::Clock::SharedPtr clock_;
JointLimitsStateDataType prev_command_;
};

template <typename LimitsType>
JointSaturationLimiter<LimitsType>::JointSaturationLimiter() : JointLimiterInterface<LimitsType>()
template <typename LimitsType, typename JointLimitsStateDataType>
JointSaturationLimiter<LimitsType, JointLimitsStateDataType>::JointSaturationLimiter()
: JointLimiterInterface<LimitsType, JointLimitsStateDataType>()
{
clock_ = std::make_shared<rclcpp::Clock>(rclcpp::Clock(RCL_ROS_TIME));
}

template <typename LimitsType>
JointSaturationLimiter<LimitsType>::~JointSaturationLimiter()
template <typename LimitsType, typename JointLimitsStateDataType>
JointSaturationLimiter<LimitsType, JointLimitsStateDataType>::~JointSaturationLimiter()
{
}

Expand Down
13 changes: 10 additions & 3 deletions joint_limits/joint_limiters.xml
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&lt;joint_limits::JointLimits&gt;"
base_class_type="joint_limits::JointLimiterInterface&lt;joint_limits::JointLimits&gt;">
<class name="joint_limits/JointTrajectoryPointSaturationLimiter"
type="JointTrajectoryPointSaturationLimiter"
base_class_type="joint_limits::JointLimiterInterface&lt;joint_limits::JointLimits, trajectory_msgs::msg::JointTrajectoryPoint&gt;">
<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&lt;joint_limits::JointLimits, joint_limits::JointControlInterfacesData&gt;">
<description>
Simple joint range limiter using clamping approach with the parsed limits.
</description>
</class>
</library>
</class_libraries>
Loading