Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b2f9d6d
added edge length method
alexanderjyuen Apr 16, 2025
d198fa8
Added corner arc class
alexanderjyuen May 30, 2025
8cb47e0
replaced double vectors with Coordinates, added methods to return sta…
alexanderjyuen May 30, 2025
88b1648
using Coordinates, fixed direction of tangents
alexanderjyuen May 30, 2025
c4dc85d
added corner arc in header, added logger in protected variable
alexanderjyuen May 30, 2025
815d88c
first pass of corner smoothing algorithm
alexanderjyuen May 30, 2025
537cf97
reassigning next edge to have a different start, if a corner occurs b…
alexanderjyuen Jun 2, 2025
e623d9d
using unique pointer instead of raw pointers for new edges and nodes
alexanderjyuen Jun 2, 2025
66eee22
added smoothing parameter
alexanderjyuen Jun 2, 2025
d33bc2d
made angle of interpolation a parameter
alexanderjyuen Jun 2, 2025
6c1b2b6
const for return methods, added flag for smoothing corners
alexanderjyuen Jun 2, 2025
9cac954
moved getEdgeLength() into the Directional Edge struct
alexanderjyuen Jun 3, 2025
2c96ceb
using float instead of double
alexanderjyuen Jun 4, 2025
d4bd9dc
smoothing radius is float, couple methods moved to protected
alexanderjyuen Jun 4, 2025
752ff04
removed signed_angle_ as a member variable
alexanderjyuen Jun 4, 2025
690ad2a
removed unnecessary member variables
alexanderjyuen Jun 4, 2025
7dc35f8
removed angle of interpolation and inferring it from path density and…
alexanderjyuen Jun 4, 2025
2626b6a
consolidated corner arc into one header function
alexanderjyuen Jun 4, 2025
5d6d37d
readded newline
alexanderjyuen Jun 4, 2025
4f7bba1
changed corner arc to corner smoothing
alexanderjyuen Jun 4, 2025
4d6b8e0
replaced the use of edges with coordinates to generate smoothing arc,…
alexanderjyuen Jun 4, 2025
f0dff73
linting
alexanderjyuen Jun 4, 2025
53f2209
fixing cpplint
alexanderjyuen Jun 4, 2025
5b0b709
linting for headers
alexanderjyuen Jun 4, 2025
c827e52
cpplinting
alexanderjyuen Jun 4, 2025
d46468f
Update nav2_route/src/path_converter.cpp
SteveMacenski Jun 5, 2025
edbe42a
Update nav2_route/src/path_converter.cpp
SteveMacenski Jun 5, 2025
46ca874
Update nav2_route/src/path_converter.cpp
SteveMacenski Jun 5, 2025
68befab
Update nav2_route/src/path_converter.cpp
SteveMacenski Jun 5, 2025
e9a1882
Update nav2_route/include/nav2_route/corner_smoothing.hpp
SteveMacenski Jun 5, 2025
9555c93
fixed divide by zeros and accessing empty route.edges
alexanderjyuen Jun 6, 2025
b3cf20a
Merge branch 'route_server_corner_smoothing' of github.com:alexanderj…
alexanderjyuen Jun 6, 2025
5a39b87
uncrustify linting
alexanderjyuen Jun 6, 2025
752d1f2
cpp linting
alexanderjyuen Jun 6, 2025
b41c896
path converter linting
alexanderjyuen Jun 6, 2025
2024731
changed all doubles to floats
alexanderjyuen Jun 9, 2025
ee16acb
added check for edges that are colinear to avoid divide by 0, fixed f…
alexanderjyuen Jun 11, 2025
4e5baaf
linting
alexanderjyuen Jun 11, 2025
cc2114a
Update nav2_route/include/nav2_route/corner_smoothing.hpp
SteveMacenski Jun 11, 2025
1ce133e
added doxygen for corner arc class
alexanderjyuen Jun 11, 2025
9dafb46
Merge branch 'route_server_corner_smoothing' of github.com:alexanderj…
alexanderjyuen Jun 11, 2025
1728030
added warning message if corner can't be smoothed
alexanderjyuen Jun 11, 2025
55d54ec
added smooth_corners to the nav2 params file
alexanderjyuen Jun 11, 2025
989d092
added smoothing flag and radius parameter to README.md'
alexanderjyuen Jun 11, 2025
7e3e547
typo in README
alexanderjyuen Jun 11, 2025
dfff167
added testing for corner smoothing
alexanderjyuen Jun 11, 2025
0a7fa13
Merge branch 'main' into route_server_corner_smoothing
alexanderjyuen Jun 12, 2025
b2903a4
Update nav2_route/include/nav2_route/corner_smoothing.hpp
SteveMacenski Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nav2_route/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_library(${library_name} SHARED
src/route_server.cpp
src/route_planner.cpp
src/route_tracker.cpp
src/corner_arc.cpp
src/edge_scorer.cpp
src/operations_manager.cpp
src/node_spatial_tree.cpp
Expand Down
65 changes: 65 additions & 0 deletions nav2_route/include/nav2_route/corner_arc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2025, Polymath Robotics
//
// 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.

#ifndef NAV2_ROUTE__CORNER_ARC_HPP_
#define NAV2_ROUTE__CORNER_ARC_HPP_

#include <vector>
#include <cmath>

#include "nav2_route/types.hpp"
#include "nav2_route/utils.hpp"

namespace nav2_route
{
class CornerArc
{
public:
/**
* @brief Constructor
*/
CornerArc(EdgePtr start_edge, EdgePtr end_edge, double minimum_radius);

~CornerArc() = default;

void interpolateArc(const double & max_angle_resolution, std::vector<geometry_msgs::msg::PoseStamped> & poses);

double getEdgeLength(const EdgePtr edge);

double getAngleBetweenEdges(const EdgePtr start_edge, const EdgePtr end_edge);

double getSignedAngleBetweenEdges(const EdgePtr start_edge, const EdgePtr end_edge);

bool isCornerValid() const { return valid_corner_; }

Coordinates getCornerStart() const { return start_coordinate_; }

Coordinates getCornerEnd() const { return end_coordinate_; }

private:
EdgePtr start_edge_;
EdgePtr end_edge_;
double start_edge_length_;
double end_edge_length_;
double minimum_radius_;
double signed_angle_;
bool valid_corner_{false};
Coordinates start_coordinate_;
Coordinates end_coordinate_;
Coordinates circle_center_coordinate_;
};

} // namespace nav2_route

#endif // NAV2_ROUTE__CORNER_ARC_HPP_
7 changes: 7 additions & 0 deletions nav2_route/include/nav2_route/path_converter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "nav2_util/node_utils.hpp"
#include "nav2_route/types.hpp"
#include "nav2_route/utils.hpp"
#include "nav2_route/corner_arc.hpp"

namespace nav2_route
{
Expand Down Expand Up @@ -78,9 +79,15 @@ class PathConverter
float x0, float y0, float x1, float y1,
std::vector<geometry_msgs::msg::PoseStamped> & poses);

double getEdgeLength(const EdgePtr edge);

protected:
rclcpp_lifecycle::LifecyclePublisher<nav_msgs::msg::Path>::SharedPtr path_pub_;
rclcpp::Logger logger_{rclcpp::get_logger("PathConverter")};
float density_;
double smoothing_radius_;
double angle_of_interpolation_;
bool smooth_corners_;
};

} // namespace nav2_route
Expand Down
133 changes: 133 additions & 0 deletions nav2_route/src/corner_arc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) 2025, Polymath Robotics
//
// 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 "nav2_route/corner_arc.hpp"
#include <iostream>

