Skip to content

Commit 8421dfb

Browse files
authored
feat: add traffic rules to enable driving for areas (#104)
Signed-off-by: Ryohsuke Mitsudome <ryohsuke.mitsudome@tier4.jp> Signed-off-by: emmeyteja <emmeyteja@gmail.com>
1 parent 551c205 commit 8421dfb

8 files changed

Lines changed: 243 additions & 3 deletions

File tree

autoware_lanelet2_extension/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ find_package(tf2 REQUIRED)
66
autoware_package()
77

88
ament_auto_add_library(${PROJECT_NAME}_lib SHARED
9+
lib/autoware_traffic_rules.cpp
910
lib/autoware_osm_parser.cpp
1011
lib/autoware_traffic_light.cpp
1112
lib/crosswalk.cpp
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 TIER IV, Inc.
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+
#ifndef AUTOWARE_LANELET2_EXTENSION__TRAFFIC_RULES__AUTOWARE_TRAFFIC_RULES_HPP_
16+
#define AUTOWARE_LANELET2_EXTENSION__TRAFFIC_RULES__AUTOWARE_TRAFFIC_RULES_HPP_
17+
18+
#include <lanelet2_traffic_rules/GermanTrafficRules.h>
19+
#include <lanelet2_traffic_rules/TrafficRulesFactory.h>
20+
21+
namespace lanelet::autoware
22+
{
23+
24+
/// @brief Autoware-specific location identifier for traffic rules registration.
25+
static constexpr const char DefaultLocation[] = "autoware";
26+
27+
/// @brief Vehicle traffic rules for Autoware based on GermanVehicle, but allowing areas in normal
28+
/// driving mode. GermanVehicle unconditionally disallows canPass(ConstArea), which prevents routing
29+
/// through areas. This class restores the GenericTrafficRules behavior that checks participant
30+
/// tags.
31+
class AutowareVehicle : public traffic_rules::GermanVehicle
32+
{
33+
public:
34+
using GermanVehicle::GermanVehicle;
35+
36+
// Allow passing through areas based on participant tags (GenericTrafficRules default behavior)
37+
bool canPass(const ConstArea & area) const override { return GenericTrafficRules::canPass(area); }
38+
};
39+
40+
} // namespace lanelet::autoware
41+
42+
#endif // AUTOWARE_LANELET2_EXTENSION__TRAFFIC_RULES__AUTOWARE_TRAFFIC_RULES_HPP_

autoware_lanelet2_extension/include/autoware_lanelet2_extension/utility/route_checker.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ namespace lanelet::utils::route
2525
{
2626
using autoware_planning_msgs::msg::LaneletRoute;
2727

28-
bool isRouteValid(const LaneletRoute & route, const lanelet::LaneletMapPtr lanelet_map_ptr_);
28+
/// @param allow_area If false, routes containing any primitive with primitive_type "area" are
29+
/// invalid (lane-only validation). If true, area ids are resolved on areaLayer.
30+
bool isRouteValid(
31+
const LaneletRoute & route, const lanelet::LaneletMapPtr lanelet_map_ptr_,
32+
bool allow_area = false);
2933
} // namespace lanelet::utils::route
3034

3135
// NOLINTEND(readability-identifier-naming)

autoware_lanelet2_extension/include/autoware_lanelet2_extension/visualization/visualization.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <visualization_msgs/msg/marker_array.hpp>
3232

3333
#include <lanelet2_core/LaneletMap.h>
34+
#include <lanelet2_core/primitives/Area.h>
3435
#include <lanelet2_core/primitives/Lanelet.h>
3536

3637
#include <string>
@@ -236,6 +237,17 @@ visualization_msgs::msg::MarkerArray hatchedRoadMarkingsAreaAsMarkerArray(
236237
*/
237238
visualization_msgs::msg::MarkerArray obstacleRemovalAreaAsMarkerArray(
238239
const lanelet::ConstPolygons3d & obstacle_removal_area, const std_msgs::msg::ColorRGBA & c);
240+
241+
/**
242+
* [laneletAreasAsMarkerArray creates marker array to visualize Lanelet2 Area layer primitives
243+
* (e.g. routing through lane-area-lane connections).]
244+
* @param areas [Lanelet2 ConstArea from map areaLayer]
245+
* @param fill_color [TRIANGLE_LIST fill color; namespace lanelet_routing_area]
246+
* @param outline_color [LINE_STRIP boundary color; namespace lanelet_routing_area_outline]
247+
*/
248+
visualization_msgs::msg::MarkerArray laneletAreasAsMarkerArray(
249+
const std::vector<lanelet::ConstArea> & areas, const std_msgs::msg::ColorRGBA & fill_color,
250+
const std_msgs::msg::ColorRGBA & outline_color);
239251
} // namespace format_v2
240252

241253
/**
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2025 TIER IV, Inc.
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 "autoware_lanelet2_extension/traffic_rules/autoware_traffic_rules.hpp"
16+
17+
namespace lanelet::autoware
18+
{
19+
20+
// Register AutowareVehicle for the Autoware location + Vehicle participant.
21+
static traffic_rules::RegisterTrafficRules<AutowareVehicle> reg(
22+
DefaultLocation, Participants::Vehicle);
23+
24+
} // namespace lanelet::autoware

autoware_lanelet2_extension/lib/route_checker.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,21 @@
2626
namespace lanelet::utils
2727
{
2828
bool route::isRouteValid(
29-
const LaneletRoute & route_msg, const lanelet::LaneletMapPtr lanelet_map_ptr_)
29+
const LaneletRoute & route_msg, const lanelet::LaneletMapPtr lanelet_map_ptr_, bool allow_area)
3030
{
3131
for (const auto & route_section : route_msg.segments) {
3232
for (const auto & primitive : route_section.primitives) {
33+
// If area is not allowed, and the primitive is an area, return false
34+
if (primitive.primitive_type == "area" && !allow_area) {
35+
return false;
36+
}
3337
const auto id = primitive.id;
3438
try {
35-
lanelet_map_ptr_->laneletLayer.get(id);
39+
if (primitive.primitive_type == "area") {
40+
lanelet_map_ptr_->areaLayer.get(id);
41+
} else {
42+
lanelet_map_ptr_->laneletLayer.get(id);
43+
}
3644
} catch (const std::exception & e) {
3745
std::cerr
3846
<< e.what()

autoware_lanelet2_extension/lib/visualization.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
#include <visualization_msgs/msg/marker_array.hpp>
3232

3333
#include <algorithm>
34+
#include <cmath>
3435
#include <iostream>
3536
#include <string>
3637
#include <unordered_map>
3738
#include <unordered_set>
39+
#include <utility>
3840
#include <vector>
3941

4042
namespace
@@ -388,6 +390,62 @@ void pushPolygonMarker(
388390
}
389391
}
390392

393+
bool areaOuterToPolygon3d(const lanelet::ConstArea & area, lanelet::Polygon3d * llt_poly)
394+
{
395+
if (llt_poly == nullptr) {
396+
return false;
397+
}
398+
llt_poly->clear();
399+
for (const auto & ls : area.outerBound()) {
400+
for (const auto & pt : ls) {
401+
llt_poly->push_back(
402+
lanelet::Point3d(
403+
lanelet::InvalId, pt.basicPoint().x(), pt.basicPoint().y(), pt.basicPoint().z()));
404+
}
405+
}
406+
if (llt_poly->size() >= 2U) {
407+
const auto & f = llt_poly->front();
408+
const auto & b = llt_poly->back();
409+
if (
410+
std::abs(f.basicPoint().x() - b.basicPoint().x()) < 1e-6 &&
411+
std::abs(f.basicPoint().y() - b.basicPoint().y()) < 1e-6) {
412+
llt_poly->pop_back();
413+
}
414+
}
415+
return llt_poly->size() >= 3U;
416+
}
417+
418+
visualization_msgs::msg::Marker makeLaneletRoutingAreaOutlineMarker(
419+
const int id, const lanelet::Polygon3d & poly, const std_msgs::msg::ColorRGBA & outline_color)
420+
{
421+
visualization_msgs::msg::Marker line;
422+
line.header.frame_id = "map";
423+
line.header.stamp = rclcpp::Time();
424+
line.ns = "lanelet_routing_area_outline";
425+
line.id = id;
426+
line.action = visualization_msgs::msg::Marker::ADD;
427+
line.type = visualization_msgs::msg::Marker::LINE_STRIP;
428+
line.scale.x = 0.05;
429+
line.scale.y = 0.0;
430+
line.scale.z = 0.0;
431+
line.color = outline_color;
432+
line.pose.orientation.w = 1.0;
433+
line.lifetime = rclcpp::Duration(0, 0);
434+
line.frame_locked = false;
435+
line.points.reserve(poly.size() + 1U);
436+
for (const auto & pt : poly) {
437+
geometry_msgs::msg::Point p;
438+
p.x = pt.x();
439+
p.y = pt.y();
440+
p.z = pt.z();
441+
line.points.push_back(p);
442+
}
443+
if (!line.points.empty()) {
444+
line.points.push_back(line.points.front());
445+
}
446+
return line;
447+
}
448+
391449
} // anonymous namespace
392450

393451
namespace lanelet
@@ -1210,6 +1268,40 @@ visualization_msgs::msg::MarkerArray obstacleRemovalAreaAsMarkerArray(
12101268
}
12111269
return marker_array;
12121270
}
1271+
1272+
visualization_msgs::msg::MarkerArray laneletAreasAsMarkerArray(
1273+
const std::vector<lanelet::ConstArea> & areas, const std_msgs::msg::ColorRGBA & fill_color,
1274+
const std_msgs::msg::ColorRGBA & outline_color)
1275+
{
1276+
visualization_msgs::msg::MarkerArray marker_array;
1277+
if (areas.empty()) {
1278+
return marker_array;
1279+
}
1280+
1281+
visualization_msgs::msg::Marker fill_marker =
1282+
createPolygonMarker("lanelet_routing_area", fill_color);
1283+
std::vector<visualization_msgs::msg::Marker> outline_markers;
1284+
outline_markers.reserve(areas.size());
1285+
int outline_id = 0;
1286+
1287+
for (const auto & area : areas) {
1288+
lanelet::Polygon3d llt_poly;
1289+
if (!areaOuterToPolygon3d(area, &llt_poly)) {
1290+
continue;
1291+
}
1292+
pushPolygonMarker(&fill_marker, llt_poly, fill_color);
1293+
outline_markers.push_back(
1294+
makeLaneletRoutingAreaOutlineMarker(outline_id++, llt_poly, outline_color));
1295+
}
1296+
1297+
if (!fill_marker.points.empty()) {
1298+
marker_array.markers.push_back(std::move(fill_marker));
1299+
}
1300+
for (auto & om : outline_markers) {
1301+
marker_array.markers.push_back(std::move(om));
1302+
}
1303+
return marker_array;
1304+
}
12131305
} // namespace format_v2
12141306
} // namespace visualization
12151307

autoware_lanelet2_extension/test/src/test_route_checker.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222

2323
#include <gtest/gtest.h>
2424
#include <lanelet2_core/LaneletMap.h>
25+
#include <lanelet2_core/primitives/Area.h>
2526

2627
#include <memory>
2728

29+
using lanelet::Area;
2830
using lanelet::Lanelet;
2931
using lanelet::LineString3d;
32+
using lanelet::LineStrings3d;
3033
using lanelet::Point3d;
3134
using lanelet::utils::getId;
3235

@@ -88,6 +91,60 @@ TEST_F(TestSuite, isRouteValid) // NOLINT for gtest
8891
"one";
8992
}
9093

94+
TEST_F(TestSuite, isRouteValid_rejects_area_when_allow_area_false) // NOLINT for gtest
95+
{
96+
autoware_planning_msgs::msg::LaneletRoute route_with_area;
97+
autoware_planning_msgs::msg::LaneletSegment segment;
98+
autoware_planning_msgs::msg::LaneletPrimitive prim;
99+
prim.primitive_type = "area";
100+
prim.id = 99999; // id is irrelevant when allow_area is false
101+
segment.primitives.push_back(prim);
102+
route_with_area.segments.push_back(segment);
103+
104+
ASSERT_FALSE(lanelet::utils::route::isRouteValid(route_with_area, sample_map_ptr, false))
105+
<< "Route with area primitive must be invalid when allow_area is false";
106+
ASSERT_FALSE(lanelet::utils::route::isRouteValid(route_with_area, sample_map_ptr))
107+
<< "Default allow_area should be false for mixed routes";
108+
}
109+
110+
TEST_F(TestSuite, isRouteValid_unknown_area_id_fails_when_allow_area_true) // NOLINT for gtest
111+
{
112+
autoware_planning_msgs::msg::LaneletRoute route_with_area;
113+
autoware_planning_msgs::msg::LaneletSegment segment;
114+
autoware_planning_msgs::msg::LaneletPrimitive prim;
115+
prim.primitive_type = "area";
116+
prim.id = 99999; // not on map
117+
segment.primitives.push_back(prim);
118+
route_with_area.segments.push_back(segment);
119+
120+
ASSERT_FALSE(lanelet::utils::route::isRouteValid(route_with_area, sample_map_ptr, true))
121+
<< "Missing area id on map should fail validation";
122+
}
123+
124+
TEST_F(TestSuite, isRouteValid_resolves_area_when_allow_area_true) // NOLINT for gtest
125+
{
126+
const lanelet::Id area_id = getId();
127+
const Point3d a1(getId(), 10.0, 0.0, 0.0);
128+
const Point3d a2(getId(), 12.0, 0.0, 0.0);
129+
const Point3d a3(getId(), 12.0, 2.0, 0.0);
130+
const Point3d a4(getId(), 10.0, 2.0, 0.0);
131+
const LineString3d ring(getId(), {a1, a2, a3, a4, a1});
132+
const LineStrings3d outer{ring};
133+
const Area test_area(area_id, outer);
134+
sample_map_ptr->add(test_area);
135+
136+
autoware_planning_msgs::msg::LaneletRoute route_with_area;
137+
autoware_planning_msgs::msg::LaneletSegment segment;
138+
autoware_planning_msgs::msg::LaneletPrimitive prim;
139+
prim.primitive_type = "area";
140+
prim.id = area_id;
141+
segment.primitives.push_back(prim);
142+
route_with_area.segments.push_back(segment);
143+
144+
ASSERT_TRUE(lanelet::utils::route::isRouteValid(route_with_area, sample_map_ptr, true))
145+
<< "Valid area id on map should pass when allow_area is true";
146+
}
147+
91148
int main(int argc, char ** argv)
92149
{
93150
testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)