laser_scan_generator_test.cpp
Go to the documentation of this file.
1 #include <gtest/gtest.h>
2 
3 #include "../../core/mock_grid_cell.h"
4 
5 #include "../../../src/core/math_utils.h"
6 #include "../../../src/core/states/robot_pose.h"
7 #include "../../../src/core/maps/plain_grid_map.h"
8 #include "../../../src/utils/data_generation/map_primitives.h"
9 #include "../../../src/utils/data_generation/grid_map_patcher.h"
10 #include "../../../src/utils/data_generation/laser_scan_generator.h"
11 
12 #include "../../../src/utils/console_view.h"
13 
14 class LaserScanGeneratorTest : public ::testing::Test {
15 protected: // methods
17  : map{std::make_shared<MockGridCell>(),
19  , rpose{map.scale() / 2, map.scale() / 2, 0} /* middle of a cell */ {}
20 protected:
21  using ScanPoints = std::vector<ScanPoint2D>;
22 protected: // consts
23  static constexpr int Map_Width = 100;
24  static constexpr int Map_Height = 100;
25  static constexpr double Map_Scale = 1;
26 
27  static constexpr int Patch_Scale = 10;
28 protected: // fields
30  const RobotPoseDelta &rpd,
31  int scale = Patch_Scale) {
32  patch_map(mp, scale);
33  rpose += rpd;
34  }
35 
36  void patch_map(const MapPrimitive &mp, int scale) {
37  auto gm_patcher = GridMapPatcher{};
38  gm_patcher.apply_text_raster(map, mp.to_stream(), {}, scale, scale);
39  }
40 
41  void check_scan_points(const ScanPoints &expected, const ScanPoints &actual,
42  const ScanPoint2D &sp_err = {Map_Scale / 2, 0.001}) {
43  ASSERT_EQ(expected.size(), actual.size());
44  for (std::size_t sp_i = 0; sp_i < expected.size(); ++sp_i) {
45  check_scan_point(expected[sp_i], actual[sp_i], sp_err);
46  auto expected_occ = actual[sp_i].is_occupied() ? 1.0 : 0;
47  auto wp = actual[sp_i].move_origin(rpose.x, rpose.y, rpose.theta);
48  ASSERT_NEAR(expected_occ, map[map.world_to_cell(wp)], 0.01);
49  }
50  }
51 
52  void check_scan_point(const ScanPoint2D &expected, const ScanPoint2D &actual,
53  const ScanPoint2D &abs_err) {
54  ASSERT_NEAR(expected.angle(), actual.angle(), abs_err.angle());
55 
56  // scale absolute range error according to relative angle
57  auto range_err = abs_err.range() / std::cos(rpose.theta + actual.angle());
58  ASSERT_NEAR(expected.range(), actual.range(), std::abs(range_err));
59  }
60 
61  void dbg_show_map_near_robot(int l, int r, int u, int d) {
62  auto scale = Patch_Scale * map.scale();
64  l * scale, r * scale, u * scale, d * scale);
65  }
66 
67 protected: // fields
71 };
72 
73 //------------------------------------------------------------------------------
74 // Degenerate cases
75 
76 TEST_F(LaserScanGeneratorTest, emptyMap4beams) {
77  auto scan = lsg.laser_scan_2D(map, rpose, 1);
78 
79  const auto Expected_SPs = ScanPoints{};
80  check_scan_points(Expected_SPs, scan.points());
81 }
82 
83 TEST_F(LaserScanGeneratorTest, insideObstacle4beams) {
84  auto occ_obs = AreaOccupancyObservation{true, Occupancy{1.0, 1.0},
85  Point2D{rpose.x, rpose.y}, 1};
86  map.update(map.world_to_cell(rpose.x, rpose.y), occ_obs);
87 
88  auto scan = lsg.laser_scan_2D(map, rpose, 1);
89  const auto Expected_SPs = ScanPoints {{0, deg2rad(-180)}, {0, deg2rad(-90)},
90  {0, deg2rad(0)}, {0, deg2rad(90)}};
91 
92  check_scan_points(Expected_SPs, scan.points());
93 }
94 
95 //------------------------------------------------------------------------------
96 // Perpendicular wall facing
97 
98 TEST_F(LaserScanGeneratorTest, leftOfCecumEntranceFacingRight4beams) {
99  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
100  auto cecum_mp = CecumTextRasterMapPrimitive{7, 4, Top_Bnd_Pos};
101  auto mp_free_space = cecum_mp.free_space()[0];
102 
103  auto pose_delta = RobotPoseDelta{
104  (mp_free_space.left() * Patch_Scale) * map.scale(),
105  (-cecum_mp.height() * Patch_Scale + 1) * map.scale(),
106  deg2rad(0)
107  };
108  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
109  auto scan = lsg.laser_scan_2D(map, rpose, 1);
110 
111  const double scale = map.scale() * Patch_Scale;
112  const auto Expected_SPs = ScanPoints{
113  {map.scale(), deg2rad(-180)},
114  {mp_free_space.hside_len() * scale, deg2rad(0)},
115  {mp_free_space.vside_len() * scale, deg2rad(90)}};
116  check_scan_points(Expected_SPs, scan.points());
117 }
118 
119 TEST_F(LaserScanGeneratorTest, rightOfCecumEntranceFacingTop4beams) {
120  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
121  auto cecum_mp = CecumTextRasterMapPrimitive{7, 4, Top_Bnd_Pos};
122  auto mp_free_space = cecum_mp.free_space()[0];
123 
124  auto pose_delta = RobotPoseDelta{
125  (mp_free_space.right() * Patch_Scale - 1) * map.scale(),
126  (-cecum_mp.height() * Patch_Scale + 1) * map.scale(),
127  deg2rad(90)
128  };
129  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
130  const double scale = map.scale() * Patch_Scale;
131  auto scan = lsg.laser_scan_2D(map, rpose, 1);
132 
133  const auto Expected_SPs = ScanPoints{
134  {map.scale(), deg2rad(-90)},
135  {mp_free_space.vside_len() * scale, deg2rad(0)},
136  {mp_free_space.hside_len() * scale, deg2rad(90)}};
137  check_scan_points(Expected_SPs, scan.points());
138 }
139 
140 TEST_F(LaserScanGeneratorTest, rightOfCecumEndFacingLeft4beams) {
141  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
142  auto cecum_mp = CecumTextRasterMapPrimitive{7, 4, Top_Bnd_Pos};
143  auto mp_free_space = cecum_mp.free_space()[0];
144 
145  auto pose_delta = RobotPoseDelta{
146  (mp_free_space.right() * Patch_Scale - 1) * map.scale(),
147  ((mp_free_space.top() - 1) * Patch_Scale) * map.scale(),
148  deg2rad(180)
149  };
150  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
151  auto scan = lsg.laser_scan_2D(map, rpose, 1);
152 
153  const auto Expected_SPs = ScanPoints{
154  {map.scale(), deg2rad(-180)},
155  {map.scale(), deg2rad(-90)},
156  {mp_free_space.hside_len() * Patch_Scale * map.scale(), deg2rad(0)}};
157  check_scan_points(Expected_SPs, scan.points());
158 }
159 
160 TEST_F(LaserScanGeneratorTest, leftOfCecumEndFacingDown4beams) {
161  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
162  auto cecum_mp = CecumTextRasterMapPrimitive{7, 4, Top_Bnd_Pos};
163  auto mp_free_space = cecum_mp.free_space()[0];
164 
165  auto pose_delta = RobotPoseDelta{
166  (mp_free_space.left() * Patch_Scale) * map.scale(),
167  ((mp_free_space.top() - 1) * Patch_Scale) * map.scale(),
168  deg2rad(270)
169  };
170  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
171  auto scan = lsg.laser_scan_2D(map, rpose, 1);
172 
173  const auto Expected_SPs = ScanPoints{
174  {map.scale(), deg2rad(-180)},
175  {map.scale(), deg2rad(-90)},
176  {mp_free_space.hside_len() * Patch_Scale * map.scale(), deg2rad(90)}};
177  check_scan_points(Expected_SPs, scan.points());
178 }
179 
180 TEST_F(LaserScanGeneratorTest, middleOfCecumEntranceFacingIn4beams) {
181  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
182  auto cecum_mp = CecumTextRasterMapPrimitive{7, 4, Top_Bnd_Pos};
183  auto mp_free_space = cecum_mp.free_space()[0];
184 
185  auto pose_delta = RobotPoseDelta{
186  (cecum_mp.width() * Patch_Scale / 2) * map.scale(),
187  (-cecum_mp.height() * Patch_Scale + 1) * map.scale(),
188  deg2rad(90)};
189  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
190  auto scan = lsg.laser_scan_2D(map, rpose, 1);
191 
192  // extra 0.5*map.scale() is for robot offset inside the cell
193  const auto Expected_SPs = ScanPoints{
194  {((Patch_Scale * mp_free_space.hside_len() + 1) / 2) * map.scale(),
195  deg2rad(-90)},
196  {Patch_Scale * mp_free_space.vside_len() * map.scale(),
197  deg2rad(0)},
198  {(Patch_Scale * mp_free_space.hside_len() / 2 + 1) * map.scale(),
199  deg2rad(90)}
200  };
201 
202  check_scan_points(Expected_SPs, scan.points());
203 }
204 
205 //------------------------------------------------------------------------------
206 // Misc fall facing
207 
208 TEST_F(LaserScanGeneratorTest, rightOfCecumEndFacingLeftBot45deg4beams) {
209  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
210  auto cecum_mp = CecumTextRasterMapPrimitive{4, 7, Top_Bnd_Pos};
211  auto mp_fspc = cecum_mp.free_space()[0];
212 
213  auto pose_delta = RobotPoseDelta{
214  (mp_fspc.right() * Patch_Scale - 1) * map.scale(),
215  ((mp_fspc.top() - 1) * Patch_Scale) * map.scale(),
216  deg2rad(225)
217  };
218  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
219  auto scan = lsg.laser_scan_2D(map, rpose, 1);
220 
221  const auto Expected_SPs = ScanPoints{
222  {map.scale(), deg2rad(-180)},
223  {map.scale(), deg2rad(-90)},
224  {mp_fspc.hside_len() * map.scale() * Patch_Scale / std::cos(deg2rad(45)),
225  deg2rad(0)},
226  {map.scale(), deg2rad(90)}};
227  check_scan_points(Expected_SPs, scan.points());
228 }
229 
230 TEST_F(LaserScanGeneratorTest, leftOfCecumEndFacingRightBot30deg4beams) {
231  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
232  auto cecum_mp = CecumTextRasterMapPrimitive{3, 4, Top_Bnd_Pos};
233  auto mp_fspc = cecum_mp.free_space()[0];
234 
235  auto pose_delta = RobotPoseDelta{
236  (mp_fspc.left() * Patch_Scale) * map.scale(),
237  ((mp_fspc.top() - 1) * Patch_Scale) * map.scale(),
238  deg2rad(-30)
239  };
240  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
241  auto scan = lsg.laser_scan_2D(map, rpose, 1);
242 
243  const auto Expected_SPs = ScanPoints{
244  {map.scale(), deg2rad(-180)},
245  {map.scale(), deg2rad(-90)},
246  {mp_fspc.hside_len() * map.scale() * Patch_Scale / std::cos(deg2rad(30)),
247  deg2rad(0)},
248  {map.scale(), deg2rad(90)}};
249  check_scan_points(Expected_SPs, scan.points());
250 }
251 
252 TEST_F(LaserScanGeneratorTest, cecumCenterFacingDown8beams240FoW) {
253  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
254  // TODO: investigate expected scan points generation for the "4, 9" case
255  auto cecum_mp = CecumTextRasterMapPrimitive{3, 9, Top_Bnd_Pos};
256  auto mp_fspc = cecum_mp.free_space()[0];
257 
258  auto pose_delta = RobotPoseDelta{
259  (cecum_mp.width() * Patch_Scale) * map.scale() / 2,
260  ((mp_fspc.top() - 1) - mp_fspc.vside_len() / 2) * Patch_Scale * map.scale(),
261  deg2rad(-90)
262  };
263  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
264  auto lsgen = LaserScanGenerator{LaserScannerParams{30, deg2rad(270.0 / 8),
265  deg2rad(270.0 / 2)}};
266  auto scan = lsgen.laser_scan_2D(map, rpose, 1);
267 
268  constexpr double A_Step = 270.0 / 8;
269  double cecum_free_w = mp_fspc.hside_len();
270  double left_w = ((Patch_Scale * cecum_free_w + 1) / 2) * map.scale();
271  double right_w = (Patch_Scale * cecum_free_w / 2) * map.scale();
272  const auto Expected_SPs = ScanPoints{
273  {left_w / std::cos(deg2rad(45 - 0 * A_Step)), deg2rad(-135 + 0 * A_Step)},
274  {left_w / std::cos(deg2rad(45 - 1 * A_Step)), deg2rad(-135 + 1 * A_Step)},
275  {left_w / std::cos(deg2rad(45 - 2 * A_Step)), deg2rad(-135 + 2 * A_Step)},
276  {left_w / std::cos(deg2rad(45 - 3 * A_Step)), deg2rad(-135 + 3 * A_Step)},
277  // 0.0 angle
278  {right_w / std::cos(deg2rad(45 - 3 * A_Step)), deg2rad(-135 + 5 * A_Step)},
279  {right_w / std::cos(deg2rad(45 - 2 * A_Step)), deg2rad(-135 + 6 * A_Step)},
280  {right_w / std::cos(deg2rad(45 - 1 * A_Step)), deg2rad(-135 + 7 * A_Step)},
281  {right_w / std::cos(deg2rad(45 - 0 * A_Step)), deg2rad(-135 + 8 * A_Step)}};
282  check_scan_points(Expected_SPs, scan.points());
283 }
284 
285 //---//
286 
287 TEST_F(LaserScanGeneratorTest, trigonometricCacheCorrectness) {
288  const auto Top_Bnd_Pos = CecumTextRasterMapPrimitive::BoundPosition::Top;
289  auto cecum_mp = CecumTextRasterMapPrimitive{10, 10, Top_Bnd_Pos};
290  auto mp_fspc = cecum_mp.free_space()[0];
291 
292  auto pose_delta = RobotPoseDelta{
293  (cecum_mp.width() * Patch_Scale) * map.scale() / 2,
294  ((mp_fspc.top() - 1) - mp_fspc.vside_len() / 2) * Patch_Scale * map.scale(),
295  deg2rad(90)
296  };
297  prepare_map_and_robot_pose(cecum_mp, pose_delta, Patch_Scale);
298 
299  const int LS_Points_Nm = 4000;
300  auto lsgen = LaserScanGenerator{
301  LaserScannerParams{30, deg2rad(270.0 / LS_Points_Nm), deg2rad(270.0 / 2)}};
302  auto scan = lsgen.laser_scan_2D(map, rpose, 1);
303 
304  const double D_Angle = deg2rad(1.3);
305  scan.trig_provider->set_base_angle(D_Angle);
306  for (auto &sp : scan.points()) {
307  ASSERT_NEAR(std::cos(sp.angle() + D_Angle),
308  scan.trig_provider->cos(sp.angle()), 0.001);
309  ASSERT_NEAR(std::sin(sp.angle() + D_Angle),
310  scan.trig_provider->sin(sp.angle()), 0.001);
311  }
312 }
313 
314 //------------------------------------------------------------------------------
315 
316 int main (int argc, char *argv[]) {
317  ::testing::InitGoogleTest(&argc, argv);
318  return RUN_ALL_TESTS();
319 }
std::vector< Rectangle > free_space() const override
void check_scan_point(const ScanPoint2D &expected, const ScanPoint2D &actual, const ScanPoint2D &abs_err)
void dbg_show_map_near_robot(int l, int r, int u, int d)
Coord world_to_cell(const Point2D &pt) const
void show_grid_map(const GridMap &map, const Point2D &center, double d_x_left, double d_x_right, double d_y_up, double d_y_down)
Definition: console_view.h:4
double theta
Definition: robot_pose.h:131
void update(const Coord &area_id, const AreaOccupancyObservation &aoo) override
Updates area with a given observation.
TEST_F(LaserScanGeneratorTest, emptyMap4beams)
void patch_map(const MapPrimitive &mp, int scale)
void apply_text_raster(MapType &dst_map, std::istream &src_raster, const DiscretePoint2D &dst_offset, int w_zoom=1, int h_zoom=1)
double angle() const
Definition: sensor_data.h:54
virtual std::istream & to_stream() const =0
int main(int argc, char *argv[])
LaserScan2D laser_scan_2D(const GridMap &map, const RobotPose &pose, double occ_threshold=1)
void check_scan_points(const ScanPoints &expected, const ScanPoints &actual, const ScanPoint2D &sp_err={Map_Scale/2, 0.001})
std::vector< ScanPoint2D > ScanPoints
double range() const
Definition: sensor_data.h:47
void prepare_map_and_robot_pose(const MapPrimitive &mp, const RobotPoseDelta &rpd, int scale=Patch_Scale)
double y
Definition: robot_pose.h:131
constexpr double deg2rad(double angle_deg)
Definition: math_utils.h:56
double x
Definition: robot_pose.h:131
virtual double scale() const
static constexpr double Map_Scale


slam_constructor
Author(s): JetBrains Research, OSLL team
autogenerated on Mon Jun 10 2019 15:08:25