namespace nav2_route
{

CornerArc::CornerArc(EdgePtr start_edge, EdgePtr end_edge, double minimum_radius)
{
start_edge_ = start_edge;
end_edge_ = end_edge;

double angle = getAngleBetweenEdges(start_edge, end_edge);

double tangent_length = minimum_radius/(std::tan(angle/2));

start_edge_length_ = getEdgeLength(start_edge);
end_edge_length_ = getEdgeLength(end_edge);

if(tangent_length < start_edge_length_ && tangent_length < end_edge_length_){
std::vector<double> start_edge_unit_tangent;
std::vector<double> end_edge_unit_tangent;
std::vector<double> unit_bisector;

start_edge_unit_tangent.push_back((start_edge->start->coords.x -start_edge->end->coords.x)/start_edge_length_);
start_edge_unit_tangent.push_back((start_edge->start->coords.y -start_edge->end->coords.y)/start_edge_length_);
end_edge_unit_tangent.push_back((end_edge->end->coords.x - end_edge->start->coords.x)/end_edge_length_);
end_edge_unit_tangent.push_back((end_edge->end->coords.y - end_edge->start->coords.y)/end_edge_length_);

double bisector_x = start_edge_unit_tangent[0]+end_edge_unit_tangent[0];
double bisector_y = start_edge_unit_tangent[1]+end_edge_unit_tangent[1];
double bisector_magnitude = std::sqrt(bisector_x*bisector_x + bisector_y*bisector_y);

unit_bisector.push_back(bisector_x/bisector_magnitude);
unit_bisector.push_back(bisector_y/bisector_magnitude);

start_coordinate_.x = start_edge->end->coords.x + start_edge_unit_tangent[0]*tangent_length;
start_coordinate_.y = start_edge->end->coords.y + start_edge_unit_tangent[1]*tangent_length;

end_coordinate_.x = end_edge->start->coords.x + end_edge_unit_tangent[0]*tangent_length;
end_coordinate_.y = end_edge->start->coords.y + end_edge_unit_tangent[1]*tangent_length;

double signed_angle = getSignedAngleBetweenEdges(start_edge, end_edge);

double bisector_length = minimum_radius/std::sin(signed_angle/2);

circle_center_coordinate_.x = end_edge->start->coords.x + unit_bisector[0]*bisector_length;
circle_center_coordinate_.y = end_edge->start->coords.y + unit_bisector[1]*bisector_length;

minimum_radius_ = minimum_radius;
valid_corner_ = true;

}
}

void CornerArc::interpolateArc(const double & max_angle_resolution, std::vector<geometry_msgs::msg::PoseStamped> & poses)
{

std::vector<double> r_start{ start_coordinate_.x-circle_center_coordinate_.x, start_coordinate_.y-circle_center_coordinate_.y };
std::vector<double> r_end{ end_coordinate_.x-circle_center_coordinate_.x, end_coordinate_.y-circle_center_coordinate_.y };
double cross = r_start[0]*r_end[1] - r_start[1]*r_end[0];
double dot = r_start[0]*r_end[0] + r_start[1]*r_end[1];
signed_angle_ = std::atan2(cross, dot);
int N = std::ceil(std::abs(signed_angle_)/max_angle_resolution);
double angle_resolution = signed_angle_/N;

float x, y;

for(int i = 0; i <= N; i++){
double angle = i*angle_resolution;
x = circle_center_coordinate_.x + (r_start[0]*std::cos(angle) - r_start[1]*std::sin(angle));
y = circle_center_coordinate_.y + (r_start[0]*std::sin(angle) + r_start[1]*std::cos(angle));
poses.push_back(utils::toMsg(x, y));
}
}

double CornerArc::getEdgeLength(const EdgePtr edge){
const Coordinates & start = edge->start->coords;
const Coordinates & end = edge->end->coords;

float dx = end.x - start.x;
float dy = end.y - start.y;

return double(sqrt(dx*dx + dy*dy));
}

double CornerArc::getAngleBetweenEdges(const EdgePtr start_edge, const EdgePtr end_edge){

double start_dx = start_edge->start->coords.x - start_edge->end->coords.x;
double start_dy = start_edge->start->coords.y - start_edge->end->coords.y;

double end_dx = end_edge->end->coords.x - end_edge->start->coords.x;
double end_dy = end_edge->end->coords.y - end_edge->start->coords.y;

double angle = acos((start_dx*end_dx + start_dy*end_dy)/(getEdgeLength(start_edge)*getEdgeLength(end_edge)));

return angle;
}

double CornerArc::getSignedAngleBetweenEdges(const EdgePtr start_edge, const EdgePtr end_edge){

double start_edge_length = getEdgeLength(start_edge);
double end_edge_length = getEdgeLength(end_edge);

double start_dx = (start_edge->start->coords.x - start_edge->end->coords.x)/start_edge_length;
double start_dy = (start_edge->start->coords.y - start_edge->end->coords.y)/start_edge_length;

double end_dx = (end_edge->end->coords.x - end_edge->start->coords.x)/end_edge_length;
double end_dy = (end_edge->end->coords.y - end_edge->start->coords.y)/end_edge_length;

double dot = start_dx*end_dx + start_dy*end_dy;
dot = std::clamp(dot, -1.0, 1.0);

double angle = std::acos(dot);

return angle;
}


} // namespace nav2_route
82 changes: 74 additions & 8 deletions nav2_route/src/path_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <algorithm>

