test_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 <gtest/gtest.h>
31 
32 #include <Eigen/Core>
33 #include <list>
34 #include <string>
35 #include <unordered_map>
36 #include <utility>
37 
38 #include <nav_msgs/Path.h>
41 
42 #include <ros/ros.h>
43 
44 namespace planner_cspace
45 {
46 namespace planner_3d
47 {
48 class StartPosePredictorTester : public ::testing::Test
49 {
50 protected:
51  void SetUp() final
52  {
53  config_ =
54  {
55  .prediction_sec_ = 0.5,
56  .switch_back_prediction_sec_ = 2.0,
57  .dist_stop_ = 0.2,
58  .lin_vel_ = 1.5,
59  .ang_vel_ = M_PI / 2,
60  };
62 
63  map_info_.width = 11;
64  map_info_.height = 11;
65  map_info_.angle = 4;
66  map_info_.origin.position.x = -5.5;
67  map_info_.origin.position.y = -5.5;
68  map_info_.origin.orientation.w = 1.0;
69  map_info_.linear_resolution = 1.0;
70  map_info_.angular_resolution = 2 * M_PI / map_info_.angle;
71  motion_cache_.reset(map_info_.linear_resolution, map_info_.angular_resolution, 4, cm_.getAddressor(), 0.1, 0.1);
72 
73  const GridAstar<3, 2>::Vec size3d(
74  static_cast<int>(map_info_.width),
75  static_cast<int>(map_info_.height),
76  static_cast<int>(map_info_.angle));
77  cm_.reset(size3d);
78  for (int a = 0; a < static_cast<int>(map_info_.angle); ++a)
79  {
80  for (int y = 0; y < static_cast<int>(map_info_.height); ++y)
81  {
82  for (int x = 0; x < static_cast<int>(map_info_.width); ++x)
83  {
84  cm_[GridAstar<3, 2>::Vec(x, y, a)] = 0;
85  }
86  }
87  }
88 
89  const std::list<StartPosePredictor::Astar::Vec> path_grid_straight =
90  {
91  StartPosePredictor::Astar::Vec(5, 5, 0), // Start is (0.0, 0.0, 0.0)
93  StartPosePredictor::Astar::Vec(7, 5, 0), // Moves 2 meters ahead
94  };
95  paths_["straight"] = std::make_pair(path_grid_straight, convertToMetricPath(path_grid_straight));
96 
97  const std::list<StartPosePredictor::Astar::Vec> path_grid_switch_back =
98  {
99  StartPosePredictor::Astar::Vec(5, 5, 0), // Start is (0.0, 0.0, 0.0)
102  StartPosePredictor::Astar::Vec(8, 6, 1), // Curv from (2.0, 0.0, 0.0) to (3.0, 1.0, pi/2)
103  StartPosePredictor::Astar::Vec(9, 5, 2), // Curv from (3.0, 1.0, 0.0) to (4.0, 0.0, pi)
104  StartPosePredictor::Astar::Vec(10, 5, 2), // goal is (5.0, 0.0, pi)
105  };
106  paths_["switch_back"] = std::make_pair(path_grid_switch_back, convertToMetricPath(path_grid_switch_back));
107  }
108 
109  nav_msgs::Path convertToMetricPath(const std::list<GridAstar<3, 2>::Vec>& path_grid) const
110  {
111  const auto path_interpolated = motion_cache_.interpolatePath(path_grid);
112 
113  nav_msgs::Path result_path;
114  result_path.header.frame_id = "map";
115  for (const auto& pose_grid : path_interpolated)
116  {
117  geometry_msgs::PoseStamped pose;
118  pose.header.frame_id = "map";
119  float x, y, yaw;
120  grid_metric_converter::grid2Metric(map_info_, pose_grid[0], pose_grid[1], pose_grid[2], x, y, yaw);
121  pose.pose = getPose(x, y, yaw);
122  result_path.poses.push_back(pose);
123  }
124  return result_path;
125  }
126 
127  geometry_msgs::Pose getPose(const double x, const double y, const double yaw) const
128  {
129  geometry_msgs::Pose pose;
130  pose.position.x = x;
131  pose.position.y = y;
132  pose.orientation.z = std::sin(yaw / 2);
133  pose.orientation.w = std::cos(yaw / 2);
134  return pose;
135  }
136 
140  costmap_cspace_msgs::MapMetaData3D map_info_;
142  std::unordered_map<std::string, std::pair<std::list<StartPosePredictor::Astar::Vec>, nav_msgs::Path>> paths_;
143 };
144 
146 {
148  // Empty path
149  EXPECT_FALSE(predictor_.process(getPose(0.0, 0.0, 0.0), cm_, map_info_, nav_msgs::Path(), start_grid));
150 
151  // Far from path
152  EXPECT_FALSE(predictor_.process(getPose(0.0, 0.5, 0.0), cm_, map_info_,
153  paths_["straight"].second, start_grid));
154 
155  // Only the last pose is remaining
156  nav_msgs::Path rotating_path;
157  rotating_path.header.frame_id = "map";
158  rotating_path.poses.resize(1);
159  rotating_path.poses[0].header.frame_id = "map";
160  rotating_path.poses[0].pose = getPose(0.01, 1.01, M_PI / 2);
161 
162  EXPECT_TRUE(predictor_.process(getPose(0.0, 1.0, 1.3), cm_, map_info_, rotating_path, start_grid));
163  EXPECT_EQ(StartPosePredictor::Astar::Vec(5, 6, 1), start_grid);
164  EXPECT_TRUE(predictor_.getPreservedPath().poses.empty());
165 }
166 
168 {
169  {
171  EXPECT_TRUE(predictor_.process(getPose(0.05, 0.05, 0.0), cm_, map_info_, paths_["straight"].second, start_grid));
172  EXPECT_EQ(StartPosePredictor::Astar::Vec(6, 5, 0), start_grid);
173  const auto preserved_path = predictor_.getPreservedPath();
174  // Only (0, 0, 0) is removed as the nearest is the first line strip, that is from (0, 0, 0) to (0.1, 0, 0).
175  ASSERT_EQ(preserved_path.poses.size(), 9);
176  for (size_t i = 0; i < preserved_path.poses.size(); ++i)
177  {
178  const auto& path_pose = preserved_path.poses[i].pose;
179  EXPECT_NEAR(path_pose.position.x, 0.1f * (i + 1), 1.0e-6);
180  EXPECT_NEAR(path_pose.position.y, 0.0, 1.0e-6);
181  EXPECT_NEAR(tf2::getYaw(path_pose.orientation), 0.0, 1.0e-6);
182  }
183  EXPECT_NEAR(predictor_.getPreservedPathLength(), 0.9, 1.0e-6);
184  }
185  {
187  EXPECT_TRUE(predictor_.process(getPose(-0.05, 0.05, 0.0), cm_, map_info_, paths_["straight"].second, start_grid));
188  EXPECT_EQ(StartPosePredictor::Astar::Vec(6, 5, 0), start_grid);
189  const auto preserved_path = predictor_.getPreservedPath();
190  // No points are removed as the robot have not reached the first line strip.
191  ASSERT_EQ(preserved_path.poses.size(), 10);
192  for (size_t i = 0; i < preserved_path.poses.size(); ++i)
193  {
194  const auto& path_pose = preserved_path.poses[i].pose;
195  EXPECT_NEAR(path_pose.position.x, 0.1f * i, 1.0e-6);
196  EXPECT_NEAR(path_pose.position.y, 0.0, 1.0e-6);
197  EXPECT_NEAR(tf2::getYaw(path_pose.orientation), 0.0, 1.0e-6);
198  }
199  EXPECT_NEAR(predictor_.getPreservedPathLength(), 1.0, 1.0e-6);
200  }
201 
202  {
203  // The robot will collide with an obstacle during the planning.
204  cm_[GridAstar<3, 2>::Vec(6, 5, 0)] = 100;
205 
207  EXPECT_FALSE(predictor_.process(getPose(0.05, 0.05, 0.0), cm_, map_info_, paths_["straight"].second, start_grid));
208  EXPECT_TRUE(predictor_.getPreservedPath().poses.empty());
209  EXPECT_EQ(predictor_.getPreservedPathLength(), 0.0);
210  }
211 }
212 
214 {
215  {
217  EXPECT_TRUE(predictor_.process(getPose(0.25, 0.0, 0.0), cm_, map_info_, paths_["switch_back"].second, start_grid));
218  // The robot will not reach the switch back within 2.0 seconds, and the start grid will be on the first line.
219  EXPECT_EQ(StartPosePredictor::Astar::Vec(7, 5, 0), start_grid);
220  const auto preserved_path = predictor_.getPreservedPath();
221  ASSERT_EQ(preserved_path.poses.size(), 17);
222  for (size_t i = 0; i < preserved_path.poses.size(); ++i)
223  {
224  const auto& path_pose = preserved_path.poses[i].pose;
225  const auto& expected_path_pose = paths_["switch_back"].second.poses[i + 3].pose;
226  EXPECT_NEAR(path_pose.position.x, expected_path_pose.position.x, 1.0e-6);
227  EXPECT_NEAR(path_pose.position.y, expected_path_pose.position.y, 1.0e-6);
228  EXPECT_NEAR(tf2::getYaw(path_pose.orientation), tf2::getYaw(expected_path_pose.orientation), 1.0e-6);
229  }
230  }
231 
232  {
234  EXPECT_TRUE(predictor_.process(getPose(0.65, 0.0, 0.0), cm_, map_info_, paths_["switch_back"].second, start_grid));
235  // The robot will reach the switch back within 2.0 seconds, and the start grid is the switch back point.
236  EXPECT_EQ(StartPosePredictor::Astar::Vec(8, 6, 1), start_grid);
237  const auto preserved_path = predictor_.getPreservedPath();
238  // A part of the first forward, the second forward, and the first turn.
239  ASSERT_EQ(preserved_path.poses.size(), 4 + 10 + 13);
240  for (size_t i = 0; i < preserved_path.poses.size(); ++i)
241  {
242  const auto& path_pose = preserved_path.poses[i].pose;
243  const auto& expected_path_pose = paths_["switch_back"].second.poses[i + 7].pose;
244  EXPECT_NEAR(path_pose.position.x, expected_path_pose.position.x, 1.0e-6);
245  EXPECT_NEAR(path_pose.position.y, expected_path_pose.position.y, 1.0e-6);
246  EXPECT_NEAR(tf2::getYaw(path_pose.orientation), tf2::getYaw(expected_path_pose.orientation), 1.0e-6);
247  }
248  }
249 }
250 
251 } // namespace planner_3d
252 } // namespace planner_cspace
253 
254 int main(int argc, char** argv)
255 {
256  testing::InitGoogleTest(&argc, argv);
257 
258  return RUN_ALL_TESTS();
259 }
planner_cspace::planner_3d::TEST_F
TEST_F(DistanceMapTest, Create)
Definition: test_distance_map.cpp:218
planner_cspace::planner_3d::grid_metric_converter::grid2Metric
void grid2Metric(const costmap_cspace_msgs::MapMetaData3D &map_info, const T x, const T y, const T yaw, float &gx, float &gy, float &gyaw)
Definition: grid_metric_converter.h:50
planner_cspace
Definition: bbf.h:33
planner_cspace::planner_3d::StartPosePredictor
Definition: start_pose_predictor.h:47
motion_cache.h
planner_cspace::planner_3d::MotionCache::reset
void reset(const float linear_resolution, const float angular_resolution, const int range, const std::function< void(CyclicVecInt< 3, 2 >, size_t &, size_t &)> gm_addr, const float interpolation_resolution, const float grid_enumeration_resolution)
Definition: motion_cache.cpp:45
tf2::getYaw
double getYaw(const A &a)
ros.h
start_pose_predictor.h
main
int main(int argc, char **argv)
Definition: test_start_pose_predictor.cpp:254
planner_cspace::planner_3d::StartPosePredictorTester::getPose
geometry_msgs::Pose getPose(const double x, const double y, const double yaw) const
Definition: test_start_pose_predictor.cpp:127
planner_cspace::planner_3d::StartPosePredictorTester::map_info_
costmap_cspace_msgs::MapMetaData3D map_info_
Definition: test_start_pose_predictor.cpp:140
planner_cspace::planner_3d::StartPosePredictorTester::motion_cache_
MotionCache motion_cache_
Definition: test_start_pose_predictor.cpp:141
planner_cspace::planner_3d::StartPosePredictorTester::config_
StartPosePredictor::Config config_
Definition: test_start_pose_predictor.cpp:138
planner_cspace::planner_3d::StartPosePredictorTester::predictor_
StartPosePredictor predictor_
Definition: test_start_pose_predictor.cpp:137
planner_cspace::GridAstar
Definition: grid_astar.h:64
planner_cspace::planner_3d::StartPosePredictor::Config::prediction_sec_
double prediction_sec_
Definition: start_pose_predictor.h:54
planner_cspace::planner_3d::StartPosePredictorTester::cm_
GridAstar< 3, 2 >::Gridmap< char, 0x40 > cm_
Definition: test_start_pose_predictor.cpp:139
planner_cspace::planner_3d::StartPosePredictor::setConfig
void setConfig(const Config &config)
Definition: start_pose_predictor.h:60
planner_cspace::GridAstar::Vec
CyclicVecInt< DIM, NONCYCLIC > Vec
Definition: grid_astar.h:67
planner_cspace::planner_3d::StartPosePredictorTester::convertToMetricPath
nav_msgs::Path convertToMetricPath(const std::list< GridAstar< 3, 2 >::Vec > &path_grid) const
Definition: test_start_pose_predictor.cpp:109
planner_cspace::planner_3d::MotionCache
Definition: motion_cache.h:44
planner_cspace::planner_3d::StartPosePredictor::Config
Definition: start_pose_predictor.h:52
planner_cspace::planner_3d::MotionCache::interpolatePath
std::list< CyclicVecFloat< 3, 2 > > interpolatePath(const std::list< CyclicVecInt< 3, 2 >> &path_grid) const
Definition: motion_cache.cpp:192
planner_cspace::GridAstar::reset
void reset(const Vec size)
Definition: grid_astar.h:147
planner_cspace::planner_3d::StartPosePredictorTester
Definition: test_start_pose_predictor.cpp:48
planner_cspace::planner_3d::StartPosePredictorTester::paths_
std::unordered_map< std::string, std::pair< std::list< StartPosePredictor::Astar::Vec >, nav_msgs::Path > > paths_
Definition: test_start_pose_predictor.cpp:142
planner_cspace::planner_3d::StartPosePredictorTester::SetUp
void SetUp() final
Definition: test_start_pose_predictor.cpp:51
planner_cspace::CyclicVecBase
Definition: cyclic_vec.h:78


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