17 #include "nav2_smoother/savitzky_golay_smoother.hpp"
18 #include "nav2_core/smoother_exceptions.hpp"
20 namespace nav2_smoother
22 using namespace nav2_util::geometry_utils;
23 using namespace std::chrono;
24 using nav2::declare_parameter_if_not_declared;
27 void SavitzkyGolaySmoother::configure(
28 const nav2::LifecycleNode::WeakPtr & parent,
29 std::string name, std::shared_ptr<tf2_ros::Buffer>,
30 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber>,
31 std::shared_ptr<nav2_costmap_2d::FootprintSubscriber>)
33 auto node = parent.lock();
34 logger_ = node->get_logger();
36 declare_parameter_if_not_declared(
37 node, name +
".do_refinement", rclcpp::ParameterValue(
true));
38 declare_parameter_if_not_declared(
39 node, name +
".refinement_num", rclcpp::ParameterValue(2));
40 declare_parameter_if_not_declared(
41 node, name +
".enforce_path_inversion", rclcpp::ParameterValue(
true));
42 declare_parameter_if_not_declared(
43 node, name +
".window_size", rclcpp::ParameterValue(7));
44 declare_parameter_if_not_declared(
45 node, name +
".poly_order", rclcpp::ParameterValue(3));
46 node->get_parameter(name +
".do_refinement", do_refinement_);
47 node->get_parameter(name +
".refinement_num", refinement_num_);
48 node->get_parameter(name +
".enforce_path_inversion", enforce_path_inversion_);
49 node->get_parameter(name +
".window_size", window_size_);
50 node->get_parameter(name +
".poly_order", poly_order_);
51 if (window_size_ % 2 == 0 || window_size_ <= 2) {
53 "Savitzky-Golay Smoother requires an odd window size of 3 or greater");
55 half_window_size_ = (window_size_ - 1) / 2;
56 calculateCoefficients();
64 Eigen::VectorXd v = Eigen::VectorXd::LinSpaced(window_size_, -half_window_size_,
66 Eigen::MatrixXd x = Eigen::MatrixXd::Ones(window_size_, poly_order_ + 1);
67 for(
int i = 1; i <= poly_order_; i++) {
68 x.col(i) = (x.col(i - 1).array() * v.array()).matrix();
71 Eigen::MatrixXd coeff_mat = (x.transpose() * x).inverse() * x.transpose();
74 sg_coeffs_ = coeff_mat.row(0).transpose();
78 nav_msgs::msg::Path & path,
79 const rclcpp::Duration & max_time)
81 steady_clock::time_point start = steady_clock::now();
82 double time_remaining = max_time.seconds();
84 bool success =
true, reversing_segment;
85 nav_msgs::msg::Path curr_path_segment;
86 curr_path_segment.header = path.header;
88 std::vector<PathSegment> path_segments{
89 PathSegment{0u,
static_cast<unsigned int>(path.poses.size() - 1)}};
90 if (enforce_path_inversion_) {
91 path_segments = nav2_util::findDirectionalPathSegments(path);
95 unsigned int minimum_points = window_size_ + 2;
96 for (
unsigned int i = 0; i != path_segments.size(); i++) {
97 if (path_segments[i].end - path_segments[i].start > minimum_points) {
99 curr_path_segment.poses.clear();
101 path.poses.begin() + path_segments[i].start,
102 path.poses.begin() + path_segments[i].end + 1,
103 std::back_inserter(curr_path_segment.poses));
106 steady_clock::time_point now = steady_clock::now();
107 time_remaining = max_time.seconds() - duration_cast<duration<double>>(now - start).count();
109 if (time_remaining <= 0.0) {
112 "Smoothing time exceeded allowed duration of %0.2f.", max_time.seconds());
117 success = success && smoothImpl(curr_path_segment, reversing_segment);
121 curr_path_segment.poses.begin(),
122 curr_path_segment.poses.end(),
123 path.poses.begin() + path_segments[i].start);
131 nav_msgs::msg::Path & path,
132 bool & reversing_segment)
134 const unsigned int & path_size = path.poses.size();
137 auto toEigenVec = [](
const geometry_msgs::msg::PoseStamped & pose) -> Eigen::Vector2d {
138 return {pose.pose.position.x, pose.pose.position.y};
141 auto applyFilterOverAxes =
142 [&](std::vector<geometry_msgs::msg::PoseStamped> & plan_pts,
143 const std::vector<Eigen::Vector2d> & init_plan_pts) ->
void
146 for (
unsigned int idx = 1; idx != path_size - 1; idx++) {
147 Eigen::Vector2d accum(0.0, 0.0);
149 for(
int j = -half_window_size_; j <= half_window_size_; j++) {
150 int path_idx = std::clamp<int>(idx + j, 0, path_size - 1);
151 accum += sg_coeffs_(j + half_window_size_) * init_plan_pts[path_idx];
153 plan_pts[idx].pose.position.x = accum.x();
154 plan_pts[idx].pose.position.y = accum.y();
158 std::vector<Eigen::Vector2d> initial_path_poses(path.poses.size());
159 std::transform(path.poses.begin(), path.poses.end(),
160 initial_path_poses.begin(), toEigenVec);
161 applyFilterOverAxes(path.poses, initial_path_poses);
164 if (do_refinement_) {
165 for (
int i = 0; i < refinement_num_; i++) {
166 std::vector<Eigen::Vector2d> reined_initial_path_poses(path.poses.size());
167 std::transform(path.poses.begin(), path.poses.end(),
168 reined_initial_path_poses.begin(), toEigenVec);
169 applyFilterOverAxes(path.poses, reined_initial_path_poses);
173 nav2_util::updateApproximatePathOrientations(path, reversing_segment);
179 #include "pluginlib/class_list_macros.hpp"
smoother interface that acts as a virtual base class for all smoother plugins
A path smoother implementation using Savitzky Golay filters.
void calculateCoefficients()
Method to calculate SavitzkyGolay Coefficients.
bool smoothImpl(nav_msgs::msg::Path &path, bool &reversing_segment)
Smoother method - does the smoothing on a segment.
bool smooth(nav_msgs::msg::Path &path, const rclcpp::Duration &max_time) override
Method to smooth given path.
A segment of a path in start/end indices.