Skip to content

Commit

Permalink
Bound closest pose by costmap and turnaround point (#2812)
Browse files Browse the repository at this point in the history
* created integrated distance util

* chng transform begin , end and always limit lp pt

* chg trnsfm begin end and use euclidean distance

* linting fix

* linting fix

* limit trnsfm to cusp

* remove print

* linting fix

* lint fix

* hypot

* use direction change as upper bound for closest pose

* bound closest waypoint by direction change

* use max_transform_dist for closest_pose_upper_bound

* remove first_element_beyond optimization todo

* added path_utils for test path generation

* initial test for path_utils

* added full failing test for path_utils

* fixed path_utils segfault

* properly initialize straight transform

* fixed right turn infinite loop

* fixed path_utils transforms for half-turns

* all path_utils tests passing

* added half turn test for path_utils

* remove unused dependencies from test_path_utils

* move path_utils classes into path_building_blocks namespace

* use hypot for euclidean_distance

* rename findDirectionChange to findCusp

* use integrated distance for lookahead distance

* Revert "use integrated distance for lookahead distance"

This reverts commit 6e4c6cd.

* parameterize transformation_begin bound

* change default to a regular constant

* use std::hypot for x, y, z

* initial failing test for transformGlobalPlan

* refactor transform global plan into separate test fixture

* make TransformGlobalPlan test fixture, and fix transforms

* no_pruning_on_large_costmap test passing

* added another transformGlobalPlan test

* added a test for all poses outside of costmap

* added good shortcut test for transformGlobalPlan

* added more tests for rpp costmap pruning

* cpplint

* remove unused rclcpp::init and rclcpp::shutdown

* change default max_distance_between_iterations to max_costmap_extent

* rename max_distance_between_iterations to max_robot_pose_search_dist

* increase default dwb prune distance

* add initial docs for max_robot_pose_search_dist to README

* add note about when to set max_robot_pose_search_dist

* rename first_element_beyond to first_after_integrated_distance

* renamed findCusp to findVelocitySignChange

* move path_utils to RPP tests

* only check velocity sign change when reversing is enabled

* do not check cusp for dwb transformed plan end

Co-authored-by: sathak93 <[email protected]>
Co-authored-by: Adam Aposhian <[email protected]>
  • Loading branch information
3 people authored Mar 21, 2022
1 parent ee2efb6 commit 7872c73
Show file tree
Hide file tree
Showing 10 changed files with 807 additions and 51 deletions.
52 changes: 24 additions & 28 deletions nav2_dwb_controller/dwb_core/src/dwb_local_planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@
#include "nav_2d_utils/conversions.hpp"
#include "nav_2d_utils/parameters.hpp"
#include "nav_2d_utils/tf_help.hpp"
#include "nav2_util/geometry_utils.hpp"
#include "nav2_util/lifecycle_node.hpp"
#include "nav2_util/node_utils.hpp"
#include "pluginlib/class_list_macros.hpp"
#include "nav_msgs/msg/path.hpp"
#include "geometry_msgs/msg/twist_stamped.hpp"

using nav2_util::declare_parameter_if_not_declared;
using nav2_util::geometry_utils::euclidean_distance;

namespace dwb_core
{
Expand Down Expand Up @@ -87,7 +89,7 @@ void DWBLocalPlanner::configure(
rclcpp::ParameterValue(true));
declare_parameter_if_not_declared(
node, dwb_plugin_name_ + ".prune_distance",
rclcpp::ParameterValue(1.0));
rclcpp::ParameterValue(2.0));
declare_parameter_if_not_declared(
node, dwb_plugin_name_ + ".debug_trajectory_details",
rclcpp::ParameterValue(false));
Expand Down Expand Up @@ -434,17 +436,6 @@ DWBLocalPlanner::scoreTrajectory(
return score;
}

double
getSquareDistance(
const geometry_msgs::msg::Pose2D & pose_a,
const geometry_msgs::msg::Pose2D & pose_b)
{
double x_diff = pose_a.x - pose_b.x;
double y_diff = pose_a.y - pose_b.y;

return x_diff * x_diff + y_diff * y_diff;
}

nav_2d_msgs::msg::Path2D
DWBLocalPlanner::transformGlobalPlan(
const nav_2d_msgs::msg::Pose2DStamped & pose)
Expand All @@ -467,46 +458,51 @@ DWBLocalPlanner::transformGlobalPlan(
nav2_costmap_2d::Costmap2D * costmap = costmap_ros_->getCostmap();
double dist_threshold = std::max(costmap->getSizeInCellsX(), costmap->getSizeInCellsY()) *
costmap->getResolution() / 2.0;
double sq_dist_threshold = dist_threshold * dist_threshold;


// If prune_plan is enabled (it is by default) then we want to restrict the
// plan to distances within that range as well.
double sq_prune_dist = prune_distance_ * prune_distance_;
double prune_dist = prune_distance_;

// Set the maximum distance we'll include points before getting to the part
// of the path where the robot is located (the start of the plan). Basically,
// these are the points the robot has already passed.
double sq_transform_start_threshold;
double transform_start_threshold;
if (prune_plan_) {
sq_transform_start_threshold = std::min(sq_dist_threshold, sq_prune_dist);
transform_start_threshold = std::min(dist_threshold, prune_dist);
} else {
sq_transform_start_threshold = sq_dist_threshold;
transform_start_threshold = dist_threshold;
}

// Set the maximum distance we'll include points after the part of the part of
// the plan near the robot (the end of the plan). This determines the amount
// of the plan passed on to the critics
double sq_transform_end_threshold;
double transform_end_threshold;
if (shorten_transformed_plan_) {
sq_transform_end_threshold = std::min(sq_dist_threshold, sq_prune_dist);
transform_end_threshold = std::min(dist_threshold, prune_dist);
} else {
sq_transform_end_threshold = sq_dist_threshold;
transform_end_threshold = dist_threshold;
}

// Find the first pose in the plan that's less than sq_transform_start_threshold
// Find the first pose in the global plan that's further than prune distance
// from the robot using integrated distance
auto prune_point = nav2_util::geometry_utils::first_after_integrated_distance(
global_plan_.poses.begin(), global_plan_.poses.end(), prune_dist);

// Find the first pose in the plan (upto prune_point) that's less than transform_start_threshold
// from the robot.
auto transformation_begin = std::find_if(
begin(global_plan_.poses), end(global_plan_.poses),
begin(global_plan_.poses), prune_point,
[&](const auto & global_plan_pose) {
return getSquareDistance(robot_pose.pose, global_plan_pose) < sq_transform_start_threshold;
return euclidean_distance(robot_pose.pose, global_plan_pose) < transform_start_threshold;
});

// Find the first pose in the end of the plan that's further than sq_transform_end_threshold
// from the robot
// Find the first pose in the end of the plan that's further than transform_end_threshold
// from the robot using integrated distance
auto transformation_end = std::find_if(
transformation_begin, end(global_plan_.poses),
[&](const auto & global_plan_pose) {
return getSquareDistance(robot_pose.pose, global_plan_pose) > sq_transform_end_threshold;
transformation_begin, global_plan_.poses.end(),
[&](const auto & pose) {
return euclidean_distance(pose, robot_pose.pose) > transform_end_threshold;
});

// Transform the near part of the global plan into the robot's frame of reference.
Expand Down
2 changes: 2 additions & 0 deletions nav2_regulated_pure_pursuit_controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Note: The maximum allowed time to collision is thresholded by the lookahead poin
| `use_rotate_to_heading` | Whether to enable rotating to rough heading and goal orientation when using holonomic planners. Recommended on for all robot types except ackermann, which cannot rotate in place. |
| `rotate_to_heading_min_angle` | The difference in the path orientation and the starting robot orientation to trigger a rotate in place, if `use_rotate_to_heading` is enabled. |
| `max_angular_accel` | Maximum allowable angular acceleration while rotating to heading, if enabled |
| `max_robot_pose_search_dist` | Maximum integrated distance along the path to bound the search for the closest pose to the robot. This is set by default to the maximum costmap extent, so it shouldn't be set manually unless there are loops within the local costmap. |

Example fully-described XML with default parameter values:

Expand Down Expand Up @@ -121,6 +122,7 @@ controller_server:
use_rotate_to_heading: true
rotate_to_heading_min_angle: 0.785
max_angular_accel: 3.2
max_robot_pose_search_dist: 10.0
cost_scaling_dist: 0.3
cost_scaling_gain: 1.0
inflation_cost_scaling_factor: 3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,14 @@ class RegulatedPurePursuitController : public nav2_core::Controller

protected:
/**
* @brief Transforms global plan into same frame as pose, clips far away poses and possibly prunes passed poses
* @brief Transforms global plan into same frame as pose and clips poses ineligible for lookaheadPoint
* Points ineligible to be selected as a lookahead point if they are any of the following:
* - Outside the local_costmap (collision avoidance cannot be assured)
* @param pose pose to transform
* @return Path in new frame
*/
nav_msgs::msg::Path transformGlobalPlan(const geometry_msgs::msg::PoseStamped & pose);
nav_msgs::msg::Path transformGlobalPlan(
const geometry_msgs::msg::PoseStamped & pose);

/**
* @brief Transform a pose to another frame.
Expand Down Expand Up @@ -232,7 +235,13 @@ class RegulatedPurePursuitController : public nav2_core::Controller
* @param pose Pose input to determine the cusp position
* @return robot distance from the cusp
*/
double findDirectionChange(const geometry_msgs::msg::PoseStamped & pose);
double findVelocitySignChange(const geometry_msgs::msg::PoseStamped & pose);

/**
* Get the greatest extent of the costmap in meters from the center.
* @return max of distance from center in meters to edge of costmap
*/
double getCostmapMaxExtent() const;

/**
* @brief Callback executed when a parameter change is detected
Expand Down Expand Up @@ -272,6 +281,7 @@ class RegulatedPurePursuitController : public nav2_core::Controller
double rotate_to_heading_min_angle_;
double goal_dist_tol_;
bool allow_reversing_;
double max_robot_pose_search_dist_;

nav_msgs::msg::Path global_plan_;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<nav_msgs::msg::Path>> global_path_pub_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ void RegulatedPurePursuitController::configure(
node, plugin_name_ + ".max_angular_accel", rclcpp::ParameterValue(3.2));
declare_parameter_if_not_declared(
node, plugin_name_ + ".allow_reversing", rclcpp::ParameterValue(false));
declare_parameter_if_not_declared(
node, plugin_name_ + ".max_robot_pose_search_dist",
rclcpp::ParameterValue(getCostmapMaxExtent()));

node->get_parameter(plugin_name_ + ".desired_linear_vel", desired_linear_vel_);
base_desired_linear_vel_ = desired_linear_vel_;
Expand Down Expand Up @@ -147,6 +150,9 @@ void RegulatedPurePursuitController::configure(
node->get_parameter(plugin_name_ + ".max_angular_accel", max_angular_accel_);
node->get_parameter(plugin_name_ + ".allow_reversing", allow_reversing_);
node->get_parameter("controller_frequency", control_frequency);
node->get_parameter(
plugin_name_ + ".max_robot_pose_search_dist",
max_robot_pose_search_dist_);

transform_tolerance_ = tf2::durationFromSec(transform_tolerance);
control_duration_ = 1.0 / control_frequency;
Expand Down Expand Up @@ -232,7 +238,8 @@ std::unique_ptr<geometry_msgs::msg::PointStamped> RegulatedPurePursuitController
return carrot_msg;
}

double RegulatedPurePursuitController::getLookAheadDistance(const geometry_msgs::msg::Twist & speed)
double RegulatedPurePursuitController::getLookAheadDistance(
const geometry_msgs::msg::Twist & speed)
{
// If using velocity-scaled look ahead distances, find and clamp the dist
// Else, use the static look ahead distance
Expand Down Expand Up @@ -270,11 +277,11 @@ geometry_msgs::msg::TwistStamped RegulatedPurePursuitController::computeVelocity
// Check for reverse driving
if (allow_reversing_) {
// Cusp check
double dist_to_direction_change = findDirectionChange(pose);
double dist_to_cusp = findVelocitySignChange(pose);

// if the lookahead distance is further than the cusp, use the cusp distance instead
if (dist_to_direction_change < lookahead_dist) {
lookahead_dist = dist_to_direction_change;
if (dist_to_cusp < lookahead_dist) {
lookahead_dist = dist_to_cusp;
}
}

Expand Down Expand Up @@ -602,23 +609,27 @@ nav_msgs::msg::Path RegulatedPurePursuitController::transformGlobalPlan(
}

// We'll discard points on the plan that are outside the local costmap
nav2_costmap_2d::Costmap2D * costmap = costmap_ros_->getCostmap();
const double max_costmap_dim = std::max(costmap->getSizeInCellsX(), costmap->getSizeInCellsY());
const double max_transform_dist = max_costmap_dim * costmap->getResolution() / 2.0;
double max_costmap_extent = getCostmapMaxExtent();

auto closest_pose_upper_bound =
nav2_util::geometry_utils::first_after_integrated_distance(
global_plan_.poses.begin(), global_plan_.poses.end(), max_robot_pose_search_dist_);

// First find the closest pose on the path to the robot
// bounded by when the path turns around (if it does) so we don't get a pose from a later
// portion of the path
auto transformation_begin =
nav2_util::geometry_utils::min_by(
global_plan_.poses.begin(), global_plan_.poses.end(),
global_plan_.poses.begin(), closest_pose_upper_bound,
[&robot_pose](const geometry_msgs::msg::PoseStamped & ps) {
return euclidean_distance(robot_pose, ps);
});

// Find points definitely outside of the costmap so we won't transform them.
// Find points up to max_transform_dist so we only transform them.
auto transformation_end = std::find_if(
transformation_begin, end(global_plan_.poses),
[&](const auto & global_plan_pose) {
return euclidean_distance(robot_pose, global_plan_pose) > max_transform_dist;
transformation_begin, global_plan_.poses.end(),
[&](const auto & pose) {
return euclidean_distance(pose, robot_pose) > max_costmap_extent;
});

// Lambda to transform a PoseStamped from global frame to local
Expand Down Expand Up @@ -652,7 +663,7 @@ nav_msgs::msg::Path RegulatedPurePursuitController::transformGlobalPlan(
return transformed_plan;
}

double RegulatedPurePursuitController::findDirectionChange(
double RegulatedPurePursuitController::findVelocitySignChange(
const geometry_msgs::msg::PoseStamped & pose)
{
// Iterating through the global path to determine the position of the cusp
Expand Down Expand Up @@ -700,6 +711,13 @@ bool RegulatedPurePursuitController::transformPose(
return false;
}

double RegulatedPurePursuitController::getCostmapMaxExtent() const
{
const double max_costmap_dim_meters = std::max(
costmap_->getSizeInMetersX(), costmap_->getSizeInMetersX());
return max_costmap_dim_meters / 2.0;
}


rcl_interfaces::msg::SetParametersResult
RegulatedPurePursuitController::dynamicParametersCallback(
Expand Down
5 changes: 5 additions & 0 deletions nav2_regulated_pure_pursuit_controller/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# tests for regulated PP
ament_add_gtest(test_regulated_pp
test_regulated_pp.cpp
path_utils/path_utils.cpp
)
ament_target_dependencies(test_regulated_pp
${dependencies}
)
target_link_libraries(test_regulated_pp
${library_name}
)

# Path utils test
ament_add_gtest(test_path_utils path_utils/test_path_utils.cpp path_utils/path_utils.cpp)
ament_target_dependencies(test_path_utils nav_msgs geometry_msgs)
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2022 Adam Aposhian
//
// 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 <memory>

#include "path_utils.hpp"

#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"

namespace path_utils
{

void append_transform_to_path(
nav_msgs::msg::Path & path,
tf2::Transform & relative_transform)
{
// Add a new empty pose
path.poses.emplace_back();
// Get the previous, last pose (after the emplace_back so the reference isn't invalidated)
auto & previous_pose = *(path.poses.end() - 2);
auto & new_pose = path.poses.back();

// get map_transform of previous_pose
tf2::Transform map_transform;
tf2::fromMsg(previous_pose.pose, map_transform);

tf2::Transform full_transform;
full_transform.mult(map_transform, relative_transform);

tf2::toMsg(full_transform, new_pose.pose);
new_pose.header.frame_id = previous_pose.header.frame_id;
}

void Straight::append(nav_msgs::msg::Path & path, double spacing) const
{
auto num_points = std::floor(length_ / spacing);
path.poses.reserve(path.poses.size() + num_points);
tf2::Transform translation(tf2::Quaternion::getIdentity(), tf2::Vector3(spacing, 0.0, 0.0));
for (size_t i = 1; i <= num_points; ++i) {
append_transform_to_path(path, translation);
}
}

double chord_length(double radius, double radians)
{
return 2 * radius * sin(radians / 2);
}

void Arc::append(nav_msgs::msg::Path & path, double spacing) const
{
double length = radius_ * std::abs(radians_);
size_t num_points = std::floor(length / spacing);
double radians_per_step = radians_ / num_points;
tf2::Transform transform(
tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), radians_per_step),
tf2::Vector3(chord_length(radius_, std::abs(radians_per_step)), 0.0, 0.0));
path.poses.reserve(path.poses.size() + num_points);
for (size_t i = 0; i < num_points; ++i) {
append_transform_to_path(path, transform);
}
}

nav_msgs::msg::Path generate_path(
geometry_msgs::msg::PoseStamped start,
double spacing,
std::initializer_list<std::unique_ptr<PathSegment>> segments)
{
nav_msgs::msg::Path path;
path.header = start.header;
path.poses.push_back(start);
for (const auto & segment : segments) {
segment->append(path, spacing);
}
return path;
}

} // namespace path_utils
Loading

0 comments on commit 7872c73

Please sign in to comment.