Skip to content

Commit

Permalink
changing formulation of Hybrid-A* to be admissible and retune (ros-na…
Browse files Browse the repository at this point in the history
…vigation#2713)

(cherry picked from commit 7213cbc)
  • Loading branch information
borongyuan committed Jan 5, 2023
1 parent dda9953 commit 20ffaf2
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 23 deletions.
28 changes: 21 additions & 7 deletions nav2_smac_planner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ We further improve in the following ways:
- Remove need for upsampling by searching with 10x smaller motion primitives (same as their upsampling ratio).
- Multi-resolution search allowing planning to occur at a coarser resolution for wider spaces (O(N^2) faster).
- Cost-aware penalty functions in search resulting in far smoother plans (further reducing requirement to smooth).
- Gradient descent smoother
- Gradient-descent, basic but fast smoother
- Faster planning than original paper by highly optimizing the template A\* algorithm.
- Faster planning via precomputed heuristic, motion primitive, and other values.
- Automatically adjusted search motion model sizes by motion model, costmap resolution, and bin sizing.
- Closest path on approach within tolerance if exact path cannot be found or in invalid space.
- Multi-model hybrid searching including Dubin and Reeds-Shepp models. More models may be trivially added.
- High unit and integration test coverage, doxygen documentation.
- Uses modern C++14 language features and individual components are easily reusable.
- Speed optimizations: no data structure graph lookups in main loop, near-zero copy main loop, dynamically generated graph and dynamic programming-based obstacle heuristic, optional recomputation of heuristics for subsiquent planning requests of the same goal, etc.
- Speed optimizations: no data structure graph lookups in main loop, near-zero copy main loop, dynamically generated graph and dynamic programming-based obstacle heuristic, optional recomputation of heuristics for subsequent planning requests of the same goal, etc.
- Templated Nodes and A\* implementation to support additional robot extensions.
- Selective re-evaluation of the obstacle heuristic per goal/map or each iteration, which can speed up subsiquent replanning 20x or more.

Expand Down Expand Up @@ -97,14 +97,14 @@ planner_server:
max_planning_time: 3.5 # max time in s for planner to plan, smooth, and upsample. Will scale maximum smoothing and upsampling times based on remaining time after planning.
motion_model_for_search: "DUBIN" # 2D Moore, Von Neumann; Hybrid Dubin, Redds-Shepp; State Lattice set internally
cost_travel_multiplier: 2.0 # For 2D: Cost multiplier to apply to search to steer away from high cost areas. Larger values will place in the center of aisles more exactly (if non-`FREE` cost potential field exists) but take slightly longer to compute. To optimize for speed, a value of 1.0 is reasonable. A reasonable tradeoff value is 2.0. A value of 0.0 effective disables steering away from obstacles and acts like a naive binary search A*.
angle_quantization_bins: 64 # For Hybrid/Lattice nodes: Number of angle bins for search, must be 1 for 2D node (no angle search)
angle_quantization_bins: 64 # For Hybrid nodes: Number of angle bins for search, must be 1 for 2D node (no angle search)
analytic_expansion_ratio: 3.5 # For Hybrid/Lattice nodes: The ratio to attempt analytic expansions during search for final approach.
analytic_expansion_max_length: 3.0 # For Hybrid/Lattice nodes: The maximum length of the analytic expansion to be considered valid to prevent unsafe shortcutting (in meters). This should be scaled with minimum turning radius and be no less than 4-5x the minimum radius
minimum_turning_radius: 0.40 # For Hybrid/Lattice nodes: minimum turning radius in m of path / vehicle
reverse_penalty: 2.1 # For Reeds-Shepp model: penalty to apply if motion is reversing, must be => 1
change_penalty: 0.15 # For Hybrid nodes: penalty to apply if motion is changing directions, must be >= 0
non_straight_penalty: 1.50 # For Hybrid nodes: penalty to apply if motion is non-straight, must be => 1
cost_penalty: 1.7 # For Hybrid nodes: penalty to apply to higher cost areas when adding into the obstacle map dynamic programming distance expansion heuristic. This drives the robot more towards the center of passages. A value between 1.3 - 3.5 is reasonable.
change_penalty: 0.0 # For Hybrid nodes: penalty to apply if motion is changing directions, must be >= 0
non_straight_penalty: 1.20 # For Hybrid nodes: penalty to apply if motion is non-straight, must be => 1
cost_penalty: 2.0 # For Hybrid nodes: penalty to apply to higher cost areas when adding into the obstacle map dynamic programming distance expansion heuristic. This drives the robot more towards the center of passages. A value between 1.3 - 3.5 is reasonable.
lookup_table_size: 20 # For Hybrid nodes: Size of the dubin/reeds-sheep distance window to cache, in meters.
cache_obstacle_heuristic: True # For Hybrid nodes: Cache the obstacle map dynamic programming distance expansion heuristic between subsiquent replannings of the same goal location. Dramatically speeds up replanning performance (40x) if costmap is largely static.
smoother:
Expand All @@ -124,7 +124,7 @@ planner_server:
## Install

```
sudo apt-get install ros-<ros2-distro>-smac-planner
sudo apt-get install ros-<ros2-distro>-nav2-smac-planner
```

## Etc (Important Side Notes)
Expand Down Expand Up @@ -155,6 +155,20 @@ I recommend users using a 5cm resolution costmap and playing with the different

Remember, the global costmap is **only** there to provide an environment for the planner to work in. It is not there for human-viewing even if a more fine resolution costmap is more human "pleasing". If you use multiple planners in the planner server, then you will want to use the highest resolution for the most needed planner and then use the downsampler to downsample to the Hybrid-A* resolution.

### Penalty Function Tuning

The penalty function defaults are tuned for all of the planners based on a 5cm costmap. While some change in this should not largely impact default behavior, it may be good to tune for your specific application and needs. The default values were tuned to have decent out of the box behavior for a large number of platforms and resolutions. In most situations, you should not need to play with them.

**However**, due to the nature of the State Lattice planner being able to use any number of custom generated minimum control sets, this planner may require more tuning to get good behavior. The defaults for State Lattice were generated using the 5cm Ackermann files you can find in this package as initial examples. After a change in formulation for the Hybrid-A* planner, the default of change penalty off seems to produce good results, but please tune to your application need and run-time speed requirements.

When tuning, the "reasonable" range for each penalty is listed below. While you may obviously tune outside of these ranges, I've found that they offer a good trade-off and outside of these ranges behaviors get suboptimal quickly.
- Cost: 1.7 - 6.0
- Non-Straight: 1.0 - 1.3
- Change: 0.0 - 0.3
- Reverse: 1.3 - 5.0

Note that change penalty must be greater than 0.0. The Non-staight, reverse, and cost penalties must be greater than 1.0, strictly.

### No path found for clearly valid goals or long compute times

Before addressing the section below, make sure you have an appropriately set max iterations parameter. If you have a 1 km2 sized warehouse, clearly 5000 expansions will be insufficient. Try increasing this value if you're unable to achieve goals or disable it with the `-1` value to see if you are now able to plan within a reasonable time period. If you still have issues, there is a secondary effect which could be happening that is good to be aware of.
Expand Down
18 changes: 4 additions & 14 deletions nav2_smac_planner/src/node_hybrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,20 +302,10 @@ float NodeHybrid::getTraversalCost(const NodePtr & child)
return NodeHybrid::travel_distance_cost;
}

