Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
truncate_path_local_action.cpp
1 // Copyright (c) 2021 RoboTech Vision
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cmath>
16 #include <limits>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #include "behaviortree_cpp/decorator_node.h"
22 #include "geometry_msgs/msg/pose_stamped.hpp"
23 #include "nav2_util/geometry_utils.hpp"
24 #include "nav2_util/robot_utils.hpp"
25 #include "nav_msgs/msg/path.hpp"
26 #include "rclcpp/rclcpp.hpp"
27 #include "tf2/LinearMath/Quaternion.hpp"
28 #include "tf2_ros/buffer.hpp"
29 #include "tf2_ros/create_timer_ros.hpp"
30 
31 #include "nav2_behavior_tree/plugins/action/truncate_path_local_action.hpp"
32 
33 namespace nav2_behavior_tree
34 {
35 
37  const std::string & name,
38  const BT::NodeConfiguration & conf)
39 : BT::ActionNodeBase(name, conf)
40 {
41  tf_buffer_ =
42  config().blackboard->template get<std::shared_ptr<tf2_ros::Buffer>>(
43  "tf_buffer");
44 }
45 
46 inline BT::NodeStatus TruncatePathLocal::tick()
47 {
48  setStatus(BT::NodeStatus::RUNNING);
49 
50  double distance_forward, distance_backward;
51  geometry_msgs::msg::PoseStamped pose;
52  double angular_distance_weight;
53  double max_robot_pose_search_dist;
54 
55  getInput("distance_forward", distance_forward);
56  getInput("distance_backward", distance_backward);
57  getInput("angular_distance_weight", angular_distance_weight);
58  getInput("max_robot_pose_search_dist", max_robot_pose_search_dist);
59 
60  bool path_pruning = std::isfinite(max_robot_pose_search_dist);
61  nav_msgs::msg::Path new_path;
62  getInput("input_path", new_path);
63  if (!path_pruning || new_path != path_) {
64  path_ = new_path;
65  closest_pose_detection_begin_ = path_.poses.begin();
66  }
67 
68  if (!getRobotPose(path_.header.frame_id, pose)) {
69  return BT::NodeStatus::FAILURE;
70  }
71 
72  if (path_.poses.empty()) {
73  setOutput("output_path", path_);
74  return BT::NodeStatus::SUCCESS;
75  }
76 
77  auto closest_pose_detection_end = path_.poses.end();
78  if (path_pruning) {
79  closest_pose_detection_end = nav2_util::geometry_utils::first_after_integrated_distance(
80  closest_pose_detection_begin_, path_.poses.end(), max_robot_pose_search_dist);
81  }
82 
83  // find the closest pose on the path
84  auto current_pose = nav2_util::geometry_utils::min_by(
85  closest_pose_detection_begin_, closest_pose_detection_end,
86  [&pose, angular_distance_weight](const geometry_msgs::msg::PoseStamped & ps) {
87  return poseDistance(pose, ps, angular_distance_weight);
88  });
89 
90  if (path_pruning) {
91  closest_pose_detection_begin_ = current_pose;
92  }
93 
94  // expand forwards to extract desired length
95  auto forward_pose_it = nav2_util::geometry_utils::first_after_integrated_distance(
96  current_pose, path_.poses.end(), distance_forward);
97 
98  // expand backwards to extract desired length
99  // Note: current_pose + 1 is used because reverse iterator points to a cell before it
100  auto backward_pose_it = nav2_util::geometry_utils::first_after_integrated_distance(
101  std::reverse_iterator(current_pose + 1), path_.poses.rend(), distance_backward);
102 
103  nav_msgs::msg::Path output_path;
104  output_path.header = path_.header;
105  output_path.poses = std::vector<geometry_msgs::msg::PoseStamped>(
106  backward_pose_it.base(), forward_pose_it);
107  setOutput("output_path", output_path);
108 
109  return BT::NodeStatus::SUCCESS;
110 }
111 
112 inline bool TruncatePathLocal::getRobotPose(
113  std::string path_frame_id, geometry_msgs::msg::PoseStamped & pose)
114 {
115  if (!getInput("pose", pose)) {
116  std::string robot_frame;
117  if (!getInput("robot_frame", robot_frame)) {
118  RCLCPP_ERROR(
119  config().blackboard->get<nav2::LifecycleNode::SharedPtr>("node")->get_logger(),
120  "Neither pose nor robot_frame specified for %s", name().c_str());
121  return false;
122  }
123  double transform_tolerance;
124  getInput("transform_tolerance", transform_tolerance);
125  if (!nav2_util::getCurrentPose(
126  pose, *tf_buffer_, path_frame_id, robot_frame, transform_tolerance))
127  {
128  RCLCPP_WARN(
129  config().blackboard->get<nav2::LifecycleNode::SharedPtr>("node")->get_logger(),
130  "Failed to lookup current robot pose for %s", name().c_str());
131  return false;
132  }
133  }
134  return true;
135 }
136 
137 double
138 TruncatePathLocal::poseDistance(
139  const geometry_msgs::msg::PoseStamped & pose1,
140  const geometry_msgs::msg::PoseStamped & pose2,
141  const double angular_distance_weight)
142 {
143  double dx = pose1.pose.position.x - pose2.pose.position.x;
144  double dy = pose1.pose.position.y - pose2.pose.position.y;
145  // taking angular distance into account in addition to spatial distance
146  // (to improve picking a correct pose near cusps and loops)
147  tf2::Quaternion q1;
148  tf2::convert(pose1.pose.orientation, q1);
149  tf2::Quaternion q2;
150  tf2::convert(pose2.pose.orientation, q2);
151  double da = angular_distance_weight * std::abs(q1.angleShortestPath(q2));
152  return std::sqrt(dx * dx + dy * dy + da * da);
153 }
154 
155 } // namespace nav2_behavior_tree
156 
157 #include "behaviortree_cpp/bt_factory.h"
158 BT_REGISTER_NODES(factory) {
159  factory.registerNodeType<nav2_behavior_tree::TruncatePathLocal>(
160  "TruncatePathLocal");
161 }
A BT::ActionNodeBase to shorten path to some distance around robot.
TruncatePathLocal(const std::string &xml_tag_name, const BT::NodeConfiguration &conf)
A nav2_behavior_tree::TruncatePathLocal constructor.