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,232 @@
// Copyright 2020 Anshumaan Singh
//
// 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 <gtest/gtest.h>
#include <memory>
#include <vector>
#include "rclcpp/rclcpp.hpp"
#include "nav2_theta_star_planner/theta_star.hpp"
#include "nav2_theta_star_planner/theta_star_planner.hpp"
class init_rclcpp
{
public:
init_rclcpp() {rclcpp::init(0, nullptr);}
~init_rclcpp() {rclcpp::shutdown();}
};
/// class created to access the protected members of the ThetaStar class
/// u is used as shorthand for use
class test_theta_star : public theta_star::ThetaStar
{
public:
int getSizeOfNodePosition()
{
return static_cast<int>(node_position_.size());
}
bool ulosCheck(const int & x0, const int & y0, const int & x1, const int & y1, double & sl_cost)
{
return losCheck(x0, y0, x1, y1, sl_cost);
}
bool uwithinLimits(const int & cx, const int & cy) {return withinLimits(cx, cy);}
bool uisGoal(const tree_node & this_node) {return isGoal(this_node);}
void uinitializePosn(int size_inc = 0)
{
node_position_.reserve(size_x_ * size_y_); initializePosn(size_inc);
}
void uaddIndex(const int & cx, const int & cy) {addIndex(cx, cy, &nodes_data_[0]);}
tree_node * ugetIndex(const int & cx, const int & cy) {return getIndex(cx, cy);}
tree_node * test_getIndex() {return &nodes_data_[0];}
void uaddToNodesData(const int & id) {addToNodesData(id);}
void uresetContainers() {nodes_data_.clear(); resetContainers();}
bool runAlgo(std::vector<coordsW> & path)
{
if (!isUnsafeToPlan()) {
return generatePath(path);
}
return false;
}
};
init_rclcpp node;
// Tests meant to test the algorithm itself and its helper functions
TEST(ThetaStarTest, test_theta_star) {
auto planner_ = std::make_unique<test_theta_star>();
planner_->costmap_ = new nav2_costmap_2d::Costmap2D(50, 50, 1.0, 0.0, 0.0, 0);
for (int i = 7; i <= 14; i++) {
for (int j = 7; j <= 14; j++) {
planner_->costmap_->setCost(i, j, 253);
}
}
coordsM s = {5, 5}, g = {18, 18};
int size_x = 20, size_y = 20;
planner_->size_x_ = size_x;
planner_->size_y_ = size_y;
geometry_msgs::msg::PoseStamped start, goal;
start.pose.position.x = s.x;
start.pose.position.y = s.y;
start.pose.orientation.w = 1.0;
goal.pose.position.x = g.x;
goal.pose.position.y = g.y;
goal.pose.orientation.w = 1.0;
/// Check if the setStartAndGoal function works properly
planner_->setStartAndGoal(start, goal);
EXPECT_TRUE(planner_->src_.x == s.x && planner_->src_.y == s.y);
EXPECT_TRUE(planner_->dst_.x == g.x && planner_->dst_.y == g.y);
/// Check if the initializePosn function works properly
planner_->uinitializePosn(size_x * size_y);
EXPECT_EQ(planner_->getSizeOfNodePosition(), (size_x * size_y));
/// Check if the withinLimits function works properly
EXPECT_TRUE(planner_->uwithinLimits(18, 18));
EXPECT_FALSE(planner_->uwithinLimits(120, 140));
tree_node n = {g.x, g.y, 120, 0, NULL, false, 20};
n.parent_id = &n;
/// Check if the isGoal function works properly
EXPECT_TRUE(planner_->uisGoal(n)); // both (x,y) are the goal coordinates
n.x = 25;
EXPECT_FALSE(planner_->uisGoal(n)); // only y coordinate matches with that of goal
n.x = g.x;
n.y = 20;
EXPECT_FALSE(planner_->uisGoal(n)); // only x coordinate matches with that of goal
n.x = 30;
EXPECT_FALSE(planner_->uisGoal(n)); // both (x, y) are different from the goal coordinate
/// Check if the isSafe functions work properly
EXPECT_TRUE(planner_->isSafe(5, 5)); // cost at this point is 0
EXPECT_FALSE(planner_->isSafe(10, 10)); // cost at this point is 253 (>LETHAL_COST)
/// Check if the functions addIndex & getIndex work properly
coordsM c = {18, 18};
planner_->uaddToNodesData(0);
planner_->uaddIndex(c.x, c.y);
tree_node * c_node = planner_->ugetIndex(c.x, c.y);
EXPECT_EQ(c_node, planner_->test_getIndex());
double sl_cost = 0.0;
/// Checking for the case where the losCheck should return the value as true
EXPECT_TRUE(planner_->ulosCheck(2, 2, 7, 20, sl_cost));
/// and as false
EXPECT_FALSE(planner_->ulosCheck(2, 2, 18, 18, sl_cost));
planner_->uresetContainers();
std::vector<coordsW> path;
/// Check if the planner returns a path for the case where a path exists
EXPECT_TRUE(planner_->runAlgo(path));
EXPECT_GT(static_cast<int>(path.size()), 0);
/// and where it doesn't exist
path.clear();
planner_->src_ = {10, 10};
EXPECT_FALSE(planner_->runAlgo(path));
EXPECT_EQ(static_cast<int>(path.size()), 0);
}
// Smoke tests meant to detect issues arising from the plugin part rather than the algorithm
TEST(ThetaStarPlanner, test_theta_star_planner) {
rclcpp_lifecycle::LifecycleNode::SharedPtr life_node =
std::make_shared<rclcpp_lifecycle::LifecycleNode>("ThetaStarPlannerTest");
std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros =
std::make_shared<nav2_costmap_2d::Costmap2DROS>("global_costmap");
costmap_ros->on_configure(rclcpp_lifecycle::State());
geometry_msgs::msg::PoseStamped start, goal;
start.pose.position.x = 0.0;
start.pose.position.y = 0.0;
start.pose.orientation.w = 1.0;
goal = start;
auto planner_2d = std::make_unique<nav2_theta_star_planner::ThetaStarPlanner>();
planner_2d->configure(life_node, "test", nullptr, costmap_ros);
planner_2d->activate();
nav_msgs::msg::Path path = planner_2d->createPlan(start, goal);
EXPECT_GT(static_cast<int>(path.poses.size()), 0);
// test if the goal is unsafe
for (int i = 7; i <= 14; i++) {
for (int j = 7; j <= 14; j++) {
costmap_ros->getCostmap()->setCost(i, j, 254);
}
}
goal.pose.position.x = 1.0;
goal.pose.position.y = 1.0;
path = planner_2d->createPlan(start, goal);
EXPECT_EQ(static_cast<int>(path.poses.size()), 0);
planner_2d->deactivate();
planner_2d->cleanup();
planner_2d.reset();
costmap_ros->on_cleanup(rclcpp_lifecycle::State());
life_node.reset();
costmap_ros.reset();
}
TEST(ThetaStarPlanner, test_theta_star_reconfigure)
{
rclcpp_lifecycle::LifecycleNode::SharedPtr life_node =
std::make_shared<rclcpp_lifecycle::LifecycleNode>("ThetaStarPlannerTest");
std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros =
std::make_shared<nav2_costmap_2d::Costmap2DROS>("global_costmap");
costmap_ros->on_configure(rclcpp_lifecycle::State());
auto planner = std::make_unique<nav2_theta_star_planner::ThetaStarPlanner>();
try {
// Expect to throw due to invalid prims file in param
planner->configure(life_node, "test", nullptr, costmap_ros);
} catch (...) {
}
planner->activate();
auto rec_param = std::make_shared<rclcpp::AsyncParametersClient>(
life_node->get_node_base_interface(), life_node->get_node_topics_interface(),
life_node->get_node_graph_interface(),
life_node->get_node_services_interface());
auto results = rec_param->set_parameters_atomically(
{rclcpp::Parameter("test.how_many_corners", 8),
rclcpp::Parameter("test.w_euc_cost", 1.0),
rclcpp::Parameter("test.w_traversal_cost", 2.0),
rclcpp::Parameter("test.use_final_approach_orientation", false),
rclcpp::Parameter("test.allow_unknown", false)});
rclcpp::spin_until_future_complete(
life_node->get_node_base_interface(),
results);
EXPECT_EQ(life_node->get_parameter("test.how_many_corners").as_int(), 8);
EXPECT_EQ(
life_node->get_parameter("test.w_euc_cost").as_double(),
1.0);
EXPECT_EQ(life_node->get_parameter("test.w_traversal_cost").as_double(), 2.0);
EXPECT_EQ(life_node->get_parameter("test.use_final_approach_orientation").as_bool(), false);
EXPECT_EQ(life_node->get_parameter("test.allow_unknown").as_bool(), false);
rclcpp::spin_until_future_complete(
life_node->get_node_base_interface(),
results);
}