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;
26 void SavitzkyGolaySmoother::configure(
27 const nav2::LifecycleNode::WeakPtr & parent,
28 std::string name, nav2::TransformBuffer::SharedPtr,
29 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber>,
30 std::shared_ptr<nav2_costmap_2d::FootprintSubscriber>)
32 auto node = parent.lock();
33 logger_ = node->get_logger();
35 do_refinement_ = node->declare_or_get_parameter(
36 name +
".do_refinement",
true);
37 refinement_num_ = node->declare_or_get_parameter(
38 name +
".refinement_num", 2);
39 enforce_path_inversion_ = node->declare_or_get_parameter(
40 name +
".enforce_path_inversion",
true);
41 window_size_ = node->declare_or_get_parameter(
42 name +
".window_size", 7);
43 poly_order_ = node->declare_or_get_parameter(
44 name +
".poly_order", 3);
46 if (window_size_ % 2 == 0 || window_size_ <= 2) {
48 "Savitzky-Golay Smoother requires an odd window size of 3 or greater");
50 half_window_size_ = (window_size_ - 1) / 2;
51 calculateCoefficients();
59 Eigen::VectorXd v = Eigen::VectorXd::LinSpaced(
60 window_size_, -half_window_size_,
62 Eigen::MatrixXd x = Eigen::MatrixXd::Ones(window_size_, poly_order_ + 1);
63 for (
int i = 1; i <= poly_order_; i++) {
64 x.col(i) = (x.col(i - 1).array() * v.array()).matrix();
67 Eigen::MatrixXd coeff_mat = (x.transpose() * x).inverse() * x.transpose();
70 sg_coeffs_ = coeff_mat.row(0).transpose();
74 nav_msgs::msg::Path & path,
75 const rclcpp::Duration & max_time)
77 steady_clock::time_point start = steady_clock::now();
78 double time_remaining = max_time.seconds();
80 bool success =
true, reversing_segment;
81 nav_msgs::msg::Path curr_path_segment;
82 curr_path_segment.header = path.header;
84 std::vector<PathSegment> path_segments{
85 PathSegment{0u,
static_cast<unsigned int>(path.poses.size() - 1)}};
86 if (enforce_path_inversion_) {
87 path_segments = nav2_util::findDirectionalPathSegments(path);
91 unsigned int minimum_points = window_size_ + 2;
92 for (
unsigned int i = 0; i != path_segments.size(); i++) {
93 if (path_segments[i].end - path_segments[i].start > minimum_points) {
95 curr_path_segment.poses.clear();
97 path.poses.begin() + path_segments[i].start,
98 path.poses.begin() + path_segments[i].end + 1,
99 std::back_inserter(curr_path_segment.poses));
102 steady_clock::time_point now = steady_clock::now();
103 time_remaining = max_time.seconds() - duration_cast<duration<double>>(now - start).count();
105 if (time_remaining <= 0.0) {
108 "Smoothing time exceeded allowed duration of %0.2f.", max_time.seconds());
113 success = success && smoothImpl(curr_path_segment, reversing_segment);
117 curr_path_segment.poses.begin(),
118 curr_path_segment.poses.end(),
119 path.poses.begin() + path_segments[i].start);
127 nav_msgs::msg::Path & path,
128 bool & reversing_segment)
130 const unsigned int & path_size = path.poses.size();
133 auto toEigenVec = [](
const geometry_msgs::msg::PoseStamped & pose) -> Eigen::Vector2d {
134 return {pose.pose.position.x, pose.pose.position.y};
137 auto applyFilterOverAxes =
138 [&](std::vector<geometry_msgs::msg::PoseStamped> & plan_pts,
139 const std::vector<Eigen::Vector2d> & init_plan_pts) ->
void
142 for (
unsigned int idx = 1; idx != path_size - 1; idx++) {
143 Eigen::Vector2d accum(0.0, 0.0);
145 for (
int j = -half_window_size_; j <= half_window_size_; j++) {
146 int path_idx = std::clamp<int>(idx + j, 0, path_size - 1);
147 accum += sg_coeffs_(j + half_window_size_) * init_plan_pts[path_idx];
149 plan_pts[idx].pose.position.x = accum.x();
150 plan_pts[idx].pose.position.y = accum.y();
154 std::vector<Eigen::Vector2d> initial_path_poses(path.poses.size());
156 path.poses.begin(), path.poses.end(),
157 initial_path_poses.begin(), toEigenVec);
158 applyFilterOverAxes(path.poses, initial_path_poses);
161 if (do_refinement_) {
162 for (
int i = 0; i < refinement_num_; i++) {
163 std::vector<Eigen::Vector2d> reined_initial_path_poses(path.poses.size());
165 path.poses.begin(), path.poses.end(),
166 reined_initial_path_poses.begin(), toEigenVec);
167 applyFilterOverAxes(path.poses, reined_initial_path_poses);
171 nav2_util::updateApproximatePathOrientations(path, reversing_segment);
177 #include "pluginlib/class_list_macros.hpp"
178 #include "nav2_ros_common/tf2_factories.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.