Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
amcl_node.hpp
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 #ifndef NAV2_AMCL__AMCL_NODE_HPP_
22 #define NAV2_AMCL__AMCL_NODE_HPP_
23 
24 #include <atomic>
25 #include <fstream>
26 #include <map>
27 #include <memory>
28 #include <sstream>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 #include "message_filters/subscriber.hpp"
34 #include "rclcpp/version.h"
35 
36 #include "geometry_msgs/msg/pose_stamped.hpp"
37 #include "nav2_ros_common/lifecycle_node.hpp"
38 #include "nav2_amcl/motion_model/motion_model.hpp"
39 #include "nav2_amcl/sensors/laser/laser.hpp"
40 #include "nav2_msgs/msg/particle.hpp"
41 #include "nav2_msgs/msg/particle_cloud.hpp"
42 #include "nav2_msgs/srv/set_initial_pose.hpp"
43 #include "nav_msgs/srv/set_map.hpp"
44 #include "pluginlib/class_loader.hpp"
45 #include "rclcpp/node_options.hpp"
46 #include "sensor_msgs/msg/laser_scan.hpp"
47 #include "nav2_ros_common/service_server.hpp"
48 #include "std_srvs/srv/empty.hpp"
49 #include "nav2_ros_common/tf2_factories.hpp"
50 
51 #define NEW_UNIFORM_SAMPLING 1
52 
53 namespace nav2_amcl
54 {
55 /*
56  * @class AmclNode
57  * @brief ROS wrapper for AMCL
58  */
60 {
61 public:
62  /*
63  * @brief AMCL constructor
64  * @param options Additional options to control creation of the node.
65  */
66  explicit AmclNode(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
67  /*
68  * @brief AMCL destructor
69  */
70  ~AmclNode();
71 
72 protected:
73  /*
74  * @brief Lifecycle configure
75  */
76  nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State & state) override;
77  /*
78  * @brief Lifecycle activate
79  */
80  nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State & state) override;
81  /*
82  * @brief Lifecycle deactivate
83  */
84  nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State & state) override;
85  /*
86  * @brief Lifecycle cleanup
87  */
88  nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State & state) override;
89  /*
90  * @brief Lifecycle shutdown
91  */
92  nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State & state) override;
93 
102  rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(
103  const std::vector<rclcpp::Parameter> & parameters);
104 
111  void updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters);
112 
113  // Dynamic parameters handler
114  rclcpp::node_interfaces::PostSetParametersCallbackHandle::SharedPtr post_set_params_handler_;
115  rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr on_set_params_handler_;
116 
117  // Since the sensor data from gazebo or the robot is not lifecycle enabled, we won't
118  // respond until we're in the active state
119  std::atomic<bool> active_{false};
120 
121  // Dedicated callback group and executor for services and subscriptions in AmclNode,
122  // in order to isolate TF timer used in message filter.
123  rclcpp::CallbackGroup::SharedPtr callback_group_;
124  rclcpp::executors::SingleThreadedExecutor::SharedPtr executor_;
125  std::unique_ptr<nav2::NodeThread> executor_thread_;
126 
127  // Pose hypothesis
128  typedef struct
129  {
130  double weight; // Total weight (weights sum to 1)
131  pf_vector_t pf_pose_mean; // Mean of pose estimate
132  pf_matrix_t pf_pose_cov; // Covariance of pose estimate
133  } amcl_hyp_t;
134 
135  // Map-related
136  /*
137  * @brief Get new map from ROS topic to localize in
138  * @param msg Map message
139  */
140  void mapReceived(const nav_msgs::msg::OccupancyGrid::ConstSharedPtr & msg);
141  /*
142  * @brief Handle a new map message
143  * @param msg Map message
144  */
145  void handleMapMessage(const nav_msgs::msg::OccupancyGrid & msg);
146  /*
147  * @brief Creates lookup table of free cells in map
148  */
149  void createFreeSpaceVector();
150  /*
151  * @brief Frees allocated map related memory
152  */
153  void freeMapDependentMemory();
154  map_t * map_{nullptr};
155  /*
156  * @brief Convert an occupancy grid map to an AMCL map
157  * @param map_msg Map message
158  * @return pointer to map for AMCL to use
159  */
160  map_t * convertMap(const nav_msgs::msg::OccupancyGrid & map_msg);
161  bool first_map_only_{true};
162  std::atomic<bool> first_map_received_{false};
163  amcl_hyp_t * initial_pose_hyp_;
164  std::recursive_mutex mutex_;
165  nav2::Subscription<nav_msgs::msg::OccupancyGrid>::ConstSharedPtr map_sub_;
166 #if NEW_UNIFORM_SAMPLING
167  struct Point2D { int32_t x; int32_t y; };
168  static std::vector<Point2D> free_space_indices;
169 #endif
170 
171  // Transforms
172  /*
173  * @brief Initialize required ROS transformations
174  */
175  void initTransforms();
176  nav2::TransformBroadcaster::SharedPtr tf_broadcaster_;
177  nav2::TransformListener::SharedPtr tf_listener_;
178  nav2::TransformBuffer::SharedPtr tf_buffer_;
179  bool sent_first_transform_{false};
180  bool latest_tf_valid_{false};
181  tf2::Transform latest_tf_;
182 
183  // Message filters
184  /*
185  * @brief Initialize incoming data message subscribers and filters
186  */
187  void initMessageFilters();
188 
189  // To Support Kilted and Older from Message Filters API change
190  #if RCLCPP_VERSION_GTE(29, 6, 0)
191  std::unique_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan>> laser_scan_sub_;
192  #else
193  std::unique_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
194  rclcpp_lifecycle::LifecycleNode>> laser_scan_sub_;
195  #endif
196 
197  nav2::MessageFilter<sensor_msgs::msg::LaserScan>::SharedPtr laser_scan_filter_;
198  message_filters::Connection laser_scan_connection_;
199 
200  // Publishers and subscribers
201  /*
202  * @brief Initialize pub subs of AMCL
203  */
204  void initPubSub();
205  nav2::Subscription<geometry_msgs::msg::PoseWithCovarianceStamped>::ConstSharedPtr
206  initial_pose_sub_;
207  nav2::Publisher<geometry_msgs::msg::PoseWithCovarianceStamped>::SharedPtr
208  pose_pub_;
209  nav2::Publisher<nav2_msgs::msg::ParticleCloud>::SharedPtr
210  particle_cloud_pub_;
211  /*
212  * @brief Handle with an initial pose estimate is received
213  */
214  void initialPoseReceived(
215  const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr & msg);
216  /*
217  * @brief Handle when a laser scan is received
218  */
219  void laserReceived(sensor_msgs::msg::LaserScan::ConstSharedPtr laser_scan);
220 
221  // Services and service callbacks
222  /*
223  * @brief Initialize state services
224  */
225  void initServices();
226  nav2::ServiceServer<std_srvs::srv::Empty>::SharedPtr global_loc_srv_;
227  /*
228  * @brief Service callback for a global relocalization request
229  */
230  void globalLocalizationCallback(
231  const std::shared_ptr<rmw_request_id_t> request_header,
232  const std::shared_ptr<std_srvs::srv::Empty::Request> request,
233  std::shared_ptr<std_srvs::srv::Empty::Response> response);
234 
235  // service server for providing an initial pose guess
236  nav2::ServiceServer<nav2_msgs::srv::SetInitialPose>::SharedPtr initial_guess_srv_;
237  /*
238  * @brief Service callback for an initial pose guess request
239  */
240  void initialPoseReceivedSrv(
241  const std::shared_ptr<rmw_request_id_t> request_header,
242  const std::shared_ptr<nav2_msgs::srv::SetInitialPose::Request> request,
243  std::shared_ptr<nav2_msgs::srv::SetInitialPose::Response> response);
244 
245  // Let amcl update samples without requiring motion
246  nav2::ServiceServer<std_srvs::srv::Empty>::SharedPtr nomotion_update_srv_;
247  /*
248  * @brief Request an AMCL update even though the robot hasn't moved
249  */
250  void nomotionUpdateCallback(
251  const std::shared_ptr<rmw_request_id_t> request_header,
252  const std::shared_ptr<std_srvs::srv::Empty::Request> request,
253  std::shared_ptr<std_srvs::srv::Empty::Response> response);
254 
255  // Nomotion update control. Used to temporarily let amcl update samples even when no motion occurs
256  std::atomic<bool> force_update_{false};
257 
258  // Odometry
259  /*
260  * @brief Initialize odometry
261  */
262  void initOdometry();
263  std::shared_ptr<nav2_amcl::MotionModel> motion_model_;
264  geometry_msgs::msg::PoseStamped latest_odom_pose_;
265  geometry_msgs::msg::PoseWithCovarianceStamped last_published_pose_;
266  double init_pose_[3]; // Initial robot pose
267  double init_cov_[3];
268  pluginlib::ClassLoader<nav2_amcl::MotionModel> plugin_loader_{"nav2_amcl",
269  "nav2_amcl::MotionModel"};
270  /*
271  * @brief Get robot pose in odom frame using TF
272  */
273  bool getOdomPose(
274  // Helper to get odometric pose from transform system
275  geometry_msgs::msg::PoseStamped & pose,
276  double & x, double & y, double & yaw,
277  const rclcpp::Time & sensor_timestamp, const std::string & frame_id);
278  std::atomic<bool> first_pose_sent_;
279 
280  // Particle filter
281  /*
282  * @brief Initialize particle filter
283  */
284  void initParticleFilter();
285  /*
286  * @brief Pose-generating function used to uniformly distribute particles over the map
287  */
288  static pf_vector_t uniformPoseGenerator(void * arg);
289  pf_t * pf_{nullptr};
290  bool pf_init_;
291  pf_vector_t pf_odom_pose_;
292  int resample_count_{0};
293  int random_seed_{-1};
294 
295  // Laser scan related
296  /*
297  * @brief Initialize laser scan
298  */
299  void initLaserScan();
300  /*
301  * @brief Create a laser object
302  */
303  std::unique_ptr<nav2_amcl::Laser> createLaserObject();
304  int scan_error_count_{0};
305  std::vector<std::unique_ptr<nav2_amcl::Laser>> lasers_;
306  std::vector<bool> lasers_update_;
307  std::map<std::string, int> frame_to_laser_;
308  rclcpp::Time last_laser_received_ts_;
309 
310  /*
311  * @brief Check if sufficient time has elapsed to get an update
312  */
313  bool checkElapsedTime(std::chrono::seconds check_interval, rclcpp::Time last_time);
314  rclcpp::Time last_time_printed_msg_;
315  /*
316  * @brief Add a new laser scanner if a new one is received in the laser scallbacks
317  */
318  bool addNewScanner(
319  int & laser_index,
320  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
321  const std::string & laser_scan_frame_id,
322  geometry_msgs::msg::PoseStamped & laser_pose);
323  /*
324  * @brief Whether the pf needs to be updated
325  */
326  bool shouldUpdateFilter(const pf_vector_t pose, pf_vector_t & delta);
327  /*
328  * @brief Update the PF
329  */
330  bool updateFilter(
331  const int & laser_index,
332  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
333  const pf_vector_t & pose);
334  /*
335  * @brief Publish particle cloud
336  */
337  void publishParticleCloud(const pf_sample_set_t * set);
338  /*
339  * @brief Get the current state estimat hypothesis from the particle cloud
340  */
341  bool getMaxWeightHyp(
342  std::vector<amcl_hyp_t> & hyps, amcl_hyp_t & max_weight_hyps,
343  int & max_weight_hyp);
344  /*
345  * @brief Publish robot pose in map frame from AMCL
346  */
347  void publishAmclPose(
348  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
349  const std::vector<amcl_hyp_t> & hyps, const int & max_weight_hyp);
350  /*
351  * @brief Determine TF transformation from map to odom
352  */
353  void calculateMaptoOdomTransform(
354  const sensor_msgs::msg::LaserScan::ConstSharedPtr & laser_scan,
355  const std::vector<amcl_hyp_t> & hyps,
356  const int & max_weight_hyp);
357  /*
358  * @brief Publish TF transformation from map to odom
359  */
360  void sendMapToOdomTransform(const tf2::TimePoint & transform_expiration);
361  /*
362  * @brief Handle a new pose estimate callback
363  */
364  void handleInitialPose(geometry_msgs::msg::PoseWithCovarianceStamped & msg);
365  /*
366  * @brief Save current pose to file for persistence
367  */
368  void savePoseToFile();
369  /*
370  * @brief Load pose from file for initialization
371  * @param pose Output pose loaded from file
372  * @return true if pose was successfully loaded
373  */
374  bool loadPoseFromFile(geometry_msgs::msg::PoseWithCovarianceStamped & pose);
375  /*
376  * @brief Timer callback for periodic pose saving
377  */
378  void savePoseTimerCallback();
379  bool init_pose_received_on_inactive{false};
380  double save_pose_rate_{0.5}; // Hz
381  bool initial_pose_is_known_{false};
382  bool set_initial_pose_{false};
383  bool always_reset_initial_pose_;
384  double initial_pose_x_;
385  double initial_pose_y_;
386  double initial_pose_z_;
387  double initial_pose_yaw_;
388 
389  /*
390  * @brief Get ROS parameters for node
391  */
392  void initParameters();
393  double alpha1_;
394  double alpha2_;
395  double alpha3_;
396  double alpha4_;
397  double alpha5_;
398  std::string base_frame_id_;
399  double beam_skip_distance_;
400  double beam_skip_error_threshold_;
401  double beam_skip_threshold_;
402  bool do_beamskip_;
403  std::string global_frame_id_;
404  double lambda_short_;
405  double laser_likelihood_max_dist_;
406  double laser_max_range_;
407  double laser_min_range_;
408  std::string sensor_model_type_;
409  int max_beams_;
410  int max_particles_;
411  int min_particles_;
412  std::string odom_frame_id_;
413  double pf_err_;
414  double pf_z_;
415  double alpha_fast_;
416  double alpha_slow_;
417  int resample_interval_;
418  std::string robot_model_type_;
419  bool initialize_at_saved_pose_;
420  std::string saved_pose_filepath_;
421  rclcpp::TimerBase::SharedPtr save_pose_timer_;
422  double sigma_hit_;
423  bool tf_broadcast_;
424  tf2::Duration transform_tolerance_;
425  double a_thresh_;
426  double d_thresh_;
427  double z_hit_;
428  double z_max_;
429  double z_short_;
430  double z_rand_;
431  std::string scan_topic_{"scan"};
432  std::string map_topic_{"map"};
433  bool freespace_downsampling_ = false;
434  bool allow_parameter_qos_overrides_ = true;
435 };
436 
437 } // namespace nav2_amcl
438 
439 #endif // NAV2_AMCL__AMCL_NODE_HPP_
A lifecycle node wrapper to enable common Nav2 needs such as manipulating parameters.
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
Definition: amcl_node.cpp:1094
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
Definition: amcl_node.cpp:1041
Definition: pf.hpp:114
Definition: map.hpp:62