17 #include "nav2_smoother/savitzky_golay_smoother.hpp"
18 #include "nav2_core/smoother_exceptions.hpp"
20 namespace nav2_smoother
23 using namespace smoother_utils;
24 using namespace nav2_util::geometry_utils;
25 using namespace std::chrono;
26 using nav2::declare_parameter_if_not_declared;
28 void SavitzkyGolaySmoother::configure(
29 const nav2::LifecycleNode::WeakPtr & parent,
30 std::string name, std::shared_ptr<tf2_ros::Buffer>,
31 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber>,
32 std::shared_ptr<nav2_costmap_2d::FootprintSubscriber>)
34 auto node = parent.lock();
35 logger_ = node->get_logger();
37 declare_parameter_if_not_declared(
38 node, name +
".do_refinement", rclcpp::ParameterValue(
true));
39 declare_parameter_if_not_declared(
40 node, name +
".refinement_num", rclcpp::ParameterValue(2));
41 declare_parameter_if_not_declared(
42 node, name +
".enforce_path_inversion", rclcpp::ParameterValue(
true));
43 declare_parameter_if_not_declared(
44 node, name +
".window_size", rclcpp::ParameterValue(7));
45 declare_parameter_if_not_declared(
46 node, name +
".poly_order", rclcpp::ParameterValue(3));
47 node->get_parameter(name +
".do_refinement", do_refinement_);
48 node->get_parameter(name +
".refinement_num", refinement_num_);
49 node->get_parameter(name +
".enforce_path_inversion", enforce_path_inversion_);
50 node->get_parameter(name +
".window_size", window_size_);
51 node->get_parameter(name +
".poly_order", poly_order_);
52 if (window_size_ % 2 == 0 || window_size_ <= 2) {
54 "Savitzky-Golay Smoother requires an odd window size of 3 or greater");
56 half_window_size_ = (window_size_ - 1) / 2;
57 calculateCoefficients();
65 Eigen::VectorXd v = Eigen::VectorXd::LinSpaced(window_size_, -half_window_size_,
67 Eigen::MatrixXd x = Eigen::MatrixXd::Ones(window_size_, poly_order_ + 1);
68 for(
int i = 1; i <= poly_order_; i++) {
69 x.col(i) = (x.col(i - 1).array() * v.array()).matrix();
72 Eigen::MatrixXd coeff_mat = (x.transpose() * x).inverse() * x.transpose();
75 sg_coeffs_ = coeff_mat.row(0).transpose();
79 nav_msgs::msg::Path & path,
80 const rclcpp::Duration & max_time)
82 steady_clock::time_point start = steady_clock::now();
83 double time_remaining = max_time.seconds();
85 bool success =
true, reversing_segment;
86 nav_msgs::msg::Path curr_path_segment;
87 curr_path_segment.header = path.header;
89 std::vector<PathSegment> path_segments{
90 PathSegment{0u,
static_cast<unsigned int>(path.poses.size() - 1)}};
91 if (enforce_path_inversion_) {
92 path_segments = findDirectionalPathSegments(path);
96 unsigned int minimum_points = window_size_ + 2;
97 for (
unsigned int i = 0; i != path_segments.size(); i++) {
98 if (path_segments[i].end - path_segments[i].start > minimum_points) {
100 curr_path_segment.poses.clear();
102 path.poses.begin() + path_segments[i].start,
103 path.poses.begin() + path_segments[i].end + 1,
104 std::back_inserter(curr_path_segment.poses));
107 steady_clock::time_point now = steady_clock::now();
108 time_remaining = max_time.seconds() - duration_cast<duration<double>>(now - start).count();
110 if (time_remaining <= 0.0) {
113 "Smoothing time exceeded allowed duration of %0.2f.", max_time.seconds());
118 success = success && smoothImpl(curr_path_segment, reversing_segment);
122 curr_path_segment.poses.begin(),
123 curr_path_segment.poses.end(),
124 path.poses.begin() + path_segments[i].start);
132 nav_msgs::msg::Path & path,
133 bool & reversing_segment)
135 const unsigned int & path_size = path.poses.size();
138 auto toEigenVec = [](
const geometry_msgs::msg::PoseStamped & pose) -> Eigen::Vector2d {
139 return {pose.pose.position.x, pose.pose.position.y};
142 auto applyFilterOverAxes =
143 [&](std::vector<geometry_msgs::msg::PoseStamped> & plan_pts,
144 const std::vector<Eigen::Vector2d> & init_plan_pts) ->
void
147 for (
unsigned int idx = 1; idx != path_size - 1; idx++) {
148 Eigen::Vector2d accum(0.0, 0.0);
150 for(
int j = -half_window_size_; j <= half_window_size_; j++) {
151 int path_idx = std::clamp<int>(idx + j, 0, path_size - 1);
152 accum += sg_coeffs_(j + half_window_size_) * init_plan_pts[path_idx];
154 plan_pts[idx].pose.position.x = accum.x();
155 plan_pts[idx].pose.position.y = accum.y();
159 std::vector<Eigen::Vector2d> initial_path_poses(path.poses.size());
160 std::transform(path.poses.begin(), path.poses.end(),
161 initial_path_poses.begin(), toEigenVec);
162 applyFilterOverAxes(path.poses, initial_path_poses);
165 if (do_refinement_) {
166 for (
int i = 0; i < refinement_num_; i++) {
167 std::vector<Eigen::Vector2d> reined_initial_path_poses(path.poses.size());
168 std::transform(path.poses.begin(), path.poses.end(),
169 reined_initial_path_poses.begin(), toEigenVec);
170 applyFilterOverAxes(path.poses, reined_initial_path_poses);
174 updateApproximatePathOrientations(path, reversing_segment);
180 #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.