Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
loopback_simulator.hpp
1 // Copyright (c) 2024, Open Navigation LLC
2 // Copyright (c) 2026, Dexory (Tony Najjar)
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #ifndef NAV2_LOOPBACK_SIM__LOOPBACK_SIMULATOR_HPP_
17 #define NAV2_LOOPBACK_SIM__LOOPBACK_SIMULATOR_HPP_
18 
19 #include <cmath>
20 #include <limits>
21 #include <memory>
22 #include <optional>
23 #include <random>
24 #include <string>
25 #include <tuple>
26 #include <vector>
27 
28 #include "nav2_ros_common/lifecycle_node.hpp"
29 #include "nav2_ros_common/service_client.hpp"
30 #include "nav2_util/twist_subscriber.hpp"
31 #include "nav2_loopback_sim/clock_publisher.hpp"
32 #include "rcl_interfaces/msg/set_parameters_result.hpp"
33 #include "geometry_msgs/msg/pose.hpp"
34 #include "geometry_msgs/msg/pose_with_covariance_stamped.hpp"
35 #include "geometry_msgs/msg/quaternion.hpp"
36 #include "geometry_msgs/msg/transform_stamped.hpp"
37 #include "geometry_msgs/msg/twist.hpp"
38 #include "geometry_msgs/msg/twist_stamped.hpp"
39 #include "geometry_msgs/msg/vector3.hpp"
40 #include "nav_msgs/msg/occupancy_grid.hpp"
41 #include "nav_msgs/msg/odometry.hpp"
42 #include "nav_msgs/srv/get_map.hpp"
43 #include "sensor_msgs/msg/laser_scan.hpp"
44 #include "nav2_ros_common/tf2_factories.hpp"
45 #include "tf2/LinearMath/Transform.hpp"
46 #include "tf2/LinearMath/Quaternion.hpp"
47 #include "tf2/LinearMath/Matrix3x3.hpp"
48 
49 namespace nav2_loopback_sim
50 {
51 
60 {
61 public:
66  explicit LoopbackSimulator(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
67  ~LoopbackSimulator() = default;
68 
69 protected:
73  nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State & state) override;
77  nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State & state) override;
81  nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State & state) override;
85  nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State & state) override;
89  nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State & state) override;
90 
94  void cmdVelCallback(const geometry_msgs::msg::Twist::ConstSharedPtr & msg);
98  void cmdVelStampedCallback(const geometry_msgs::msg::TwistStamped::ConstSharedPtr & msg);
102  void initialPoseCallback(
103  const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr & msg);
107  void setupTimerCallback();
111  void timerCallback();
115  void odomTimerCallback();
119  void publishLaserScan();
125  rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(
126  const std::vector<rclcpp::Parameter> & parameters);
131  void updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters);
132 
136  void getBaseToLaserTf();
140  void getMap();
144  void publishTransforms(
145  geometry_msgs::msg::TransformStamped & map_to_odom,
146  geometry_msgs::msg::TransformStamped & odom_to_base_link);
150  void publishOdometry(const geometry_msgs::msg::TransformStamped & odom_to_base_link);
154  std::tuple<double, double, double> getLaserPose();
160  void getLaserScan(int num_samples, sensor_msgs::msg::LaserScan & scan_msg);
161 
168  static geometry_msgs::msg::Quaternion addYawToQuat(
169  const geometry_msgs::msg::Quaternion & quaternion, double yaw_to_add);
170 
171  // Parameters
172  double update_dur_;
173  std::string base_frame_id_;
174  std::string map_frame_id_;
175  std::string odom_frame_id_;
176  std::string scan_frame_id_;
177  double odom_publish_dur_;
178  double scan_publish_dur_;
179  bool publish_map_odom_tf_;
180  double scan_range_min_;
181  double scan_range_max_;
182  double scan_angle_min_;
183  double scan_angle_max_;
184  double scan_angle_increment_;
185  bool use_inf_;
186  double scan_noise_std_;
187  bool publish_scan_;
188  bool publish_clock_;
189  double speed_factor_;
190 
191  // State
192  std::optional<geometry_msgs::msg::Twist> curr_cmd_vel_;
193  rclcpp::Time curr_cmd_vel_time_;
194  bool has_initial_pose_{false};
195  geometry_msgs::msg::Pose initial_pose_;
196  bool has_map_{false};
197  nav_msgs::msg::OccupancyGrid map_;
198  bool has_base_to_laser_{false};
199  tf2::Transform tf_base_to_laser_;
200  std::mt19937 rng_{std::random_device{}()};
201  geometry_msgs::msg::TransformStamped t_map_to_odom_;
202  geometry_msgs::msg::TransformStamped t_odom_to_base_link_;
203 
204  // ROS interfaces
205  nav2::TransformBroadcaster::SharedPtr tf_broadcaster_;
206  nav2::TransformBuffer::SharedPtr tf_buffer_;
207  nav2::TransformListener::SharedPtr tf_listener_;
208 
209  nav2::Subscription<geometry_msgs::msg::PoseWithCovarianceStamped>::SharedPtr
210  initial_pose_sub_;
211  std::unique_ptr<nav2_util::TwistSubscriber> cmd_vel_sub_;
212 
213  nav2::Publisher<nav_msgs::msg::Odometry>::SharedPtr odom_pub_;
214  nav2::Publisher<sensor_msgs::msg::LaserScan>::SharedPtr scan_pub_;
215 
216  nav2::ServiceClient<nav_msgs::srv::GetMap>::SharedPtr map_client_;
217 
218  rclcpp::TimerBase::SharedPtr setup_timer_;
219  rclcpp::TimerBase::SharedPtr timer_;
220  rclcpp::TimerBase::SharedPtr odom_timer_;
221  rclcpp::TimerBase::SharedPtr scan_timer_;
222 
223  std::unique_ptr<ClockPublisher> clock_publisher_;
224  rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr param_validator_;
225  rclcpp::node_interfaces::PostSetParametersCallbackHandle::SharedPtr param_updater_;
226 };
227 
228 } // namespace nav2_loopback_sim
229 
230 #endif // NAV2_LOOPBACK_SIM__LOOPBACK_SIMULATOR_HPP_
A lifecycle node wrapper to enable common Nav2 needs such as manipulating parameters.
A loopback simulator that replaces a physics simulator to create a frictionless, inertialess,...
void odomTimerCallback()
Periodic odometry publishing callback.
void cmdVelCallback(const geometry_msgs::msg::Twist::ConstSharedPtr &msg)
Callback for incoming cmd_vel (unstamped Twist)
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate dynamic parameter changes (pre-set callback)
std::tuple< double, double, double > getLaserPose()
Compute the laser pose in the map frame.
void publishTransforms(geometry_msgs::msg::TransformStamped &map_to_odom, geometry_msgs::msg::TransformStamped &odom_to_base_link)
Publish map->odom and odom->base_link transforms.
void publishOdometry(const geometry_msgs::msg::TransformStamped &odom_to_base_link)
Publish nav_msgs::Odometry from the current odom->base transform.
static geometry_msgs::msg::Quaternion addYawToQuat(const geometry_msgs::msg::Quaternion &quaternion, double yaw_to_add)
Add a yaw rotation to a quaternion.
void getMap()
Request the map from the map server.
void cmdVelStampedCallback(const geometry_msgs::msg::TwistStamped::ConstSharedPtr &msg)
Callback for incoming cmd_vel (stamped TwistStamped)
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply validated dynamic parameter changes (post-set callback)
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configure the node: declare parameters, create pubs/subs/timers.
void timerCallback()
Main update callback: integrates cmd_vel and publishes TF.
void initialPoseCallback(const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr &msg)
Callback for incoming initial pose.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Cleanup the node: release all resources.
void publishLaserScan()
Publish a simulated laser scan from the map.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivate the node: stop timers, reset cmd_vel.
void getLaserScan(int num_samples, sensor_msgs::msg::LaserScan &scan_msg)
Raycast the map to fill a LaserScan message.
void setupTimerCallback()
Periodic setup callback: publishes identity TFs and fetches map.
LoopbackSimulator(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
Construct a LoopbackSimulator node.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activate the node: start publishing.
void getBaseToLaserTf()
Look up the static transform from base to laser frame.
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Shutdown the node.