#include "nav2_route/path_converter.hpp"

namespace nav2_route
{

Expand All @@ -30,9 +29,19 @@ void PathConverter::configure(nav2_util::LifecycleNode::SharedPtr node)
nav2_util::declare_parameter_if_not_declared(
node, "path_density", rclcpp::ParameterValue(0.05));
density_ = static_cast<float>(node->get_parameter("path_density").as_double());
nav2_util::declare_parameter_if_not_declared(
node, "smoothing_radius", rclcpp::ParameterValue(1.0));
smoothing_radius_ = node->get_parameter("smoothing_radius").as_double();
nav2_util::declare_parameter_if_not_declared(
node, "angle_of_interpolation", rclcpp::ParameterValue(0.1));
angle_of_interpolation_ = node->get_parameter("angle_of_interpolation").as_double();
nav2_util::declare_parameter_if_not_declared(
node, "smooth_corners", rclcpp::ParameterValue(false));
smooth_corners_ = node->get_parameter("smooth_corners").as_bool();

path_pub_ = node->create_publisher<nav_msgs::msg::Path>("plan", 1);
path_pub_->on_activate();
logger_ = node->get_logger();
}

nav_msgs::msg::Path PathConverter::densify(
Expand All @@ -54,19 +63,66 @@ nav_msgs::msg::Path PathConverter::densify(
interpolateEdge(start.x, start.y, end.x, end.y, path.poses);
}

// Make a copy of all the edges as we'll modify the edges during
// smoothing and we don't want to modify the original edges on the route
std::vector<EdgePtr> edges{route.edges.begin(), route.edges.end()};

// Make new edges and new nodes as end points will change depending
// On presence of smoothed corner. However we also do not want to
// modify the original route
std::vector<std::unique_ptr<DirectionalEdge>> new_edges;
std::vector<std::unique_ptr<Node>> new_nodes;

Coordinates start = edges[0]->start->coords;
Coordinates end;

// Fill in path via route edges
for (unsigned int i = 0; i != route.edges.size(); i++) {
const EdgePtr edge = route.edges[i];
const Coordinates & start = edge->start->coords;
const Coordinates & end = edge->end->coords;
interpolateEdge(start.x, start.y, end.x, end.y, path.poses);

for (unsigned int i = 0; i < edges.size() - 1; i++) {

const EdgePtr edge = edges[i];
const EdgePtr next_edge = edges[i+1];

end = edge->end->coords;

CornerArc corner_arc(edge, next_edge, smoothing_radius_);

if(corner_arc.isCornerValid() && smooth_corners_){
//if an arc exists, end of the first edge is the start of the arc
end = corner_arc.getCornerStart();
interpolateEdge(start.x, start.y, end.x, end.y, path.poses);

corner_arc.interpolateArc(angle_of_interpolation_, path.poses);

//if an arc exists, start of the subsequent edge is end of the arc
auto new_start_node = std::make_unique<Node>(*next_edge->start);
new_start_node->coords = corner_arc.getCornerEnd();
auto new_next_edge = std::make_unique<DirectionalEdge>(*next_edge);
new_next_edge->start = new_start_node.get();

//reassign the next edge to have the modified start coordinate
edges[i+1] = new_next_edge.get();
start = corner_arc.getCornerEnd();

//Store pointer to edges to keep them alive in this method
new_nodes.emplace_back(std::move(new_start_node));
new_edges.emplace_back(std::move(new_next_edge));

}else{
interpolateEdge(start.x, start.y, end.x, end.y, path.poses);
start = end;
}
}

if (route.edges.empty()) {
if (edges.empty()) {
path.poses.push_back(utils::toMsg(route.start_node->coords.x, route.start_node->coords.y));
} else {

interpolateEdge(edges.back()->start->coords.x, edges.back()->start->coords.y,
edges.back()->end->coords.x, edges.back()->end->coords.y, path.poses);

path.poses.push_back(
utils::toMsg(route.edges.back()->end->coords.x, route.edges.back()->end->coords.y));
utils::toMsg(edges.back()->end->coords.x, edges.back()->end->coords.y));
}

// Set path poses orientations for each point
Expand Down Expand Up @@ -121,4 +177,14 @@ void PathConverter::interpolateEdge(
}
}

double PathConverter::getEdgeLength(const EdgePtr edge){
const Coordinates & start = edge->start->coords;
const Coordinates & end = edge->end->coords;

float dx = end.x - start.x;
float dy = end.y - start.y;

return double(sqrt(dx*dx + dy*dy));
}

} // namespace nav2_route
Loading