start_pose_predictor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023, the neonavigation authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <algorithm>
31 #include <vector>
32 
33 #include <ros/ros.h>
34 #include <tf2/utils.h>
35 
37 
38 namespace planner_cspace
39 {
40 namespace planner_3d
41 {
42 bool StartPosePredictor::process(const geometry_msgs::Pose& robot_pose,
44  const costmap_cspace_msgs::MapMetaData3D& map_info,
45  const nav_msgs::Path& previous_path_msg,
46  StartPosePredictor::Astar::Vec& result_start_grid)
47 {
48  clear();
49  preserved_path_.header = previous_path_msg.header;
50  previous_path_2d_.fromMsg(previous_path_msg);
51  map_info_ = map_info;
52 
53  if (previous_path_2d_.empty() || !removeAlreadyPassed(robot_pose))
54  {
55  clear();
56  return false;
57  }
58  const double initial_eta = getInitialETA(robot_pose, previous_path_2d_.front());
59  const std::vector<double> etas = previous_path_2d_.getEstimatedTimeOfArrivals(
60  previous_path_2d_.begin(), previous_path_2d_.end(), config_.lin_vel_, config_.ang_vel_, initial_eta);
61 
62  const trajectory_tracker::Path2D::ConstIterator local_goal_it = getSwitchBack(etas);
63  if (local_goal_it != previous_path_2d_.end())
64  {
65  return buildResults(local_goal_it, robot_pose, cm, result_start_grid);
66  }
67  const trajectory_tracker::Path2D::ConstIterator expected_pose_it = getExpectedPose(etas);
68  if (expected_pose_it != previous_path_2d_.end())
69  {
70  return buildResults(expected_pose_it, robot_pose, cm, result_start_grid);
71  }
72  ROS_DEBUG("The robot will reach the goal in %f sec.", etas.back());
73  return buildResults(previous_path_2d_.end() - 1, robot_pose, cm, result_start_grid);
74 }
75 
77 {
78  previous_path_2d_.clear();
79  preserved_path_.poses.clear();
80  preserved_path_length_ = 0.0;
81 }
82 
83 bool StartPosePredictor::removeAlreadyPassed(const geometry_msgs::Pose& start_metric)
84 {
85  const Eigen::Vector2d robot_pose_2d(start_metric.position.x, start_metric.position.y);
86  double dist_err;
88  std::tie(it_nearest, dist_err) =
90  if (dist_err > config_.dist_stop_)
91  {
92  ROS_WARN("The robot is too far from path. An empty path is published. dist: %f, thr: %f",
93  dist_err, config_.dist_stop_);
94  return false;
95  }
96  if (previous_path_2d_.size() == 1)
97  {
98  return true;
99  }
100  size_t nearest_pos = it_nearest - previous_path_2d_.begin();
101  if (nearest_pos == 1)
102  {
103  // findNearestWithDistance() never returns begin() and (nearest_pos == 1) means that the first line strip is the
104  // nearest. In this case, no points should be removed when the robot is behind the path.
105  const double distance_from_start = std::hypot(start_metric.position.x - previous_path_2d_.begin()->pos_.x(),
106  start_metric.position.y - previous_path_2d_.begin()->pos_.y());
107  if (std::abs(distance_from_start - dist_err) < 1.0e-6)
108  {
109  ROS_DEBUG("Nearest pose in previous path: (%f, %f, %f), dist: %f, index: 0, remaining poses num: %lu",
110  previous_path_2d_.begin()->pos_.x(), previous_path_2d_.begin()->pos_.y(),
111  previous_path_2d_.begin()->yaw_, dist_err, previous_path_2d_.size());
112  return true;
113  }
114  }
115  ROS_DEBUG("Nearest pose in previous path: (%f, %f, %f), dist: %f, index: %lu, remaining poses num: %lu",
116  it_nearest->pos_.x(), it_nearest->pos_.y(), it_nearest->yaw_, dist_err, nearest_pos,
117  previous_path_2d_.size() - nearest_pos);
118  previous_path_2d_.erase(previous_path_2d_.begin(), it_nearest);
119  return true;
120 }
121 
123  const geometry_msgs::Pose& robot_pose, const trajectory_tracker::Pose2D& initial_path_pose) const
124 {
125  double angle_diff = std::abs(initial_path_pose.yaw_ - tf2::getYaw(robot_pose.orientation));
126  angle_diff = (angle_diff > M_PI) ? 2 * M_PI - angle_diff : angle_diff; // Normalize
127  const double dist_diff = std::hypot(initial_path_pose.pos_.x() - robot_pose.position.x,
128  initial_path_pose.pos_.y() - robot_pose.position.y);
129  return std::max(std::abs(angle_diff) / config_.ang_vel_, dist_diff / config_.lin_vel_);
130 }
131 
133  const trajectory_tracker::Path2D::ConstIterator& expected_start_pose_it,
134  const geometry_msgs::Pose& robot_pose,
136  StartPosePredictor::Astar::Vec& start_grid)
137 {
138  if (isPathColliding(previous_path_2d_.begin(), expected_start_pose_it, cm))
139  {
140  ROS_WARN("The robot might collide with an obstacle during the next planning. An empty path is published.");
141  clear();
142  return false;
143  }
144 
146  map_info_, start_grid[0], start_grid[1], start_grid[2],
147  expected_start_pose_it->pos_.x(), expected_start_pose_it->pos_.y(), expected_start_pose_it->yaw_);
148  start_grid.cycleUnsigned(map_info_.angle);
149  const size_t previous_path_size = previous_path_2d_.size();
150  previous_path_2d_.erase(expected_start_pose_it + 1, previous_path_2d_.end());
151  ROS_DEBUG("Robot Pose: (%f, %f, %f), Start Pose: (%f, %f, %f), Start Grid: (%d, %d, %d), path size: %lu -> %lu",
152  robot_pose.position.x, robot_pose.position.y, tf2::getYaw(robot_pose.orientation),
153  expected_start_pose_it->pos_.x(), expected_start_pose_it->pos_.y(), expected_start_pose_it->yaw_,
154  start_grid[0], start_grid[1], start_grid[2], previous_path_size, previous_path_2d_.size());
155  if (!previous_path_2d_.empty())
156  {
159  // The last pose will be the first pose of the next planning, and it should be removed from preserved_path_.
160  preserved_path_.poses.pop_back();
161  }
162  return true;
163 }
164 
166 {
167  // Return true when all of the x, y and yaw in the grid coordinate are integers.
168  Astar::Vecf grid;
169  grid_metric_converter::metric2Grid(map_info_, grid[0], grid[1], grid[2], pose.pos_.x(), pose.pos_.y(), pose.yaw_);
170  const float x_diff = std::abs(grid[0] - 0.5 - std::round(grid[0] - 0.5));
171  const float y_diff = std::abs(grid[1] - 0.5 - std::round(grid[1] - 0.5));
172  const float r_diff = std::abs(grid[2] - std::round(grid[2]));
173  return (x_diff < 1.0e-3) && (y_diff < 1.0e-3) && (r_diff < 1.0e-3);
174 }
175 
179 {
180  for (auto it = begin; it != end; ++it)
181  {
182  Astar::Vec grid;
183  grid_metric_converter::metric2Grid(map_info_, grid[0], grid[1], grid[2], it->pos_.x(), it->pos_.y(), it->yaw_);
184  grid.cycleUnsigned(map_info_.angle);
185  if (cm[grid] == 100)
186  {
187  ROS_DEBUG("Path colliding at (%f, %f, %f)", it->pos_.x(), it->pos_.y(), it->yaw_);
188  return true;
189  }
190  }
191  return false;
192 }
193 
195 {
196  const auto local_goal_its = previous_path_2d_.enumerateLocalGoals(
197  previous_path_2d_.begin(), previous_path_2d_.end(), true, false);
198  if (local_goal_its.empty())
199  {
200  return previous_path_2d_.end();
201  }
202 
203  for (const auto& local_goal : local_goal_its)
204  {
205  // Note that Path2D::findNearest() never returns the begin iterator and we can safely subtract 1.
206  const size_t local_goal_index = local_goal - previous_path_2d_.begin() - 1;
207  const double local_goal_eta = etas[local_goal_index];
208  ROS_DEBUG("Switch back index: %lu, eta: %f", local_goal_index, local_goal_eta);
209  if (isGridCenter(previous_path_2d_[local_goal_index]))
210  {
211  if ((config_.prediction_sec_ < local_goal_eta) &&
212  (local_goal_eta < config_.switch_back_prediction_sec_))
213  {
214  ROS_INFO("The robot will reach a switch back point in %f sec. The next plan starts from the switch back.",
215  local_goal_eta);
216  return local_goal - 1;
217  }
218  }
219  else
220  {
221  ROS_WARN("The switch back point is not placed on a grid center. (%f, %f, %f) -> (%f, %f, %f)",
222  previous_path_2d_[local_goal_index].pos_.x(), previous_path_2d_[local_goal_index].pos_.y(),
223  previous_path_2d_[local_goal_index].yaw_, previous_path_2d_[local_goal_index + 1].pos_.x(),
224  previous_path_2d_[local_goal_index + 1].pos_.y(), previous_path_2d_[local_goal_index + 1].yaw_);
225  }
226  }
227  return previous_path_2d_.end();
228 }
229 
231 {
232  for (size_t i = 0; i < previous_path_2d_.size() - 1; ++i)
233  {
234  if (etas[i] > config_.prediction_sec_)
235  {
237  {
238  ROS_DEBUG("The robot will reach a grid center in %f sec.", etas[i]);
239  return previous_path_2d_.begin() + i;
240  }
241  }
242  }
243  return previous_path_2d_.end();
244 }
245 
246 } // namespace planner_3d
247 } // namespace planner_cspace
planner_cspace::planner_3d::StartPosePredictor::clear
void clear()
Definition: start_pose_predictor.cpp:76
planner_cspace
Definition: bbf.h:33
tf2::getYaw
double getYaw(const A &a)
planner_cspace::planner_3d::StartPosePredictor::getExpectedPose
trajectory_tracker::Path2D::ConstIterator getExpectedPose(const std::vector< double > &etas) const
Definition: start_pose_predictor.cpp:230
utils.h
ros.h
trajectory_tracker::Pose2D
planner_cspace::planner_3d::StartPosePredictor::isGridCenter
bool isGridCenter(const trajectory_tracker::Pose2D &pose) const
Definition: start_pose_predictor.cpp:165
start_pose_predictor.h
trajectory_tracker::Path2D::findNearestWithDistance
std::pair< ConstIterator, double > findNearestWithDistance(const ConstIterator &begin, const ConstIterator &end, const Eigen::Vector2d &target, const float max_search_range=0, const float epsilon=1e-6) const
trajectory_tracker::Path2D::ConstIterator
std::vector< Pose2D >::const_iterator ConstIterator
planner_cspace::planner_3d::StartPosePredictor::getInitialETA
double getInitialETA(const geometry_msgs::Pose &robot_pose, const trajectory_tracker::Pose2D &initial_path_pose) const
Definition: start_pose_predictor.cpp:122
planner_cspace::planner_3d::StartPosePredictor::config_
Config config_
Definition: start_pose_predictor.h:95
planner_cspace::planner_3d::StartPosePredictor::preserved_path_length_
double preserved_path_length_
Definition: start_pose_predictor.h:99
planner_cspace::planner_3d::grid_metric_converter::metric2Grid
void metric2Grid(const costmap_cspace_msgs::MapMetaData3D &map_info, int &x, int &y, int &yaw, const float gx, const float gy, const float gyaw)
Definition: grid_metric_converter.h:62
trajectory_tracker::Path2D::toMsg
void toMsg(PATH_TYPE &path) const
ROS_DEBUG
#define ROS_DEBUG(...)
planner_cspace::GridAstar
Definition: grid_astar.h:64
trajectory_tracker::Path2D::fromMsg
void fromMsg(const PATH_TYPE &path, const double in_place_turn_eps=1.0e-6)
planner_cspace::planner_3d::StartPosePredictor::Config::switch_back_prediction_sec_
double switch_back_prediction_sec_
Definition: start_pose_predictor.h:55
planner_cspace::planner_3d::StartPosePredictor::Config::prediction_sec_
double prediction_sec_
Definition: start_pose_predictor.h:54
ROS_WARN
#define ROS_WARN(...)
planner_cspace::planner_3d::StartPosePredictor::process
bool process(const geometry_msgs::Pose &robot_pose, const GridAstar< 3, 2 >::Gridmap< char, 0x40 > &cm, const costmap_cspace_msgs::MapMetaData3D &map_info, const nav_msgs::Path &previous_path_msg, Astar::Vec &result_start_grid)
Definition: start_pose_predictor.cpp:42
planner_cspace::planner_3d::StartPosePredictor::previous_path_2d_
trajectory_tracker::Path2D previous_path_2d_
Definition: start_pose_predictor.h:97
planner_cspace::planner_3d::StartPosePredictor::removeAlreadyPassed
bool removeAlreadyPassed(const geometry_msgs::Pose &robot_pose)
Definition: start_pose_predictor.cpp:83
planner_cspace::planner_3d::StartPosePredictor::Config::lin_vel_
double lin_vel_
Definition: start_pose_predictor.h:57
planner_cspace::planner_3d::StartPosePredictor::map_info_
costmap_cspace_msgs::MapMetaData3D map_info_
Definition: start_pose_predictor.h:96
planner_cspace::planner_3d::StartPosePredictor::getSwitchBack
trajectory_tracker::Path2D::ConstIterator getSwitchBack(const std::vector< double > &etas) const
Definition: start_pose_predictor.cpp:194
trajectory_tracker::Path2D::enumerateLocalGoals
std::vector< ConstIterator > enumerateLocalGoals(const ConstIterator &begin, const ConstIterator &end, const bool allow_switch_back, const bool allow_in_place_turn=true, const double epsilon=1e-6) const
planner_cspace::planner_3d::StartPosePredictor::Config::ang_vel_
double ang_vel_
Definition: start_pose_predictor.h:58
trajectory_tracker::Path2D::length
float length() const
planner_cspace::GridAstar< 3, 2 >::Vec
CyclicVecInt< DIM, NONCYCLIC > Vec
Definition: grid_astar.h:67
planner_cspace::planner_3d::StartPosePredictor::isPathColliding
bool isPathColliding(const trajectory_tracker::Path2D::ConstIterator &begin, const trajectory_tracker::Path2D::ConstIterator &end, const GridAstar< 3, 2 >::Gridmap< char, 0x40 > &cm)
Definition: start_pose_predictor.cpp:176
trajectory_tracker::Pose2D::pos_
Eigen::Vector2d pos_
planner_cspace::planner_3d::StartPosePredictor::preserved_path_
nav_msgs::Path preserved_path_
Definition: start_pose_predictor.h:98
ROS_INFO
#define ROS_INFO(...)
trajectory_tracker::Pose2D::yaw_
float yaw_
planner_cspace::planner_3d::StartPosePredictor::buildResults
bool buildResults(const trajectory_tracker::Path2D::ConstIterator &expected_start_pose_it, const geometry_msgs::Pose &start_metric, const GridAstar< 3, 2 >::Gridmap< char, 0x40 > &cm, Astar::Vec &start_grid)
Definition: start_pose_predictor.cpp:132
trajectory_tracker::Path2D::getEstimatedTimeOfArrivals
std::vector< double > getEstimatedTimeOfArrivals(const ConstIterator &begin, const ConstIterator &end, const double linear_speed, const double angular_speed, const double initial_eta_sec=0.0) const
planner_cspace::planner_3d::StartPosePredictor::Config::dist_stop_
double dist_stop_
Definition: start_pose_predictor.h:56
planner_cspace::CyclicVecBase
Definition: cyclic_vec.h:78


planner_cspace
Author(s): Atsushi Watanabe
autogenerated on Fri May 16 2025 02:15:23