Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
velocity_smoother.cpp
1 // Copyright (c) 2022 Samsung Research
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <chrono>
16 #include <limits>
17 #include <memory>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "nav2_velocity_smoother/velocity_smoother.hpp"
23 
24 using namespace std::chrono_literals;
25 using std::placeholders::_1;
26 using rcl_interfaces::msg::ParameterType;
27 
28 namespace nav2_velocity_smoother
29 {
30 
31 VelocitySmoother::VelocitySmoother(const rclcpp::NodeOptions & options)
32 : LifecycleNode("velocity_smoother", "", options),
33  last_command_time_{0, 0, get_clock()->get_clock_type()}
34 {
35 }
36 
38 {
39  if (timer_) {
40  timer_->cancel();
41  timer_.reset();
42  }
43 }
44 
45 nav2::CallbackReturn
46 VelocitySmoother::on_configure(const rclcpp_lifecycle::State & state)
47 {
48  RCLCPP_INFO(get_logger(), "Configuring velocity smoother");
49  auto node = shared_from_this();
50 
51  // Smoothing metadata
52  smoothing_frequency_ = node->declare_or_get_parameter(
53  "smoothing_frequency", 20.0);
54  std::string feedback_type = node->declare_or_get_parameter(
55  "feedback", std::string("OPEN_LOOP"));
56  scale_velocities_ = node->declare_or_get_parameter("scale_velocities", false);
57 
58  // Kinematics
59  max_velocities_ = node->declare_or_get_parameter(
60  "max_velocity", std::vector<double>{0.50, 0.0, 2.5});
61  min_velocities_ = node->declare_or_get_parameter(
62  "min_velocity", std::vector<double>{-0.50, 0.0, -2.5});
63  max_accels_ = node->declare_or_get_parameter(
64  "max_accel", std::vector<double>{2.5, 0.0, 3.2});
65  max_decels_ = node->declare_or_get_parameter(
66  "max_decel", std::vector<double>{-2.5, 0.0, -3.2});
67 
68  // Get feature parameters
69  odom_topic_ = node->declare_or_get_parameter("odom_topic", std::string("odom"));
70  odom_duration_ = node->declare_or_get_parameter("odom_duration", 0.1);
71  odom_smoother_ = std::make_unique<nav2_util::OdomSmoother>(node, odom_duration_, odom_topic_);
72  deadband_velocities_ = node->declare_or_get_parameter(
73  "deadband_velocity", std::vector<double>{0.0, 0.0, 0.0});
74  double velocity_timeout_dbl = node->declare_or_get_parameter("velocity_timeout", 1.0);
75  velocity_timeout_ = rclcpp::Duration::from_seconds(velocity_timeout_dbl);
76 
77  // Check if parameters are properly set
78  size_t size = max_velocities_.size();
79  is_6dof_ = (size == 6);
80 
81  if ((size != 3 && size != 6) ||
82  min_velocities_.size() != size ||
83  max_accels_.size() != size ||
84  max_decels_.size() != size ||
85  deadband_velocities_.size() != size)
86  {
87  RCLCPP_ERROR(
88  get_logger(),
89  "Invalid setting of kinematic and/or deadband limits!"
90  " All limits must be size of 3 (x, y, theta) or 6 (x, y, z, r, p, y)");
91  on_cleanup(state);
92  return nav2::CallbackReturn::FAILURE;
93  }
94 
95  for (unsigned int i = 0; i != size; i++) {
96  if (max_decels_[i] > 0.0) {
97  RCLCPP_ERROR(
98  get_logger(),
99  "Positive values set of deceleration! These should be negative to slow down!");
100  on_cleanup(state);
101  return nav2::CallbackReturn::FAILURE;
102  }
103  if (max_accels_[i] < 0.0) {
104  RCLCPP_ERROR(
105  get_logger(),
106  "Negative values set of acceleration! These should be positive to speed up!");
107  on_cleanup(state);
108  return nav2::CallbackReturn::FAILURE;
109  }
110  if (min_velocities_[i] > 0.0) {
111  RCLCPP_ERROR(
112  get_logger(), "Positive values set of min_velocities! These should be negative!");
113  on_cleanup(state);
114  return nav2::CallbackReturn::FAILURE;
115  }
116  if (max_velocities_[i] < 0.0) {
117  RCLCPP_ERROR(
118  get_logger(), "Negative values set of max_velocities! These should be positive!");
119  on_cleanup(state);
120  return nav2::CallbackReturn::FAILURE;
121  }
122  if (min_velocities_[i] > max_velocities_[i]) {
123  RCLCPP_ERROR(get_logger(), "Min velocities are higher than max velocities!");
124  on_cleanup(state);
125  return nav2::CallbackReturn::FAILURE;
126  }
127  }
128 
129  // Get control type
130  if (feedback_type == "OPEN_LOOP") {
131  open_loop_ = true;
132  } else if (feedback_type == "CLOSED_LOOP") {
133  open_loop_ = false;
134  } else {
135  RCLCPP_ERROR(
136  get_logger(),
137  "Invalid feedback_type, options are OPEN_LOOP and CLOSED_LOOP.");
138  on_cleanup(state);
139  return nav2::CallbackReturn::FAILURE;
140  }
141 
142  // Setup inputs / outputs
143  smoothed_cmd_pub_ = std::make_unique<nav2_util::TwistPublisher>(node, "cmd_vel_smoothed");
144  cmd_sub_ = std::make_unique<nav2_util::TwistSubscriber>(
145  node,
146  "cmd_vel",
147  std::bind(&VelocitySmoother::inputCommandCallback, this, std::placeholders::_1),
148  std::bind(&VelocitySmoother::inputCommandStampedCallback, this, std::placeholders::_1));
149 
150  bool use_realtime_priority = node->declare_or_get_parameter("use_realtime_priority", false);
151  if (use_realtime_priority) {
152  try {
153  nav2::setSoftRealTimePriority();
154  } catch (const std::runtime_error & e) {
155  RCLCPP_ERROR(get_logger(), "%s", e.what());
156  on_cleanup(state);
157  return nav2::CallbackReturn::FAILURE;
158  }
159  }
160 
161  return nav2::CallbackReturn::SUCCESS;
162 }
163 
164 nav2::CallbackReturn
165 VelocitySmoother::on_activate(const rclcpp_lifecycle::State &)
166 {
167  RCLCPP_INFO(get_logger(), "Activating");
168  smoothed_cmd_pub_->on_activate();
169  double timer_duration_ms = 1000.0 / smoothing_frequency_;
170  timer_ = this->create_timer(
171  std::chrono::milliseconds(static_cast<int>(timer_duration_ms)),
172  std::bind(&VelocitySmoother::smootherTimer, this));
173 
174  // Add callback for dynamic parameters
175  auto node = shared_from_this();
176  post_set_params_handler_ = node->add_post_set_parameters_callback(
177  std::bind(
179  this, std::placeholders::_1));
180  on_set_params_handler_ = node->add_on_set_parameters_callback(
181  std::bind(
183  this, std::placeholders::_1));
184 
185  // create bond connection
186  createBond();
187  return nav2::CallbackReturn::SUCCESS;
188 }
189 
190 nav2::CallbackReturn
191 VelocitySmoother::on_deactivate(const rclcpp_lifecycle::State &)
192 {
193  RCLCPP_INFO(get_logger(), "Deactivating");
194  if (timer_) {
195  timer_->cancel();
196  timer_.reset();
197  }
198  smoothed_cmd_pub_->on_deactivate();
199 
200  auto node = shared_from_this();
201  if (post_set_params_handler_ && node) {
202  node->remove_post_set_parameters_callback(post_set_params_handler_.get());
203  }
204  post_set_params_handler_.reset();
205  if (on_set_params_handler_ && node) {
206  node->remove_on_set_parameters_callback(on_set_params_handler_.get());
207  }
208  on_set_params_handler_.reset();
209 
210  // destroy bond connection
211  destroyBond();
212  return nav2::CallbackReturn::SUCCESS;
213 }
214 
215 nav2::CallbackReturn
216 VelocitySmoother::on_cleanup(const rclcpp_lifecycle::State &)
217 {
218  RCLCPP_INFO(get_logger(), "Cleaning up");
219  smoothed_cmd_pub_.reset();
220  odom_smoother_.reset();
221  cmd_sub_.reset();
222  return nav2::CallbackReturn::SUCCESS;
223 }
224 
225 nav2::CallbackReturn
226 VelocitySmoother::on_shutdown(const rclcpp_lifecycle::State &)
227 {
228  RCLCPP_INFO(get_logger(), "Shutting down");
229  return nav2::CallbackReturn::SUCCESS;
230 }
231 
232 void VelocitySmoother::inputCommandStampedCallback(
233  const geometry_msgs::msg::TwistStamped::ConstSharedPtr & msg)
234 {
235  // If message contains NaN or Inf, ignore
236  if (!nav2_util::validateTwist(msg->twist)) {
237  RCLCPP_ERROR(get_logger(), "Velocity message contains NaNs or Infs! Ignoring as invalid!");
238  return;
239  }
240 
241  command_ = *msg;
242  if (msg->header.stamp.sec == 0 && msg->header.stamp.nanosec == 0) {
243  last_command_time_ = now();
244  } else {
245  last_command_time_ = msg->header.stamp;
246  }
247  received_first_command_ = true;
248 }
249 
251  const geometry_msgs::msg::Twist::ConstSharedPtr & msg)
252 {
253  auto twist_stamped = std::make_shared<geometry_msgs::msg::TwistStamped>();
254  twist_stamped->twist = *msg;
255  inputCommandStampedCallback(twist_stamped);
256 }
257 
259  const double v_curr, const double v_cmd, const double accel, const double decel)
260 {
261  // Exploiting vector scaling properties
262  double dv = v_cmd - v_curr;
263 
264  double v_component_max;
265  double v_component_min;
266 
267  // Accelerating if magnitude of v_cmd is above magnitude of v_curr
268  // and if v_cmd and v_curr have the same sign (i.e. speed is NOT passing through 0.0)
269  // Decelerating otherwise
270  if (abs(v_cmd) >= abs(v_curr) && v_curr * v_cmd >= 0.0) {
271  v_component_max = accel / smoothing_frequency_;
272  v_component_min = -accel / smoothing_frequency_;
273  } else {
274  v_component_max = -decel / smoothing_frequency_;
275  v_component_min = decel / smoothing_frequency_;
276  }
277 
278  if (dv > v_component_max) {
279  return v_component_max / dv;
280  }
281 
282  if (dv < v_component_min) {
283  return v_component_min / dv;
284  }
285 
286  return -1.0;
287 }
288 
290  const double v_curr, const double v_cmd,
291  const double accel, const double decel, const double eta)
292 {
293  double dv = v_cmd - v_curr;
294 
295  double v_component_max;
296  double v_component_min;
297 
298  // Accelerating if magnitude of v_cmd is above magnitude of v_curr
299  // and if v_cmd and v_curr have the same sign (i.e. speed is NOT passing through 0.0)
300  // Decelerating otherwise
301  if (abs(v_cmd) >= abs(v_curr) && v_curr * v_cmd >= 0.0) {
302  v_component_max = accel / smoothing_frequency_;
303  v_component_min = -accel / smoothing_frequency_;
304  } else {
305  v_component_max = -decel / smoothing_frequency_;
306  v_component_min = decel / smoothing_frequency_;
307  }
308 
309  return v_curr + std::clamp(eta * dv, v_component_min, v_component_max);
310 }
311 
313 {
314  std::lock_guard<std::mutex> lock(mutex_);
315  // Wait until the first command is received
316  if (!received_first_command_) {
317  return;
318  }
319 
320  auto const delta_time_since_last_command = now() - last_command_time_;
321 
322  auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>();
323  cmd_vel->header.frame_id = command_.header.frame_id;
324  // Smooth the timestamp of the smoothed message
325  // Do not keep the same timestamp of the last command; this causes jerky behavior
326  // See https://github.com/ros-navigation/navigation2/issues/5857
327  cmd_vel->header.stamp = command_.header.stamp + delta_time_since_last_command;
328 
329  // Check for velocity timeout. If nothing received, publish zeros to apply deceleration
330  if (delta_time_since_last_command > velocity_timeout_) {
331  if (last_cmd_.twist == geometry_msgs::msg::Twist() || stopped_) {
332  stopped_ = true;
333  return;
334  }
335  command_ = geometry_msgs::msg::TwistStamped();
336  command_.header.stamp = now();
337  }
338 
339  stopped_ = false;
340 
341  // Get current velocity based on feedback type
342  geometry_msgs::msg::TwistStamped current_;
343  if (open_loop_) {
344  current_ = last_cmd_;
345  } else {
346  current_ = odom_smoother_->getTwistStamped();
347  }
348 
349  // Apply absolute velocity restrictions to the command
350  if(!is_6dof_) {
351  command_.twist.linear.x = std::clamp(
352  command_.twist.linear.x, min_velocities_[0],
353  max_velocities_[0]);
354  command_.twist.linear.y = std::clamp(
355  command_.twist.linear.y, min_velocities_[1],
356  max_velocities_[1]);
357  command_.twist.angular.z = std::clamp(
358  command_.twist.angular.z, min_velocities_[2],
359  max_velocities_[2]);
360  } else {
361  command_.twist.linear.x = std::clamp(
362  command_.twist.linear.x, min_velocities_[0],
363  max_velocities_[0]);
364  command_.twist.linear.y = std::clamp(
365  command_.twist.linear.y, min_velocities_[1],
366  max_velocities_[1]);
367  command_.twist.linear.z = std::clamp(
368  command_.twist.linear.z, min_velocities_[2],
369  max_velocities_[2]);
370  command_.twist.angular.x = std::clamp(
371  command_.twist.angular.x, min_velocities_[3],
372  max_velocities_[3]);
373  command_.twist.angular.y = std::clamp(
374  command_.twist.angular.y, min_velocities_[4],
375  max_velocities_[4]);
376  command_.twist.angular.z = std::clamp(
377  command_.twist.angular.z, min_velocities_[5],
378  max_velocities_[5]);
379  }
380 
381  // Find if any component is not within the acceleration constraints. If so, store the most
382  // significant scale factor to apply to the vector <dvx, dvy, dvw>, eta, to reduce all axes
383  // proportionally to follow the same direction, within change of velocity bounds.
384  // In case eta reduces another axis out of its own limit, apply accel constraint to guarantee
385  // output is within limits, even if it deviates from requested command slightly.
386  double eta = 1.0;
387  if (scale_velocities_) {
388  double curr_eta = -1.0;
389  if (!is_6dof_) {
390  curr_eta = findEtaConstraint(
391  current_.twist.linear.x, command_.twist.linear.x, max_accels_[0], max_decels_[0]);
392  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
393  eta = curr_eta;
394  }
395 
396  curr_eta = findEtaConstraint(
397  current_.twist.linear.y, command_.twist.linear.y, max_accels_[1], max_decels_[1]);
398  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
399  eta = curr_eta;
400  }
401 
402  curr_eta = findEtaConstraint(
403  current_.twist.angular.z, command_.twist.angular.z, max_accels_[2], max_decels_[2]);
404  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
405  eta = curr_eta;
406  }
407  } else {
408  curr_eta = findEtaConstraint(
409  current_.twist.linear.x, command_.twist.linear.x, max_accels_[0], max_decels_[0]);
410  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
411  eta = curr_eta;
412  }
413 
414  curr_eta = findEtaConstraint(
415  current_.twist.linear.y, command_.twist.linear.y, max_accels_[1], max_decels_[1]);
416  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
417  eta = curr_eta;
418  }
419 
420  curr_eta = findEtaConstraint(
421  current_.twist.linear.z, command_.twist.linear.z, max_accels_[2], max_decels_[2]);
422  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
423  eta = curr_eta;
424  }
425 
426  curr_eta = findEtaConstraint(
427  current_.twist.angular.x, command_.twist.angular.x, max_accels_[3], max_decels_[3]);
428  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
429  eta = curr_eta;
430  }
431 
432  curr_eta = findEtaConstraint(
433  current_.twist.angular.y, command_.twist.angular.y, max_accels_[4], max_decels_[4]);
434  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
435  eta = curr_eta;
436  }
437 
438  curr_eta = findEtaConstraint(
439  current_.twist.angular.z, command_.twist.angular.z, max_accels_[5], max_decels_[5]);
440  if (curr_eta > 0.0 && std::fabs(1.0 - curr_eta) > std::fabs(1.0 - eta)) {
441  eta = curr_eta;
442  }
443  }
444  }
445 
446  if (!is_6dof_) {
447  cmd_vel->twist.linear.x = applyConstraints(
448  current_.twist.linear.x, command_.twist.linear.x, max_accels_[0], max_decels_[0], eta);
449  cmd_vel->twist.linear.y = applyConstraints(
450  current_.twist.linear.y, command_.twist.linear.y, max_accels_[1], max_decels_[1], eta);
451  cmd_vel->twist.angular.z = applyConstraints(
452  current_.twist.angular.z, command_.twist.angular.z, max_accels_[2], max_decels_[2], eta);
453  } else {
454  cmd_vel->twist.linear.x = applyConstraints(
455  current_.twist.linear.x, command_.twist.linear.x, max_accels_[0], max_decels_[0], eta);
456  cmd_vel->twist.linear.y = applyConstraints(
457  current_.twist.linear.y, command_.twist.linear.y, max_accels_[1], max_decels_[1], eta);
458  cmd_vel->twist.linear.z = applyConstraints(
459  current_.twist.linear.z, command_.twist.linear.z, max_accels_[2], max_decels_[2], eta);
460  cmd_vel->twist.angular.x = applyConstraints(
461  current_.twist.angular.x, command_.twist.angular.x, max_accels_[3], max_decels_[3], eta);
462  cmd_vel->twist.angular.y = applyConstraints(
463  current_.twist.angular.y, command_.twist.angular.y, max_accels_[4], max_decels_[4], eta);
464  cmd_vel->twist.angular.z = applyConstraints(
465  current_.twist.angular.z, command_.twist.angular.z, max_accels_[5], max_decels_[5], eta);
466  }
467 
468  last_cmd_ = *cmd_vel;
469 
470 
471  // Apply deadband restrictions & publish
472  if (!is_6dof_) {
473  cmd_vel->twist.linear.x =
474  fabs(cmd_vel->twist.linear.x) < deadband_velocities_[0] ? 0.0 : cmd_vel->twist.linear.x;
475  cmd_vel->twist.linear.y =
476  fabs(cmd_vel->twist.linear.y) < deadband_velocities_[1] ? 0.0 : cmd_vel->twist.linear.y;
477  cmd_vel->twist.linear.z = command_.twist.linear.z;
478  cmd_vel->twist.angular.x = command_.twist.angular.x;
479  cmd_vel->twist.angular.y = command_.twist.angular.y;
480  cmd_vel->twist.angular.z =
481  fabs(cmd_vel->twist.angular.z) < deadband_velocities_[2] ? 0.0 : cmd_vel->twist.angular.z;
482  } else {
483  cmd_vel->twist.linear.x =
484  fabs(cmd_vel->twist.linear.x) < deadband_velocities_[0] ? 0.0 : cmd_vel->twist.linear.x;
485  cmd_vel->twist.linear.y =
486  fabs(cmd_vel->twist.linear.y) < deadband_velocities_[1] ? 0.0 : cmd_vel->twist.linear.y;
487  cmd_vel->twist.linear.z =
488  fabs(cmd_vel->twist.linear.z) < deadband_velocities_[2] ? 0.0 : cmd_vel->twist.linear.z;
489  cmd_vel->twist.angular.x =
490  fabs(cmd_vel->twist.angular.x) < deadband_velocities_[3] ? 0.0 : cmd_vel->twist.angular.x;
491  cmd_vel->twist.angular.y =
492  fabs(cmd_vel->twist.angular.y) < deadband_velocities_[4] ? 0.0 : cmd_vel->twist.angular.y;
493  cmd_vel->twist.angular.z =
494  fabs(cmd_vel->twist.angular.z) < deadband_velocities_[5] ? 0.0 : cmd_vel->twist.angular.z;
495  }
496  smoothed_cmd_pub_->publish(std::move(cmd_vel));
497 }
498 
499 rcl_interfaces::msg::SetParametersResult VelocitySmoother::validateParameterUpdatesCallback(
500  const std::vector<rclcpp::Parameter> & parameters)
501 {
502  rcl_interfaces::msg::SetParametersResult result;
503  result.successful = true;
504  for (const auto & parameter : parameters) {
505  const auto & param_type = parameter.get_type();
506  const auto & param_name = parameter.get_name();
507  if (param_name.find('.') != std::string::npos) {
508  continue;
509  }
510 
511  if (param_type == ParameterType::PARAMETER_DOUBLE) {
512  if (parameter.as_double() <= 0.0 && param_name == "smoothing_frequency") {
513  RCLCPP_WARN(
514  get_logger(), "The value of smoothing_frequency is incorrectly set to %f, "
515  "it should be >0. Ignoring parameter update.",
516  parameter.as_double());
517  result.successful = false;
518  break;
519  } else if (parameter.as_double() < 0.0) {
520  RCLCPP_WARN(
521  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
522  "it should be >=0. Ignoring parameter update.",
523  param_name.c_str(), parameter.as_double());
524  result.successful = false;
525  break;
526  }
527  } else if (param_type == ParameterType::PARAMETER_DOUBLE_ARRAY) {
528  size_t size = is_6dof_ ? 6 : 3;
529  if (parameter.as_double_array().size() != size) {
530  RCLCPP_WARN(
531  get_logger(), "Invalid size of parameter %s. Must be size %ld",
532  param_name.c_str(), size);
533  result.successful = false;
534  break;
535  } else if (param_name == "max_velocity" || param_name == "max_accel") {
536  for (auto val : parameter.as_double_array()) {
537  if (val < 0.0) {
538  RCLCPP_WARN(
539  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
540  "it should be >=0. Ignoring parameter update.",
541  param_name.c_str(), val);
542  result.successful = false;
543  break;
544  }
545  }
546  } else if (param_name == "min_velocity" || param_name == "max_decel") {
547  for (auto val : parameter.as_double_array()) {
548  if (val > 0.0) {
549  RCLCPP_WARN(
550  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
551  "it should be <=0. Ignoring parameter update.",
552  param_name.c_str(), val);
553  result.successful = false;
554  break;
555  }
556  }
557  }
558  } else if (param_type == ParameterType::PARAMETER_STRING) {
559  if (param_name == "feedback") {
560  if (parameter.as_string() != "OPEN_LOOP" && parameter.as_string() != "CLOSED_LOOP") {
561  RCLCPP_WARN(
562  get_logger(),
563  "Invalid feedback_type, options are OPEN_LOOP and CLOSED_LOOP. "
564  "Ignoring parameter update.");
565  result.successful = false;
566  break;
567  }
568  }
569  }
570  }
571  return result;
572 }
573 
574 void VelocitySmoother::updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters)
575 {
576  std::lock_guard<std::mutex> lock(mutex_);
577 
578  for (const auto & parameter : parameters) {
579  const auto & param_type = parameter.get_type();
580  const auto & param_name = parameter.get_name();
581  if (param_name.find('.') != std::string::npos) {
582  continue;
583  }
584 
585  if (param_type == ParameterType::PARAMETER_DOUBLE) {
586  if (param_name == "smoothing_frequency") {
587  smoothing_frequency_ = parameter.as_double();
588  if (timer_) {
589  timer_->cancel();
590  timer_.reset();
591  }
592 
593  double timer_duration_ms = 1000.0 / smoothing_frequency_;
594  timer_ = this->create_timer(
595  std::chrono::milliseconds(static_cast<int>(timer_duration_ms)),
596  std::bind(&VelocitySmoother::smootherTimer, this));
597  } else if (param_name == "velocity_timeout") {
598  velocity_timeout_ = rclcpp::Duration::from_seconds(parameter.as_double());
599  } else if (param_name == "odom_duration") {
600  odom_duration_ = parameter.as_double();
601  odom_smoother_ =
602  std::make_unique<nav2_util::OdomSmoother>(
603  shared_from_this(), odom_duration_, odom_topic_);
604  }
605  } else if (param_type == ParameterType::PARAMETER_DOUBLE_ARRAY) {
606  if (param_name == "max_velocity") {
607  max_velocities_ = parameter.as_double_array();
608  } else if (param_name == "min_velocity") {
609  min_velocities_ = parameter.as_double_array();
610  } else if (param_name == "max_accel") {
611  max_accels_ = parameter.as_double_array();
612  } else if (param_name == "max_decel") {
613  max_decels_ = parameter.as_double_array();
614  } else if (param_name == "deadband_velocity") {
615  deadband_velocities_ = parameter.as_double_array();
616  }
617  } else if (param_type == ParameterType::PARAMETER_STRING) {
618  if (param_name == "feedback") {
619  if (parameter.as_string() == "OPEN_LOOP") {
620  open_loop_ = true;
621  odom_smoother_.reset();
622  } else if (parameter.as_string() == "CLOSED_LOOP") {
623  open_loop_ = false;
624  odom_smoother_ =
625  std::make_unique<nav2_util::OdomSmoother>(
626  shared_from_this(), odom_duration_, odom_topic_);
627  }
628  }
629  }
630  }
631 }
632 
633 } // namespace nav2_velocity_smoother
634 
635 #include "rclcpp_components/register_node_macro.hpp"
636 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_velocity_smoother::VelocitySmoother)
void destroyBond()
Destroy bond connection to lifecycle manager.
nav2::LifecycleNode::SharedPtr shared_from_this()
Get a shared pointer of this.
rclcpp::GenericTimer< CallbackT >::SharedPtr create_timer(std::chrono::duration< DurationRepT, DurationT > period, CallbackT callback, rclcpp::CallbackGroup::SharedPtr group=nullptr)
Create a sim-time-aware timer for Nav2 lifecycle nodes.
void createBond()
Create bond connection to lifecycle manager.
This class that smooths cmd_vel velocities for robot bases.
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in Shutdown state.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
~VelocitySmoother()
Destructor for nav2_velocity_smoother::VelocitySmoother.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Calls clean up states and resets member variables.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activates member variables.
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configures parameters and member variables.
double findEtaConstraint(const double v_curr, const double v_cmd, const double accel, const double decel)
Find the scale factor, eta, which scales axis into acceleration range.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivates member variables.
void smootherTimer()
Main worker timer function.
double applyConstraints(const double v_curr, const double v_cmd, const double accel, const double decel, const double eta)
Apply acceleration and scale factor constraints.
void inputCommandCallback(const geometry_msgs::msg::Twist::ConstSharedPtr &msg)
Callback for incoming velocity commands.