23 #include "nav2_amcl/amcl_node.hpp"
35 #include "nav2_amcl/angleutils.hpp"
36 #include "nav2_util/geometry_utils.hpp"
37 #include "nav2_amcl/pf/pf.hpp"
38 #include "nav2_util/string_utils.hpp"
39 #include "nav2_amcl/sensors/laser/laser.hpp"
40 #include "rclcpp/node_options.hpp"
41 #include "tf2/convert.hpp"
42 #include "tf2/utils.hpp"
43 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
44 #include "tf2/LinearMath/Transform.hpp"
45 #include "nav2_ros_common/tf2_factories.hpp"
47 #include "nav2_amcl/portable_utils.hpp"
48 #include "nav2_ros_common/validate_messages.hpp"
50 using rcl_interfaces::msg::ParameterType;
51 using namespace std::chrono_literals;
55 using nav2_util::geometry_utils::orientationAroundZAxis;
57 AmclNode::AmclNode(
const rclcpp::NodeOptions & options)
58 : nav2::LifecycleNode(
"amcl",
"", options)
60 RCLCPP_INFO(get_logger(),
"Creating");
74 AmclNode::on_configure(
const rclcpp_lifecycle::State & )
76 RCLCPP_INFO(get_logger(),
"Configuring");
77 callback_group_ = create_callback_group(
78 rclcpp::CallbackGroupType::MutuallyExclusive,
false);
87 executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
88 executor_->add_callback_group(callback_group_, get_node_base_interface());
89 executor_thread_ = std::make_unique<nav2::NodeThread>(executor_);
90 return nav2::CallbackReturn::SUCCESS;
94 AmclNode::on_activate(
const rclcpp_lifecycle::State & )
96 RCLCPP_INFO(get_logger(),
"Activating");
99 pose_pub_->on_activate();
100 particle_cloud_pub_->on_activate();
102 first_pose_sent_ =
false;
108 if (set_initial_pose_) {
110 if (initialize_at_saved_pose_) {
111 std::ifstream file(saved_pose_filepath_);
112 if (file.is_open()) {
115 "Both initial_pose parameters and saved pose file exist. Using ROS parameters.");
119 auto msg = std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>();
121 msg->header.stamp = now();
122 msg->header.frame_id = global_frame_id_;
123 msg->pose.pose.position.x = initial_pose_x_;
124 msg->pose.pose.position.y = initial_pose_y_;
125 msg->pose.pose.position.z = initial_pose_z_;
126 msg->pose.pose.orientation = orientationAroundZAxis(initial_pose_yaw_);
128 initialPoseReceived(msg);
129 }
else if (initialize_at_saved_pose_) {
130 geometry_msgs::msg::PoseWithCovarianceStamped saved_pose;
131 if (loadPoseFromFile(saved_pose)) {
132 auto msg = std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>(saved_pose);
133 initialPoseReceived(msg);
137 "initialize_at_saved_pose is true but no saved pose file found at: %s",
138 saved_pose_filepath_.c_str());
139 return nav2::CallbackReturn::FAILURE;
141 }
else if (init_pose_received_on_inactive) {
142 handleInitialPose(last_published_pose_);
146 if (save_pose_rate_ > 0.0) {
147 save_pose_timer_ = this->create_timer(
148 std::chrono::duration<double>(1.0 / save_pose_rate_),
149 std::bind(&AmclNode::savePoseTimerCallback,
this));
152 auto node = shared_from_this();
154 post_set_params_handler_ = node->add_post_set_parameters_callback(
156 &AmclNode::updateParametersCallback,
157 this, std::placeholders::_1));
158 on_set_params_handler_ = node->add_on_set_parameters_callback(
160 &AmclNode::validateParameterUpdatesCallback,
161 this, std::placeholders::_1));
166 return nav2::CallbackReturn::SUCCESS;
170 AmclNode::on_deactivate(
const rclcpp_lifecycle::State & )
172 RCLCPP_INFO(get_logger(),
"Deactivating");
177 pose_pub_->on_deactivate();
178 particle_cloud_pub_->on_deactivate();
181 if (save_pose_timer_) {
182 save_pose_timer_->cancel();
183 save_pose_timer_.reset();
187 remove_post_set_parameters_callback(post_set_params_handler_.get());
188 post_set_params_handler_.reset();
189 remove_on_set_parameters_callback(on_set_params_handler_.get());
190 on_set_params_handler_.reset();
195 return nav2::CallbackReturn::SUCCESS;
199 AmclNode::on_cleanup(
const rclcpp_lifecycle::State & )
201 RCLCPP_INFO(get_logger(),
"Cleaning up");
203 executor_thread_.reset();
207 global_loc_srv_.reset();
208 initial_guess_srv_.reset();
209 nomotion_update_srv_.reset();
210 initial_pose_sub_.reset();
211 laser_scan_connection_.disconnect();
212 tf_listener_.reset();
213 laser_scan_filter_.reset();
214 laser_scan_sub_.reset();
222 first_map_received_ =
false;
223 free_space_indices.resize(0);
226 tf_broadcaster_.reset();
231 particle_cloud_pub_.reset();
234 motion_model_.reset();
242 lasers_update_.clear();
243 frame_to_laser_.clear();
244 force_update_ =
true;
247 if (set_initial_pose_ && initial_pose_is_known_) {
251 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.x)));
255 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.y)));
259 rclcpp::ParameterValue(last_published_pose_.pose.pose.position.z)));
263 rclcpp::ParameterValue(tf2::getYaw(last_published_pose_.pose.pose.orientation))));
266 return nav2::CallbackReturn::SUCCESS;
270 AmclNode::on_shutdown(
const rclcpp_lifecycle::State & )
272 RCLCPP_INFO(get_logger(),
"Shutting down");
273 return nav2::CallbackReturn::SUCCESS;
277 AmclNode::checkElapsedTime(std::chrono::seconds check_interval, rclcpp::Time last_time)
279 rclcpp::Duration elapsed_time = now() - last_time;
280 if (elapsed_time.nanoseconds() * 1e-9 > check_interval.count()) {
286 #if NEW_UNIFORM_SAMPLING
287 std::vector<AmclNode::Point2D> AmclNode::free_space_indices;
291 AmclNode::getOdomPose(
292 geometry_msgs::msg::PoseStamped & odom_pose,
293 double & x,
double & y,
double & yaw,
294 const rclcpp::Time & sensor_timestamp,
const std::string & frame_id)
297 geometry_msgs::msg::PoseStamped ident;
298 ident.header.frame_id = frame_id;
299 ident.header.stamp = sensor_timestamp;
300 tf2::toMsg(tf2::Transform::getIdentity(), ident.pose);
303 tf_buffer_->transform(ident, odom_pose, odom_frame_id_);
304 }
catch (tf2::TransformException & e) {
306 if (scan_error_count_ % 20 == 0) {
308 get_logger(),
"(%d) consecutive laser scan transforms failed: (%s)", scan_error_count_,
314 scan_error_count_ = 0;
315 x = odom_pose.pose.position.x;
316 y = odom_pose.pose.position.y;
317 yaw = tf2::getYaw(odom_pose.pose.orientation);
323 AmclNode::uniformPoseGenerator(
void * arg)
327 #if NEW_UNIFORM_SAMPLING
328 unsigned int rand_index = drand48() * free_space_indices.size();
329 AmclNode::Point2D free_point = free_space_indices[rand_index];
331 p.v[0] = MAP_WXGX(map, free_point.x);
332 p.v[1] = MAP_WYGY(map, free_point.y);
333 p.v[2] = drand48() * 2 * M_PI - M_PI;
335 double min_x, max_x, min_y, max_y;
337 min_x = (map->size_x * map->scale) / 2.0 - map->origin_x;
338 max_x = (map->size_x * map->scale) / 2.0 + map->origin_x;
339 min_y = (map->size_y * map->scale) / 2.0 - map->origin_y;
340 max_y = (map->size_y * map->scale) / 2.0 + map->origin_y;
344 RCLCPP_DEBUG(get_logger(),
"Generating new uniform sample");
346 p.v[0] = min_x + drand48() * (max_x - min_x);
347 p.v[1] = min_y + drand48() * (max_y - min_y);
348 p.v[2] = drand48() * 2 * M_PI - M_PI;
351 i = MAP_GXWX(map, p.v[0]);
352 j = MAP_GYWY(map, p.v[1]);
353 if (MAP_VALID(map, i, j) && (map->cells[MAP_INDEX(map, i, j)].occ_state == -1)) {
362 AmclNode::globalLocalizationCallback(
363 const std::shared_ptr<rmw_request_id_t>,
364 const std::shared_ptr<std_srvs::srv::Empty::Request>,
365 std::shared_ptr<std_srvs::srv::Empty::Response>)
367 std::lock_guard<std::recursive_mutex> cfl(mutex_);
370 RCLCPP_ERROR(get_logger(),
"Cannot initialize globally before a map is received");
374 RCLCPP_INFO(get_logger(),
"Initializing with uniform distribution");
377 pf_, (pf_init_model_fn_t)AmclNode::uniformPoseGenerator,
378 reinterpret_cast<void *
>(map_));
379 RCLCPP_INFO(get_logger(),
"Global initialisation done!");
380 initial_pose_is_known_ =
true;
385 AmclNode::initialPoseReceivedSrv(
386 const std::shared_ptr<rmw_request_id_t>,
387 const std::shared_ptr<nav2_msgs::srv::SetInitialPose::Request> req,
388 std::shared_ptr<nav2_msgs::srv::SetInitialPose::Response>)
390 initialPoseReceived(std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>(req->pose));
395 AmclNode::nomotionUpdateCallback(
396 const std::shared_ptr<rmw_request_id_t>,
397 const std::shared_ptr<std_srvs::srv::Empty::Request>,
398 std::shared_ptr<std_srvs::srv::Empty::Response>)
400 RCLCPP_INFO(get_logger(),
"Requesting no-motion update");
401 force_update_ =
true;
405 AmclNode::initialPoseReceived(
406 const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr & msg)
408 std::lock_guard<std::recursive_mutex> cfl(mutex_);
410 RCLCPP_INFO(get_logger(),
"initialPoseReceived");
412 if (!nav2::validateMsg(*msg)) {
413 RCLCPP_ERROR(get_logger(),
"Received initialpose message is malformed. Rejecting.");
416 if (msg->header.frame_id != global_frame_id_) {
419 "Ignoring initial pose in frame \"%s\"; initial poses must be in the global frame, \"%s\"",
420 msg->header.frame_id.c_str(),
421 global_frame_id_.c_str());
424 if (first_map_received_) {
425 const int map_x = MAP_GXWX(map_, msg->pose.pose.position.x);
426 const int map_y = MAP_GYWY(map_, msg->pose.pose.position.y);
427 if (!MAP_VALID(map_, map_x, map_y)) {
429 get_logger(),
"Received initialpose from message is out of the size of map. Rejecting.");
435 last_published_pose_ = *msg;
438 init_pose_received_on_inactive =
true;
440 get_logger(),
"Received initial pose request, "
441 "but AMCL is not yet in the active state");
444 handleInitialPose(last_published_pose_);
448 AmclNode::handleInitialPose(geometry_msgs::msg::PoseWithCovarianceStamped & msg)
450 std::lock_guard<std::recursive_mutex> cfl(mutex_);
453 geometry_msgs::msg::TransformStamped tx_odom;
455 rclcpp::Time rclcpp_time = now();
456 tf2::TimePoint tf2_time(std::chrono::nanoseconds(rclcpp_time.nanoseconds()));
459 tx_odom = tf_buffer_->lookupTransform(
460 base_frame_id_, tf2_ros::fromMsg(msg.header.stamp),
461 base_frame_id_, tf2_time, odom_frame_id_);
462 }
catch (tf2::TransformException & e) {
467 if (sent_first_transform_) {
468 RCLCPP_WARN(get_logger(),
"Failed to transform initial pose in time (%s)", e.what());
470 tf2::impl::Converter<false, true>::convert(tf2::Transform::getIdentity(), tx_odom.transform);
473 tf2::Transform tx_odom_tf2;
474 tf2::impl::Converter<true, false>::convert(tx_odom.transform, tx_odom_tf2);
476 tf2::Transform pose_old;
477 tf2::impl::Converter<true, false>::convert(msg.pose.pose, pose_old);
479 tf2::Transform pose_new = pose_old * tx_odom_tf2;
484 get_logger(),
"Setting pose (%.6f): %.3f %.3f %.3f",
485 now().nanoseconds() * 1e-9,
486 pose_new.getOrigin().x(),
487 pose_new.getOrigin().y(),
488 tf2::getYaw(pose_new.getRotation()));
492 pf_init_pose_mean.v[0] = pose_new.getOrigin().x();
493 pf_init_pose_mean.v[1] = pose_new.getOrigin().y();
494 pf_init_pose_mean.v[2] = tf2::getYaw(pose_new.getRotation());
498 for (
int i = 0; i < 2; i++) {
499 for (
int j = 0; j < 2; j++) {
500 pf_init_pose_cov.m[i][j] = msg.pose.covariance[6 * i + j];
504 pf_init_pose_cov.m[2][2] = msg.pose.covariance[6 * 5 + 5];
506 pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
508 init_pose_received_on_inactive =
false;
509 initial_pose_is_known_ =
true;
513 AmclNode::laserReceived(sensor_msgs::msg::LaserScan::ConstSharedPtr laser_scan)
515 std::lock_guard<std::recursive_mutex> cfl(mutex_);
519 if (!active_) {
return;}
520 if (!first_map_received_) {
521 if (checkElapsedTime(2s, last_time_printed_msg_)) {
522 RCLCPP_WARN(get_logger(),
"Waiting for map....");
523 last_time_printed_msg_ = now();
528 std::string laser_scan_frame_id = laser_scan->header.frame_id;
529 last_laser_received_ts_ = now();
530 int laser_index = -1;
531 geometry_msgs::msg::PoseStamped laser_pose;
534 if (frame_to_laser_.find(laser_scan_frame_id) == frame_to_laser_.end()) {
535 if (!addNewScanner(laser_index, laser_scan, laser_scan_frame_id, laser_pose)) {
540 laser_index = frame_to_laser_[laser_scan->header.frame_id];
546 latest_odom_pose_, pose.v[0], pose.v[1], pose.v[2],
547 laser_scan->header.stamp, base_frame_id_))
549 RCLCPP_ERROR(get_logger(),
"Couldn't determine robot's pose associated with laser scan");
554 bool force_publication =
false;
557 pf_odom_pose_ = pose;
560 for (
unsigned int i = 0; i < lasers_update_.size(); i++) {
561 lasers_update_[i] =
true;
564 force_publication =
true;
568 if (shouldUpdateFilter(pose, delta)) {
569 for (
unsigned int i = 0; i < lasers_update_.size(); i++) {
570 lasers_update_[i] =
true;
573 if (lasers_update_[laser_index]) {
574 motion_model_->odometryUpdate(pf_, pose, delta);
576 force_update_ =
false;
579 bool resampled =
false;
582 if (lasers_update_[laser_index]) {
583 updateFilter(laser_index, laser_scan, pose);
586 if (!(++resample_count_ % resample_interval_)) {
587 pf_update_resample(pf_,
reinterpret_cast<void *
>(map_));
592 RCLCPP_DEBUG(get_logger(),
"Num samples: %d\n", set->sample_count);
594 if (!force_update_) {
595 publishParticleCloud(set);
598 if (resampled || force_publication || !first_pose_sent_) {
599 amcl_hyp_t max_weight_hyps;
600 std::vector<amcl_hyp_t> hyps;
601 int max_weight_hyp = -1;
602 if (getMaxWeightHyp(hyps, max_weight_hyps, max_weight_hyp)) {
603 publishAmclPose(laser_scan, hyps, max_weight_hyp);
604 calculateMaptoOdomTransform(laser_scan, hyps, max_weight_hyp);
606 if (tf_broadcast_ ==
true) {
609 auto stamp = tf2_ros::fromMsg(laser_scan->header.stamp);
610 tf2::TimePoint transform_expiration = stamp + transform_tolerance_;
611 sendMapToOdomTransform(transform_expiration);
612 sent_first_transform_ =
true;
615 RCLCPP_ERROR(get_logger(),
"No pose!");
617 }
else if (latest_tf_valid_) {
618 if (tf_broadcast_ ==
true) {
621 tf2::TimePoint transform_expiration = tf2_ros::fromMsg(laser_scan->header.stamp) +
622 transform_tolerance_;
623 sendMapToOdomTransform(transform_expiration);
628 bool AmclNode::addNewScanner(
630 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
631 const std::string & laser_scan_frame_id,
632 geometry_msgs::msg::PoseStamped & laser_pose)
634 lasers_.push_back(createLaserObject());
635 lasers_update_.push_back(
true);
636 laser_index = frame_to_laser_.size();
638 geometry_msgs::msg::PoseStamped ident;
639 ident.header.frame_id = laser_scan_frame_id;
640 ident.header.stamp = rclcpp::Time();
641 tf2::toMsg(tf2::Transform::getIdentity(), ident.pose);
643 tf_buffer_->transform(ident, laser_pose, base_frame_id_, transform_tolerance_);
644 }
catch (tf2::TransformException & e) {
646 get_logger(),
"Couldn't transform from %s to %s, "
647 "even though the message notifier is in use: (%s)",
648 laser_scan->header.frame_id.c_str(),
649 base_frame_id_.c_str(), e.what());
654 laser_pose_v.v[0] = laser_pose.pose.position.x;
655 laser_pose_v.v[1] = laser_pose.pose.position.y;
657 laser_pose_v.v[2] = 0;
658 lasers_[laser_index]->SetLaserPose(laser_pose_v);
659 frame_to_laser_[laser_scan->header.frame_id] = laser_index;
665 delta.v[0] = pose.v[0] - pf_odom_pose_.v[0];
666 delta.v[1] = pose.v[1] - pf_odom_pose_.v[1];
667 delta.v[2] = angleutils::angle_diff(pose.v[2], pf_odom_pose_.v[2]);
670 bool update = fabs(delta.v[0]) > d_thresh_ ||
671 fabs(delta.v[1]) > d_thresh_ ||
672 fabs(delta.v[2]) > a_thresh_;
673 update = update || force_update_;
677 bool AmclNode::updateFilter(
678 const int & laser_index,
679 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
683 ldata.laser = lasers_[laser_index].get();
684 ldata.range_count = laser_scan->ranges.size();
690 geometry_msgs::msg::QuaternionStamped min_q, inc_q;
691 min_q.header.stamp = laser_scan->header.stamp;
692 min_q.header.frame_id = laser_scan->header.frame_id;
693 min_q.quaternion = orientationAroundZAxis(laser_scan->angle_min);
695 inc_q.header = min_q.header;
696 inc_q.quaternion = orientationAroundZAxis(laser_scan->angle_min + laser_scan->angle_increment);
698 tf_buffer_->transform(min_q, min_q, base_frame_id_);
699 tf_buffer_->transform(inc_q, inc_q, base_frame_id_);
700 }
catch (tf2::TransformException & e) {
702 get_logger(),
"Unable to transform min/max laser angles into base frame: %s",
706 double angle_min = tf2::getYaw(min_q.quaternion);
707 double angle_increment = tf2::getYaw(inc_q.quaternion) - angle_min;
710 angle_increment = fmod(angle_increment + 5 * M_PI, 2 * M_PI) - M_PI;
713 get_logger(),
"Laser %d angles in base frame: min: %.3f inc: %.3f", laser_index, angle_min,
717 if (laser_scan->range_max <= 0.0) {
719 get_logger(),
"wrong range_max of laser_scan data: %f. The message could be malformed."
720 " Ignore this message and stop updating.",
721 laser_scan->range_max);
726 if (laser_max_range_ > 0.0) {
727 ldata.range_max = std::min(laser_scan->range_max,
static_cast<float>(laser_max_range_));
729 ldata.range_max = laser_scan->range_max;
732 if (laser_min_range_ > 0.0) {
733 range_min = std::max(laser_scan->range_min,
static_cast<float>(laser_min_range_));
735 range_min = laser_scan->range_min;
739 ldata.ranges =
new double[ldata.range_count][2];
740 for (
int i = 0; i < ldata.range_count; i++) {
743 if (laser_scan->ranges[i] <= range_min) {
744 ldata.ranges[i][0] = ldata.range_max;
746 ldata.ranges[i][0] = laser_scan->ranges[i];
749 ldata.ranges[i][1] = angle_min +
750 (i * angle_increment);
753 lasers_update_[laser_index] =
false;
754 pf_odom_pose_ = pose;
762 if (!initial_pose_is_known_) {
return;}
763 auto cloud_with_weights_msg = std::make_unique<nav2_msgs::msg::ParticleCloud>();
764 cloud_with_weights_msg->header.stamp = this->now();
765 cloud_with_weights_msg->header.frame_id = global_frame_id_;
766 cloud_with_weights_msg->particles.resize(set->sample_count);
768 for (
int i = 0; i < set->sample_count; i++) {
769 cloud_with_weights_msg->particles[i].pose.position.x = set->samples[i].pose.v[0];
770 cloud_with_weights_msg->particles[i].pose.position.y = set->samples[i].pose.v[1];
771 cloud_with_weights_msg->particles[i].pose.position.z = 0;
772 cloud_with_weights_msg->particles[i].pose.orientation = orientationAroundZAxis(
773 set->samples[i].pose.v[2]);
774 cloud_with_weights_msg->particles[i].weight = set->samples[i].weight;
777 particle_cloud_pub_->publish(std::move(cloud_with_weights_msg));
781 AmclNode::getMaxWeightHyp(
782 std::vector<amcl_hyp_t> & hyps, amcl_hyp_t & max_weight_hyps,
783 int & max_weight_hyp)
786 double max_weight = 0.0;
787 hyps.resize(pf_->sets[pf_->current_set].cluster_count);
788 for (
int hyp_count = 0;
789 hyp_count < pf_->sets[pf_->current_set].cluster_count; hyp_count++)
794 if (!pf_get_cluster_stats(pf_, hyp_count, &weight, &pose_mean, &pose_cov)) {
795 RCLCPP_ERROR(get_logger(),
"Couldn't get stats on cluster %d", hyp_count);
799 hyps[hyp_count].weight = weight;
800 hyps[hyp_count].pf_pose_mean = pose_mean;
801 hyps[hyp_count].pf_pose_cov = pose_cov;
803 if (hyps[hyp_count].weight > max_weight) {
804 max_weight = hyps[hyp_count].weight;
805 max_weight_hyp = hyp_count;
809 if (max_weight > 0.0) {
811 get_logger(),
"Max weight pose: %.3f %.3f %.3f",
812 hyps[max_weight_hyp].pf_pose_mean.v[0],
813 hyps[max_weight_hyp].pf_pose_mean.v[1],
814 hyps[max_weight_hyp].pf_pose_mean.v[2]);
816 max_weight_hyps = hyps[max_weight_hyp];
823 AmclNode::publishAmclPose(
824 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
825 const std::vector<amcl_hyp_t> & hyps,
const int & max_weight_hyp)
828 if (!initial_pose_is_known_) {
829 if (checkElapsedTime(2s, last_time_printed_msg_)) {
831 get_logger(),
"AMCL cannot publish a pose or update the transform. "
832 "Please set the initial pose...");
833 last_time_printed_msg_ = now();
838 auto p = std::make_unique<geometry_msgs::msg::PoseWithCovarianceStamped>();
840 p->header.frame_id = global_frame_id_;
841 p->header.stamp = laser_scan->header.stamp;
843 p->pose.pose.position.x = hyps[max_weight_hyp].pf_pose_mean.v[0];
844 p->pose.pose.position.y = hyps[max_weight_hyp].pf_pose_mean.v[1];
845 p->pose.pose.orientation = orientationAroundZAxis(hyps[max_weight_hyp].pf_pose_mean.v[2]);
848 for (
int i = 0; i < 2; i++) {
849 for (
int j = 0; j < 2; j++) {
853 p->pose.covariance[6 * i + j] = set->cov.m[i][j];
856 p->pose.covariance[6 * 5 + 5] = set->cov.m[2][2];
858 for (
auto covariance_value : p->pose.covariance) {
859 temp += covariance_value;
861 temp += p->pose.pose.position.x + p->pose.pose.position.y;
862 if (!std::isnan(temp)) {
863 RCLCPP_DEBUG(get_logger(),
"Publishing pose");
864 last_published_pose_ = *p;
865 first_pose_sent_ =
true;
866 pose_pub_->publish(std::move(p));
869 get_logger(),
"AMCL covariance or pose is NaN, likely due to an invalid "
870 "configuration or faulty sensor measurements! Pose is not available!");
874 get_logger(),
"New pose: %6.3f %6.3f %6.3f",
875 hyps[max_weight_hyp].pf_pose_mean.v[0],
876 hyps[max_weight_hyp].pf_pose_mean.v[1],
877 hyps[max_weight_hyp].pf_pose_mean.v[2]);
881 AmclNode::calculateMaptoOdomTransform(
882 const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
883 const std::vector<amcl_hyp_t> & hyps,
const int & max_weight_hyp)
886 geometry_msgs::msg::PoseStamped odom_to_map;
889 q.setRPY(0, 0, hyps[max_weight_hyp].pf_pose_mean.v[2]);
890 tf2::Transform tmp_tf(q, tf2::Vector3(
891 hyps[max_weight_hyp].pf_pose_mean.v[0],
892 hyps[max_weight_hyp].pf_pose_mean.v[1],
895 geometry_msgs::msg::PoseStamped tmp_tf_stamped;
896 tmp_tf_stamped.header.frame_id = base_frame_id_;
897 tmp_tf_stamped.header.stamp = laser_scan->header.stamp;
898 tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);
900 tf_buffer_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);
901 }
catch (tf2::TransformException & e) {
902 RCLCPP_DEBUG(get_logger(),
"Failed to subtract base to odom transform: (%s)", e.what());
906 tf2::impl::Converter<true, false>::convert(odom_to_map.pose, latest_tf_);
907 latest_tf_valid_ =
true;
911 AmclNode::sendMapToOdomTransform(
const tf2::TimePoint & transform_expiration)
914 if (!initial_pose_is_known_) {
return;}
915 geometry_msgs::msg::TransformStamped tmp_tf_stamped;
916 tmp_tf_stamped.header.frame_id = global_frame_id_;
917 tmp_tf_stamped.header.stamp = tf2_ros::toMsg(transform_expiration);
918 tmp_tf_stamped.child_frame_id = odom_frame_id_;
919 tf2::impl::Converter<false, true>::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);
920 tf_broadcaster_->sendTransform(tmp_tf_stamped);
923 std::unique_ptr<nav2_amcl::Laser>
924 AmclNode::createLaserObject()
926 RCLCPP_INFO(get_logger(),
"createLaserObject");
928 if (sensor_model_type_ ==
"beam") {
929 return std::make_unique<nav2_amcl::BeamModel>(
930 z_hit_, z_short_, z_max_, z_rand_, sigma_hit_, lambda_short_,
931 0.0, max_beams_, map_);
934 if (sensor_model_type_ ==
"likelihood_field_prob") {
935 return std::make_unique<nav2_amcl::LikelihoodFieldModelProb>(
936 z_hit_, z_rand_, sigma_hit_,
937 laser_likelihood_max_dist_, do_beamskip_, beam_skip_distance_, beam_skip_threshold_,
938 beam_skip_error_threshold_, max_beams_, map_);
941 return std::make_unique<nav2_amcl::LikelihoodFieldModel>(
942 z_hit_, z_rand_, sigma_hit_,
943 laser_likelihood_max_dist_, max_beams_, map_);
947 AmclNode::initParameters()
951 alpha1_ = this->declare_or_get_parameter(
"alpha1", 0.2);
952 alpha2_ = this->declare_or_get_parameter(
"alpha2", 0.2);
953 alpha3_ = this->declare_or_get_parameter(
"alpha3", 0.2);
954 alpha4_ = this->declare_or_get_parameter(
"alpha4", 0.2);
955 alpha5_ = this->declare_or_get_parameter(
"alpha5", 0.2);
956 base_frame_id_ = this->declare_or_get_parameter(
"base_frame_id", std::string{
"base_footprint"});
957 beam_skip_distance_ = this->declare_or_get_parameter(
"beam_skip_distance", 0.5);
958 beam_skip_error_threshold_ = this->declare_or_get_parameter(
"beam_skip_error_threshold", 0.9);
959 beam_skip_threshold_ = this->declare_or_get_parameter(
"beam_skip_threshold", 0.3);
960 do_beamskip_ = this->declare_or_get_parameter(
"do_beamskip",
false);
961 global_frame_id_ = this->declare_or_get_parameter(
"global_frame_id", std::string{
"map"});
962 lambda_short_ = this->declare_or_get_parameter(
"lambda_short", 0.1);
963 laser_likelihood_max_dist_ = this->declare_or_get_parameter(
"laser_likelihood_max_dist", 2.0);
964 laser_max_range_ = this->declare_or_get_parameter(
"laser_max_range", 100.0);
965 laser_min_range_ = this->declare_or_get_parameter(
"laser_min_range", -1.0);
966 sensor_model_type_ = this->declare_or_get_parameter(
967 "laser_model_type", std::string{
"likelihood_field"});
968 set_initial_pose_ = this->declare_or_get_parameter(
"set_initial_pose",
false);
969 initial_pose_x_ = this->declare_or_get_parameter(
"initial_pose.x", 0.0);
970 initial_pose_y_ = this->declare_or_get_parameter(
"initial_pose.y", 0.0);
971 initial_pose_z_ = this->declare_or_get_parameter(
"initial_pose.z", 0.0);
972 initial_pose_yaw_ = this->declare_or_get_parameter(
"initial_pose.yaw", 0.0);
973 max_beams_ = this->declare_or_get_parameter(
"max_beams", 60);
974 max_particles_ = this->declare_or_get_parameter(
"max_particles", 2000);
975 min_particles_ = this->declare_or_get_parameter(
"min_particles", 500);
976 odom_frame_id_ = this->declare_or_get_parameter(
"odom_frame_id", std::string{
"odom"});
977 pf_err_ = this->declare_or_get_parameter(
"pf_err", 0.05);
978 pf_z_ = this->declare_or_get_parameter(
"pf_z", 0.99);
979 alpha_fast_ = this->declare_or_get_parameter(
"recovery_alpha_fast", 0.0);
980 alpha_slow_ = this->declare_or_get_parameter(
"recovery_alpha_slow", 0.0);
981 resample_interval_ = this->declare_or_get_parameter(
"resample_interval", 1);
982 robot_model_type_ = this->declare_or_get_parameter(
983 "robot_model_type", std::string{
"nav2_amcl::DifferentialMotionModel"});
984 save_pose_rate_ = this->declare_or_get_parameter(
"save_pose_rate", 0.5);
985 initialize_at_saved_pose_ = this->declare_or_get_parameter(
"initialize_at_saved_pose",
false);
986 saved_pose_filepath_ = this->declare_or_get_parameter(
987 "saved_pose_filepath", std::string(
"/tmp/amcl_saved_pose"));
988 sigma_hit_ = this->declare_or_get_parameter(
"sigma_hit", 0.2);
989 tf_broadcast_ = this->declare_or_get_parameter(
"tf_broadcast",
true);
990 tmp_tol = this->declare_or_get_parameter(
"transform_tolerance", 1.0);
991 a_thresh_ = this->declare_or_get_parameter(
"update_min_a", 0.2);
992 d_thresh_ = this->declare_or_get_parameter(
"update_min_d", 0.25);
993 z_hit_ = this->declare_or_get_parameter(
"z_hit", 0.5);
994 z_max_ = this->declare_or_get_parameter(
"z_max", 0.05);
995 z_rand_ = this->declare_or_get_parameter(
"z_rand", 0.5);
996 z_short_ = this->declare_or_get_parameter(
"z_short", 0.05);
997 first_map_only_ = this->declare_or_get_parameter(
"first_map_only",
false);
998 always_reset_initial_pose_ = this->declare_or_get_parameter(
"always_reset_initial_pose",
false);
999 scan_topic_ = this->declare_or_get_parameter(
"scan_topic", std::string{
"scan"});
1000 map_topic_ = this->declare_or_get_parameter(
"map_topic", std::string{
"map"});
1001 freespace_downsampling_ = this->declare_or_get_parameter(
"freespace_downsampling",
false);
1002 allow_parameter_qos_overrides_ = this->declare_or_get_parameter(
1003 "allow_parameter_qos_overrides",
true);
1004 random_seed_ = this->declare_or_get_parameter(
"random_seed", -1);
1006 transform_tolerance_ = tf2::durationFromSec(tmp_tol);
1007 last_time_printed_msg_ = now();
1010 if (laser_likelihood_max_dist_ < 0) {
1012 get_logger(),
"You've set laser_likelihood_max_dist to be negative,"
1013 " this isn't allowed so it will be set to default value 2.0.");
1014 laser_likelihood_max_dist_ = 2.0;
1016 if (max_particles_ < 0) {
1018 get_logger(),
"You've set max_particles to be negative,"
1019 " this isn't allowed so it will be set to default value 2000.");
1020 max_particles_ = 2000;
1023 if (min_particles_ < 0) {
1025 get_logger(),
"You've set min_particles to be negative,"
1026 " this isn't allowed so it will be set to default value 500.");
1027 min_particles_ = 500;
1030 if (min_particles_ > max_particles_) {
1032 get_logger(),
"You've set min_particles to be greater than max particles,"
1033 " this isn't allowed so max_particles will be set to min_particles.");
1034 max_particles_ = min_particles_;
1037 if (resample_interval_ <= 0) {
1039 get_logger(),
"You've set resample_interval to be zero or negative,"
1040 " this isn't allowed so it will be set to default value to 1.");
1041 resample_interval_ = 1;
1044 if (always_reset_initial_pose_) {
1045 initial_pose_is_known_ =
false;
1049 rcl_interfaces::msg::SetParametersResult AmclNode::validateParameterUpdatesCallback(
1050 const std::vector<rclcpp::Parameter> & parameters)
1052 rcl_interfaces::msg::SetParametersResult result;
1053 result.successful =
true;
1054 for (
const auto & parameter : parameters) {
1055 const auto & param_type = parameter.get_type();
1056 const auto & param_name = parameter.get_name();
1057 if (param_name.find(
'.') != std::string::npos) {
1060 if (param_type == ParameterType::PARAMETER_DOUBLE) {
1061 if (param_name ==
"save_pose_rate") {
1064 }
else if (parameter.as_double() < 0.0 &&
1065 (param_name !=
"laser_min_range" || param_name !=
"laser_max_range"))
1068 get_logger(),
"The value of parameter '%s' is incorrectly set to %f, "
1069 "it should be >=0. Ignoring parameter update.",
1070 param_name.c_str(), parameter.as_double());
1071 result.successful =
false;
1073 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
1074 if (parameter.as_int() <= 0.0 && param_name ==
"resample_interval") {
1076 get_logger(),
"The value of resample_interval is incorrectly set, "
1077 "it should be >0. Ignoring parameter update.");
1078 result.successful =
false;
1079 }
else if (parameter.as_int() < 0.0) {
1081 get_logger(),
"The value of parameter '%s' is incorrectly set to %ld, "
1082 "it should be >=0. Ignoring parameter update.",
1083 param_name.c_str(), parameter.as_int());
1084 result.successful =
false;
1085 }
else if (param_name ==
"max_particles" && parameter.as_int() < min_particles_) {
1087 get_logger(),
"The value of max_particles is incorrectly set, "
1088 "it should be larger than min_particles. Ignoring parameter update.");
1089 result.successful =
false;
1090 }
else if (param_name ==
"min_particles" && parameter.as_int() > max_particles_) {
1092 get_logger(),
"The value of min_particles is incorrectly set, "
1093 "it should be smaller than max particles. Ignoring parameter update.");
1094 result.successful =
false;
1102 AmclNode::updateParametersCallback(
1103 const std::vector<rclcpp::Parameter> & parameters)
1105 std::lock_guard<std::recursive_mutex> cfl(mutex_);
1107 bool reinit_pf =
false;
1108 bool reinit_odom =
false;
1109 bool reinit_laser =
false;
1110 bool reinit_map =
false;
1112 for (
const auto & parameter : parameters) {
1113 const auto & param_type = parameter.get_type();
1114 const auto & param_name = parameter.get_name();
1115 if (param_name.find(
'.') != std::string::npos) {
1118 if (param_type == ParameterType::PARAMETER_DOUBLE) {
1119 if (param_name ==
"alpha1") {
1120 alpha1_ = parameter.as_double();
1122 }
else if (param_name ==
"alpha2") {
1123 alpha2_ = parameter.as_double();
1125 }
else if (param_name ==
"alpha3") {
1126 alpha3_ = parameter.as_double();
1128 }
else if (param_name ==
"alpha4") {
1129 alpha4_ = parameter.as_double();
1131 }
else if (param_name ==
"alpha5") {
1132 alpha5_ = parameter.as_double();
1134 }
else if (param_name ==
"beam_skip_distance") {
1135 beam_skip_distance_ = parameter.as_double();
1136 reinit_laser =
true;
1137 }
else if (param_name ==
"beam_skip_error_threshold") {
1138 beam_skip_error_threshold_ = parameter.as_double();
1139 reinit_laser =
true;
1140 }
else if (param_name ==
"beam_skip_threshold") {
1141 beam_skip_threshold_ = parameter.as_double();
1142 reinit_laser =
true;
1143 }
else if (param_name ==
"lambda_short") {
1144 lambda_short_ = parameter.as_double();
1145 reinit_laser =
true;
1146 }
else if (param_name ==
"laser_likelihood_max_dist") {
1147 laser_likelihood_max_dist_ = parameter.as_double();
1148 reinit_laser =
true;
1149 }
else if (param_name ==
"laser_max_range") {
1150 laser_max_range_ = parameter.as_double();
1151 reinit_laser =
true;
1152 }
else if (param_name ==
"laser_min_range") {
1153 laser_min_range_ = parameter.as_double();
1154 reinit_laser =
true;
1155 }
else if (param_name ==
"pf_err") {
1156 pf_err_ = parameter.as_double();
1158 }
else if (param_name ==
"pf_z") {
1159 pf_z_ = parameter.as_double();
1161 }
else if (param_name ==
"recovery_alpha_fast") {
1162 alpha_fast_ = parameter.as_double();
1164 }
else if (param_name ==
"recovery_alpha_slow") {
1165 alpha_slow_ = parameter.as_double();
1167 }
else if (param_name ==
"save_pose_rate") {
1168 save_pose_rate_ = parameter.as_double();
1169 }
else if (param_name ==
"sigma_hit") {
1170 sigma_hit_ = parameter.as_double();
1171 reinit_laser =
true;
1172 }
else if (param_name ==
"transform_tolerance") {
1173 double tmp_tol = parameter.as_double();
1174 transform_tolerance_ = tf2::durationFromSec(tmp_tol);
1175 reinit_laser =
true;
1176 }
else if (param_name ==
"update_min_a") {
1177 a_thresh_ = parameter.as_double();
1178 }
else if (param_name ==
"update_min_d") {
1179 d_thresh_ = parameter.as_double();
1180 }
else if (param_name ==
"z_hit") {
1181 z_hit_ = parameter.as_double();
1182 reinit_laser =
true;
1183 }
else if (param_name ==
"z_max") {
1184 z_max_ = parameter.as_double();
1185 reinit_laser =
true;
1186 }
else if (param_name ==
"z_rand") {
1187 z_rand_ = parameter.as_double();
1188 reinit_laser =
true;
1189 }
else if (param_name ==
"z_short") {
1190 z_short_ = parameter.as_double();
1191 reinit_laser =
true;
1193 }
else if (param_type == ParameterType::PARAMETER_STRING) {
1194 if (param_name ==
"base_frame_id") {
1195 base_frame_id_ = parameter.as_string();
1196 }
else if (param_name ==
"global_frame_id") {
1197 global_frame_id_ = parameter.as_string();
1198 }
else if (param_name ==
"map_topic") {
1199 map_topic_ = parameter.as_string();
1201 }
else if (param_name ==
"laser_model_type") {
1202 sensor_model_type_ = parameter.as_string();
1203 reinit_laser =
true;
1204 }
else if (param_name ==
"odom_frame_id") {
1205 odom_frame_id_ = parameter.as_string();
1206 reinit_laser =
true;
1207 }
else if (param_name ==
"scan_topic") {
1208 scan_topic_ = parameter.as_string();
1209 reinit_laser =
true;
1210 }
else if (param_name ==
"robot_model_type") {
1211 robot_model_type_ = parameter.as_string();
1213 }
else if (param_name ==
"saved_pose_filepath") {
1214 saved_pose_filepath_ = parameter.as_string();
1216 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
1217 if (param_name ==
"do_beamskip") {
1218 do_beamskip_ = parameter.as_bool();
1219 reinit_laser =
true;
1220 }
else if (param_name ==
"tf_broadcast") {
1221 tf_broadcast_ = parameter.as_bool();
1222 }
else if (param_name ==
"set_initial_pose") {
1223 set_initial_pose_ = parameter.as_bool();
1224 }
else if (param_name ==
"first_map_only") {
1225 first_map_only_ = parameter.as_bool();
1226 }
else if (param_name ==
"initialize_at_saved_pose") {
1227 initialize_at_saved_pose_ = parameter.as_bool();
1229 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
1230 if (param_name ==
"max_beams") {
1231 max_beams_ = parameter.as_int();
1232 reinit_laser =
true;
1233 }
else if (param_name ==
"max_particles") {
1234 max_particles_ = parameter.as_int();
1236 }
else if (param_name ==
"min_particles") {
1237 min_particles_ = parameter.as_int();
1239 }
else if (param_name ==
"resample_interval") {
1240 resample_interval_ = parameter.as_int();
1251 initParticleFilter();
1256 motion_model_.reset();
1263 lasers_update_.clear();
1264 frame_to_laser_.clear();
1265 laser_scan_connection_.disconnect();
1266 laser_scan_filter_.reset();
1267 laser_scan_sub_.reset();
1269 initMessageFilters();
1276 first_map_received_ =
false;
1277 map_sub_ = create_subscription<nav_msgs::msg::OccupancyGrid>(
1279 std::bind(&AmclNode::mapReceived,
this, std::placeholders::_1),
1285 AmclNode::mapReceived(
const nav_msgs::msg::OccupancyGrid::ConstSharedPtr & msg)
1287 RCLCPP_DEBUG(get_logger(),
"AmclNode: A new map was received.");
1288 if (!nav2::validateMsg(*msg)) {
1289 RCLCPP_ERROR(get_logger(),
"Received map message is malformed. Rejecting.");
1292 if (first_map_only_ && first_map_received_) {
1295 handleMapMessage(*msg);
1296 first_map_received_ =
true;
1300 AmclNode::handleMapMessage(
const nav_msgs::msg::OccupancyGrid & msg)
1302 std::lock_guard<std::recursive_mutex> cfl(mutex_);
1305 get_logger(),
"Received a %d X %d map @ %.3f m/pix",
1308 msg.info.resolution);
1309 if (msg.header.frame_id != global_frame_id_) {
1311 get_logger(),
"Frame_id of map received:'%s' doesn't match global_frame_id:'%s'. This could"
1312 " cause issues with reading published topics",
1313 msg.header.frame_id.c_str(),
1314 global_frame_id_.c_str());
1316 freeMapDependentMemory();
1317 map_ = convertMap(msg);
1319 #if NEW_UNIFORM_SAMPLING
1320 createFreeSpaceVector();
1325 AmclNode::createFreeSpaceVector()
1327 int delta = freespace_downsampling_ ? 2 : 1;
1329 free_space_indices.resize(0);
1330 for (
int i = 0; i < map_->size_x; i += delta) {
1331 for (
int j = 0; j < map_->size_y; j += delta) {
1332 if (map_->cells[MAP_INDEX(map_, i, j)].occ_state == -1) {
1333 AmclNode::Point2D point = {i, j};
1334 free_space_indices.push_back(point);
1341 AmclNode::freeMapDependentMemory()
1351 lasers_update_.clear();
1352 frame_to_laser_.clear();
1358 AmclNode::convertMap(
const nav_msgs::msg::OccupancyGrid & map_msg)
1360 map_t * map = map_alloc();
1362 map->size_x = map_msg.info.width;
1363 map->size_y = map_msg.info.height;
1364 map->scale = map_msg.info.resolution;
1365 map->origin_x = map_msg.info.origin.position.x + (map->size_x / 2) * map->scale;
1366 map->origin_y = map_msg.info.origin.position.y + (map->size_y / 2) * map->scale;
1372 for (
int i = 0; i < map->size_x * map->size_y; i++) {
1373 if (map_msg.data[i] == 0) {
1374 map->cells[i].occ_state = -1;
1375 }
else if (map_msg.data[i] == 100) {
1376 map->cells[i].occ_state = +1;
1378 map->cells[i].occ_state = 0;
1386 AmclNode::initTransforms()
1388 RCLCPP_INFO(get_logger(),
"initTransforms");
1391 tf_buffer_ = nav2::create_transform_buffer(
this, callback_group_);
1392 tf_listener_ = nav2::create_transform_listener(*tf_buffer_,
this,
true);
1393 tf_broadcaster_ = nav2::create_transform_broadcaster(shared_from_this());
1395 sent_first_transform_ =
false;
1396 latest_tf_valid_ =
false;
1397 latest_tf_ = tf2::Transform::getIdentity();
1401 AmclNode::initMessageFilters()
1403 auto sub_opt = nav2::interfaces::createSubscriptionOptions(
1404 scan_topic_, allow_parameter_qos_overrides_);
1406 #if RCLCPP_VERSION_GTE(29, 6, 0)
1407 laser_scan_sub_ = std::make_unique<message_filters::Subscriber<sensor_msgs::msg::LaserScan>>(
1410 laser_scan_sub_ = std::make_unique<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
1411 rclcpp_lifecycle::LifecycleNode>>(
1412 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(shared_from_this()),
1416 laser_scan_filter_ = nav2::create_message_filter<sensor_msgs::msg::LaserScan>(
1417 *laser_scan_sub_, *tf_buffer_, odom_frame_id_, 10,
1418 this, transform_tolerance_);
1421 laser_scan_connection_ = laser_scan_filter_->registerCallback(
1422 std::bind(&AmclNode::laserReceived,
this, std::placeholders::_1));
1426 AmclNode::initPubSub()
1428 RCLCPP_INFO(get_logger(),
"initPubSub");
1430 particle_cloud_pub_ = create_publisher<nav2_msgs::msg::ParticleCloud>(
1434 pose_pub_ = create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>(
1438 initial_pose_sub_ = create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
1440 std::bind(&AmclNode::initialPoseReceived,
this, std::placeholders::_1));
1442 map_sub_ = create_subscription<nav_msgs::msg::OccupancyGrid>(
1444 std::bind(&AmclNode::mapReceived,
this, std::placeholders::_1),
1447 RCLCPP_INFO(get_logger(),
"Subscribed to map topic.");
1451 AmclNode::initServices()
1453 global_loc_srv_ = create_service<std_srvs::srv::Empty>(
1454 "reinitialize_global_localization",
1456 &AmclNode::globalLocalizationCallback,
this, std::placeholders::_1,
1457 std::placeholders::_2, std::placeholders::_3));
1459 initial_guess_srv_ = create_service<nav2_msgs::srv::SetInitialPose>(
1462 &AmclNode::initialPoseReceivedSrv,
this, std::placeholders::_1, std::placeholders::_2,
1463 std::placeholders::_3));
1465 nomotion_update_srv_ = create_service<std_srvs::srv::Empty>(
1466 "request_nomotion_update",
1468 &AmclNode::nomotionUpdateCallback,
this, std::placeholders::_1, std::placeholders::_2,
1469 std::placeholders::_3));
1473 AmclNode::initOdometry()
1479 init_pose_[0] = last_published_pose_.pose.pose.position.x;
1480 init_pose_[1] = last_published_pose_.pose.pose.position.y;
1481 init_pose_[2] = tf2::getYaw(last_published_pose_.pose.pose.orientation);
1483 if (!initial_pose_is_known_) {
1484 init_cov_[0] = 0.5 * 0.5;
1485 init_cov_[1] = 0.5 * 0.5;
1486 init_cov_[2] = (M_PI / 12.0) * (M_PI / 12.0);
1488 init_cov_[0] = last_published_pose_.pose.covariance[0];
1489 init_cov_[1] = last_published_pose_.pose.covariance[7];
1490 init_cov_[2] = last_published_pose_.pose.covariance[35];
1493 motion_model_ = plugin_loader_.createSharedInstance(robot_model_type_);
1494 motion_model_->initialize(alpha1_, alpha2_, alpha3_, alpha4_, alpha5_);
1496 latest_odom_pose_ = geometry_msgs::msg::PoseStamped();
1500 AmclNode::initParticleFilter()
1504 min_particles_, max_particles_, alpha_slow_, alpha_fast_,
1505 (pf_init_model_fn_t)AmclNode::uniformPoseGenerator);
1509 if (random_seed_ >= 0) {
1512 srand48(
static_cast<int>(random_seed_));
1514 srand48(
static_cast<int>(std::time(
nullptr)));
1517 pf_->pop_err = pf_err_;
1522 pf_init_pose_mean.v[0] = init_pose_[0];
1523 pf_init_pose_mean.v[1] = init_pose_[1];
1524 pf_init_pose_mean.v[2] = init_pose_[2];
1527 pf_init_pose_cov.m[0][0] = init_cov_[0];
1528 pf_init_pose_cov.m[1][1] = init_cov_[1];
1529 pf_init_pose_cov.m[2][2] = init_cov_[2];
1531 pf_init(pf_, pf_init_pose_mean, pf_init_pose_cov);
1534 resample_count_ = 0;
1535 memset(&pf_odom_pose_, 0,
sizeof(pf_odom_pose_));
1539 AmclNode::initLaserScan()
1541 scan_error_count_ = 0;
1542 last_laser_received_ts_ = rclcpp::Time(0);
1546 AmclNode::savePoseTimerCallback()
1548 if (!active_ || !first_pose_sent_) {
1555 AmclNode::savePoseToFile()
1557 std::string tmp_path = saved_pose_filepath_ +
".tmp";
1559 std::ofstream file(tmp_path);
1560 if (!file.is_open()) {
1562 get_logger(),
"Failed to open pose file for writing: %s",
1567 auto & pose = last_published_pose_;
1568 double timestamp = pose.header.stamp.sec +
1569 static_cast<double>(pose.header.stamp.nanosec) / 1e9;
1570 file << std::fixed << std::setprecision(9);
1571 file <<
"timestamp: " << timestamp <<
"\n";
1572 file <<
"frame_id: " << pose.header.frame_id <<
"\n";
1573 file << std::setprecision(6);
1574 file <<
"x: " << pose.pose.pose.position.x <<
"\n";
1575 file <<
"y: " << pose.pose.pose.position.y <<
"\n";
1576 file <<
"z: " << pose.pose.pose.position.z <<
"\n";
1577 file <<
"yaw: " << tf2::getYaw(pose.pose.pose.orientation) <<
"\n";
1581 if (std::rename(tmp_path.c_str(), saved_pose_filepath_.c_str()) != 0) {
1583 get_logger(),
"Failed to rename pose file from %s to %s",
1584 tmp_path.c_str(), saved_pose_filepath_.c_str());
1586 }
catch (
const std::exception & e) {
1587 RCLCPP_WARN(get_logger(),
"Failed to save pose to file: %s", e.what());
1592 AmclNode::loadPoseFromFile(geometry_msgs::msg::PoseWithCovarianceStamped & pose)
1594 std::ifstream file(saved_pose_filepath_);
1595 if (!file.is_open()) {
1601 double x = 0.0, y = 0.0, z = 0.0, yaw = 0.0;
1602 double timestamp = 0.0;
1603 std::string frame_id;
1605 while (std::getline(file, line)) {
1606 if (line.empty() || line[0] ==
'#') {
1609 std::istringstream iss(line);
1611 if (std::getline(iss, key,
':')) {
1612 if (key ==
"frame_id") {
1614 std::getline(iss, frame_id);
1620 }
else if (key ==
"y") {
1622 }
else if (key ==
"z") {
1624 }
else if (key ==
"yaw") {
1626 }
else if (key ==
"timestamp") {
1633 pose.header.frame_id = frame_id.empty() ? global_frame_id_ : frame_id;
1634 pose.header.stamp = now();
1635 pose.pose.pose.position.x = x;
1636 pose.pose.pose.position.y = y;
1637 pose.pose.pose.position.z = z;
1638 pose.pose.pose.orientation = orientationAroundZAxis(yaw);
1642 "Loaded saved pose from file: x=%.3f, y=%.3f, z=%.3f, yaw=%.3f, "
1643 "originally saved at timestamp=%.3f, frame=%s",
1644 x, y, z, yaw, timestamp, pose.header.frame_id.c_str());
1647 }
catch (
const std::exception & e) {
1648 RCLCPP_WARN(get_logger(),
"Failed to parse saved pose file: %s", e.what());
1655 #include "rclcpp_components/register_node_macro.hpp"
A QoS profile for latched, reliable topics with a history of 1 messages.
A QoS profile for latched, reliable topics with a history of 10 messages.
A QoS profile for best-effort sensor data with a history of 10 messages.