Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
amcl_node.cpp
1 /*
2  * Copyright (c) 2008, Willow Garage, Inc.
3  * All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  */
20 
21 /* Author: Brian Gerkey */
22 
23 #include "nav2_amcl/amcl_node.hpp"
24 
25 #include <algorithm>
26 #include <memory>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #include "message_filters/subscriber.h"
32 #include "nav2_amcl/angleutils.hpp"
33 #include "nav2_util/geometry_utils.hpp"
34 #include "nav2_amcl/pf/pf.hpp"
35 #include "nav2_util/string_utils.hpp"
36 #include "nav2_amcl/sensors/laser/laser.hpp"
37 #include "tf2/convert.h"
38 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
39 #include "tf2/LinearMath/Transform.h"
40 #include "tf2_ros/buffer.h"
41 #include "tf2_ros/message_filter.h"
42 #include "tf2_ros/transform_broadcaster.h"
43 #include "tf2_ros/transform_listener.h"
44 #include "tf2_ros/create_timer_ros.h"
45 
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wpedantic"
48 #include "tf2/utils.h"
49 #pragma GCC diagnostic pop
50 
51 #include "nav2_amcl/portable_utils.hpp"
52 #include "nav2_util/validate_messages.hpp"
53 
54 using namespace std::placeholders;
55 using rcl_interfaces::msg::ParameterType;
56 using namespace std::chrono_literals;
57 
58 namespace nav2_amcl
59 {
60 using nav2_util::geometry_utils::orientationAroundZAxis;
61 
62 AmclNode::AmclNode(const rclcpp::NodeOptions & options)
63 : nav2_util::LifecycleNode("amcl", "", options)
64 {
65  RCLCPP_INFO(get_logger(), "Creating");
66 
67  init_pose_[0] = 0.0;
68  init_pose_[1] = 0.0;
69  init_pose_[2] = 0.0;
70  init_cov_[0] = 0.0;
71  init_cov_[1] = 0.0;
72  init_cov_[2] = 0.0;
73 
74  add_parameter(
75  "alpha1", rclcpp::ParameterValue(0.2),
76  "This is the alpha1 parameter", "These are additional constraints for alpha1");
77 
78  add_parameter(
79  "alpha2", rclcpp::ParameterValue(0.2),
80  "This is the alpha2 parameter", "These are additional constraints for alpha2");
81 
82  add_parameter(
83  "alpha3", rclcpp::ParameterValue(0.2),
84  "This is the alpha3 parameter", "These are additional constraints for alpha3");
85 
86  add_parameter(
87  "alpha4", rclcpp::ParameterValue(0.2),
88  "This is the alpha4 parameter", "These are additional constraints for alpha4");
89 
90  add_parameter(
91  "alpha5", rclcpp::ParameterValue(0.2),
92  "This is the alpha5 parameter", "These are additional constraints for alpha5");
93 
94  add_parameter(
95  "base_frame_id", rclcpp::ParameterValue(std::string("base_footprint")),
96  "Which frame to use for the robot base");
97 
98  add_parameter("beam_skip_distance", rclcpp::ParameterValue(0.5));
99  add_parameter("beam_skip_error_threshold", rclcpp::ParameterValue(0.9));
100  add_parameter("beam_skip_threshold", rclcpp::ParameterValue(0.3));
101  add_parameter("do_beamskip", rclcpp::ParameterValue(false));
102 
103  add_parameter(
104  "global_frame_id", rclcpp::ParameterValue(std::string("map")),
105  "The name of the coordinate frame published by the localization system");
106 
107  add_parameter(
108  "lambda_short", rclcpp::ParameterValue(0.1),
109  "Exponential decay parameter for z_short part of model");
110 
111  add_parameter(
112  "laser_likelihood_max_dist", rclcpp::ParameterValue(2.0),
113  "Maximum distance to do obstacle inflation on map, for use in likelihood_field model");
114 
115  add_parameter(
116  "laser_max_range", rclcpp::ParameterValue(100.0),
117  "Maximum scan range to be considered",
118  "-1.0 will cause the laser's reported maximum range to be used");
119 
120  add_parameter(
121  "laser_min_range", rclcpp::ParameterValue(-1.0),
122  "Minimum scan range to be considered",
123  "-1.0 will cause the laser's reported minimum range to be used");
124 
125  add_parameter(
126  "laser_model_type", rclcpp::ParameterValue(std::string("likelihood_field")),
127  "Which model to use, either beam, likelihood_field, or likelihood_field_prob",
128  "Same as likelihood_field but incorporates the beamskip feature, if enabled");
129 
130  add_parameter(
131  "set_initial_pose", rclcpp::ParameterValue(false),
132  "Causes AMCL to set initial pose from the initial_pose* parameters instead of "
133  "waiting for the initial_pose message");
134 
135  add_parameter(
136  "initial_pose.x", rclcpp::ParameterValue(0.0),
137  "X coordinate of the initial robot pose in the map frame");
138 
139  add_parameter(
140  "initial_pose.y", rclcpp::ParameterValue(0.0),
141  "Y coordinate of the initial robot pose in the map frame");
142 
143  add_parameter(
144  "initial_pose.z", rclcpp::ParameterValue(0.0),
145  "Z coordinate of the initial robot pose in the map frame");
146 
147  add_parameter(
148  "initial_pose.yaw", rclcpp::ParameterValue(0.0),
149  "Yaw of the initial robot pose in the map frame");
150 
151  add_parameter(
152  "max_beams", rclcpp::ParameterValue(60),
153  "How many evenly-spaced beams in each scan to be used when updating the filter");
154 
155  add_parameter(
156  "max_particles", rclcpp::ParameterValue(2000),
157  "Maximum allowed number of particles");
158 
159  add_parameter(
160  "min_particles", rclcpp::ParameterValue(500),
161  "Minimum allowed number of particles");
162 
163  add_parameter(
164  "odom_frame_id", rclcpp::ParameterValue(std::string("odom")),
165  "Which frame to use for odometry");
166 
167  add_parameter("pf_err", rclcpp::ParameterValue(0.05));
168  add_parameter("pf_z", rclcpp::ParameterValue(0.99));
169 
170  add_parameter(
171  "recovery_alpha_fast", rclcpp::ParameterValue(0.0),
172  "Exponential decay rate for the fast average weight filter, used in deciding when to recover "
173  "by adding random poses",
174  "A good value might be 0.1");
175 
176  add_parameter(
177  "recovery_alpha_slow", rclcpp::ParameterValue(0.0),
178  "Exponential decay rate for the slow average weight filter, used in deciding when to recover "
179  "by adding random poses",
180  "A good value might be 0.001");
181 
182  add_parameter(
183  "resample_interval", rclcpp::ParameterValue(1),
184  "Number of filter updates required before resampling");
185 
186  add_parameter("robot_model_type", rclcpp::ParameterValue("nav2_amcl::DifferentialMotionModel"));
187 
188  add_parameter(
189  "save_pose_rate", rclcpp::ParameterValue(0.5),
190  "Maximum rate (Hz) at which to store the last estimated pose and covariance to the parameter "
191  "server, in the variables ~initial_pose_* and ~initial_cov_*. This saved pose will be used "
192  "on subsequent runs to initialize the filter",
193  "-1.0 to disable");
194 
195  add_parameter("sigma_hit", rclcpp::ParameterValue(0.2));
196 
197  add_parameter(
198  "tf_broadcast", rclcpp::ParameterValue(true),
199  "Set this to false to prevent amcl from publishing the transform between the global frame and "
200  "the odometry frame");
201 
202  add_parameter(
203  "transform_tolerance", rclcpp::ParameterValue(1.0),
204  "Time with which to post-date the transform that is published, to indicate that this transform "
205  "is valid into the future");
206 
207  add_parameter(
208  "update_min_a", rclcpp::ParameterValue(0.2),
209  "Rotational movement required before performing a filter update");
210 
211  add_parameter(
212  "update_min_d", rclcpp::ParameterValue(0.25),
213  "Translational movement required before performing a filter update");
214 
215  add_parameter("z_hit", rclcpp::ParameterValue(0.5));
216  add_parameter("z_max", rclcpp::ParameterValue(0.05));
217  add_parameter("z_rand", rclcpp::ParameterValue(0.5));
218  add_parameter("z_short", rclcpp::ParameterValue(0.05));
219 
220  add_parameter(
221  "always_reset_initial_pose", rclcpp::ParameterValue(false),
222  "Requires that AMCL is provided an initial pose either via topic or initial_pose* parameter "
223  "(with parameter set_initial_pose: true) when reset. Otherwise, by default AMCL will use the"
224  "last known pose to initialize");
225 
226  add_parameter(
227  "scan_topic", rclcpp::ParameterValue("scan"),
228  "Topic to subscribe to in order to receive the laser scan for localization");
229 
230  add_parameter(
231  "map_topic", rclcpp::ParameterValue("map"),
232  "Topic to subscribe to in order to receive the map to localize on");
233 
234  add_parameter(
235  "first_map_only", rclcpp::ParameterValue(false),
236  "Set this to true, when you want to load a new map published from the map_server");
237 
238  add_parameter(
239  "freespace_downsampling", rclcpp::ParameterValue(
240  false),
241  "Downsample the free space used by the Pose Generator. Use it with large maps to save memory");
242 }
243 
244 AmclNode::~AmclNode()
245 {
246 }
247 
248 nav2_util::CallbackReturn
249 AmclNode::on_configure(const rclcpp_lifecycle::State & /*state*/)
250 {
251  RCLCPP_INFO(get_logger(), "Configuring");
252  callback_group_ = create_callback_group(
253  rclcpp::CallbackGroupType::MutuallyExclusive, false);
254  initParameters();
255  initTransforms();
256  initParticleFilter();
257  initLaserScan();
258  initMessageFilters();
259  initPubSub();
260  initServices();
261  initOdometry();
262  executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
263  executor_->add_callback_group(callback_group_, get_node_base_interface());
264  executor_thread_ = std::make_unique<nav2_util::NodeThread>(executor_);
265  return nav2_util::CallbackReturn::SUCCESS;
266 }
267 
268 nav2_util::CallbackReturn
269 AmclNode::on_activate(const rclcpp_lifecycle::State & /*state*/)
270 {
271  RCLCPP_INFO(get_logger(), "Activating");
272 
273  // Lifecycle publishers must be explicitly activated
274  pose_pub_->on_activate();
275  particle_cloud_pub_->on_activate();
276 
277  first_pose_sent_ = false;
278 
279  // Keep track of whether we're in the active state. We won't
280  // process incoming callbacks until we are
281  active_ = true;
282 
283  if (set_initial_pose_) {
284  auto msg = std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>();
285 
286  msg->header.stamp = now();
287  msg->header.frame_id = global_frame_id_;
288  msg->pose.pose.position.x = initial_pose_x_;
289  msg->pose.pose.position.y = initial_pose_y_;
290  msg->pose.pose.position.z = initial_pose_z_;
291  msg->pose.pose.orientation = orientationAroundZAxis(initial_pose_yaw_);
292 
293  initialPoseReceived(msg);
294  } else if (init_pose_received_on_inactive) {
295  handleInitialPose(last_published_pose_);
296  }
297 
298  auto node = shared_from_this();
299  // Add callback for dynamic parameters
300  dyn_params_handler_ = node->add_on_set_parameters_callback(
301  std::bind(
302  &AmclNode::dynamicParametersCallback,
303  this, std::placeholders::_1));
304 
305  // create bond connection
306  createBond();
307 
308  return nav2_util::CallbackReturn::SUCCESS;
309 }
310 
311 nav2_util::CallbackReturn
312 AmclNode::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
313 {
314  RCLCPP_INFO(get_logger(), "Deactivating");
315 
316  active_ = false;
317 
318  // Lifecycle publishers must be explicitly deactivated
319  pose_pub_->on_deactivate();
320  particle_cloud_pub_->on_deactivate();
321 
322  // shutdown and reset dynamic parameter handler
323  remove_on_set_parameters_callback(dyn_params_handler_.get());
324  dyn_params_handler_.reset();
325 
326  // destroy bond connection
327  destroyBond();
328 
329  return nav2_util::CallbackReturn::SUCCESS;
330 }
331 
332 nav2_util::CallbackReturn
333 AmclNode::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
334 {
335  RCLCPP_INFO(get_logger(), "Cleaning up");
336 
337  executor_thread_.reset();
338 
339  // Get rid of the inputs first (services and message filter input), so we
340  // don't continue to process incoming messages
341  global_loc_srv_.reset();
342  initial_guess_srv_.reset();
343  nomotion_update_srv_.reset();
344  initial_pose_sub_.reset();
345  laser_scan_connection_.disconnect();
346  tf_listener_.reset(); // listener may access lase_scan_filter_, so it should be reset earlier
347  laser_scan_filter_.reset();
348  laser_scan_sub_.reset();
349 
350  // Map
351  map_sub_.reset(); // map_sub_ may access map_, so it should be reset earlier
352  if (map_ != NULL) {
353  map_free(map_);
354  map_ = nullptr;
355  }
356  first_map_received_ = false;
357  free_space_indices.resize(0);
358 
359  // Transforms
360  tf_broadcaster_.reset();
361  tf_buffer_.reset();
362 
363  // PubSub
364  pose_pub_.reset();
365  particle_cloud_pub_.reset();
366 
367  // Odometry
368  motion_model_.reset();
369 
370  // Particle Filter
371  pf_free(pf_);
372  pf_ = nullptr;
373 
374  // Laser Scan
375  lasers_.clear();
376  lasers_update_.clear();
377  frame_to_laser_.clear();
378  force_update_ = true;
379 
380  if (set_initial_pose_) {
381  set_parameter(
382  rclcpp::Parameter(
383  "initial_pose.x",
384  rclcpp::ParameterValue(last_published_pose_.pose.pose.position.x)));
385  set_parameter(
386  rclcpp::Parameter(
387  "initial_pose.y",
388  rclcpp::ParameterValue(last_published_pose_.pose.pose.position.y)));
389  set_parameter(
390  rclcpp::Parameter(
391  "initial_pose.z",
392  rclcpp::ParameterValue(last_published_pose_.pose.pose.position.z)));
393  set_parameter(
394  rclcpp::Parameter(
395  "initial_pose.yaw",
396  rclcpp::ParameterValue(tf2::getYaw(last_published_pose_.pose.pose.orientation))));
397  }
398 
399  return nav2_util::CallbackReturn::SUCCESS;
400 }
401 
402 nav2_util::CallbackReturn
403 AmclNode::on_shutdown(const rclcpp_lifecycle::State & /*state*/)
404 {
405  RCLCPP_INFO(get_logger(), "Shutting down");
406  return nav2_util::CallbackReturn::SUCCESS;
407 }
408 
409 bool
410 AmclNode::checkElapsedTime(std::chrono::seconds check_interval, rclcpp::Time last_time)
411 {
412  rclcpp::Duration elapsed_time = now() - last_time;
413  if (elapsed_time.nanoseconds() * 1e-9 > check_interval.count()) {
414  return true;
415  }
416  return false;
417 }
418 
419 #if NEW_UNIFORM_SAMPLING
420 std::vector<AmclNode::Point2D> AmclNode::free_space_indices;
421 #endif
422 
423 bool
424 AmclNode::getOdomPose(
425  geometry_msgs::msg::PoseStamped & odom_pose,
426  double & x, double & y, double & yaw,
427  const rclcpp::Time & sensor_timestamp, const std::string & frame_id)
428 {
429  // Get the robot's pose
430  geometry_msgs::msg::PoseStamped ident;
431  ident.header.frame_id = nav2_util::strip_leading_slash(frame_id);
432  ident.header.stamp = sensor_timestamp;
433  tf2::toMsg(tf2::Transform::getIdentity(), ident.pose);
434 
435  try {
436  tf_buffer_->transform(ident, odom_pose, odom_frame_id_);
437  } catch (tf2::TransformException & e) {
438  ++scan_error_count_;
439  if (scan_error_count_ % 20 == 0) {
440  RCLCPP_ERROR(
441  get_logger(), "(%d) consecutive laser scan transforms failed: (%s)", scan_error_count_,
442  e.what());
443  }
444  return false;
445  }
446 
447  scan_error_count_ = 0; // reset since we got a good transform
448  x = odom_pose.pose.position.x;
449  y = odom_pose.pose.position.y;
450  yaw = tf2::getYaw(odom_pose.pose.orientation);
451 
452  return true;
453 }
454 
456 AmclNode::uniformPoseGenerator(void * arg)
457 {
458  map_t * map = reinterpret_cast<map_t *>(arg);
459 
460 #if NEW_UNIFORM_SAMPLING
461  unsigned int rand_index = drand48() * free_space_indices.size();
462  AmclNode::Point2D free_point = free_space_indices[rand_index];
463  pf_vector_t p;
464  p.v[0] = MAP_WXGX(map, free_point.x);
465  p.v[1] = MAP_WYGY(map, free_point.y);
466  p.v[2] = drand48() * 2 * M_PI - M_PI;
467 #else
468  double min_x, max_x, min_y, max_y;
469 
470  min_x = (map->size_x * map->scale) / 2.0 - map->origin_x;
471  max_x = (map->size_x * map->scale) / 2.0 + map->origin_x;
472  min_y = (map->size_y * map->scale) / 2.0 - map->origin_y;
473  max_y = (map->size_y * map->scale) / 2.0 + map->origin_y;
474 
475  pf_vector_t p;
476 
477  RCLCPP_DEBUG(get_logger(), "Generating new uniform sample");
478  for (;; ) {
479  p.v[0] = min_x + drand48() * (max_x - min_x);
480  p.v[1] = min_y + drand48() * (max_y - min_y);
481  p.v[2] = drand48() * 2 * M_PI - M_PI;
482  // Check that it's a free cell
483  int i, j;
484  i = MAP_GXWX(map, p.v[0]);
485  j = MAP_GYWY(map, p.v[1]);
486  if (MAP_VALID(map, i, j) && (map->cells[MAP_INDEX(map, i, j)].occ_state == -1)) {
487  break;
488  }
489  }
490 #endif
491  return p;
492 }
493 
494 void
495 AmclNode::globalLocalizationCallback(
496  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
497  const std::shared_ptr<std_srvs::srv::Empty::Request>/*req*/,
498  std::shared_ptr<std_srvs::srv::Empty::Response>/*res*/)
499 {
500  std::lock_guard<std::recursive_mutex> cfl(mutex_);
501 
502  RCLCPP_INFO(get_logger(), "Initializing with uniform distribution");
503 
504  pf_init_model(
505  pf_, (pf_init_model_fn_t)AmclNode::uniformPoseGenerator,
506  reinterpret_cast<void *>(map_));
507  RCLCPP_INFO(get_logger(), "Global initialisation done!");
508  initial_pose_is_known_ = true;
509  pf_init_ = false;
510 }
511 
512 void
513 AmclNode::initialPoseReceivedSrv(
514  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
515  const std::shared_ptr<nav2_msgs::srv::SetInitialPose::Request> req,
516  std::shared_ptr<nav2_msgs::srv::SetInitialPose::Response>/*res*/)
517 {
518  initialPoseReceived(std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>(req->pose));
519 }
520 
521 // force nomotion updates (amcl updating without requiring motion)
522 void
523 AmclNode::nomotionUpdateCallback(
524  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
525  const std::shared_ptr<std_srvs::srv::Empty::Request>/*req*/,
526  std::shared_ptr<std_srvs::srv::Empty::Response>/*res*/)
527 {
528  RCLCPP_INFO(get_logger(), "Requesting no-motion update");
529  force_update_ = true;
530 }
531 
532 void
533 AmclNode::initialPoseReceived(geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg)
534 {
535  std::lock_guard<std::recursive_mutex> cfl(mutex_);
536 
537  RCLCPP_INFO(get_logger(), "initialPoseReceived");
538 
539  if (!nav2_util::validateMsg(*msg)) {
540  RCLCPP_ERROR(get_logger(), "Received initialpose message is malformed. Rejecting.");
541  return;
542  }
543  if (nav2_util::strip_leading_slash(msg->header.frame_id) != global_frame_id_) {
544  RCLCPP_WARN(
545  get_logger(),
546  "Ignoring initial pose in frame \"%s\"; initial poses must be in the global frame, \"%s\"",
547  nav2_util::strip_leading_slash(msg->header.frame_id).c_str(),
548  global_frame_id_.c_str());
549  return;
550  }
551  if (first_map_received_ && (abs(msg->pose.pose.position.x) > map_->size_x ||
552  abs(msg->pose.pose.position.y) > map_->size_y))
553  {
554  RCLCPP_ERROR(
555  get_logger(), "Received initialpose from message is out of the size of map. Rejecting.");
556  return;
557  }
558 
559  // Overriding last published pose to initial pose
560  last_published_pose_ = *msg;
561 
562  if (!active_) {
563  init_pose_received_on_inactive = true;
564  RCLCPP_WARN(
565  get_logger(), "Received initial pose request, "
566  "but AMCL is not yet in the active state");
567  return;
568  }
569  handleInitialPose(*msg);
570 }
571 
572 void
573 AmclNode::handleInitialPose(geometry_msgs::msg::PoseWithCovarianceStamped & msg)
574 {
575  std::lock_guard<std::recursive_mutex> cfl(mutex_);
576  // In case the client sent us a pose estimate in the past, integrate the
577  // intervening odometric change.
578  geometry_msgs::msg::TransformStamped tx_odom;
579  try {
580  rclcpp::Time rclcpp_time = now();
581  tf2::TimePoint tf2_time(std::chrono::nanoseconds(rclcpp_time.nanoseconds()));
582 
583  // Check if the transform is available
584  tx_odom = tf_buffer_->lookupTransform(
585  base_frame_id_, tf2_ros::fromMsg(msg.header.stamp),
586  base_frame_id_, tf2_time, odom_frame_id_);
587  } catch (tf2::TransformException & e) {
588  // If we've never sent a transform, then this is normal, because the
589  // global_frame_id_ frame doesn't exist. We only care about in-time
590  // transformation for on-the-move pose-setting, so ignoring this
591  // startup condition doesn't really cost us anything.
592  if (sent_first_transform_) {
593  RCLCPP_WARN(get_logger(), "Failed to transform initial pose in time (%s)", e.what());
594  }
595  tf2::impl::Converter<false, true>::convert(tf2::Transform::getIdentity(), tx_odom.transform);
596  }
597 
598  tf2::Transform tx_odom_tf2;
599  tf2::impl::Converter<true, false>::convert(tx_odom.transform, tx_odom_tf2);
600 
601  tf2::Transform pose_old;
602  tf2::impl::Converter<true, false>::convert(msg.pose.pose, pose_old);
603 
604  tf2::Transform pose_new = pose_old * tx_odom_tf2;
605 
606  // Transform into the global frame
607 
608  RCLCPP_INFO(
609  get_logger(), "Setting pose (%.6f): %.3f %.3f %.3f",
610  now().nanoseconds() * 1e-9,
611  pose_new.getOrigin().x(),
612  pose_new.getOrigin().y(),
613  tf2::getYaw(pose_new.getRotation()));
614 
615  // Re-initialize the filter
616  pf_vector_t pf_init_pose_mean = pf_vector_zero();
617  pf_init_pose_mean.v[0] = pose_new.getOrigin().x();
618  pf_init_pose_mean.v[1] = pose_new.getOrigin().y();
619  pf_init_pose_mean.v[2] = tf2::getYaw(pose_new.getRotation());
620 
621  pf_matrix_t pf_init_pose_cov = pf_matrix_zero();
622  // Copy in the covariance, converting from 6-D to 3-D
623  for (int i = 0; i < 2; i++) {
624  for (int j = 0; j < 2; j++) {
625  pf_init_pose_cov.m[i][j] = msg.pose.covariance[6 * i + j];
626  }
627  }
628 
629  pf_init_pose_cov.m[2][2] = msg.pose.covariance[6 * 5 + 5];
630 
631  pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
632  pf_init_ = false;
633  init_pose_received_on_inactive = false;
634  initial_pose_is_known_ = true;
635 }
636 
637 void
638 AmclNode::laserReceived(sensor_msgs::msg::LaserScan::ConstSharedPtr laser_scan)
639 {
640  std::lock_guard<std::recursive_mutex> cfl(mutex_);
641 
642  // Since the sensor data is continually being published by the simulator or robot,
643  // we don't want our callbacks to fire until we're in the active state
644  if (!active_) {return;}
645  if (!first_map_received_) {
646  if (checkElapsedTime(2s, last_time_printed_msg_)) {
647  RCLCPP_WARN(get_logger(), "Waiting for map....");
648  last_time_printed_msg_ = now();
649  }
650  return;
651  }
652 
653  std::string laser_scan_frame_id = nav2_util::strip_leading_slash(laser_scan->header.frame_id);
654  last_laser_received_ts_ = now();
655  int laser_index = -1;
656  geometry_msgs::msg::PoseStamped laser_pose;
657 
658  // Do we have the base->base_laser Tx yet?
659  if (frame_to_laser_.find(laser_scan_frame_id) == frame_to_laser_.end()) {
660  if (!addNewScanner(laser_index, laser_scan, laser_scan_frame_id, laser_pose)) {
661  return; // could not find transform
662  }
663  } else {
664  // we have the laser pose, retrieve laser index
665  laser_index = frame_to_laser_[laser_scan->header.frame_id];
666  }
667 
668  // Where was the robot when this scan was taken?
669  pf_vector_t pose;
670  if (!getOdomPose(
671  latest_odom_pose_, pose.v[0], pose.v[1], pose.v[2],
672  laser_scan->header.stamp, base_frame_id_))
673  {
674  RCLCPP_ERROR(get_logger(), "Couldn't determine robot's pose associated with laser scan");
675  return;
676  }
677 
678  pf_vector_t delta = pf_vector_zero();
679  bool force_publication = false;
680  if (!pf_init_) {
681  // Pose at last filter update
682  pf_odom_pose_ = pose;
683  pf_init_ = true;
684 
685  for (unsigned int i = 0; i < lasers_update_.size(); i++) {
686  lasers_update_[i] = true;
687  }
688 
689  force_publication = true;
690  resample_count_ = 0;
691  } else {
692  // Set the laser update flags
693  if (shouldUpdateFilter(pose, delta)) {
694  for (unsigned int i = 0; i < lasers_update_.size(); i++) {
695  lasers_update_[i] = true;
696  }
697  }
698  if (lasers_update_[laser_index]) {
699  motion_model_->odometryUpdate(pf_, pose, delta);
700  }
701  force_update_ = false;
702  }
703 
704  bool resampled = false;
705 
706  // If the robot has moved, update the filter
707  if (lasers_update_[laser_index]) {
708  updateFilter(laser_index, laser_scan, pose);
709 
710  // Resample the particles
711  if (!(++resample_count_ % resample_interval_)) {
712  pf_update_resample(pf_, reinterpret_cast<void *>(map_));
713  resampled = true;
714  }
715 
716  pf_sample_set_t * set = pf_->sets + pf_->current_set;
717  RCLCPP_DEBUG(get_logger(), "Num samples: %d\n", set->sample_count);
718 
719  if (!force_update_) {
720  publishParticleCloud(set);
721  }
722  }
723  if (resampled || force_publication || !first_pose_sent_) {
724  amcl_hyp_t max_weight_hyps;
725  std::vector<amcl_hyp_t> hyps;
726  int max_weight_hyp = -1;
727  if (getMaxWeightHyp(hyps, max_weight_hyps, max_weight_hyp)) {
728  publishAmclPose(laser_scan, hyps, max_weight_hyp);
729  calculateMaptoOdomTransform(laser_scan, hyps, max_weight_hyp);
730 
731  if (tf_broadcast_ == true) {
732  // We want to send a transform that is good up until a
733  // tolerance time so that odom can be used
734  auto stamp = tf2_ros::fromMsg(laser_scan->header.stamp);
735  tf2::TimePoint transform_expiration = stamp + transform_tolerance_;
736  sendMapToOdomTransform(transform_expiration);
737  sent_first_transform_ = true;
738  }
739  } else {
740  RCLCPP_ERROR(get_logger(), "No pose!");
741  }
742  } else if (latest_tf_valid_) {
743  if (tf_broadcast_ == true) {
744  // Nothing changed, so we'll just republish the last transform, to keep
745  // everybody happy.
746  tf2::TimePoint transform_expiration = tf2_ros::fromMsg(laser_scan->header.stamp) +
747  transform_tolerance_;
748  sendMapToOdomTransform(transform_expiration);
749  }
750  }
751 }
752 
753 bool AmclNode::addNewScanner(
754  int & laser_index,
755  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
756  const std::string & laser_scan_frame_id,
757  geometry_msgs::msg::PoseStamped & laser_pose)
758 {
759  lasers_.push_back(createLaserObject());
760  lasers_update_.push_back(true);
761  laser_index = frame_to_laser_.size();
762 
763  geometry_msgs::msg::PoseStamped ident;
764  ident.header.frame_id = laser_scan_frame_id;
765  ident.header.stamp = rclcpp::Time();
766  tf2::toMsg(tf2::Transform::getIdentity(), ident.pose);
767  try {
768  tf_buffer_->transform(ident, laser_pose, base_frame_id_, transform_tolerance_);
769  } catch (tf2::TransformException & e) {
770  RCLCPP_ERROR(
771  get_logger(), "Couldn't transform from %s to %s, "
772  "even though the message notifier is in use: (%s)",
773  laser_scan->header.frame_id.c_str(),
774  base_frame_id_.c_str(), e.what());
775  return false;
776  }
777 
778  pf_vector_t laser_pose_v;
779  laser_pose_v.v[0] = laser_pose.pose.position.x;
780  laser_pose_v.v[1] = laser_pose.pose.position.y;
781  // laser mounting angle gets computed later -> set to 0 here!
782  laser_pose_v.v[2] = 0;
783  lasers_[laser_index]->SetLaserPose(laser_pose_v);
784  frame_to_laser_[laser_scan->header.frame_id] = laser_index;
785  return true;
786 }
787 
788 bool AmclNode::shouldUpdateFilter(const pf_vector_t pose, pf_vector_t & delta)
789 {
790  delta.v[0] = pose.v[0] - pf_odom_pose_.v[0];
791  delta.v[1] = pose.v[1] - pf_odom_pose_.v[1];
792  delta.v[2] = angleutils::angle_diff(pose.v[2], pf_odom_pose_.v[2]);
793 
794  // See if we should update the filter
795  bool update = fabs(delta.v[0]) > d_thresh_ ||
796  fabs(delta.v[1]) > d_thresh_ ||
797  fabs(delta.v[2]) > a_thresh_;
798  update = update || force_update_;
799  return update;
800 }
801 
802 bool AmclNode::updateFilter(
803  const int & laser_index,
804  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
805  const pf_vector_t & pose)
806 {
807  nav2_amcl::LaserData ldata;
808  ldata.laser = lasers_[laser_index].get();
809  ldata.range_count = laser_scan->ranges.size();
810  // To account for lasers that are mounted upside-down, we determine the
811  // min, max, and increment angles of the laser in the base frame.
812  //
813  // Construct min and max angles of laser, in the base_link frame.
814  // Here we set the roll pich yaw of the lasers. We assume roll and pich are zero.
815  geometry_msgs::msg::QuaternionStamped min_q, inc_q;
816  min_q.header.stamp = laser_scan->header.stamp;
817  min_q.header.frame_id = nav2_util::strip_leading_slash(laser_scan->header.frame_id);
818  min_q.quaternion = orientationAroundZAxis(laser_scan->angle_min);
819 
820  inc_q.header = min_q.header;
821  inc_q.quaternion = orientationAroundZAxis(laser_scan->angle_min + laser_scan->angle_increment);
822  try {
823  tf_buffer_->transform(min_q, min_q, base_frame_id_);
824  tf_buffer_->transform(inc_q, inc_q, base_frame_id_);
825  } catch (tf2::TransformException & e) {
826  RCLCPP_WARN(
827  get_logger(), "Unable to transform min/max laser angles into base frame: %s",
828  e.what());
829  return false;
830  }
831  double angle_min = tf2::getYaw(min_q.quaternion);
832  double angle_increment = tf2::getYaw(inc_q.quaternion) - angle_min;
833 
834  // wrapping angle to [-pi .. pi]
835  angle_increment = fmod(angle_increment + 5 * M_PI, 2 * M_PI) - M_PI;
836 
837  RCLCPP_DEBUG(
838  get_logger(), "Laser %d angles in base frame: min: %.3f inc: %.3f", laser_index, angle_min,
839  angle_increment);
840 
841  // Check the validity of range_max, must > 0.0
842  if (laser_scan->range_max <= 0.0) {
843  RCLCPP_WARN(
844  get_logger(), "wrong range_max of laser_scan data: %f. The message could be malformed."
845  " Ignore this message and stop updating.",
846  laser_scan->range_max);
847  return false;
848  }
849 
850  // Apply range min/max thresholds, if the user supplied them
851  if (laser_max_range_ > 0.0) {
852  ldata.range_max = std::min(laser_scan->range_max, static_cast<float>(laser_max_range_));
853  } else {
854  ldata.range_max = laser_scan->range_max;
855  }
856  double range_min;
857  if (laser_min_range_ > 0.0) {
858  range_min = std::max(laser_scan->range_min, static_cast<float>(laser_min_range_));
859  } else {
860  range_min = laser_scan->range_min;
861  }
862 
863  // The LaserData destructor will free this memory
864  ldata.ranges = new double[ldata.range_count][2];
865  for (int i = 0; i < ldata.range_count; i++) {
866  // amcl doesn't (yet) have a concept of min range. So we'll map short
867  // readings to max range.
868  if (laser_scan->ranges[i] <= range_min) {
869  ldata.ranges[i][0] = ldata.range_max;
870  } else {
871  ldata.ranges[i][0] = laser_scan->ranges[i];
872  }
873  // Compute bearing
874  ldata.ranges[i][1] = angle_min +
875  (i * angle_increment);
876  }
877  lasers_[laser_index]->sensorUpdate(pf_, reinterpret_cast<nav2_amcl::LaserData *>(&ldata));
878  lasers_update_[laser_index] = false;
879  pf_odom_pose_ = pose;
880  return true;
881 }
882 
883 void
884 AmclNode::publishParticleCloud(const pf_sample_set_t * set)
885 {
886  // If initial pose is not known, AMCL does not know the current pose
887  if (!initial_pose_is_known_) {return;}
888  auto cloud_with_weights_msg = std::make_unique<nav2_msgs::msg::ParticleCloud>();
889  cloud_with_weights_msg->header.stamp = this->now();
890  cloud_with_weights_msg->header.frame_id = global_frame_id_;
891  cloud_with_weights_msg->particles.resize(set->sample_count);
892 
893  for (int i = 0; i < set->sample_count; i++) {
894  cloud_with_weights_msg->particles[i].pose.position.x = set->samples[i].pose.v[0];
895  cloud_with_weights_msg->particles[i].pose.position.y = set->samples[i].pose.v[1];
896  cloud_with_weights_msg->particles[i].pose.position.z = 0;
897  cloud_with_weights_msg->particles[i].pose.orientation = orientationAroundZAxis(
898  set->samples[i].pose.v[2]);
899  cloud_with_weights_msg->particles[i].weight = set->samples[i].weight;
900  }
901 
902  particle_cloud_pub_->publish(std::move(cloud_with_weights_msg));
903 }
904 
905 bool
906 AmclNode::getMaxWeightHyp(
907  std::vector<amcl_hyp_t> & hyps, amcl_hyp_t & max_weight_hyps,
908  int & max_weight_hyp)
909 {
910  // Read out the current hypotheses
911  double max_weight = 0.0;
912  hyps.resize(pf_->sets[pf_->current_set].cluster_count);
913  for (int hyp_count = 0;
914  hyp_count < pf_->sets[pf_->current_set].cluster_count; hyp_count++)
915  {
916  double weight;
917  pf_vector_t pose_mean;
918  pf_matrix_t pose_cov;
919  if (!pf_get_cluster_stats(pf_, hyp_count, &weight, &pose_mean, &pose_cov)) {
920  RCLCPP_ERROR(get_logger(), "Couldn't get stats on cluster %d", hyp_count);
921  return false;
922  }
923 
924  hyps[hyp_count].weight = weight;
925  hyps[hyp_count].pf_pose_mean = pose_mean;
926  hyps[hyp_count].pf_pose_cov = pose_cov;
927 
928  if (hyps[hyp_count].weight > max_weight) {
929  max_weight = hyps[hyp_count].weight;
930  max_weight_hyp = hyp_count;
931  }
932  }
933 
934  if (max_weight > 0.0) {
935  RCLCPP_DEBUG(
936  get_logger(), "Max weight pose: %.3f %.3f %.3f",
937  hyps[max_weight_hyp].pf_pose_mean.v[0],
938  hyps[max_weight_hyp].pf_pose_mean.v[1],
939  hyps[max_weight_hyp].pf_pose_mean.v[2]);
940 
941  max_weight_hyps = hyps[max_weight_hyp];
942  return true;
943  }
944  return false;
945 }
946 
947 void
948 AmclNode::publishAmclPose(
949  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
950  const std::vector<amcl_hyp_t> & hyps, const int & max_weight_hyp)
951 {
952  // If initial pose is not known, AMCL does not know the current pose
953  if (!initial_pose_is_known_) {
954  if (checkElapsedTime(2s, last_time_printed_msg_)) {
955  RCLCPP_WARN(
956  get_logger(), "AMCL cannot publish a pose or update the transform. "
957  "Please set the initial pose...");
958  last_time_printed_msg_ = now();
959  }
960  return;
961  }
962 
963  auto p = std::make_unique<geometry_msgs::msg::PoseWithCovarianceStamped>();
964  // Fill in the header
965  p->header.frame_id = global_frame_id_;
966  p->header.stamp = laser_scan->header.stamp;
967  // Copy in the pose
968  p->pose.pose.position.x = hyps[max_weight_hyp].pf_pose_mean.v[0];
969  p->pose.pose.position.y = hyps[max_weight_hyp].pf_pose_mean.v[1];
970  p->pose.pose.orientation = orientationAroundZAxis(hyps[max_weight_hyp].pf_pose_mean.v[2]);
971  // Copy in the covariance, converting from 3-D to 6-D
972  pf_sample_set_t * set = pf_->sets + pf_->current_set;
973  for (int i = 0; i < 2; i++) {
974  for (int j = 0; j < 2; j++) {
975  // Report the overall filter covariance, rather than the
976  // covariance for the highest-weight cluster
977  // p->covariance[6*i+j] = hyps[max_weight_hyp].pf_pose_cov.m[i][j];
978  p->pose.covariance[6 * i + j] = set->cov.m[i][j];
979  }
980  }
981  p->pose.covariance[6 * 5 + 5] = set->cov.m[2][2];
982  float temp = 0.0;
983  for (auto covariance_value : p->pose.covariance) {
984  temp += covariance_value;
985  }
986  temp += p->pose.pose.position.x + p->pose.pose.position.y;
987  if (!std::isnan(temp)) {
988  RCLCPP_DEBUG(get_logger(), "Publishing pose");
989  last_published_pose_ = *p;
990  first_pose_sent_ = true;
991  pose_pub_->publish(std::move(p));
992  } else {
993  RCLCPP_WARN(
994  get_logger(), "AMCL covariance or pose is NaN, likely due to an invalid "
995  "configuration or faulty sensor measurements! Pose is not available!");
996  }
997 
998  RCLCPP_DEBUG(
999  get_logger(), "New pose: %6.3f %6.3f %6.3f",
1000  hyps[max_weight_hyp].pf_pose_mean.v[0],
1001  hyps[max_weight_hyp].pf_pose_mean.v[1],
1002  hyps[max_weight_hyp].pf_pose_mean.v[2]);
1003 }
1004 
1005 void
1006 AmclNode::calculateMaptoOdomTransform(
1007  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
1008  const std::vector<amcl_hyp_t> & hyps, const int & max_weight_hyp)
1009 {
1010  // subtracting base to odom from map to base and send map to odom instead
1011  geometry_msgs::msg::PoseStamped odom_to_map;
1012  try {
1013  tf2::Quaternion q;
1014  q.setRPY(0, 0, hyps[max_weight_hyp].pf_pose_mean.v[2]);
1015  tf2::Transform tmp_tf(q, tf2::Vector3(
1016  hyps[max_weight_hyp].pf_pose_mean.v[0],
1017  hyps[max_weight_hyp].pf_pose_mean.v[1],
1018  0.0));
1019 
1020  geometry_msgs::msg::PoseStamped tmp_tf_stamped;
1021  tmp_tf_stamped.header.frame_id = base_frame_id_;
1022  tmp_tf_stamped.header.stamp = laser_scan->header.stamp;
1023  tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);
1024 
1025  tf_buffer_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);
1026  } catch (tf2::TransformException & e) {
1027  RCLCPP_DEBUG(get_logger(), "Failed to subtract base to odom transform: (%s)", e.what());
1028  return;
1029  }
1030 
1031  tf2::impl::Converter<true, false>::convert(odom_to_map.pose, latest_tf_);
1032  latest_tf_valid_ = true;
1033 }
1034 
1035 void
1036 AmclNode::sendMapToOdomTransform(const tf2::TimePoint & transform_expiration)
1037 {
1038  // AMCL will update transform only when it has knowledge about robot's initial position
1039  if (!initial_pose_is_known_) {return;}
1040  geometry_msgs::msg::TransformStamped tmp_tf_stamped;
1041  tmp_tf_stamped.header.frame_id = global_frame_id_;
1042  tmp_tf_stamped.header.stamp = tf2_ros::toMsg(transform_expiration);
1043  tmp_tf_stamped.child_frame_id = odom_frame_id_;
1044  tf2::impl::Converter<false, true>::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);
1045  tf_broadcaster_->sendTransform(tmp_tf_stamped);
1046 }
1047 
1048 std::unique_ptr<nav2_amcl::Laser>
1049 AmclNode::createLaserObject()
1050 {
1051  RCLCPP_INFO(get_logger(), "createLaserObject");
1052 
1053  if (sensor_model_type_ == "beam") {
1054  return std::make_unique<nav2_amcl::BeamModel>(
1055  z_hit_, z_short_, z_max_, z_rand_, sigma_hit_, lambda_short_,
1056  0.0, max_beams_, map_);
1057  }
1058 
1059  if (sensor_model_type_ == "likelihood_field_prob") {
1060  return std::make_unique<nav2_amcl::LikelihoodFieldModelProb>(
1061  z_hit_, z_rand_, sigma_hit_,
1062  laser_likelihood_max_dist_, do_beamskip_, beam_skip_distance_, beam_skip_threshold_,
1063  beam_skip_error_threshold_, max_beams_, map_);
1064  }
1065 
1066  return std::make_unique<nav2_amcl::LikelihoodFieldModel>(
1067  z_hit_, z_rand_, sigma_hit_,
1068  laser_likelihood_max_dist_, max_beams_, map_);
1069 }
1070 
1071 void
1072 AmclNode::initParameters()
1073 {
1074  double save_pose_rate;
1075  double tmp_tol;
1076 
1077  get_parameter("alpha1", alpha1_);
1078  get_parameter("alpha2", alpha2_);
1079  get_parameter("alpha3", alpha3_);
1080  get_parameter("alpha4", alpha4_);
1081  get_parameter("alpha5", alpha5_);
1082  get_parameter("base_frame_id", base_frame_id_);
1083  get_parameter("beam_skip_distance", beam_skip_distance_);
1084  get_parameter("beam_skip_error_threshold", beam_skip_error_threshold_);
1085  get_parameter("beam_skip_threshold", beam_skip_threshold_);
1086  get_parameter("do_beamskip", do_beamskip_);
1087  get_parameter("global_frame_id", global_frame_id_);
1088  get_parameter("lambda_short", lambda_short_);
1089  get_parameter("laser_likelihood_max_dist", laser_likelihood_max_dist_);
1090  get_parameter("laser_max_range", laser_max_range_);
1091  get_parameter("laser_min_range", laser_min_range_);
1092  get_parameter("laser_model_type", sensor_model_type_);
1093  get_parameter("set_initial_pose", set_initial_pose_);
1094  get_parameter("initial_pose.x", initial_pose_x_);
1095  get_parameter("initial_pose.y", initial_pose_y_);
1096  get_parameter("initial_pose.z", initial_pose_z_);
1097  get_parameter("initial_pose.yaw", initial_pose_yaw_);
1098  get_parameter("max_beams", max_beams_);
1099  get_parameter("max_particles", max_particles_);
1100  get_parameter("min_particles", min_particles_);
1101  get_parameter("odom_frame_id", odom_frame_id_);
1102  get_parameter("pf_err", pf_err_);
1103  get_parameter("pf_z", pf_z_);
1104  get_parameter("recovery_alpha_fast", alpha_fast_);
1105  get_parameter("recovery_alpha_slow", alpha_slow_);
1106  get_parameter("resample_interval", resample_interval_);
1107  get_parameter("robot_model_type", robot_model_type_);
1108  get_parameter("save_pose_rate", save_pose_rate);
1109  get_parameter("sigma_hit", sigma_hit_);
1110  get_parameter("tf_broadcast", tf_broadcast_);
1111  get_parameter("transform_tolerance", tmp_tol);
1112  get_parameter("update_min_a", a_thresh_);
1113  get_parameter("update_min_d", d_thresh_);
1114  get_parameter("z_hit", z_hit_);
1115  get_parameter("z_max", z_max_);
1116  get_parameter("z_rand", z_rand_);
1117  get_parameter("z_short", z_short_);
1118  get_parameter("first_map_only", first_map_only_);
1119  get_parameter("always_reset_initial_pose", always_reset_initial_pose_);
1120  get_parameter("scan_topic", scan_topic_);
1121  get_parameter("map_topic", map_topic_);
1122  get_parameter("freespace_downsampling", freespace_downsampling_);
1123 
1124  save_pose_period_ = tf2::durationFromSec(1.0 / save_pose_rate);
1125  transform_tolerance_ = tf2::durationFromSec(tmp_tol);
1126 
1127  odom_frame_id_ = nav2_util::strip_leading_slash(odom_frame_id_);
1128  base_frame_id_ = nav2_util::strip_leading_slash(base_frame_id_);
1129  global_frame_id_ = nav2_util::strip_leading_slash(global_frame_id_);
1130 
1131  last_time_printed_msg_ = now();
1132 
1133  // Semantic checks
1134  if (laser_likelihood_max_dist_ < 0) {
1135  RCLCPP_WARN(
1136  get_logger(), "You've set laser_likelihood_max_dist to be negative,"
1137  " this isn't allowed so it will be set to default value 2.0.");
1138  laser_likelihood_max_dist_ = 2.0;
1139  }
1140  if (max_particles_ < 0) {
1141  RCLCPP_WARN(
1142  get_logger(), "You've set max_particles to be negative,"
1143  " this isn't allowed so it will be set to default value 2000.");
1144  max_particles_ = 2000;
1145  }
1146 
1147  if (min_particles_ < 0) {
1148  RCLCPP_WARN(
1149  get_logger(), "You've set min_particles to be negative,"
1150  " this isn't allowed so it will be set to default value 500.");
1151  min_particles_ = 500;
1152  }
1153 
1154  if (min_particles_ > max_particles_) {
1155  RCLCPP_WARN(
1156  get_logger(), "You've set min_particles to be greater than max particles,"
1157  " this isn't allowed so max_particles will be set to min_particles.");
1158  max_particles_ = min_particles_;
1159  }
1160 
1161  if (resample_interval_ <= 0) {
1162  RCLCPP_WARN(
1163  get_logger(), "You've set resample_interval to be zero or negative,"
1164  " this isn't allowed so it will be set to default value to 1.");
1165  resample_interval_ = 1;
1166  }
1167 
1168  if (always_reset_initial_pose_) {
1169  initial_pose_is_known_ = false;
1170  }
1171 }
1172 
1177 rcl_interfaces::msg::SetParametersResult
1178 AmclNode::dynamicParametersCallback(
1179  std::vector<rclcpp::Parameter> parameters)
1180 {
1181  std::lock_guard<std::recursive_mutex> cfl(mutex_);
1182  rcl_interfaces::msg::SetParametersResult result;
1183  double save_pose_rate;
1184  double tmp_tol;
1185 
1186  int max_particles = max_particles_;
1187  int min_particles = min_particles_;
1188 
1189  bool reinit_pf = false;
1190  bool reinit_odom = false;
1191  bool reinit_laser = false;
1192  bool reinit_map = false;
1193 
1194  for (auto parameter : parameters) {
1195  const auto & param_type = parameter.get_type();
1196  const auto & param_name = parameter.get_name();
1197 
1198  if (param_type == ParameterType::PARAMETER_DOUBLE) {
1199  if (param_name == "alpha1") {
1200  alpha1_ = parameter.as_double();
1201  // alpha restricted to be non-negative
1202  if (alpha1_ < 0.0) {
1203  RCLCPP_WARN(
1204  get_logger(), "You've set alpha1 to be negative,"
1205  " this isn't allowed, so the alpha1 will be set to be zero.");
1206  alpha1_ = 0.0;
1207  }
1208  reinit_odom = true;
1209  } else if (param_name == "alpha2") {
1210  alpha2_ = parameter.as_double();
1211  // alpha restricted to be non-negative
1212  if (alpha2_ < 0.0) {
1213  RCLCPP_WARN(
1214  get_logger(), "You've set alpha2 to be negative,"
1215  " this isn't allowed, so the alpha2 will be set to be zero.");
1216  alpha2_ = 0.0;
1217  }
1218  reinit_odom = true;
1219  } else if (param_name == "alpha3") {
1220  alpha3_ = parameter.as_double();
1221  // alpha restricted to be non-negative
1222  if (alpha3_ < 0.0) {
1223  RCLCPP_WARN(
1224  get_logger(), "You've set alpha3 to be negative,"
1225  " this isn't allowed, so the alpha3 will be set to be zero.");
1226  alpha3_ = 0.0;
1227  }
1228  reinit_odom = true;
1229  } else if (param_name == "alpha4") {
1230  alpha4_ = parameter.as_double();
1231  // alpha restricted to be non-negative
1232  if (alpha4_ < 0.0) {
1233  RCLCPP_WARN(
1234  get_logger(), "You've set alpha4 to be negative,"
1235  " this isn't allowed, so the alpha4 will be set to be zero.");
1236  alpha4_ = 0.0;
1237  }
1238  reinit_odom = true;
1239  } else if (param_name == "alpha5") {
1240  alpha5_ = parameter.as_double();
1241  // alpha restricted to be non-negative
1242  if (alpha5_ < 0.0) {
1243  RCLCPP_WARN(
1244  get_logger(), "You've set alpha5 to be negative,"
1245  " this isn't allowed, so the alpha5 will be set to be zero.");
1246  alpha5_ = 0.0;
1247  }
1248  reinit_odom = true;
1249  } else if (param_name == "beam_skip_distance") {
1250  beam_skip_distance_ = parameter.as_double();
1251  reinit_laser = true;
1252  } else if (param_name == "beam_skip_error_threshold") {
1253  beam_skip_error_threshold_ = parameter.as_double();
1254  reinit_laser = true;
1255  } else if (param_name == "beam_skip_threshold") {
1256  beam_skip_threshold_ = parameter.as_double();
1257  reinit_laser = true;
1258  } else if (param_name == "lambda_short") {
1259  lambda_short_ = parameter.as_double();
1260  reinit_laser = true;
1261  } else if (param_name == "laser_likelihood_max_dist") {
1262  laser_likelihood_max_dist_ = parameter.as_double();
1263  reinit_laser = true;
1264  } else if (param_name == "laser_max_range") {
1265  laser_max_range_ = parameter.as_double();
1266  reinit_laser = true;
1267  } else if (param_name == "laser_min_range") {
1268  laser_min_range_ = parameter.as_double();
1269  reinit_laser = true;
1270  } else if (param_name == "pf_err") {
1271  pf_err_ = parameter.as_double();
1272  reinit_pf = true;
1273  } else if (param_name == "pf_z") {
1274  pf_z_ = parameter.as_double();
1275  reinit_pf = true;
1276  } else if (param_name == "recovery_alpha_fast") {
1277  alpha_fast_ = parameter.as_double();
1278  reinit_pf = true;
1279  } else if (param_name == "recovery_alpha_slow") {
1280  alpha_slow_ = parameter.as_double();
1281  reinit_pf = true;
1282  } else if (param_name == "save_pose_rate") {
1283  save_pose_rate = parameter.as_double();
1284  save_pose_period_ = tf2::durationFromSec(1.0 / save_pose_rate);
1285  } else if (param_name == "sigma_hit") {
1286  sigma_hit_ = parameter.as_double();
1287  reinit_laser = true;
1288  } else if (param_name == "transform_tolerance") {
1289  tmp_tol = parameter.as_double();
1290  transform_tolerance_ = tf2::durationFromSec(tmp_tol);
1291  reinit_laser = true;
1292  } else if (param_name == "update_min_a") {
1293  a_thresh_ = parameter.as_double();
1294  } else if (param_name == "update_min_d") {
1295  d_thresh_ = parameter.as_double();
1296  } else if (param_name == "z_hit") {
1297  z_hit_ = parameter.as_double();
1298  reinit_laser = true;
1299  } else if (param_name == "z_max") {
1300  z_max_ = parameter.as_double();
1301  reinit_laser = true;
1302  } else if (param_name == "z_rand") {
1303  z_rand_ = parameter.as_double();
1304  reinit_laser = true;
1305  } else if (param_name == "z_short") {
1306  z_short_ = parameter.as_double();
1307  reinit_laser = true;
1308  }
1309  } else if (param_type == ParameterType::PARAMETER_STRING) {
1310  if (param_name == "base_frame_id") {
1311  base_frame_id_ = parameter.as_string();
1312  } else if (param_name == "global_frame_id") {
1313  global_frame_id_ = parameter.as_string();
1314  } else if (param_name == "map_topic") {
1315  map_topic_ = parameter.as_string();
1316  reinit_map = true;
1317  } else if (param_name == "laser_model_type") {
1318  sensor_model_type_ = parameter.as_string();
1319  reinit_laser = true;
1320  } else if (param_name == "odom_frame_id") {
1321  odom_frame_id_ = parameter.as_string();
1322  reinit_laser = true;
1323  } else if (param_name == "scan_topic") {
1324  scan_topic_ = parameter.as_string();
1325  reinit_laser = true;
1326  } else if (param_name == "robot_model_type") {
1327  robot_model_type_ = parameter.as_string();
1328  reinit_odom = true;
1329  }
1330  } else if (param_type == ParameterType::PARAMETER_BOOL) {
1331  if (param_name == "do_beamskip") {
1332  do_beamskip_ = parameter.as_bool();
1333  reinit_laser = true;
1334  } else if (param_name == "tf_broadcast") {
1335  tf_broadcast_ = parameter.as_bool();
1336  } else if (param_name == "set_initial_pose") {
1337  set_initial_pose_ = parameter.as_bool();
1338  } else if (param_name == "first_map_only") {
1339  first_map_only_ = parameter.as_bool();
1340  }
1341  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
1342  if (param_name == "max_beams") {
1343  max_beams_ = parameter.as_int();
1344  reinit_laser = true;
1345  } else if (param_name == "max_particles") {
1346  max_particles_ = parameter.as_int();
1347  reinit_pf = true;
1348  } else if (param_name == "min_particles") {
1349  min_particles_ = parameter.as_int();
1350  reinit_pf = true;
1351  } else if (param_name == "resample_interval") {
1352  resample_interval_ = parameter.as_int();
1353  }
1354  }
1355  }
1356 
1357  // Checking if the minimum particles is greater than max_particles_
1358  if (min_particles_ > max_particles_) {
1359  RCLCPP_ERROR(
1360  this->get_logger(),
1361  "You've set min_particles to be greater than max particles,"
1362  " this isn't allowed.");
1363  // sticking to the old values
1364  max_particles_ = max_particles;
1365  min_particles_ = min_particles;
1366  result.successful = false;
1367  return result;
1368  }
1369 
1370  // Re-initialize the particle filter
1371  if (reinit_pf) {
1372  if (pf_ != NULL) {
1373  pf_free(pf_);
1374  pf_ = NULL;
1375  }
1376  initParticleFilter();
1377  }
1378 
1379  // Re-initialize the odometry
1380  if (reinit_odom) {
1381  motion_model_.reset();
1382  initOdometry();
1383  }
1384 
1385  // Re-initialize the lasers and it's filters
1386  if (reinit_laser) {
1387  lasers_.clear();
1388  lasers_update_.clear();
1389  frame_to_laser_.clear();
1390  laser_scan_connection_.disconnect();
1391  laser_scan_filter_.reset();
1392  laser_scan_sub_.reset();
1393 
1394  initMessageFilters();
1395  }
1396 
1397  // Re-initialize the map
1398  if (reinit_map) {
1399  map_sub_.reset();
1400  map_sub_ = create_subscription<nav_msgs::msg::OccupancyGrid>(
1401  map_topic_, rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
1402  std::bind(&AmclNode::mapReceived, this, std::placeholders::_1));
1403  }
1404 
1405  result.successful = true;
1406  return result;
1407 }
1408 
1409 void
1410 AmclNode::mapReceived(const nav_msgs::msg::OccupancyGrid::SharedPtr msg)
1411 {
1412  RCLCPP_DEBUG(get_logger(), "AmclNode: A new map was received.");
1413  if (!nav2_util::validateMsg(*msg)) {
1414  RCLCPP_ERROR(get_logger(), "Received map message is malformed. Rejecting.");
1415  return;
1416  }
1417  if (first_map_only_ && first_map_received_) {
1418  return;
1419  }
1420  handleMapMessage(*msg);
1421  first_map_received_ = true;
1422 }
1423 
1424 void
1425 AmclNode::handleMapMessage(const nav_msgs::msg::OccupancyGrid & msg)
1426 {
1427  std::lock_guard<std::recursive_mutex> cfl(mutex_);
1428 
1429  RCLCPP_INFO(
1430  get_logger(), "Received a %d X %d map @ %.3f m/pix",
1431  msg.info.width,
1432  msg.info.height,
1433  msg.info.resolution);
1434  if (msg.header.frame_id != global_frame_id_) {
1435  RCLCPP_WARN(
1436  get_logger(), "Frame_id of map received:'%s' doesn't match global_frame_id:'%s'. This could"
1437  " cause issues with reading published topics",
1438  msg.header.frame_id.c_str(),
1439  global_frame_id_.c_str());
1440  }
1441  freeMapDependentMemory();
1442  map_ = convertMap(msg);
1443 
1444 #if NEW_UNIFORM_SAMPLING
1445  createFreeSpaceVector();
1446 #endif
1447 }
1448 
1449 void
1450 AmclNode::createFreeSpaceVector()
1451 {
1452  int delta = freespace_downsampling_ ? 2 : 1;
1453  // Index of free space
1454  free_space_indices.resize(0);
1455  for (int i = 0; i < map_->size_x; i += delta) {
1456  for (int j = 0; j < map_->size_y; j += delta) {
1457  if (map_->cells[MAP_INDEX(map_, i, j)].occ_state == -1) {
1458  AmclNode::Point2D point = {i, j};
1459  free_space_indices.push_back(point);
1460  }
1461  }
1462  }
1463 }
1464 
1465 void
1466 AmclNode::freeMapDependentMemory()
1467 {
1468  if (map_ != NULL) {
1469  map_free(map_);
1470  map_ = NULL;
1471  }
1472 
1473  // Clear queued laser objects because they hold pointers to the existing
1474  // map, #5202.
1475  lasers_.clear();
1476  lasers_update_.clear();
1477  frame_to_laser_.clear();
1478 }
1479 
1480 // Convert an OccupancyGrid map message into the internal representation. This function
1481 // allocates a map_t and returns it.
1482 map_t *
1483 AmclNode::convertMap(const nav_msgs::msg::OccupancyGrid & map_msg)
1484 {
1485  map_t * map = map_alloc();
1486 
1487  map->size_x = map_msg.info.width;
1488  map->size_y = map_msg.info.height;
1489  map->scale = map_msg.info.resolution;
1490  map->origin_x = map_msg.info.origin.position.x + (map->size_x / 2) * map->scale;
1491  map->origin_y = map_msg.info.origin.position.y + (map->size_y / 2) * map->scale;
1492 
1493  map->cells =
1494  reinterpret_cast<map_cell_t *>(malloc(sizeof(map_cell_t) * map->size_x * map->size_y));
1495 
1496  // Convert to player format
1497  for (int i = 0; i < map->size_x * map->size_y; i++) {
1498  if (map_msg.data[i] == 0) {
1499  map->cells[i].occ_state = -1;
1500  } else if (map_msg.data[i] == 100) {
1501  map->cells[i].occ_state = +1;
1502  } else {
1503  map->cells[i].occ_state = 0;
1504  }
1505  }
1506 
1507  return map;
1508 }
1509 
1510 void
1511 AmclNode::initTransforms()
1512 {
1513  RCLCPP_INFO(get_logger(), "initTransforms");
1514 
1515  // Initialize transform listener and broadcaster
1516  tf_buffer_ = std::make_shared<tf2_ros::Buffer>(get_clock());
1517  auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
1518  get_node_base_interface(),
1519  get_node_timers_interface(),
1520  callback_group_);
1521  tf_buffer_->setCreateTimerInterface(timer_interface);
1522  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_, this, true);
1523  tf_broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(shared_from_this());
1524 
1525  sent_first_transform_ = false;
1526  latest_tf_valid_ = false;
1527  latest_tf_ = tf2::Transform::getIdentity();
1528 }
1529 
1530 void
1531 AmclNode::initMessageFilters()
1532 {
1533  auto sub_opt = rclcpp::SubscriptionOptions();
1534  sub_opt.callback_group = callback_group_;
1535  laser_scan_sub_ = std::make_unique<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
1536  rclcpp_lifecycle::LifecycleNode>>(
1537  shared_from_this(), scan_topic_, rmw_qos_profile_sensor_data, sub_opt);
1538 
1539  laser_scan_filter_ = std::make_unique<tf2_ros::MessageFilter<sensor_msgs::msg::LaserScan>>(
1540  *laser_scan_sub_, *tf_buffer_, odom_frame_id_, 10,
1541  get_node_logging_interface(),
1542  get_node_clock_interface(),
1543  transform_tolerance_);
1544 
1545 
1546  laser_scan_connection_ = laser_scan_filter_->registerCallback(
1547  std::bind(
1548  &AmclNode::laserReceived,
1549  this, std::placeholders::_1));
1550 }
1551 
1552 void
1553 AmclNode::initPubSub()
1554 {
1555  RCLCPP_INFO(get_logger(), "initPubSub");
1556 
1557  particle_cloud_pub_ = create_publisher<nav2_msgs::msg::ParticleCloud>(
1558  "particle_cloud",
1559  rclcpp::SensorDataQoS());
1560 
1561  pose_pub_ = create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>(
1562  "amcl_pose",
1563  rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
1564 
1565  initial_pose_sub_ = create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
1566  "initialpose", rclcpp::SystemDefaultsQoS(),
1567  std::bind(&AmclNode::initialPoseReceived, this, std::placeholders::_1));
1568 
1569  map_sub_ = create_subscription<nav_msgs::msg::OccupancyGrid>(
1570  map_topic_, rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
1571  std::bind(&AmclNode::mapReceived, this, std::placeholders::_1));
1572 
1573  RCLCPP_INFO(get_logger(), "Subscribed to map topic.");
1574 }
1575 
1576 void
1577 AmclNode::initServices()
1578 {
1579  global_loc_srv_ = create_service<std_srvs::srv::Empty>(
1580  "reinitialize_global_localization",
1581  std::bind(&AmclNode::globalLocalizationCallback, this, _1, _2, _3));
1582 
1583  initial_guess_srv_ = create_service<nav2_msgs::srv::SetInitialPose>(
1584  "set_initial_pose",
1585  std::bind(&AmclNode::initialPoseReceivedSrv, this, _1, _2, _3));
1586 
1587  nomotion_update_srv_ = create_service<std_srvs::srv::Empty>(
1588  "request_nomotion_update",
1589  std::bind(&AmclNode::nomotionUpdateCallback, this, _1, _2, _3));
1590 }
1591 
1592 void
1593 AmclNode::initOdometry()
1594 {
1595  // TODO(mjeronimo): We should handle persistance of the last known pose of the robot. We could
1596  // then read that pose here and initialize using that.
1597 
1598  // When pausing and resuming, remember the last robot pose so we don't start at 0:0 again
1599  init_pose_[0] = last_published_pose_.pose.pose.position.x;
1600  init_pose_[1] = last_published_pose_.pose.pose.position.y;
1601  init_pose_[2] = tf2::getYaw(last_published_pose_.pose.pose.orientation);
1602 
1603  if (!initial_pose_is_known_) {
1604  init_cov_[0] = 0.5 * 0.5;
1605  init_cov_[1] = 0.5 * 0.5;
1606  init_cov_[2] = (M_PI / 12.0) * (M_PI / 12.0);
1607  } else {
1608  init_cov_[0] = last_published_pose_.pose.covariance[0];
1609  init_cov_[1] = last_published_pose_.pose.covariance[7];
1610  init_cov_[2] = last_published_pose_.pose.covariance[35];
1611  }
1612 
1613  motion_model_ = plugin_loader_.createSharedInstance(robot_model_type_);
1614  motion_model_->initialize(alpha1_, alpha2_, alpha3_, alpha4_, alpha5_);
1615 
1616  latest_odom_pose_ = geometry_msgs::msg::PoseStamped();
1617 }
1618 
1619 void
1620 AmclNode::initParticleFilter()
1621 {
1622  // Create the particle filter
1623  pf_ = pf_alloc(
1624  min_particles_, max_particles_, alpha_slow_, alpha_fast_,
1625  (pf_init_model_fn_t)AmclNode::uniformPoseGenerator);
1626  pf_->pop_err = pf_err_;
1627  pf_->pop_z = pf_z_;
1628 
1629  // Initialize the filter
1630  pf_vector_t pf_init_pose_mean = pf_vector_zero();
1631  pf_init_pose_mean.v[0] = init_pose_[0];
1632  pf_init_pose_mean.v[1] = init_pose_[1];
1633  pf_init_pose_mean.v[2] = init_pose_[2];
1634 
1635  pf_matrix_t pf_init_pose_cov = pf_matrix_zero();
1636  pf_init_pose_cov.m[0][0] = init_cov_[0];
1637  pf_init_pose_cov.m[1][1] = init_cov_[1];
1638  pf_init_pose_cov.m[2][2] = init_cov_[2];
1639 
1640  pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
1641 
1642  pf_init_ = false;
1643  resample_count_ = 0;
1644  memset(&pf_odom_pose_, 0, sizeof(pf_odom_pose_));
1645 }
1646 
1647 void
1648 AmclNode::initLaserScan()
1649 {
1650  scan_error_count_ = 0;
1651  last_laser_received_ts_ = rclcpp::Time(0);
1652 }
1653 
1654 } // namespace nav2_amcl
1655 
1656 #include "rclcpp_components/register_node_macro.hpp"
1657 
1658 // Register the component with class_loader.
1659 // This acts as a sort of entry point, allowing the component to be discoverable when its library
1660 // is being loaded into a running process.
1661 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_amcl::AmclNode)
Definition: map.hpp:62