add humble-navigation2

This commit is contained in:
X-lanni
2025-05-27 19:03:40 +08:00
parent 974abb5e1e
commit e74ec539c2
1280 changed files with 204114 additions and 0 deletions
@@ -0,0 +1,10 @@
# Tests
ament_add_gtest(velocity_smoother_tests
test_velocity_smoother.cpp
)
ament_target_dependencies(velocity_smoother_tests
${dependencies}
)
target_link_libraries(velocity_smoother_tests
${library_name}
)
@@ -0,0 +1,704 @@
// Copyright (c) 2022 Samsung Research
//
// 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.
#include <math.h>
#include <memory>
#include <string>
#include <vector>
#include <limits>
#include "gtest/gtest.h"
#include "rclcpp/rclcpp.hpp"
#include "nav2_velocity_smoother/velocity_smoother.hpp"
#include "nav_msgs/msg/odometry.hpp"
#include "geometry_msgs/msg/twist.hpp"
using namespace std::chrono_literals;
class RclCppFixture
{
public:
RclCppFixture() {rclcpp::init(0, nullptr);}
~RclCppFixture() {rclcpp::shutdown();}
};
RclCppFixture g_rclcppfixture;
class VelSmootherShim : public nav2_velocity_smoother::VelocitySmoother
{
public:
VelSmootherShim()
: VelocitySmoother() {}
void configure(const rclcpp_lifecycle::State & state) {this->on_configure(state);}
void activate(const rclcpp_lifecycle::State & state) {this->on_activate(state);}
void deactivate(const rclcpp_lifecycle::State & state) {this->on_deactivate(state);}
void cleanup(const rclcpp_lifecycle::State & state) {this->on_cleanup(state);}
void shutdown(const rclcpp_lifecycle::State & state) {this->on_shutdown(state);}
bool isOdomSmoother() {return odom_smoother_ ? true : false;}
bool hasCommandMsg() {return last_command_time_.nanoseconds() != 0;}
geometry_msgs::msg::Twist::SharedPtr lastCommandMsg() {return command_;}
void sendCommandMsg(geometry_msgs::msg::Twist::SharedPtr msg) {inputCommandCallback(msg);}
};
TEST(VelocitySmootherTest, openLoopTestTimer)
{
auto smoother =
std::make_shared<VelSmootherShim>();
std::vector<double> deadbands{0.2, 0.0, 0.0};
smoother->declare_parameter("scale_velocities", rclcpp::ParameterValue(true));
smoother->set_parameter(rclcpp::Parameter("scale_velocities", true));
smoother->declare_parameter("deadband_velocity", rclcpp::ParameterValue(deadbands));
smoother->set_parameter(rclcpp::Parameter("deadband_velocity", deadbands));
rclcpp_lifecycle::State state;
smoother->configure(state);
smoother->activate(state);
std::vector<double> linear_vels;
auto subscription = smoother->create_subscription<geometry_msgs::msg::Twist>(
"cmd_vel_smoothed",
1,
[&](geometry_msgs::msg::Twist::SharedPtr msg) {
linear_vels.push_back(msg->linear.x);
});
// Send a velocity command
auto cmd = std::make_shared<geometry_msgs::msg::Twist>();
cmd->linear.x = 1.0; // Max is 0.5, so should threshold
smoother->sendCommandMsg(cmd);
// Process velocity smoothing and send updated odometry based on commands
auto start = smoother->now();
while (smoother->now() - start < 1.5s) {
rclcpp::spin_some(smoother->get_node_base_interface());
}
// Sanity check we have the approximately right number of messages for the timespan and timeout
EXPECT_GT(linear_vels.size(), 19u);
EXPECT_LT(linear_vels.size(), 30u);
// Should have last command be a stop since we timed out the command stream
EXPECT_EQ(linear_vels.back(), 0.0);
// From deadband, first few should be 0 until above 0.2
for (unsigned int i = 0; i != linear_vels.size(); i++) {
if (linear_vels[i] != 0) {
EXPECT_GT(linear_vels[i], 0.2);
break;
}
}
// Process to make sure stops at limit in velocity,
// doesn't exceed acceleration
for (unsigned int i = 0; i != linear_vels.size(); i++) {
EXPECT_TRUE(linear_vels[i] <= 0.5);
}
}
TEST(VelocitySmootherTest, approxClosedLoopTestTimer)
{
auto smoother =
std::make_shared<VelSmootherShim>();
smoother->declare_parameter("feedback", rclcpp::ParameterValue(std::string("CLOSED_LOOP")));
smoother->set_parameter(rclcpp::Parameter("feedback", std::string("CLOSED_LOOP")));
rclcpp_lifecycle::State state;
smoother->configure(state);
smoother->activate(state);
std::vector<double> linear_vels;
auto subscription = smoother->create_subscription<geometry_msgs::msg::Twist>(
"cmd_vel_smoothed",
1,
[&](geometry_msgs::msg::Twist::SharedPtr msg) {
linear_vels.push_back(msg->linear.x);
});
auto odom_pub = smoother->create_publisher<nav_msgs::msg::Odometry>("odom", 1);
odom_pub->on_activate();
nav_msgs::msg::Odometry odom_msg;
odom_msg.header.frame_id = "odom";
odom_msg.child_frame_id = "base_link";
// Fill buffer with 0 twisted-commands
for (unsigned int i = 0; i != 30; i++) {
odom_msg.header.stamp = smoother->now() + rclcpp::Duration::from_seconds(i * 0.01);
odom_pub->publish(odom_msg);
}
// Send a velocity command
auto cmd = std::make_shared<geometry_msgs::msg::Twist>();
cmd->linear.x = 1.0; // Max is 0.5, so should threshold
smoother->sendCommandMsg(cmd);
// Process velocity smoothing and send updated odometry based on commands
auto start = smoother->now();
while (smoother->now() - start < 1.5s) {
odom_msg.header.stamp = smoother->now();
if (linear_vels.size() > 0) {
odom_msg.twist.twist.linear.x = linear_vels.back();
}
odom_pub->publish(odom_msg);
rclcpp::spin_some(smoother->get_node_base_interface());
}
// Sanity check we have the approximately right number of messages for the timespan and timeout
EXPECT_GT(linear_vels.size(), 19u);
EXPECT_LT(linear_vels.size(), 30u);
// Should have last command be a stop since we timed out the command stream
EXPECT_EQ(linear_vels.back(), 0.0);
// Process to make sure stops at limit in velocity,
// doesn't exceed acceleration
for (unsigned int i = 0; i != linear_vels.size(); i++) {
if (i > 0) {
double diff = linear_vels[i] - linear_vels[i - 1];
EXPECT_LT(diff, 0.126); // default accel of 0.5 / 20 hz = 0.125
}
EXPECT_TRUE(linear_vels[i] <= 0.5);
}
}
TEST(VelocitySmootherTest, testfindEtaConstraint)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double accel = 0.1; // dv 0.005
double decel = -1.0; // dv 0.05
// In range
// Constant positive
EXPECT_EQ(smoother->findEtaConstraint(1.0, 1.0, accel, decel), -1);
// Constant negative
EXPECT_EQ(smoother->findEtaConstraint(-1.0, -1.0, accel, decel), -1);
// Positive To Positive Accel
EXPECT_EQ(smoother->findEtaConstraint(0.5, 0.504, accel, decel), -1);
// Positive To Positive Decel
EXPECT_EQ(smoother->findEtaConstraint(0.5, 0.46, accel, decel), -1);
// 0 To Positive Accel
EXPECT_EQ(smoother->findEtaConstraint(0.0, 0.004, accel, decel), -1);
// Positive To 0 Decel
EXPECT_EQ(smoother->findEtaConstraint(0.04, 0.0, accel, decel), -1);
// Negative To Negative Accel
EXPECT_EQ(smoother->findEtaConstraint(-0.5, -0.504, accel, decel), -1);
// Negative To Negative Decel
EXPECT_EQ(smoother->findEtaConstraint(-0.5, -0.46, accel, decel), -1);
// 0 To Negative Accel
EXPECT_EQ(smoother->findEtaConstraint(0.0, -0.004, accel, decel), -1);
// Negative To 0 Decel
EXPECT_EQ(smoother->findEtaConstraint(-0.04, 0.0, accel, decel), -1);
// Negative to Positive
EXPECT_EQ(smoother->findEtaConstraint(-0.02, 0.02, accel, decel), -1);
// Positive to Negative
EXPECT_EQ(smoother->findEtaConstraint(0.02, -0.02, accel, decel), -1);
// Faster than limit
// Positive To Positive Accel
EXPECT_EQ(smoother->findEtaConstraint(0.5, 1.5, accel, decel), 0.005);
// Positive To Positive Decel
EXPECT_EQ(smoother->findEtaConstraint(1.5, 0.5, accel, decel), 0.05);
// 0 To Positive Accel
EXPECT_EQ(smoother->findEtaConstraint(0.0, 1.0, accel, decel), 0.005);
// Positive To 0 Decel
EXPECT_EQ(smoother->findEtaConstraint(1.0, 0.0, accel, decel), 0.05);
// Negative To Negative Accel
EXPECT_EQ(smoother->findEtaConstraint(-0.5, -1.5, accel, decel), 0.005);
// Negative To Negative Decel
EXPECT_EQ(smoother->findEtaConstraint(-1.5, -0.5, accel, decel), 0.05);
// 0 To Negative Accel
EXPECT_EQ(smoother->findEtaConstraint(0.0, -1.0, accel, decel), 0.005);
// Negative To 0 Decel
EXPECT_EQ(smoother->findEtaConstraint(-1.0, 0.0, accel, decel), 0.05);
// Negative to Positive
EXPECT_EQ(smoother->findEtaConstraint(-0.2, 0.8, accel, decel), 0.05);
// Positive to Negative
EXPECT_EQ(smoother->findEtaConstraint(0.2, -0.8, accel, decel), 0.05);
}
TEST(VelocitySmootherTest, testapplyConstraints)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
// Apply examples from testfindEtaConstraint
// In range, so no eta or acceleration limit impact
EXPECT_EQ(smoother->applyConstraints(1.0, 1.0, 1.5, -2.0, no_eta), 1.0);
EXPECT_EQ(smoother->applyConstraints(0.5, 0.55, 1.5, -2.0, no_eta), 0.55);
EXPECT_EQ(smoother->applyConstraints(0.5, 0.45, 1.5, -2.0, no_eta), 0.45);
// Too high, without eta
EXPECT_NEAR(smoother->applyConstraints(1.0, 2.0, 1.5, -2.0, no_eta), 1.075, 0.01);
// Too high, with eta applied on its own axis
EXPECT_NEAR(smoother->applyConstraints(1.0, 2.0, 1.5, -2.0, 0.075), 1.075, 0.01);
// On another virtual axis that is OK
EXPECT_NEAR(smoother->applyConstraints(0.5, 0.55, 1.5, -2.0, 0.075), 0.503, 0.01);
// In a more realistic situation, applied to angular
EXPECT_NEAR(smoother->applyConstraints(0.8, 1.0, 3.2, -3.2, 0.75), 1.075, 0.95);
}
TEST(VelocitySmootherTest, testapplyConstraintsPositiveToPositiveAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = 1.0;
target_vel = 2.0;
steps_to_target = abs(target_vel - init_vel) / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel + i * dv_accel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsZeroToPositiveAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = 0.0;
target_vel = 2.0;
steps_to_target = abs(target_vel - init_vel) / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel + i * dv_accel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsNegativeToPositiveDecelAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_below_zero, steps_above_zero;
init_vel = -1.0;
target_vel = 2.0;
steps_below_zero = -init_vel / dv_decel;
steps_above_zero = target_vel / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_below_zero + steps_above_zero + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
if (v_curr > 0) {
EXPECT_NEAR(v_curr, (i - steps_below_zero) * dv_accel, 0.001);
} else {
EXPECT_NEAR(v_curr, init_vel + i * dv_decel, 0.001);
}
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsNegativeToNegativeAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = -1.0;
target_vel = -2.0;
steps_to_target = abs(target_vel - init_vel) / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel - i * dv_accel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsZeroToNegativeAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = 0.0;
target_vel = -2.0;
steps_to_target = abs(target_vel - init_vel) / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel - i * dv_accel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsPositiveToNegativeDecelAccel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_accel = accel / 20.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_below_zero, steps_above_zero;
init_vel = 1.0;
target_vel = -2.0;
steps_above_zero = init_vel / dv_decel;
steps_below_zero = -target_vel / dv_accel;
v_curr = init_vel;
for (size_t i = 1; i < steps_below_zero + steps_above_zero + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
if (v_curr < 0) {
EXPECT_NEAR(v_curr, -static_cast<int>(i - steps_above_zero) * dv_accel, 0.001);
} else {
EXPECT_NEAR(v_curr, init_vel - i * dv_decel, 0.001);
}
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsPositiveToPositiveDecel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
// Test asymetric accel/decel use cases
double accel = 0.1;
double decel = -1.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = 2.0;
target_vel = 1.0;
steps_to_target = abs(target_vel - init_vel) / dv_decel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel - i * dv_decel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsPositiveToZeroDecel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = 2.0;
target_vel = 0.0;
steps_to_target = abs(target_vel - init_vel) / dv_decel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel - i * dv_decel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsNegativeToNegativeDecel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = -2.0;
target_vel = -1.0;
steps_to_target = abs(target_vel - init_vel) / dv_decel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel + i * dv_decel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testapplyConstraintsNegativeToZeroDecel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
// default frequency is 20.0
smoother->configure(state);
double no_eta = 1.0;
double accel = 0.1;
double decel = -1.0;
double dv_decel = -decel / 20.0;
double init_vel, target_vel, v_curr;
uint steps_to_target;
init_vel = -2.0;
target_vel = 0.0;
steps_to_target = abs(target_vel - init_vel) / dv_decel;
v_curr = init_vel;
for (size_t i = 1; i < steps_to_target + 1; i++) {
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, init_vel + i * dv_decel, 0.001);
}
EXPECT_NEAR(v_curr, target_vel, 0.001);
v_curr = smoother->applyConstraints(v_curr, target_vel, accel, decel, no_eta);
EXPECT_NEAR(v_curr, target_vel, 0.001);
}
TEST(VelocitySmootherTest, testCommandCallback)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
smoother->configure(state);
smoother->activate(state);
auto pub = smoother->create_publisher<geometry_msgs::msg::Twist>("cmd_vel", 1);
pub->on_activate();
auto msg = std::make_unique<geometry_msgs::msg::Twist>();
msg->linear.x = 100.0;
pub->publish(std::move(msg));
rclcpp::spin_some(smoother->get_node_base_interface());
EXPECT_TRUE(smoother->hasCommandMsg());
EXPECT_EQ(smoother->lastCommandMsg()->linear.x, 100.0);
}
TEST(VelocitySmootherTest, testClosedLoopSub)
{
auto smoother =
std::make_shared<VelSmootherShim>();
smoother->declare_parameter("feedback", rclcpp::ParameterValue(std::string("OPEN_LOOP")));
smoother->set_parameter(rclcpp::Parameter("feedback", std::string("CLOSED_LOOP")));
rclcpp_lifecycle::State state;
smoother->configure(state);
EXPECT_TRUE(smoother->isOdomSmoother());
}
TEST(VelocitySmootherTest, testInvalidParams)
{
auto smoother =
std::make_shared<VelSmootherShim>();
std::vector<double> max_vels{0.0, 0.0}; // invalid size
smoother->declare_parameter("max_velocity", rclcpp::ParameterValue(max_vels));
rclcpp_lifecycle::State state;
EXPECT_THROW(smoother->configure(state), std::runtime_error);
smoother->set_parameter(rclcpp::Parameter("feedback", std::string("LAWLS")));
EXPECT_THROW(smoother->configure(state), std::runtime_error);
}
TEST(VelocitySmootherTest, testInvalidParamsAccelDecel)
{
auto smoother =
std::make_shared<VelSmootherShim>();
std::vector<double> bad_test_accel{-10.0, -10.0, -10.0};
std::vector<double> bad_test_decel{10.0, 10.0, 10.0};
std::vector<double> bad_test_min_vel{10.0, 10.0, 10.0};
std::vector<double> bad_test_max_vel{-10.0, -10.0, -10.0};
smoother->declare_parameter("max_velocity", rclcpp::ParameterValue(bad_test_max_vel));
smoother->declare_parameter("min_velocity", rclcpp::ParameterValue(bad_test_min_vel));
rclcpp_lifecycle::State state;
EXPECT_THROW(smoother->configure(state), std::runtime_error);
smoother->set_parameter(rclcpp::Parameter("max_accel", rclcpp::ParameterValue(bad_test_accel)));
EXPECT_THROW(smoother->configure(state), std::runtime_error);
smoother->set_parameter(rclcpp::Parameter("max_decel", rclcpp::ParameterValue(bad_test_decel)));
EXPECT_THROW(smoother->configure(state), std::runtime_error);
}
TEST(VelocitySmootherTest, testDynamicParameter)
{
auto smoother =
std::make_shared<VelSmootherShim>();
rclcpp_lifecycle::State state;
smoother->configure(state);
smoother->activate(state);
EXPECT_FALSE(smoother->isOdomSmoother());
auto rec_param = std::make_shared<rclcpp::AsyncParametersClient>(
smoother->get_node_base_interface(), smoother->get_node_topics_interface(),
smoother->get_node_graph_interface(),
smoother->get_node_services_interface());
std::vector<double> max_vel{10.0, 10.0, 10.0};
std::vector<double> min_vel{0.0, 0.0, 0.0};
std::vector<double> max_accel{10.0, 10.0, 10.0};
std::vector<double> min_accel{0.0, 0.0, 0.0};
std::vector<double> deadband{0.0, 0.0, 0.0};
std::vector<double> bad_test{0.0, 0.0};
std::vector<double> bad_test_accel{-10.0, -10.0, -10.0};
std::vector<double> bad_test_decel{10.0, 10.0, 10.0};
auto results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("smoothing_frequency", 100.0),
rclcpp::Parameter("feedback", std::string("CLOSED_LOOP")),
rclcpp::Parameter("scale_velocities", true),
rclcpp::Parameter("max_velocity", max_vel),
rclcpp::Parameter("min_velocity", min_vel),
rclcpp::Parameter("max_accel", max_accel),
rclcpp::Parameter("max_decel", min_accel),
rclcpp::Parameter("odom_topic", std::string("TEST")),
rclcpp::Parameter("odom_duration", 2.0),
rclcpp::Parameter("velocity_timeout", 4.0),
rclcpp::Parameter("deadband_velocity", deadband)});
rclcpp::spin_until_future_complete(
smoother->get_node_base_interface(),
results);
EXPECT_EQ(smoother->get_parameter("smoothing_frequency").as_double(), 100.0);
EXPECT_EQ(smoother->get_parameter("feedback").as_string(), std::string("CLOSED_LOOP"));
EXPECT_EQ(smoother->get_parameter("scale_velocities").as_bool(), true);
EXPECT_EQ(smoother->get_parameter("max_velocity").as_double_array(), max_vel);
EXPECT_EQ(smoother->get_parameter("min_velocity").as_double_array(), min_vel);
EXPECT_EQ(smoother->get_parameter("max_accel").as_double_array(), max_accel);
EXPECT_EQ(smoother->get_parameter("max_decel").as_double_array(), min_accel);
EXPECT_EQ(smoother->get_parameter("odom_topic").as_string(), std::string("TEST"));
EXPECT_EQ(smoother->get_parameter("odom_duration").as_double(), 2.0);
EXPECT_EQ(smoother->get_parameter("velocity_timeout").as_double(), 4.0);
EXPECT_EQ(smoother->get_parameter("deadband_velocity").as_double_array(), deadband);
// Test reverting
results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("feedback", std::string("OPEN_LOOP"))});
rclcpp::spin_until_future_complete(
smoother->get_node_base_interface(), results);
EXPECT_EQ(smoother->get_parameter("feedback").as_string(), std::string("OPEN_LOOP"));
// Test invalid change
results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("feedback", std::string("LAWLS"))});
rclcpp::spin_until_future_complete(smoother->get_node_base_interface(), results);
EXPECT_FALSE(results.get().successful);
// Test invalid size
results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("max_velocity", bad_test)});
rclcpp::spin_until_future_complete(smoother->get_node_base_interface(), results);
EXPECT_FALSE(results.get().successful);
// Test invalid accel / decel
results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("max_accel", bad_test_accel)});
rclcpp::spin_until_future_complete(smoother->get_node_base_interface(), results);
EXPECT_FALSE(results.get().successful);
results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("max_decel", bad_test_decel)});
rclcpp::spin_until_future_complete(smoother->get_node_base_interface(), results);
EXPECT_FALSE(results.get().successful);
// test full state after major changes
smoother->deactivate(state);
smoother->cleanup(state);
smoother->shutdown(state);
smoother.reset();
}