orientation_tools_tests.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 *
3 * Software License Agreement (BSD License)
4 *
5 * Copyright (c) 2021, Michael Ferguson
6 * Copyright (c) 2009, Willow Garage, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of Willow Garage, Inc. nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 * Author: Michael Ferguson
37 *********************************************************************/
38 
39 #include <gtest/gtest.h>
40 #include <tf2/utils.h> // getYaw
42 
43 using namespace graceful_controller;
44 
45 TEST(OrientationToolsTests, test_bad_penultimate_pose)
46 {
47  // Most global planners plan on a discrete grid,
48  // but then add the start/end poses in continuous space.
49  // This can lead to an odd orientations near the start/end of the path.
50  std::vector<geometry_msgs::PoseStamped> path;
51  for (size_t i = 0; i < 10; ++i)
52  {
53  geometry_msgs::PoseStamped pose;
54  pose.pose.position.x = i * 0.05;
55  pose.pose.position.y = 0.0;
56  pose.pose.orientation.w = 1.0;
57  path.push_back(pose);
58  }
59 
60  // Set final pose to be off the grid and slightly BACK from last discrete pose
61  path.back().pose.position.x = 0.378;
62  path.back().pose.position.y = 0.02;
63  // Set final pose to have a small rotation
64  {
65  double final_yaw = 0.15;
66  path.back().pose.orientation.z = sin(final_yaw / 2.0);
67  path.back().pose.orientation.w = cos(final_yaw / 2.0);
68  }
69 
70  // Apply orientations
71  path = addOrientations(path);
72 
73  // Last orientation should be unaltered
74  EXPECT_EQ(0.0, path.back().pose.orientation.x);
75  EXPECT_EQ(0.0, path.back().pose.orientation.y);
76  EXPECT_FLOAT_EQ(0.15, tf2::getYaw(path.back().pose.orientation));
77 
78  // Early orientations should be forward
79  for (size_t i = 0; i < 8; ++i)
80  {
81  EXPECT_EQ(0.0, path[i].pose.orientation.x);
82  EXPECT_EQ(0.0, path[i].pose.orientation.y);
83  EXPECT_FLOAT_EQ(0.0, tf2::getYaw(path[i].pose.orientation));
84  }
85 
86  // Penultimate pose should be crazy
87  EXPECT_EQ(0.0, path[8].pose.orientation.x);
88  EXPECT_EQ(0.0, path[8].pose.orientation.y);
89  EXPECT_FLOAT_EQ(2.4037776, tf2::getYaw(path[8].pose.orientation));
90 
91  // Now filter the penultimate pose away
92  std::vector<geometry_msgs::PoseStamped> filtered_path;
93  filtered_path = applyOrientationFilter(path, 0.785 /* 45 degrees */, 0.25);
94 
95  // Should have removed one pose (penultimate one)
96  EXPECT_EQ(9, static_cast<int>(filtered_path.size()));
97 
98  // Last orientation should be unaltered
99  EXPECT_EQ(0.0, filtered_path.back().pose.orientation.x);
100  EXPECT_EQ(0.0, filtered_path.back().pose.orientation.y);
101  EXPECT_FLOAT_EQ(0.15, tf2::getYaw(filtered_path.back().pose.orientation));
102 
103  // Early orientations should be forward
104  for (size_t i = 0; i < 7; ++i)
105  {
106  EXPECT_EQ(0.0, filtered_path[i].pose.orientation.x);
107  EXPECT_EQ(0.0, filtered_path[i].pose.orientation.y);
108  EXPECT_FLOAT_EQ(0.0, tf2::getYaw(filtered_path[i].pose.orientation));
109  }
110 
111  // Penultimate pose should be "better"
112  EXPECT_EQ(0.0, filtered_path[7].pose.orientation.x);
113  EXPECT_EQ(0.0, filtered_path[7].pose.orientation.y);
114  EXPECT_FLOAT_EQ(0.62024951, tf2::getYaw(filtered_path[7].pose.orientation));
115 }
116 
117 TEST(OrientationToolsTests, test_bad_initial_pose)
118 {
119  // Most global planners plan on a discrete grid,
120  // but then add the start/end poses in continuous space.
121  // This can lead to an odd orientations near the start/end of the path.
122  std::vector<geometry_msgs::PoseStamped> path;
123  for (size_t i = 0; i < 10; ++i)
124  {
125  geometry_msgs::PoseStamped pose;
126  pose.pose.position.x = i * 0.05;
127  pose.pose.position.y = 0.0;
128  pose.pose.orientation.w = 1.0;
129  path.push_back(pose);
130  }
131 
132  // Set initial pose to be off the grid, leading to bad initial orientation
133  path.front().pose.position.x = 0.051;
134  path.front().pose.position.y = 0.01;
135  // Set final pose to have a small rotation
136  {
137  double final_yaw = -0.12;
138  path.back().pose.orientation.z = sin(final_yaw / 2.0);
139  path.back().pose.orientation.w = cos(final_yaw / 2.0);
140  }
141 
142  // Apply orientations
143  path = addOrientations(path);
144 
145  // Last orientation should be unaltered
146  EXPECT_EQ(0.0, path.back().pose.orientation.x);
147  EXPECT_EQ(0.0, path.back().pose.orientation.y);
148  EXPECT_FLOAT_EQ(-0.12, tf2::getYaw(path.back().pose.orientation));
149 
150  // Initial pose orientation should be crazy
151  EXPECT_EQ(0.0, path.front().pose.orientation.x);
152  EXPECT_EQ(0.0, path.front().pose.orientation.y);
153  EXPECT_FLOAT_EQ(-1.670465, tf2::getYaw(path.front().pose.orientation));
154 
155  // Other orientations should be forward
156  for (size_t i = 1; i < 9; ++i)
157  {
158  EXPECT_EQ(0.0, path[i].pose.orientation.x);
159  EXPECT_EQ(0.0, path[i].pose.orientation.y);
160  EXPECT_FLOAT_EQ(0.0, tf2::getYaw(path[i].pose.orientation));
161  }
162 
163  // Now filter the initial pose away
164  std::vector<geometry_msgs::PoseStamped> filtered_path;
165  filtered_path = applyOrientationFilter(path, 0.785 /* 45 degrees */, 0.25);
166 
167  // Should have removed one pose
168  EXPECT_EQ(9, static_cast<int>(filtered_path.size()));
169 
170  // Initial orientation should be better
171  EXPECT_EQ(0.0, filtered_path.front().pose.orientation.x);
172  EXPECT_EQ(0.0, filtered_path.front().pose.orientation.y);
173  EXPECT_FLOAT_EQ(-0.2013171, tf2::getYaw(filtered_path.front().pose.orientation));
174 
175  // Other orientations should be forward
176  for (size_t i = 1; i < 8; ++i)
177  {
178  EXPECT_EQ(0.0, filtered_path[i].pose.orientation.x);
179  EXPECT_EQ(0.0, filtered_path[i].pose.orientation.y);
180  EXPECT_FLOAT_EQ(0.0, tf2::getYaw(filtered_path[i].pose.orientation));
181  }
182 
183  // Last orientation should be unaltered
184  EXPECT_EQ(0.0, filtered_path.back().pose.orientation.x);
185  EXPECT_EQ(0.0, filtered_path.back().pose.orientation.y);
186  EXPECT_FLOAT_EQ(-0.12, tf2::getYaw(filtered_path.back().pose.orientation));
187 }
188 
189 TEST(OrientationToolsTests, test_zigzag)
190 {
191  // This is an entirely unlikely path
192  // Any global planner creating this path should be garbage collected
193  std::vector<geometry_msgs::PoseStamped> path;
194  for (size_t i = 0; i < 20; ++i)
195  {
196  geometry_msgs::PoseStamped pose;
197  pose.pose.position.x = i * 0.05;
198  if (i % 2 == 0)
199  {
200  pose.pose.position.y = 0.15;
201  }
202  else
203  {
204  pose.pose.position.y = 0.0;
205  }
206  pose.pose.orientation.w = 1.0;
207  path.push_back(pose);
208  }
209 
210  // Apply orientations
211  path = addOrientations(path);
212 
213  // Now filter this awful path
214  std::vector<geometry_msgs::PoseStamped> filtered_path;
215  filtered_path = applyOrientationFilter(path, 0.785 /* 45 degrees */, 1.0);
216 
217  // Should have removed some poses
218  EXPECT_EQ(6, static_cast<int>(filtered_path.size()));
219 
220  // Make sure the distance between remaining poses isn't too far
221  for (size_t i = 1; i < filtered_path.size(); ++i)
222  {
223  double dx = filtered_path[i].pose.position.x - filtered_path[i-1].pose.position.x;
224  double dy = filtered_path[i].pose.position.y - filtered_path[i-1].pose.position.y;
225  EXPECT_TRUE(std::hypot(dx, dy) < 0.25);
226  }
227 }
228 
229 TEST(OrientationToolsTests, test_yaw_gap_tolerance)
230 {
231  // This test verifies that poses do not exceed the yaw_gap_tolerance
232  std::vector<geometry_msgs::PoseStamped> path;
233 
234  // Initial pose is at origin
235  geometry_msgs::PoseStamped pose;
236  pose.pose.position.x = 0.0;
237  pose.pose.position.y = 0.0;
238  pose.pose.orientation.w = 1.0;
239  path.push_back(pose);
240 
241  // Next pose is back just a bit
242  pose.pose.position.x = -0.05;
243  pose.pose.position.y = -0.01;
244  path.push_back(pose);
245 
246  // Add remaining poses
247  for (size_t i = 0; i < 8; ++i)
248  {
249  pose.pose.position.x = -0.1;
250  pose.pose.position.y = -0.01 + i * 0.05;
251  path.push_back(pose);
252  }
253 
254  // Update final pose to have a rotation
255  {
256  double final_yaw = 1.0;
257  path.back().pose.orientation.z = sin(final_yaw / 2.0);
258  path.back().pose.orientation.w = cos(final_yaw / 2.0);
259  }
260 
261  // Apply orientations
262  path = addOrientations(path);
263 
264  // Last orientation should be unaltered
265  EXPECT_EQ(0.0, path.back().pose.orientation.x);
266  EXPECT_EQ(0.0, path.back().pose.orientation.y);
267  EXPECT_FLOAT_EQ(1.0, tf2::getYaw(path.back().pose.orientation));
268 
269  // Initial pose orientation should be way back
270  EXPECT_EQ(0.0, path.front().pose.orientation.x);
271  EXPECT_EQ(0.0, path.front().pose.orientation.y);
272  EXPECT_FLOAT_EQ(-2.9441972, tf2::getYaw(path.front().pose.orientation));
273 
274  // Second pose orientation should be straight back
275  EXPECT_EQ(0.0, path[1].pose.orientation.x);
276  EXPECT_EQ(0.0, path[1].pose.orientation.y);
277  EXPECT_FLOAT_EQ(3.1415927, tf2::getYaw(path[1].pose.orientation));
278 
279  // Other orientations should be up
280  for (size_t i = 2; i < 9; ++i)
281  {
282  EXPECT_EQ(0.0, path[i].pose.orientation.x);
283  EXPECT_EQ(0.0, path[i].pose.orientation.y);
284  EXPECT_FLOAT_EQ(1.5707964, tf2::getYaw(path[i].pose.orientation));
285  }
286 
287  // Now filter (without a max separation distance)
288  std::vector<geometry_msgs::PoseStamped> filtered_path;
289  filtered_path = applyOrientationFilter(path, 0.1, 10.0);
290  // Removes lots of poses
291  EXPECT_EQ(3, static_cast<int>(filtered_path.size()));
292 
293  // Now filter with decent max separation distance
294  filtered_path = applyOrientationFilter(path, 0.1, 0.15);
295  // Removes not so many poses
296  EXPECT_EQ(7, static_cast<int>(filtered_path.size()));
297 }
298 
299 int main(int argc, char** argv)
300 {
301  testing::InitGoogleTest(&argc, argv);
302  return RUN_ALL_TESTS();
303 }
graceful_controller::applyOrientationFilter
std::vector< geometry_msgs::PoseStamped > applyOrientationFilter(const std::vector< geometry_msgs::PoseStamped > &path, double yaw_tolerance, double gap_tolerance)
Filter a path for orientation noise.
Definition: orientation_tools.cpp:120
tf2::getYaw
double getYaw(const A &a)
utils.h
TEST
TEST(OrientationToolsTests, test_bad_penultimate_pose)
Definition: orientation_tools_tests.cpp:45
main
int main(int argc, char **argv)
Definition: orientation_tools_tests.cpp:299
graceful_controller
orientation_tools.hpp
graceful_controller::addOrientations
std::vector< geometry_msgs::PoseStamped > addOrientations(const std::vector< geometry_msgs::PoseStamped > &path)
Add orientation to each pose in a path.
Definition: orientation_tools.cpp:96


graceful_controller_ros
Author(s): Michael Ferguson
autogenerated on Wed Oct 23 2024 02:43:04