lanelet2_matching.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019
3  * FZI Forschungszentrum Informatik, Karlsruhe, Germany (www.fzi.de)
4  * KIT, Institute of Measurement and Control, Karlsruhe, Germany (www.mrt.kit.edu)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <lanelet2_io/Io.h>
32 #include <lanelet2_projection/UTM.h>
35 
36 #include "LaneletMatching.h"
37 #include "gtest/gtest.h"
38 
39 using namespace lanelet;
40 
41 namespace {
42 template <typename MatchVectorT>
43 std::string toString(const MatchVectorT& matchVector) {
44  std::string out;
45  for (auto& match : matchVector) {
46  out += std::to_string(match.lanelet.id());
47  if (match.lanelet.inverted()) {
48  out += "inv";
49  }
50  out += " ";
51  }
52  return out;
53 }
54 
55 inline BasicPolygon2d absoluteHull(const BasicPolygon2d& relativeHull, const matching::Pose2d& pose) {
56  BasicPolygon2d hullPoints;
57  hullPoints.reserve(relativeHull.size());
58  for (const auto& hullPt : relativeHull) {
59  hullPoints.push_back(pose * hullPt);
60  }
61  return hullPoints;
62 }
63 } // namespace
64 
65 class MatchingUtilitiesBase : public ::testing::Test {
70  /* looks like this:
71  *
72  * p1----ls11->---p2 p5----ls15->--p6
73  * | |
74  * | |
75  * ls13 ls14 ...
76  * | |
77  * v v
78  * | |
79  * p3----ls12->---p4 p7----ls16->--p8
80  *
81  * right: ll21 right: ll23
82  * down: ll22 (pedestrian)
83  *
84  */
85  public:
87  map = std::make_shared<LaneletMap>();
88  map->add(ll21);
89  map->add(ll22);
90  map->add(ll23);
91  }
93 
94  lanelet::Point3d p1{1, 0, 0}, p2{2, 2, 0}, p3{3, 0, 2}, p4{4, 2, 2};
95  lanelet::Point3d p5{1, 100, 0}, p6{2, 102, 0}, p7{3, 100, 2}, p8{4, 102, 2};
96  lanelet::LineString3d ls11{11, {p1, p2}}, ls12{12, {p3, p4}}, ls13{13, {p1, p3}}, ls14{14, {p2, p4}};
97  lanelet::LineString3d ls15{15, {p5, p6}}, ls16{16, {p7, p8}};
100  lanelet::Lanelet ll21{21, ls11, ls12, vehicleAttr}, ll22{22, ls14, ls13, pedestrianAttr};
101  lanelet::Lanelet ll23{23, ls15, ls16, vehicleAttr};
102 };
103 
104 TEST_F(MatchingUtilitiesBase, fixtureSetupSuccessful) { // NOLINT
105  EXPECT_TRUE(map->laneletLayer.exists(21));
106  EXPECT_TRUE(map->laneletLayer.exists(22));
107  EXPECT_TRUE(map->laneletLayer.exists(23));
108  EXPECT_EQ(3ul, map->laneletLayer.size());
109 }
110 
111 TEST_F(MatchingUtilitiesBase, absoluteHull) { // NOLINT
112  matching::Object2d obj;
113 
114  obj.pose.translation() = BasicPoint2d{10, 0};
115  obj.pose.linear() = Eigen::Rotation2D<double>(-1.5 * M_PI).matrix();
116 
117  obj.absoluteHull = absoluteHull(matching::Hull2d{BasicPoint2d{-0.5, -1}, BasicPoint2d{2, 1}}, obj.pose);
118 
119  EXPECT_DOUBLE_EQ(10 + 1, obj.absoluteHull.at(0).x());
120  EXPECT_DOUBLE_EQ(0 - 0.5, obj.absoluteHull.at(0).y());
121 
122  EXPECT_DOUBLE_EQ(10 - 1, obj.absoluteHull.at(1).x());
123  EXPECT_DOUBLE_EQ(0 + 2, obj.absoluteHull.at(1).y());
124 }
125 
127  matching::Object2d obj;
128  obj.pose.translation() = lanelet::BasicPoint2d(2.05, 1.);
129  obj.pose.linear() = Eigen::Rotation2D<double>(150. / 180. * M_PI).matrix();
130 
131  EXPECT_EQ(2ul, matching::utils::findWithin(map->laneletLayer, obj, 0.1).size());
132 
133  obj.absoluteHull = absoluteHull(
134  matching::Hull2d{BasicPoint2d{-1, -0.9}, BasicPoint2d{2, -0.9}, BasicPoint2d{2, 0.9}, BasicPoint2d{1, 0.9}},
135  obj.pose);
136 
137  std::vector<std::pair<double, Lanelet>> laneletsWithDistance =
138  matching::utils::findWithin(map->laneletLayer, obj, 100);
139 
140  EXPECT_EQ(3ul, laneletsWithDistance.size());
141 }
142 
145  obj.pose.translation() = lanelet::BasicPoint2d(2.05, 1.);
146  obj.pose.linear() = Eigen::Rotation2D<double>(1. / 180. * M_PI).matrix(); // one degree orientation
147  obj.absoluteHull = absoluteHull(
148  matching::Hull2d{BasicPoint2d{-1, -0.9}, BasicPoint2d{2, -0.9}, BasicPoint2d{2, 0.9}, BasicPoint2d{1, 0.9}},
149  obj.pose);
150 
151  using namespace matching::utils;
152  obj.vonMisesKappa = 0.5;
153  EXPECT_THROW(getMahalanobisDistSq(map->laneletLayer.get(21), obj), MatchingError) // NOLINT
154  << "should throw on covariance = zero";
155 
156  obj.positionCovariance = matching::PositionCovariance2d::Ones();
157  EXPECT_THROW(getMahalanobisDistSq(map->laneletLayer.get(21), obj), MatchingError) // NOLINT
158  << "should throw on determinant = zero";
159 
160  obj.positionCovariance = matching::PositionCovariance2d::Identity() * 2.;
161  obj.vonMisesKappa = 1. / (10. / 180. * M_PI); // covariance of 10 degrees
162 
163  double mahaDist21 = getMahalanobisDistSq(map->laneletLayer.get(21), obj);
164  EXPECT_NEAR(0.011, mahaDist21, 10e-2);
165 
166  double mahaDist21inv = getMahalanobisDistSq(map->laneletLayer.get(21).invert(), obj);
167  EXPECT_NEAR(320.411, mahaDist21inv, 10e-2);
168 }
169 
171  public:
173  obj.pose.translation() = lanelet::BasicPoint2d(1., 1.);
174  obj.pose.linear() = Eigen::Rotation2D<double>(90.1 / 180. * M_PI).matrix();
175  obj.absoluteHull = absoluteHull(
176  matching::Hull2d{BasicPoint2d{-1, -0.9}, BasicPoint2d{2, -0.9}, BasicPoint2d{2, 0.9}, BasicPoint2d{1, 0.9}},
177  obj.pose);
178  obj.positionCovariance = matching::PositionCovariance2d::Identity() * 2.;
179  obj.vonMisesKappa = 1. / (10. / 180. * M_PI); // covariance of 10 degrees
180  }
182 };
183 
184 TEST_F(MatchingBase, deterministicNonConst) { // NOLINT
185  using namespace lanelet::matching;
186  auto matches = getDeterministicMatches(*map, obj, 4.);
187  for (size_t i = 1; i < matches.size(); i++) {
188  EXPECT_TRUE(matches.at(i).distance >= matches.at(i - 1).distance)
189  << "Not sorted: at i=" << i - 1 << " dist = " << matches.at(i - 1).distance << "at i=" << i
190  << " dist = " << matches.at(i).distance;
191  }
192  EXPECT_EQ(4ul, matches.size());
193  EXPECT_NEAR(0., matches.at(0).distance, 0.1);
194 }
195 
196 TEST_F(MatchingBase, deterministicConst) { // NOLINT
197  using namespace lanelet::matching;
198  const LaneletMap& constMap = *map;
199  auto matches = getDeterministicMatches(constMap, obj, 4.);
200  EXPECT_EQ(4ul, matches.size());
201 }
202 
203 TEST_F(MatchingBase, probabilisticNonConst) { // NOLINT
204  using namespace lanelet::matching;
205  auto matches = getProbabilisticMatches(*map, obj, 4.);
206  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
207  for (size_t i = 1; i < matches.size(); i++) {
208  EXPECT_TRUE(matches.at(i).mahalanobisDistSq >= matches.at(i - 1).mahalanobisDistSq) << "Not sorted at i=" << i;
209  }
210  EXPECT_NEAR(0.0, matches.at(0).mahalanobisDistSq, 0.001);
211  EXPECT_EQ(22, matches.at(0).lanelet.id());
212  EXPECT_FALSE(matches.at(0).lanelet.inverted()) << "best match must be non inverted 21";
213  EXPECT_EQ(4ul, matches.size());
214 }
215 
216 TEST_F(MatchingBase, probabilisticConst) { // NOLINT
217  using namespace lanelet::matching;
218  const LaneletMap& constMap = *map;
219  auto matches = getProbabilisticMatches(constMap, obj, 4.);
220  EXPECT_EQ(4ul, matches.size());
221 }
222 
224  using namespace lanelet::matching;
225 
226  LaneletLayer emptyLayer;
227  EXPECT_TRUE(isCloseTo(map->laneletLayer, obj, 4.));
228  EXPECT_FALSE(isCloseTo(emptyLayer, obj, 4.));
229 
230  EXPECT_TRUE(isWithin(map->laneletLayer, obj));
231  EXPECT_FALSE(isWithin(emptyLayer, obj));
232 
233  Object2d objWithEmptyHull;
234  objWithEmptyHull.pose.translation() = obj.pose.translation();
235  EXPECT_TRUE(matching::isCloseTo(map->laneletLayer, objWithEmptyHull, 1.));
236 }
237 
238 TEST_F(MatchingBase, filterNonCompliantProbabilistic) { // NOLINT
239  using namespace lanelet::matching;
240  auto matches = getProbabilisticMatches(*map, obj, 4.);
241  EXPECT_EQ(4ul, matches.size());
242  EXPECT_EQ("22 21inv 21 22inv ", toString(matches));
243 
246  auto compliantMatches = removeNonRuleCompliantMatches(matches, trafficRulesPtr);
247  EXPECT_EQ(1ul, compliantMatches.size()) << "should exclude pedestrian lanelets and inverted one-way lanelets";
248  EXPECT_EQ("21 ", toString(compliantMatches));
249 }
250 
251 TEST_F(MatchingBase, filterNonCompliantDeterminstic) { // NOLINT
252  using namespace lanelet::matching;
253  auto matches = getDeterministicMatches(*map, obj, 4.);
254  EXPECT_EQ(4ul, matches.size());
255 
258  auto compliantMatches = removeNonRuleCompliantMatches(matches, trafficRulesPtr);
259  EXPECT_EQ(1ul, compliantMatches.size()) << "filterNonCompliantProbabilistic for details";
260 }
static constexpr char Germany[]
Hull2d absoluteHull
Hull of the object in map coordinates, position is used for matching when hull is empty...
Definition: Types.h:56
std::vector< LaneletMatchProbabilistic > getProbabilisticMatches(LaneletMap &map, const ObjectWithCovariance2d &obj, double maxDist)
get probabilistic lanelet matches of an object with a maximum deterministic euler distance of maxDist...
static constexpr const char Road[]
static constexpr const char Vehicle[]
static constexpr const char Location[]
std::shared_ptr< LaneletMap > LaneletMapPtr
matching::ObjectWithCovariance2d obj
static constexpr const char Walkway[]
Pose2d pose
Pose of the object in map coordinates.
Definition: Types.h:55
double getMahalanobisDistSq(const ConstLanelet &lanelet, const ObjectWithCovariance2d &obj)
Compute squared mahalanobis distance based on pose and covariance, hull is not used.
Definition: Utilities.cpp:73
Eigen::Transform< double, 2, Eigen::Isometry, Eigen::DontAlign > Pose2d
a 2d pose
Definition: Types.h:44
MatchVectorT removeNonRuleCompliantMatches(const MatchVectorT &matches, const lanelet::traffic_rules::TrafficRulesPtr &trafficRulesPtr)
Remove non traffic rule compliant probabilistic lanelet matches.
static TrafficRulesUPtr create(const std::string &location, const std::string &participant, TrafficRules::Configuration configuration=TrafficRules::Configuration())
BasicPoint p2
Thrown when matching is not possible.
Definition: Exceptions.h:43
TEST_F(MatchingUtilitiesBase, fixtureSetupSuccessful)
double vonMisesKappa
kappa as defined in https://en.wikipedia.org/wiki/Von_Mises_distribution
Definition: Types.h:61
static constexpr const char Urban[]
std::vector< LaneletMatch > getDeterministicMatches(LaneletMap &map, const Object2d &obj, double maxDist)
get deterministic lanelet matches of an object with a maximum distance of maxDist, sorted ascending by distance
auto findWithin(LayerT &map, const Object2d &obj, double maxDist) -> std::vector< std::pair< double, traits::LayerPrimitiveType< LayerT >>>
Find all primitives as close as or closer than maxDist to an object.
Definition: Utilities.h:48
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > BasicPoint2d
BasicPoint p1
static constexpr const char Subtype[]
PositionCovariance2d positionCovariance
Definition: Types.h:60
bool isCloseTo(const LayerT &map, const Object2d &obj, double maxDist)
Determine whether an object is within a maximum distance to any primitive of the layer.
bool isWithin(const LayerT &map, const Object2d &obj)
Determine whether an object is (at least partially) within any primitive of the layer.
std::shared_ptr< TrafficRules > TrafficRulesPtr


lanelet2_matching
Author(s): Maximilian Naumann
autogenerated on Tue Jun 6 2023 02:23:52