// Note(stevemacenski): `travel_cost_raw` at one point contained a term:
// `+ motion_table.cost_penalty * normalized_cost;`
// It has been removed, but we may want to readdress this point and determine
// the technically and theoretically correctness of that choice. I feel technically speaking
// that term has merit, but it doesn't seem to impact performance or path quality.
// W/o it lowers the travel cost, which would drive the heuristics up proportionally where I
// would expect it to plan much faster in all cases, but I only see it in some cases. Since
// this term would weight against moving to high cost zones, I would expect to see more smooth
// central motion, but I only see it in some cases, possibly because the obstacle heuristic is
// already driving the robot away from high cost areas; implicitly handling this. However,
// then I would expect that not adding it here would make it unbalanced enough that path quality
// would suffer, which I did not see in my limited experimentation, possibly due to the smoother.
float travel_cost = 0.0;
float travel_cost_raw = NodeHybrid::travel_distance_cost;
float travel_cost_raw =
NodeHybrid::travel_distance_cost +
(NodeHybrid::travel_distance_cost * motion_table.cost_penalty * normalized_cost);

if (child->getMotionPrimitiveIndex() == 0 || child->getMotionPrimitiveIndex() == 3) {
// New motion is a straight motion, no additional costs to be applied
Expand Down Expand Up @@ -462,7 +452,7 @@ float NodeHybrid::getObstacleHeuristic(
new_idx = static_cast<unsigned int>(static_cast<int>(idx) + neighborhood[i]);
cost = static_cast<float>(sampled_costmap->getCost(idx));
travel_cost =
((i <= 3) ? 1.0 : sqrt_2) + (motion_table.cost_penalty * cost / 252.0);
((i <= 3) ? 1.0 : sqrt_2) + (((i <= 3) ? 1.0 : sqrt_2) * motion_table.cost_penalty * cost / 252.0);
current_accumulated_cost = last_accumulated_cost + travel_cost;

// if neighbor path is better and non-lethal, set new cost and add to queue
Expand Down
4 changes: 2 additions & 2 deletions nav2_smac_planner/src/smac_planner_hybrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ void SmacPlannerHybrid::configure(
node, name + ".reverse_penalty", rclcpp::ParameterValue(2.0));
node->get_parameter(name + ".reverse_penalty", _search_info.reverse_penalty);
nav2_util::declare_parameter_if_not_declared(
node, name + ".change_penalty", rclcpp::ParameterValue(0.20));
node, name + ".change_penalty", rclcpp::ParameterValue(0.0));
node->get_parameter(name + ".change_penalty", _search_info.change_penalty);
nav2_util::declare_parameter_if_not_declared(
node, name + ".non_straight_penalty", rclcpp::ParameterValue(1.25));
node, name + ".non_straight_penalty", rclcpp::ParameterValue(1.2));
node->get_parameter(name + ".non_straight_penalty", _search_info.non_straight_penalty);
nav2_util::declare_parameter_if_not_declared(
node, name + ".cost_penalty", rclcpp::ParameterValue(2.0));
Expand Down

0 comments on commit 20ffaf2

Please sign in to comment.