Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Gesel <paulgesel@gmail.com>
  • Loading branch information
pac48 committed Jan 17, 2024
1 parent 9bd096c commit aa77647
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 12 deletions.
4 changes: 4 additions & 0 deletions moveit_core/online_signal_smoothing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,8 @@ if(BUILD_TESTING)
# Lowpass filter unit test
ament_add_gtest(test_butterworth_filter test/test_butterworth_filter.cpp)
target_link_libraries(test_butterworth_filter moveit_butterworth_filter)

# Acceleration filter unit test
ament_add_gtest(test_acceleration_filter test/test_acceleration_filter.cpp)
target_link_libraries(test_acceleration_filter moveit_acceleration_filter moveit_test_utils)
endif()
42 changes: 30 additions & 12 deletions moveit_core/online_signal_smoothing/src/acceleration_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ bool AccelerationLimitedPlugin::initialize(rclcpp::Node::SharedPtr node, moveit:
cur_acceleration_ = Eigen::VectorXd::Zero(num_joints);

// get node parameters and store in member variables
if (!node_->has_parameter("update_rate") || !node_->has_parameter("move_group_name"))
{
return false;
}
auto param_listener = online_signal_smoothing::ParamListener(node_);
auto params = param_listener.get_params();
update_rate_ = params.update_rate;
Expand Down Expand Up @@ -217,6 +221,18 @@ double jointLimitAccelerationScalingFactor(const Eigen::VectorXd& accelerations,
return min_scaling_factor;
}

inline bool update_data(const OSQPDataWrapperPtr& data, OSQPWorkspace* work, Eigen::SparseMatrix<double>& A_sparse,
const Eigen::VectorXd& lower_bound, const Eigen::VectorXd& upper_bound)
{
data->update_A(work, A_sparse);
size_t num_constraints = A_sparse.rows();
data->u.block(0, 0, num_constraints - 1, 1) = upper_bound;
data->l.block(0, 0, num_constraints - 1, 1) = lower_bound;
data->u[num_constraints - 1] = ALPHA_UPPER_BOUND;
data->l[num_constraints - 1] = ALPHA_LOWER_BOUND;
return 0 == osqp_update_bounds(work, data->l.data(), data->u.data());
}

bool AccelerationLimitedPlugin::doSmoothing(Eigen::VectorXd& positions, Eigen::VectorXd& velocities,
Eigen::VectorXd& /* unused */)
{
Expand All @@ -229,17 +245,14 @@ bool AccelerationLimitedPlugin::doSmoothing(Eigen::VectorXd& positions, Eigen::V
num_joints_, num_positions);
return false;
}

auto update_data = [this](Eigen::SparseMatrix<double>& A_sparse, const Eigen::VectorXd& lower_bound,
const Eigen::VectorXd& upper_bound) {
data_->update_A(work_, A_sparse);
size_t num_constraints = A_sparse.rows();
data_->u.block(0, 0, num_constraints - 1, 1) = upper_bound;
data_->l.block(0, 0, num_constraints - 1, 1) = lower_bound;
data_->u[num_constraints - 1] = ALPHA_UPPER_BOUND;
data_->l[num_constraints - 1] = ALPHA_LOWER_BOUND;
osqp_update_bounds(work_, data_->l.data(), data_->u.data());
};
else if (last_positions_.size() != positions.size())
{
RCLCPP_ERROR_THROTTLE(node_->get_logger(), *node_->get_clock(), 1000,
"The length of the last joint positions not equal to the current, expected %zu got %zu. Make "
"sure the reset was called.",
last_positions_.size(), positions.size());
return false;
}

size_t num_constraints = num_joints_ + 1;
Eigen::VectorXd positions_offset = last_positions_ - positions;
Expand All @@ -252,7 +265,12 @@ bool AccelerationLimitedPlugin::doSmoothing(Eigen::VectorXd& positions, Eigen::V
Eigen::VectorXd vel_point = last_positions_ + last_velocities_ * update_rate_;
Eigen::VectorXd upper_bound = vel_point - positions + max_acceleration_limits_ * (update_rate_ * update_rate_);
Eigen::VectorXd lower_bound = vel_point - positions + min_acceleration_limits_ * (update_rate_ * update_rate_);
update_data(A_sparse_, lower_bound, upper_bound);
if (!update_data(data_, work_, A_sparse_, lower_bound, upper_bound))
{
RCLCPP_ERROR_THROTTLE(node_->get_logger(), *node_->get_clock(), 1000,
"failed to set osqp_update_bounds. Make sure the robot's acceleration limits are valid");
return false;
}

if (positions_offset.norm() < COMMAND_DIFFERENCE_THRESHOLD && velocities_offset.norm() < COMMAND_DIFFERENCE_THRESHOLD)
{
Expand Down
197 changes: 197 additions & 0 deletions moveit_core/online_signal_smoothing/test/test_acceleration_filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2024, PickNik Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of PickNik Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/

#include <gtest/gtest.h>
#include <moveit/online_signal_smoothing/acceleration_filter.h>
#include <moveit/utils/robot_model_test_utils.h>

constexpr char* move_group_name = "panda_arm";

class AccelerationFilterTest : public testing::Test
{
protected:
void SetUp() override
{
robot_model_ = moveit::core::loadTestingRobotModel("panda");
};

void set_limit(std::optional<Eigen::VectorXd> acceleration_limit)
{
const std::vector<moveit::core::JointModel*> joint_models = robot_model_->getJointModels();
auto joint_model_group = robot_model_->getJointModelGroup(move_group_name);
size_t ind = 0;
for (auto& joint_model : joint_models)
{
if (!joint_model_group->hasJointModel(joint_model->getName()))
{
continue;
}
std::vector<moveit_msgs::msg::JointLimits> joint_bounds_msg(joint_model->getVariableBoundsMsg());
for (auto& joint_bound : joint_bounds_msg)
{
joint_bound.has_acceleration_limits = acceleration_limit.has_value();
if (joint_bound.has_acceleration_limits)
{
joint_bound.max_acceleration = acceleration_limit.value()[ind];
}
}
joint_model->setVariableBounds(joint_bounds_msg);
ind++;
}
}

protected:
moveit::core::RobotModelPtr robot_model_;
};

TEST_F(AccelerationFilterTest, FilterInitialize)
{
online_signal_smoothing::AccelerationLimitedPlugin filter;

rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>("AccelerationFilterTest");
node->declare_parameter<std::string>("move_group_name", move_group_name);

// fail because the number of joint is wrong
EXPECT_FALSE(filter.initialize(node, robot_model_, 3));

// fail because the update_rate parameter is not set
EXPECT_FALSE(filter.initialize(node, robot_model_, 7));
node->declare_parameter<double>("update_rate", 0.01);

// fail because the robot does not have acceleration limits
set_limit({});
EXPECT_FALSE(filter.initialize(node, robot_model_, 7));

// succeed with acceleration limits
Eigen::VectorXd acceleration_limit = 1.2 * Eigen::VectorXd::Ones(7);
set_limit(acceleration_limit);
EXPECT_TRUE(filter.initialize(node, robot_model_, 7));
}

TEST_F(AccelerationFilterTest, FilterDoSmooth)
{
online_signal_smoothing::AccelerationLimitedPlugin filter;

rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>("AccelerationFilterTest");
node->declare_parameter<std::string>("move_group_name", move_group_name);
double update_rate = 1.0;
node->declare_parameter<double>("update_rate", update_rate);
Eigen::VectorXd acceleration_limit = 1.2 * Eigen::VectorXd::Ones(7);
set_limit(acceleration_limit);

EXPECT_TRUE(filter.initialize(node, robot_model_, 7));

// fail when called with the wrong number of joints
Eigen::VectorXd position = Eigen::VectorXd::Zero(5);
Eigen::VectorXd velocity = Eigen::VectorXd::Zero(5);
Eigen::VectorXd acceleration = Eigen::VectorXd::Zero(5);
EXPECT_FALSE(filter.doSmoothing(position, velocity, acceleration));

// fail because reset was not called
position = Eigen::VectorXd::Zero(7);
velocity = Eigen::VectorXd::Zero(7);
acceleration = Eigen::VectorXd::Zero(7);
EXPECT_FALSE(filter.doSmoothing(position, velocity, acceleration));

// succeed
filter.reset(Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7));
EXPECT_TRUE(filter.doSmoothing(position, velocity, acceleration));

// succeed: apply acceleration limits
filter.reset(Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7));
position.array() = 3.0;
EXPECT_TRUE(filter.doSmoothing(position, velocity, acceleration));
EXPECT_TRUE((position.array() - update_rate * update_rate * acceleration_limit.array()).matrix().norm() < 1E-3);

// succeed: apply acceleration limits
position.array() = 0.9;
filter.reset(position * 0, velocity * 0, acceleration * 0);
EXPECT_TRUE(filter.doSmoothing(position, velocity, acceleration));
EXPECT_TRUE((position.array() - 0.9).matrix().norm() < 1E-3);

// succeed: slow down
velocity = 10 * Eigen::VectorXd::Ones(7);
filter.reset(Eigen::VectorXd::Zero(7), velocity, Eigen::VectorXd::Zero(7));
position.array() = 0.01;
Eigen::VectorXd expected_offset =
velocity.array() * update_rate - update_rate * update_rate * acceleration_limit.array();
EXPECT_TRUE(filter.doSmoothing(position, velocity, acceleration));
EXPECT_TRUE((velocity * update_rate - expected_offset).norm() < 1E-3);
}

TEST_F(AccelerationFilterTest, FilterBadAccelerationConfig)
{
online_signal_smoothing::AccelerationLimitedPlugin filter;

rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>("AccelerationFilterTest");
node->declare_parameter<std::string>("move_group_name", move_group_name);
double update_rate = 0.1;
node->declare_parameter<double>("update_rate", update_rate);
Eigen::VectorXd acceleration_limit = -1.0 * Eigen::VectorXd::Ones(7);
set_limit(acceleration_limit);
EXPECT_TRUE(filter.initialize(node, robot_model_, 7));
Eigen::VectorXd position = Eigen::VectorXd::Zero(7);
Eigen::VectorXd velocity = Eigen::VectorXd::Zero(7);
Eigen::VectorXd acceleration = Eigen::VectorXd::Zero(7);
EXPECT_TRUE(filter.reset(position, velocity, acceleration));
EXPECT_FALSE(filter.doSmoothing(position, velocity, acceleration));
}

TEST_F(AccelerationFilterTest, FilterDoSmoothRandomized)
{
double update_rate = 0.1;
for (size_t iter = 0; iter < 64; ++iter)
{
online_signal_smoothing::AccelerationLimitedPlugin filter;

rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>("AccelerationFilterTest");
node->declare_parameter<std::string>("move_group_name", move_group_name);
node->declare_parameter<double>("update_rate", update_rate);
Eigen::VectorXd acceleration_limit = 1.2 * (1.0 + Eigen::VectorXd::Random(7).array());
set_limit(acceleration_limit);
EXPECT_TRUE(filter.initialize(node, robot_model_, 7));
filter.reset(Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7), Eigen::VectorXd::Zero(7));
Eigen::VectorXd position = Eigen::VectorXd::Random(7);
Eigen::VectorXd velocity = Eigen::VectorXd::Random(7);
Eigen::VectorXd acceleration = Eigen::VectorXd::Random(7);
EXPECT_TRUE(filter.doSmoothing(position, velocity, acceleration));
}
}

int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
rclcpp::init(argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit aa77647

Please sign in to comment.