23 #include "nav2_amcl/amcl_node.hpp"
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"
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wpedantic"
48 #include "tf2/utils.h"
49 #pragma GCC diagnostic pop
51 #include "nav2_amcl/portable_utils.hpp"
52 #include "nav2_util/validate_messages.hpp"
54 using namespace std::placeholders;
55 using rcl_interfaces::msg::ParameterType;
56 using namespace std::chrono_literals;
60 using nav2_util::geometry_utils::orientationAroundZAxis;
62 AmclNode::AmclNode(
const rclcpp::NodeOptions & options)
63 : nav2_util::LifecycleNode(
"amcl",
"", options)
65 RCLCPP_INFO(get_logger(),
"Creating");
75 "alpha1", rclcpp::ParameterValue(0.2),
76 "This is the alpha1 parameter",
"These are additional constraints for alpha1");
79 "alpha2", rclcpp::ParameterValue(0.2),
80 "This is the alpha2 parameter",
"These are additional constraints for alpha2");
83 "alpha3", rclcpp::ParameterValue(0.2),
84 "This is the alpha3 parameter",
"These are additional constraints for alpha3");
87 "alpha4", rclcpp::ParameterValue(0.2),
88 "This is the alpha4 parameter",
"These are additional constraints for alpha4");
91 "alpha5", rclcpp::ParameterValue(0.2),
92 "This is the alpha5 parameter",
"These are additional constraints for alpha5");
95 "base_frame_id", rclcpp::ParameterValue(std::string(
"base_footprint")),
96 "Which frame to use for the robot base");
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));
104 "global_frame_id", rclcpp::ParameterValue(std::string(
"map")),
105 "The name of the coordinate frame published by the localization system");
108 "lambda_short", rclcpp::ParameterValue(0.1),
109 "Exponential decay parameter for z_short part of model");
112 "laser_likelihood_max_dist", rclcpp::ParameterValue(2.0),
113 "Maximum distance to do obstacle inflation on map, for use in likelihood_field model");
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");
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");
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");
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");
136 "initial_pose.x", rclcpp::ParameterValue(0.0),
137 "X coordinate of the initial robot pose in the map frame");
140 "initial_pose.y", rclcpp::ParameterValue(0.0),
141 "Y coordinate of the initial robot pose in the map frame");
144 "initial_pose.z", rclcpp::ParameterValue(0.0),
145 "Z coordinate of the initial robot pose in the map frame");
148 "initial_pose.yaw", rclcpp::ParameterValue(0.0),
149 "Yaw of the initial robot pose in the map frame");
152 "max_beams", rclcpp::ParameterValue(60),
153 "How many evenly-spaced beams in each scan to be used when updating the filter");
156 "max_particles", rclcpp::ParameterValue(2000),
157 "Maximum allowed number of particles");
160 "min_particles", rclcpp::ParameterValue(500),
161 "Minimum allowed number of particles");
164 "odom_frame_id", rclcpp::ParameterValue(std::string(
"odom")),
165 "Which frame to use for odometry");
167 add_parameter(
"pf_err", rclcpp::ParameterValue(0.05));
168 add_parameter(
"pf_z", rclcpp::ParameterValue(0.99));
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");
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");
183 "resample_interval", rclcpp::ParameterValue(1),
184 "Number of filter updates required before resampling");
186 add_parameter(
"robot_model_type", rclcpp::ParameterValue(
"nav2_amcl::DifferentialMotionModel"));
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",
195 add_parameter(
"sigma_hit", rclcpp::ParameterValue(0.2));
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");
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");
208 "update_min_a", rclcpp::ParameterValue(0.2),
209 "Rotational movement required before performing a filter update");
212 "update_min_d", rclcpp::ParameterValue(0.25),
213 "Translational movement required before performing a filter update");
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));
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");
227 "scan_topic", rclcpp::ParameterValue(
"scan"),
228 "Topic to subscribe to in order to receive the laser scan for localization");
231 "map_topic", rclcpp::ParameterValue(
"map"),
232 "Topic to subscribe to in order to receive the map to localize on");
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");
239 "freespace_downsampling", rclcpp::ParameterValue(
241 "Downsample the free space used by the Pose Generator. Use it with large maps to save memory");
244 AmclNode::~AmclNode()
248 nav2_util::CallbackReturn
249 AmclNode::on_configure(
const rclcpp_lifecycle::State & )
251 RCLCPP_INFO(get_logger(),
"Configuring");
252 callback_group_ = create_callback_group(
253 rclcpp::CallbackGroupType::MutuallyExclusive,
false);
256 initParticleFilter();
258 initMessageFilters();
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;
268 nav2_util::CallbackReturn
269 AmclNode::on_activate(
const rclcpp_lifecycle::State & )
271 RCLCPP_INFO(get_logger(),
"Activating");
274 pose_pub_->on_activate();
275 particle_cloud_pub_->on_activate();
277 first_pose_sent_ =
false;
283 if (set_initial_pose_) {
284 auto msg = std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>();
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_);
293 initialPoseReceived(msg);
294 }
else if (init_pose_received_on_inactive) {
295 handleInitialPose(last_published_pose_);
298 auto node = shared_from_this();
300 dyn_params_handler_ = node->add_on_set_parameters_callback(
302 &AmclNode::dynamicParametersCallback,
303 this, std::placeholders::_1));
308 return nav2_util::CallbackReturn::SUCCESS;
311 nav2_util::CallbackReturn
312 AmclNode::on_deactivate(
const rclcpp_lifecycle::State & )
314 RCLCPP_INFO(get_logger(),
"Deactivating");
319 pose_pub_->on_deactivate();
320 particle_cloud_pub_->on_deactivate();
323 remove_on_set_parameters_callback(dyn_params_handler_.get());
324 dyn_params_handler_.reset();
329 return nav2_util::CallbackReturn::SUCCESS;
332 nav2_util::CallbackReturn
333 AmclNode::on_cleanup(
const rclcpp_lifecycle::State & )
335 RCLCPP_INFO(get_logger(),
"Cleaning up");
337 executor_thread_.reset();
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();
347 laser_scan_filter_.reset();
348 laser_scan_sub_.reset();
356 first_map_received_ =
false;
357 free_space_indices.resize(0);
360 tf_broadcaster_.reset();
365 particle_cloud_pub_.reset();
368 motion_model_.reset();
376 lasers_update_.clear();
377 frame_to_laser_.clear();
378 force_update_ =
true;
380 if (set_initial_pose_) {
384 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.x)));
388 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.y)));
392 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.z)));
396 rclcpp::ParameterValue(tf2::getYaw(last_published_pose_.pose.pose.orientation))));
399 return nav2_util::CallbackReturn::SUCCESS;
402 nav2_util::CallbackReturn
403 AmclNode::on_shutdown(
const rclcpp_lifecycle::State & )
405 RCLCPP_INFO(get_logger(),
"Shutting down");
406 return nav2_util::CallbackReturn::SUCCESS;
410 AmclNode::checkElapsedTime(std::chrono::seconds check_interval, rclcpp::Time last_time)
412 rclcpp::Duration elapsed_time = now() - last_time;
413 if (elapsed_time.nanoseconds() * 1e-9 > check_interval.count()) {
419 #if NEW_UNIFORM_SAMPLING
420 std::vector<AmclNode::Point2D> AmclNode::free_space_indices;
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)
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);
436 tf_buffer_->transform(ident, odom_pose, odom_frame_id_);
437 }
catch (tf2::TransformException & e) {
439 if (scan_error_count_ % 20 == 0) {
441 get_logger(),
"(%d) consecutive laser scan transforms failed: (%s)", scan_error_count_,
447 scan_error_count_ = 0;
448 x = odom_pose.pose.position.x;
449 y = odom_pose.pose.position.y;
450 yaw = tf2::getYaw(odom_pose.pose.orientation);
456 AmclNode::uniformPoseGenerator(
void * arg)
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];
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;
468 double min_x, max_x, min_y, max_y;
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;
477 RCLCPP_DEBUG(get_logger(),
"Generating new uniform sample");
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;
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)) {
495 AmclNode::globalLocalizationCallback(
496 const std::shared_ptr<rmw_request_id_t>,
497 const std::shared_ptr<std_srvs::srv::Empty::Request>,
498 std::shared_ptr<std_srvs::srv::Empty::Response>)
500 std::lock_guard<std::recursive_mutex> cfl(mutex_);
502 RCLCPP_INFO(get_logger(),
"Initializing with uniform distribution");
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;
513 AmclNode::initialPoseReceivedSrv(
514 const std::shared_ptr<rmw_request_id_t>,
515 const std::shared_ptr<nav2_msgs::srv::SetInitialPose::Request> req,
516 std::shared_ptr<nav2_msgs::srv::SetInitialPose::Response>)
518 initialPoseReceived(std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>(req->pose));
523 AmclNode::nomotionUpdateCallback(
524 const std::shared_ptr<rmw_request_id_t>,
525 const std::shared_ptr<std_srvs::srv::Empty::Request>,
526 std::shared_ptr<std_srvs::srv::Empty::Response>)
528 RCLCPP_INFO(get_logger(),
"Requesting no-motion update");
529 force_update_ =
true;
533 AmclNode::initialPoseReceived(geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg)
535 std::lock_guard<std::recursive_mutex> cfl(mutex_);
537 RCLCPP_INFO(get_logger(),
"initialPoseReceived");
539 if (!nav2_util::validateMsg(*msg)) {
540 RCLCPP_ERROR(get_logger(),
"Received initialpose message is malformed. Rejecting.");
543 if (nav2_util::strip_leading_slash(msg->header.frame_id) != global_frame_id_) {
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());
551 if (first_map_received_ && (abs(msg->pose.pose.position.x) > map_->size_x ||
552 abs(msg->pose.pose.position.y) > map_->size_y))
555 get_logger(),
"Received initialpose from message is out of the size of map. Rejecting.");
560 last_published_pose_ = *msg;
563 init_pose_received_on_inactive =
true;
565 get_logger(),
"Received initial pose request, "
566 "but AMCL is not yet in the active state");
569 handleInitialPose(*msg);
573 AmclNode::handleInitialPose(geometry_msgs::msg::PoseWithCovarianceStamped & msg)
575 std::lock_guard<std::recursive_mutex> cfl(mutex_);
578 geometry_msgs::msg::TransformStamped tx_odom;
580 rclcpp::Time rclcpp_time = now();
581 tf2::TimePoint tf2_time(std::chrono::nanoseconds(rclcpp_time.nanoseconds()));
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) {
592 if (sent_first_transform_) {
593 RCLCPP_WARN(get_logger(),
"Failed to transform initial pose in time (%s)", e.what());
595 tf2::impl::Converter<false, true>::convert(tf2::Transform::getIdentity(), tx_odom.transform);
598 tf2::Transform tx_odom_tf2;
599 tf2::impl::Converter<true, false>::convert(tx_odom.transform, tx_odom_tf2);
601 tf2::Transform pose_old;
602 tf2::impl::Converter<true, false>::convert(msg.pose.pose, pose_old);
604 tf2::Transform pose_new = pose_old * tx_odom_tf2;
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()));
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());
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];
629 pf_init_pose_cov.m[2][2] = msg.pose.covariance[6 * 5 + 5];
631 pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
633 init_pose_received_on_inactive =
false;
634 initial_pose_is_known_ =
true;
638 AmclNode::laserReceived(sensor_msgs::msg::LaserScan::ConstSharedPtr laser_scan)
640 std::lock_guard<std::recursive_mutex> cfl(mutex_);
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();
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;
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)) {
665 laser_index = frame_to_laser_[laser_scan->header.frame_id];
671 latest_odom_pose_, pose.v[0], pose.v[1], pose.v[2],
672 laser_scan->header.stamp, base_frame_id_))
674 RCLCPP_ERROR(get_logger(),
"Couldn't determine robot's pose associated with laser scan");
679 bool force_publication =
false;
682 pf_odom_pose_ = pose;
685 for (
unsigned int i = 0; i < lasers_update_.size(); i++) {
686 lasers_update_[i] =
true;
689 force_publication =
true;
693 if (shouldUpdateFilter(pose, delta)) {
694 for (
unsigned int i = 0; i < lasers_update_.size(); i++) {
695 lasers_update_[i] =
true;
698 if (lasers_update_[laser_index]) {
699 motion_model_->odometryUpdate(pf_, pose, delta);
701 force_update_ =
false;
704 bool resampled =
false;
707 if (lasers_update_[laser_index]) {
708 updateFilter(laser_index, laser_scan, pose);
711 if (!(++resample_count_ % resample_interval_)) {
712 pf_update_resample(pf_,
reinterpret_cast<void *
>(map_));
717 RCLCPP_DEBUG(get_logger(),
"Num samples: %d\n", set->sample_count);
719 if (!force_update_) {
720 publishParticleCloud(set);
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);
731 if (tf_broadcast_ ==
true) {
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;
740 RCLCPP_ERROR(get_logger(),
"No pose!");
742 }
else if (latest_tf_valid_) {
743 if (tf_broadcast_ ==
true) {
746 tf2::TimePoint transform_expiration = tf2_ros::fromMsg(laser_scan->header.stamp) +
747 transform_tolerance_;
748 sendMapToOdomTransform(transform_expiration);
753 bool AmclNode::addNewScanner(
755 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
756 const std::string & laser_scan_frame_id,
757 geometry_msgs::msg::PoseStamped & laser_pose)
759 lasers_.push_back(createLaserObject());
760 lasers_update_.push_back(
true);
761 laser_index = frame_to_laser_.size();
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);
768 tf_buffer_->transform(ident, laser_pose, base_frame_id_, transform_tolerance_);
769 }
catch (tf2::TransformException & e) {
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());
779 laser_pose_v.v[0] = laser_pose.pose.position.x;
780 laser_pose_v.v[1] = laser_pose.pose.position.y;
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;
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]);
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_;
802 bool AmclNode::updateFilter(
803 const int & laser_index,
804 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
808 ldata.laser = lasers_[laser_index].get();
809 ldata.range_count = laser_scan->ranges.size();
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);
820 inc_q.header = min_q.header;
821 inc_q.quaternion = orientationAroundZAxis(laser_scan->angle_min + laser_scan->angle_increment);
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) {
827 get_logger(),
"Unable to transform min/max laser angles into base frame: %s",
831 double angle_min = tf2::getYaw(min_q.quaternion);
832 double angle_increment = tf2::getYaw(inc_q.quaternion) - angle_min;
835 angle_increment = fmod(angle_increment + 5 * M_PI, 2 * M_PI) - M_PI;
838 get_logger(),
"Laser %d angles in base frame: min: %.3f inc: %.3f", laser_index, angle_min,
842 if (laser_scan->range_max <= 0.0) {
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);
851 if (laser_max_range_ > 0.0) {
852 ldata.range_max = std::min(laser_scan->range_max,
static_cast<float>(laser_max_range_));
854 ldata.range_max = laser_scan->range_max;
857 if (laser_min_range_ > 0.0) {
858 range_min = std::max(laser_scan->range_min,
static_cast<float>(laser_min_range_));
860 range_min = laser_scan->range_min;
864 ldata.ranges =
new double[ldata.range_count][2];
865 for (
int i = 0; i < ldata.range_count; i++) {
868 if (laser_scan->ranges[i] <= range_min) {
869 ldata.ranges[i][0] = ldata.range_max;
871 ldata.ranges[i][0] = laser_scan->ranges[i];
874 ldata.ranges[i][1] = angle_min +
875 (i * angle_increment);
878 lasers_update_[laser_index] =
false;
879 pf_odom_pose_ = 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);
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;
902 particle_cloud_pub_->publish(std::move(cloud_with_weights_msg));
906 AmclNode::getMaxWeightHyp(
907 std::vector<amcl_hyp_t> & hyps, amcl_hyp_t & max_weight_hyps,
908 int & max_weight_hyp)
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++)
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);
924 hyps[hyp_count].weight = weight;
925 hyps[hyp_count].pf_pose_mean = pose_mean;
926 hyps[hyp_count].pf_pose_cov = pose_cov;
928 if (hyps[hyp_count].weight > max_weight) {
929 max_weight = hyps[hyp_count].weight;
930 max_weight_hyp = hyp_count;
934 if (max_weight > 0.0) {
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]);
941 max_weight_hyps = hyps[max_weight_hyp];
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)
953 if (!initial_pose_is_known_) {
954 if (checkElapsedTime(2s, last_time_printed_msg_)) {
956 get_logger(),
"AMCL cannot publish a pose or update the transform. "
957 "Please set the initial pose...");
958 last_time_printed_msg_ = now();
963 auto p = std::make_unique<geometry_msgs::msg::PoseWithCovarianceStamped>();
965 p->header.frame_id = global_frame_id_;
966 p->header.stamp = laser_scan->header.stamp;
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]);
973 for (
int i = 0; i < 2; i++) {
974 for (
int j = 0; j < 2; j++) {
978 p->pose.covariance[6 * i + j] = set->cov.m[i][j];
981 p->pose.covariance[6 * 5 + 5] = set->cov.m[2][2];
983 for (
auto covariance_value : p->pose.covariance) {
984 temp += covariance_value;
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));
994 get_logger(),
"AMCL covariance or pose is NaN, likely due to an invalid "
995 "configuration or faulty sensor measurements! Pose is not available!");
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]);
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)
1011 geometry_msgs::msg::PoseStamped odom_to_map;
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],
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);
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());
1031 tf2::impl::Converter<true, false>::convert(odom_to_map.pose, latest_tf_);
1032 latest_tf_valid_ =
true;
1036 AmclNode::sendMapToOdomTransform(
const tf2::TimePoint & transform_expiration)
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);
1048 std::unique_ptr<nav2_amcl::Laser>
1049 AmclNode::createLaserObject()
1051 RCLCPP_INFO(get_logger(),
"createLaserObject");
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_);
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_);
1066 return std::make_unique<nav2_amcl::LikelihoodFieldModel>(
1067 z_hit_, z_rand_, sigma_hit_,
1068 laser_likelihood_max_dist_, max_beams_, map_);
1072 AmclNode::initParameters()
1074 double save_pose_rate;
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_);
1124 save_pose_period_ = tf2::durationFromSec(1.0 / save_pose_rate);
1125 transform_tolerance_ = tf2::durationFromSec(tmp_tol);
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_);
1131 last_time_printed_msg_ = now();
1134 if (laser_likelihood_max_dist_ < 0) {
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;
1140 if (max_particles_ < 0) {
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;
1147 if (min_particles_ < 0) {
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;
1154 if (min_particles_ > max_particles_) {
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_;
1161 if (resample_interval_ <= 0) {
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;
1168 if (always_reset_initial_pose_) {
1169 initial_pose_is_known_ =
false;
1177 rcl_interfaces::msg::SetParametersResult
1178 AmclNode::dynamicParametersCallback(
1179 std::vector<rclcpp::Parameter> parameters)
1181 std::lock_guard<std::recursive_mutex> cfl(mutex_);
1182 rcl_interfaces::msg::SetParametersResult result;
1183 double save_pose_rate;
1186 int max_particles = max_particles_;
1187 int min_particles = min_particles_;
1189 bool reinit_pf =
false;
1190 bool reinit_odom =
false;
1191 bool reinit_laser =
false;
1192 bool reinit_map =
false;
1194 for (
auto parameter : parameters) {
1195 const auto & param_type = parameter.get_type();
1196 const auto & param_name = parameter.get_name();
1198 if (param_type == ParameterType::PARAMETER_DOUBLE) {
1199 if (param_name ==
"alpha1") {
1200 alpha1_ = parameter.as_double();
1202 if (alpha1_ < 0.0) {
1204 get_logger(),
"You've set alpha1 to be negative,"
1205 " this isn't allowed, so the alpha1 will be set to be zero.");
1209 }
else if (param_name ==
"alpha2") {
1210 alpha2_ = parameter.as_double();
1212 if (alpha2_ < 0.0) {
1214 get_logger(),
"You've set alpha2 to be negative,"
1215 " this isn't allowed, so the alpha2 will be set to be zero.");
1219 }
else if (param_name ==
"alpha3") {
1220 alpha3_ = parameter.as_double();
1222 if (alpha3_ < 0.0) {
1224 get_logger(),
"You've set alpha3 to be negative,"
1225 " this isn't allowed, so the alpha3 will be set to be zero.");
1229 }
else if (param_name ==
"alpha4") {
1230 alpha4_ = parameter.as_double();
1232 if (alpha4_ < 0.0) {
1234 get_logger(),
"You've set alpha4 to be negative,"
1235 " this isn't allowed, so the alpha4 will be set to be zero.");
1239 }
else if (param_name ==
"alpha5") {
1240 alpha5_ = parameter.as_double();
1242 if (alpha5_ < 0.0) {
1244 get_logger(),
"You've set alpha5 to be negative,"
1245 " this isn't allowed, so the alpha5 will be set to be zero.");
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();
1273 }
else if (param_name ==
"pf_z") {
1274 pf_z_ = parameter.as_double();
1276 }
else if (param_name ==
"recovery_alpha_fast") {
1277 alpha_fast_ = parameter.as_double();
1279 }
else if (param_name ==
"recovery_alpha_slow") {
1280 alpha_slow_ = parameter.as_double();
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;
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();
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();
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();
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();
1348 }
else if (param_name ==
"min_particles") {
1349 min_particles_ = parameter.as_int();
1351 }
else if (param_name ==
"resample_interval") {
1352 resample_interval_ = parameter.as_int();
1358 if (min_particles_ > max_particles_) {
1361 "You've set min_particles to be greater than max particles,"
1362 " this isn't allowed.");
1364 max_particles_ = max_particles;
1365 min_particles_ = min_particles;
1366 result.successful =
false;
1376 initParticleFilter();
1381 motion_model_.reset();
1388 lasers_update_.clear();
1389 frame_to_laser_.clear();
1390 laser_scan_connection_.disconnect();
1391 laser_scan_filter_.reset();
1392 laser_scan_sub_.reset();
1394 initMessageFilters();
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));
1405 result.successful =
true;
1410 AmclNode::mapReceived(
const nav_msgs::msg::OccupancyGrid::SharedPtr msg)
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.");
1417 if (first_map_only_ && first_map_received_) {
1420 handleMapMessage(*msg);
1421 first_map_received_ =
true;
1425 AmclNode::handleMapMessage(
const nav_msgs::msg::OccupancyGrid & msg)
1427 std::lock_guard<std::recursive_mutex> cfl(mutex_);
1430 get_logger(),
"Received a %d X %d map @ %.3f m/pix",
1433 msg.info.resolution);
1434 if (msg.header.frame_id != global_frame_id_) {
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());
1441 freeMapDependentMemory();
1442 map_ = convertMap(msg);
1444 #if NEW_UNIFORM_SAMPLING
1445 createFreeSpaceVector();
1450 AmclNode::createFreeSpaceVector()
1452 int delta = freespace_downsampling_ ? 2 : 1;
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);
1466 AmclNode::freeMapDependentMemory()
1476 lasers_update_.clear();
1477 frame_to_laser_.clear();
1483 AmclNode::convertMap(
const nav_msgs::msg::OccupancyGrid & map_msg)
1485 map_t * map = map_alloc();
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;
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;
1503 map->cells[i].occ_state = 0;
1511 AmclNode::initTransforms()
1513 RCLCPP_INFO(get_logger(),
"initTransforms");
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(),
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());
1525 sent_first_transform_ =
false;
1526 latest_tf_valid_ =
false;
1527 latest_tf_ = tf2::Transform::getIdentity();
1531 AmclNode::initMessageFilters()
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);
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_);
1546 laser_scan_connection_ = laser_scan_filter_->registerCallback(
1548 &AmclNode::laserReceived,
1549 this, std::placeholders::_1));
1553 AmclNode::initPubSub()
1555 RCLCPP_INFO(get_logger(),
"initPubSub");
1557 particle_cloud_pub_ = create_publisher<nav2_msgs::msg::ParticleCloud>(
1559 rclcpp::SensorDataQoS());
1561 pose_pub_ = create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>(
1563 rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
1565 initial_pose_sub_ = create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
1566 "initialpose", rclcpp::SystemDefaultsQoS(),
1567 std::bind(&AmclNode::initialPoseReceived,
this, std::placeholders::_1));
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));
1573 RCLCPP_INFO(get_logger(),
"Subscribed to map topic.");
1577 AmclNode::initServices()
1579 global_loc_srv_ = create_service<std_srvs::srv::Empty>(
1580 "reinitialize_global_localization",
1581 std::bind(&AmclNode::globalLocalizationCallback,
this, _1, _2, _3));
1583 initial_guess_srv_ = create_service<nav2_msgs::srv::SetInitialPose>(
1585 std::bind(&AmclNode::initialPoseReceivedSrv,
this, _1, _2, _3));
1587 nomotion_update_srv_ = create_service<std_srvs::srv::Empty>(
1588 "request_nomotion_update",
1589 std::bind(&AmclNode::nomotionUpdateCallback,
this, _1, _2, _3));
1593 AmclNode::initOdometry()
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);
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);
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];
1613 motion_model_ = plugin_loader_.createSharedInstance(robot_model_type_);
1614 motion_model_->initialize(alpha1_, alpha2_, alpha3_, alpha4_, alpha5_);
1616 latest_odom_pose_ = geometry_msgs::msg::PoseStamped();
1620 AmclNode::initParticleFilter()
1624 min_particles_, max_particles_, alpha_slow_, alpha_fast_,
1625 (pf_init_model_fn_t)AmclNode::uniformPoseGenerator);
1626 pf_->pop_err = pf_err_;
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];
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];
1640 pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
1643 resample_count_ = 0;
1644 memset(&pf_odom_pose_, 0,
sizeof(pf_odom_pose_));
1648 AmclNode::initLaserScan()
1650 scan_error_count_ = 0;
1651 last_laser_received_ts_ = rclcpp::Time(0);
1656 #include "rclcpp_components/register_node_macro.hpp"