Skip to content

Commit 5a9ce4d

Browse files
authored
feat: filter only lanelets close to ego (#366)
1 parent 753b2b2 commit 5a9ce4d

File tree

7 files changed

+101
-2
lines changed

7 files changed

+101
-2
lines changed

docs/use_case/images/search_range.jpg

52.6 KB
Loading

docs/use_case/obstacle_segmentation.en.md

+14
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ Launching the file executes the following steps:
2828
4. The evaluation node passes the point cloud and the polygon of the non-detected area to perception_eval for evaluation. The result is dumped into a file.
2929
5. When the playback of the rosbag is finished, Autoware's launch is automatically terminated, and the evaluation is completed.
3030

31+
### How to calculate polygon for non-detectable area
32+
33+
The non-detect area is calculated as the area where the polygon given in the scenario overlaps with the road_lanelet.
34+
It is calculated according to the following steps:
35+
36+
1. get the transform of map_to_base_link at the time of the header.stamp in pointcloud, and transform the polygon to the map coordinate system.
37+
2. get the road_lanelet in the range of search_range (see the figure below) from the point where the vehicle is.
38+
3. take the intersection of the road_lanelet and polygon obtained in step 2.
39+
4. return the array of polygons obtained in step 3 to the base_link coordinate system (to match the coordinate system to filter the pointcloud).
40+
41+
![search_range](./images/search_range.jpg)
42+
43+
In step 2, by narrowing down to the lanelets in the range where polygons can exist, the intersection process with lanelets that are obvious to return empty polygons in step 3 is omitted.
44+
3145
## Evaluation Result
3246

3347
The results are calculated for each subscription. The format and available states are described below.

docs/use_case/obstacle_segmentation.ja.md

+14
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ launch を立ち上げると以下のことが実行され、評価される。
2727
4. 評価ノードが点群と非検知エリアのpolygonをperception_eval に渡して評価する。結果をファイルに記録する
2828
5. bag の再生が終了すると自動で launch が終了して評価が終了する
2929

30+
### 非検知エリアのpolygonの計算方法
31+
32+
非検知エリアは、シナリオで与えられたpolygonと、走行路(road_lanelet)の重なり合ったエリアとして算出される。
33+
以下のステップに従って計算される。
34+
35+
1. pointcloudのheader.stampの時刻でのmap_to_base_linkのtransformを取得して、polygonをmap座標系に変換する
36+
2. 車両のいるpointから、search_range(下図参照)の範囲のroad_laneletを取得する
37+
3. 2で取得したroad_laneletとpolygonのintersectionを取る
38+
4. 3で取得したpolygonの配列をbase_link座標系へ戻す(pointcloudをフィルタするために座標系を一致させる)
39+
40+
![search_range](./images/search_range.jpg)
41+
42+
ステップ2で、polygonが存在し得る範囲のlaneletに絞ることで、ステップ3で空のpolygonが返ってくるとわかりきっているlaneletとのintersection処理を省いている。
43+
3044
## 評価結果
3145

3246
topic の subscribe 1 回につき、以下に記述する判定結果が出力される。

driving_log_replayer/driving_log_replayer/obstacle_segmentation.py

+15
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ def is_clockwise(cls, v: list[list[number]]) -> list | None:
8888
def to_float(cls, v: number) -> float:
8989
return float(v)
9090

91+
def search_range(self) -> float:
92+
coord: list = []
93+
for p in self.polygon_2d:
94+
coord.append([p[0], p[1]])
95+
poly = Polygon(coord)
96+
min_x, min_y, max_x, max_y = poly.bounds
97+
abs_max_x = max(abs(min_x), abs(max_x))
98+
abs_max_y = max(abs(min_y), abs(max_y))
99+
return np.sqrt(abs_max_x**2 + abs_max_y**2)
100+
91101

92102
class BoundingBoxCondition(BaseModel):
93103
Start: number | None = None
@@ -375,6 +385,11 @@ def get_non_detection_area_in_base_link(
375385
return line_strip, list_intersection_area
376386

377387

388+
def set_ego_point(map_to_baselink: TransformStamped) -> Point:
389+
t = map_to_baselink.transform.translation
390+
return Point(x=t.x, y=t.y, z=t.z)
391+
392+
378393
@dataclass
379394
class Detection(EvaluationItem):
380395
name: str = "Detection"

driving_log_replayer/scripts/obstacle_segmentation_evaluator_node.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from diagnostic_msgs.msg import DiagnosticStatus
2424
from geometry_msgs.msg import PoseStamped
2525
from geometry_msgs.msg import TransformStamped
26+
import lanelet2 # noqa
27+
from lanelet2_extension_python.utility.query import getLaneletsWithinRange
2628
import numpy as np
2729
from perception_eval.config import SensingEvaluationConfig
2830
from perception_eval.manager import SensingEvaluationManager
@@ -48,6 +50,7 @@
4850
from driving_log_replayer.obstacle_segmentation import get_sensing_frame_config
4951
from driving_log_replayer.obstacle_segmentation import ObstacleSegmentationResult
5052
from driving_log_replayer.obstacle_segmentation import ObstacleSegmentationScenario
53+
from driving_log_replayer.obstacle_segmentation import set_ego_point
5154
from driving_log_replayer.obstacle_segmentation import transform_proposed_area
5255
import driving_log_replayer.perception_eval_conversions as eval_conversions
5356
from driving_log_replayer_analyzer.data import convert_str_to_dist_type
@@ -80,7 +83,12 @@ def __init__(self, name: str) -> None:
8083
"lanelet2_map.osm",
8184
).as_posix()
8285
self.__road_lanelets = road_lanelets_from_file(map_path)
83-
86+
if self._scenario.Evaluation.Conditions.NonDetection is not None:
87+
self.__search_range = (
88+
self._scenario.Evaluation.Conditions.NonDetection.ProposedArea.search_range()
89+
)
90+
else:
91+
self.__search_range = 0.0
8492
self.declare_parameter("vehicle_model", "")
8593
self.__vehicle_model = (
8694
self.get_parameter("vehicle_model").get_parameter_value().string_value
@@ -320,7 +328,13 @@ def get_non_detection_area(
320328
)
321329
# get intersection
322330
marker_id = 0
323-
for road in self.__road_lanelets:
331+
ego_point = set_ego_point(map_to_baselink)
332+
near_road_lanelets = getLaneletsWithinRange(
333+
self.__road_lanelets,
334+
ego_point,
335+
self.__search_range,
336+
)
337+
for road in near_road_lanelets:
324338
poly_lanelet = to_shapely_polygon(road)
325339
i_area = poly_lanelet.intersection(proposed_area_in_map)
326340
if not i_area.is_empty:

driving_log_replayer/test/unittest/test_lanelet2.py

+15
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from geometry_msgs.msg import Point
16+
import lanelet2 # noqa
1517
from lanelet2.core import getId
1618
from lanelet2.core import Lanelet
1719
from lanelet2.core import LineString3d
1820
from lanelet2.core import Point3d
21+
from lanelet2_extension_python.utility.query import getLaneletsWithinRange
1922
from shapely.geometry import Polygon
2023

2124
from driving_log_replayer.lanelet2_util import to_shapely_polygon
@@ -46,3 +49,15 @@ def test_intersection_no_overlapping() -> None:
4649
s_poly = Polygon(((-1.0, -1.0), (-1.0, -2.0), (-2.0, -2.0), (-2.0, -1.0)))
4750
i_area = l_poly.intersection(s_poly)
4851
assert i_area.is_empty
52+
53+
54+
def test_get_lanelets_within_range() -> None:
55+
lanes = [get_a_lanelet(), get_a_lanelet(index=4)]
56+
near_lanelets = getLaneletsWithinRange(lanes, Point(x=1.0, y=3.0, z=0.0), 1.0)
57+
assert len(near_lanelets) == 2 # noqa
58+
59+
60+
def test_get_lanelets_within_range_no_lane() -> None:
61+
lanes = [get_a_lanelet(), get_a_lanelet(index=4)]
62+
near_lanelets = getLaneletsWithinRange(lanes, Point(x=1.0, y=3.0, z=0.0), 0.5)
63+
assert len(near_lanelets) == 0

driving_log_replayer/test/unittest/test_obstacle_segmentation.py

+27
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,30 @@ def test_intersection_polygon_orientation() -> None:
623623
b = ShapelyPoint(2, 1).buffer(1.5)
624624
i_poly: Polygon = a.intersection(b)
625625
assert i_poly.exterior.is_ccw is False # clockwise
626+
627+
628+
def test_search_range() -> None:
629+
proposed_area = ProposedAreaCondition(
630+
polygon_2d=[[1.0, 1.0], [2.0, -2.0], [-3.0, -3.0], [-2.0, 4.0]],
631+
z_min=0.0,
632+
z_max=2.0,
633+
)
634+
assert proposed_area.search_range() == 5.0 # noqa
635+
636+
637+
def draw_search_range() -> None:
638+
from matplotlib import patches
639+
from matplotlib import pyplot as plt
640+
641+
points = [[1.0, 1.0], [2.0, -2.0], [-3.0, -3.0], [-2.0, 4.0], [1.0, 1.0]]
642+
poly = patches.Polygon(xy=points, closed=True)
643+
bound = patches.Rectangle(xy=(-3, -3), width=5, height=7, ec="g", linewidth="2.0", fill=False)
644+
search_range = patches.Circle(xy=(0, 0), radius=5, fill=False, ec="r")
645+
646+
_, ax = plt.subplots(figsize=(7, 7))
647+
ax.add_patch(poly)
648+
ax.add_patch(bound)
649+
ax.add_patch(search_range)
650+
ax.autoscale()
651+
ax.grid()
652+
plt.show()

0 commit comments

Comments
 (0)