Skip to content

Commit c25fc31

Browse files
committed
added tests
Signed-off-by: silanus23 <[email protected]>
1 parent b899454 commit c25fc31

File tree

2 files changed

+333
-0
lines changed

2 files changed

+333
-0
lines changed

nav2_util/test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ target_link_libraries(test_twist_publisher ${library_name} rclcpp::rclcpp ${geom
2929

3030
ament_add_gtest(test_twist_subscriber test_twist_subscriber.cpp)
3131
target_link_libraries(test_twist_subscriber ${library_name} rclcpp::rclcpp ${geometry_msgs_TARGETS})
32+
33+
ament_add_gtest(test_path_utils test_path_utils.cpp)
34+
target_link_libraries(test_path_utils ${library_name} ${nav_2d_msgs_TARGETS} ${geometry_msgs_TARGETS})

nav2_util/test/test_path_utils.cpp

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
// Copyright (c) 2025 Berkan Tali
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 <vector>
18+
19+
#include "gtest/gtest.h"
20+
#include "geometry_msgs/msg/pose_stamped.hpp"
21+
#include "nav_msgs/msg/path.hpp"
22+
#include "nav2_util/path_utils.hpp"
23+
24+
geometry_msgs::msg::PoseStamped createPose(double x, double y)
25+
{
26+
geometry_msgs::msg::PoseStamped pose;
27+
pose.pose.position.x = x;
28+
pose.pose.position.y = y;
29+
pose.pose.position.z = 0.0;
30+
pose.pose.orientation.w = 1.0;
31+
pose.header.frame_id = "map";
32+
return pose;
33+
}
34+
35+
void generateCirclePath(
36+
nav_msgs::msg::Path & path,
37+
double center_x, double center_y, double radius,
38+
int num_points, double start_angle = 0.0, double end_angle = 2.0 * M_PI)
39+
{
40+
const double angle_step = (end_angle - start_angle) / (num_points - 1);
41+
for (int i = 0; i < num_points; ++i) {
42+
const double angle = start_angle + i * angle_step;
43+
path.poses.push_back(createPose(
44+
center_x + radius * std::cos(angle),
45+
center_y + radius * std::sin(angle)));
46+
}
47+
}
48+
49+
class CloverleafPathTest : public ::testing::Test
50+
{
51+
protected:
52+
void SetUp() override
53+
{
54+
generateCirclePath(target_path, 5.0, 0.0, 5.0, 50);
55+
generateCirclePath(target_path, -5.0, 0.0, 5.0, 50);
56+
generateCirclePath(target_path, 0.0, 5.0, 5.0, 50);
57+
58+
nav_msgs::msg::Path robot_path;
59+
generateCirclePath(robot_path, 5.0, 0.0, 4.8, 50);
60+
robot_trajectory = robot_path.poses;
61+
}
62+
nav_msgs::msg::Path target_path;
63+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
64+
};
65+
66+
class RetracingCircleTest : public ::testing::Test
67+
{
68+
protected:
69+
void SetUp() override
70+
{
71+
generateCirclePath(target_path, 0.0, 0.0, 5.0, 50, 0.0, 2.0 * M_PI);
72+
generateCirclePath(target_path, 0.0, 0.0, 5.0, 50, 2.0 * M_PI, 0.0);
73+
nav_msgs::msg::Path robot_path;
74+
generateCirclePath(robot_path, 0.0, 0.0, 5.2, 50, 0.0, 2.0 * M_PI);
75+
generateCirclePath(robot_path, 0.0, 0.0, 5.2, 50, 2.0 * M_PI, 0.0);
76+
robot_trajectory = robot_path.poses;
77+
}
78+
nav_msgs::msg::Path target_path;
79+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
80+
};
81+
82+
class ZigZagPathTest : public ::testing::Test
83+
{
84+
protected:
85+
void SetUp() override
86+
{
87+
target_path.poses.push_back(createPose(0.0, 0.0));
88+
target_path.poses.push_back(createPose(10.0, 0.0));
89+
target_path.poses.push_back(createPose(10.0, 5.0));
90+
target_path.poses.push_back(createPose(0.0, 5.0));
91+
target_path.poses.push_back(createPose(0.0, 10.0));
92+
93+
robot_trajectory = {
94+
createPose(1.0, 0.2), createPose(5.0, 0.2), createPose(9.0, 0.2),
95+
createPose(9.8, 1.0), createPose(10.2, 2.5), createPose(9.8, 4.0),
96+
createPose(8.0, 5.2), createPose(5.0, 5.2), createPose(2.0, 5.2),
97+
createPose(0.2, 6.0), createPose(0.2, 8.0), createPose(0.2, 9.8)
98+
};
99+
}
100+
nav_msgs::msg::Path target_path;
101+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
102+
};
103+
104+
class HairpinTurnTest : public ::testing::Test
105+
{
106+
protected:
107+
void SetUp() override
108+
{
109+
target_path.poses.push_back(createPose(0.0, 1.0));
110+
target_path.poses.push_back(createPose(10.0, 1.0));
111+
target_path.poses.push_back(createPose(10.0, -1.0));
112+
target_path.poses.push_back(createPose(0.0, -1.0));
113+
114+
robot_trajectory = {
115+
createPose(1.0, 1.2), createPose(3.0, 1.2), createPose(5.0, 1.2),
116+
createPose(7.0, 1.2), createPose(8.5, 1.0), createPose(9.2, 0.5),
117+
createPose(9.8, 0.0), createPose(9.8, -0.5), createPose(9.2, -1.0),
118+
createPose(8.0, -1.2), createPose(6.0, -1.2), createPose(4.0, -1.2),
119+
createPose(2.0, -1.2)
120+
};
121+
}
122+
nav_msgs::msg::Path target_path;
123+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
124+
};
125+
126+
class CuttingCornerTest : public ::testing::Test
127+
{
128+
protected:
129+
void SetUp() override
130+
{
131+
target_path.poses.push_back(createPose(0.0, 0.0));
132+
target_path.poses.push_back(createPose(10.0, 0.0));
133+
target_path.poses.push_back(createPose(10.0, 10.0));
134+
135+
robot_trajectory = {
136+
createPose(0.0, 0.2), createPose(2.0, 0.2), createPose(4.0, 0.2),
137+
createPose(6.0, 0.2), createPose(8.0, 0.2), createPose(9.0, 1.0),
138+
createPose(9.8, 2.0), createPose(9.8, 4.0), createPose(9.8, 6.0),
139+
createPose(9.8, 8.0), createPose(9.8, 10.0)
140+
};
141+
expected_distances = {0.2, 0.2, 0.2, 0.2, 0.2, 1.0, 0.2, 0.2, 0.2, 0.2, 0.2};
142+
}
143+
nav_msgs::msg::Path target_path;
144+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
145+
std::vector<double> expected_distances;
146+
};
147+
148+
class RetracingPathTest : public ::testing::Test
149+
{
150+
protected:
151+
void SetUp() override
152+
{
153+
for (int i = 0; i <= 10; ++i) {target_path.poses.push_back(createPose(i, 0.0));}
154+
for (int i = 9; i >= 0; --i) {target_path.poses.push_back(createPose(i, 0.0));}
155+
for (int i = 0; i <= 10; ++i) {robot_trajectory.push_back(createPose(i, 0.5));}
156+
for (int i = 9; i >= 0; --i) {robot_trajectory.push_back(createPose(i, 0.5));}
157+
}
158+
nav_msgs::msg::Path target_path;
159+
std::vector<geometry_msgs::msg::PoseStamped> robot_trajectory;
160+
};
161+
162+
TEST(PathUtilsTest, EmptyAndSinglePointPaths)
163+
{
164+
auto robot_pose = createPose(5.0, 5.0);
165+
nav_msgs::msg::Path empty_path;
166+
167+
auto result = nav2_util::distanceFromPath(empty_path, robot_pose);
168+
EXPECT_EQ(result.distance, std::numeric_limits<double>::infinity());
169+
170+
nav_msgs::msg::Path single_point_path;
171+
single_point_path.poses.push_back(createPose(0.0, 0.0));
172+
result = nav2_util::distanceFromPath(single_point_path, robot_pose);
173+
EXPECT_NEAR(result.distance, 7.071, 0.01);
174+
}
175+
176+
TEST_F(CuttingCornerTest, TrajectoryCutsCorner)
177+
{
178+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
179+
const auto & robot_pose = robot_trajectory[i];
180+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
181+
EXPECT_NEAR(result.distance, expected_distances[i], 0.1);
182+
}
183+
}
184+
185+
TEST_F(RetracingPathTest, TrajectoryFollowsRetracingPath)
186+
{
187+
const double expected_distance = 0.5;
188+
189+
for (const auto & robot_pose : robot_trajectory) {
190+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
191+
EXPECT_NEAR(result.distance, expected_distance, 1e-6);
192+
}
193+
}
194+
195+
TEST_F(CloverleafPathTest, TrajectoryFollowsCloverleafLoop)
196+
{
197+
for (const auto & robot_pose : robot_trajectory) {
198+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
199+
EXPECT_LT(result.distance, 0.25);
200+
}
201+
}
202+
203+
TEST_F(RetracingCircleTest, TrajectoryFollowsRetracingCircle)
204+
{
205+
const double expected_distance = 0.2;
206+
207+
for (const auto & robot_pose : robot_trajectory) {
208+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
209+
EXPECT_NEAR(result.distance, expected_distance, 0.01);
210+
}
211+
}
212+
213+
TEST_F(ZigZagPathTest, TrajectoryFollowsZigZagPath)
214+
{
215+
for (const auto & robot_pose : robot_trajectory) {
216+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
217+
EXPECT_LT(result.distance, 1.0);
218+
}
219+
}
220+
221+
TEST_F(HairpinTurnTest, TrajectoryFollowsHairpinTurn)
222+
{
223+
for (const auto & robot_pose : robot_trajectory) {
224+
auto result = nav2_util::distanceFromPath(target_path, robot_pose);
225+
EXPECT_LT(result.distance, 1.5);
226+
}
227+
}
228+
229+
class CuttingCornerWindowedTest : public CuttingCornerTest {};
230+
231+
TEST_F(CuttingCornerWindowedTest, WindowedSearch)
232+
{
233+
size_t start_index = 0;
234+
const double search_window = 11.0;
235+
236+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
237+
const auto & robot_pose = robot_trajectory[i];
238+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
239+
EXPECT_NEAR(result.distance, expected_distances[i], 0.15);
240+
}
241+
}
242+
243+
class RetracingPathWindowedTest : public RetracingPathTest {};
244+
245+
TEST_F(RetracingPathWindowedTest, WindowedSearch)
246+
{
247+
const double expected_distance = 0.5;
248+
const double search_window = 21.0;
249+
size_t start_index = 0;
250+
251+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
252+
const auto & robot_pose = robot_trajectory[i];
253+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
254+
EXPECT_NEAR(result.distance, expected_distance, 1e-6);
255+
}
256+
}
257+
258+
class CloverleafPathWindowedTest : public CloverleafPathTest {};
259+
260+
TEST_F(CloverleafPathWindowedTest, WindowedSearch)
261+
{
262+
const double search_window = 50.0;
263+
size_t start_index = 0;
264+
265+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
266+
const auto & robot_pose = robot_trajectory[i];
267+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
268+
EXPECT_LT(result.distance, 0.25);
269+
}
270+
}
271+
272+
class RetracingCircleWindowedTest : public RetracingCircleTest {};
273+
274+
TEST_F(RetracingCircleWindowedTest, WindowedSearch)
275+
{
276+
const double expected_distance = 0.2;
277+
const double search_window = 100.0;
278+
size_t start_index = 0;
279+
280+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
281+
const auto & robot_pose = robot_trajectory[i];
282+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
283+
EXPECT_NEAR(result.distance, expected_distance, 0.05);
284+
}
285+
}
286+
287+
class ZigZagPathWindowedTest : public ZigZagPathTest {};
288+
289+
TEST_F(ZigZagPathWindowedTest, WindowedSearch)
290+
{
291+
const double search_window = 12.0;
292+
size_t start_index = 0;
293+
294+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
295+
const auto & robot_pose = robot_trajectory[i];
296+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
297+
EXPECT_LT(result.distance, 1.0);
298+
}
299+
}
300+
301+
class HairpinTurnWindowedTest : public HairpinTurnTest {};
302+
303+
TEST_F(HairpinTurnWindowedTest, WindowedSearch)
304+
{
305+
const double search_window = 13.0;
306+
size_t start_index = 0;
307+
308+
for (size_t i = 0; i < robot_trajectory.size(); ++i) {
309+
const auto & robot_pose = robot_trajectory[i];
310+
auto result = nav2_util::distanceFromPath(target_path, robot_pose, start_index, search_window);
311+
EXPECT_LT(result.distance, 1.5);
312+
}
313+
}
314+
315+
TEST(PathUtilsWindowedTest, EdgeCases)
316+
{
317+
auto robot_pose = createPose(5.0, 5.0);
318+
nav_msgs::msg::Path test_path;
319+
test_path.poses.push_back(createPose(0.0, 0.0));
320+
test_path.poses.push_back(createPose(10.0, 0.0));
321+
322+
auto result = nav2_util::distanceFromPath(test_path, robot_pose, 1, 5.0);
323+
EXPECT_NEAR(result.distance, 7.071, 0.01);
324+
325+
result = nav2_util::distanceFromPath(test_path, robot_pose, 0, 0.0);
326+
EXPECT_NEAR(result.distance, 7.071, 0.01);
327+
328+
result = nav2_util::distanceFromPath(test_path, robot_pose, 10, 5.0);
329+
EXPECT_EQ(result.distance, std::numeric_limits<double>::infinity());
330+
}

0 commit comments

Comments
 (0)