Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature-shape2d-polygon-class-for-intermediate-layer-and-cluster-integration #616

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 64 additions & 6 deletions code/mapping/ext_modules/mapping_common/shape.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from dataclasses import dataclass
from typing import Optional

import rospy

from .transform import Transform2D
from typing import List, Optional
michalal7 marked this conversation as resolved.
Show resolved Hide resolved
from shapely.geometry import Polygon as ShapelyPolygon
from .transform import Transform2D, Point2
from mapping import msg

from tf.transformations import quaternion_from_euler
from visualization_msgs.msg import Marker
from geometry_msgs.msg import Point


@dataclass
Expand Down Expand Up @@ -155,7 +154,66 @@ def to_marker(self, transform: Transform2D) -> Marker:
return m


_shape_supported_classes = [Rectangle, Circle]
@dataclass(init=False)
class Polygon(Shape2D):
"""Polygon defined by a list of Point2 objects."""

# points attribute does not need a redundant point for start and end
points: List[Point2]

def __init__(self, points: List[Point2], offset: Optional[Transform2D] = None):
if offset is None:
michalal7 marked this conversation as resolved.
Show resolved Hide resolved
offset = Transform2D.identity()
super().__init__(offset=offset)
self.points = points

@staticmethod
def _from_ros_msg(m: msg.Shape2D) -> "Shape2D":
assert len(m.dimensions) >= 6 and (
len(m.dimensions) % 2 == 0
), "Polygon requires at least 3 points."
# Konvertiere die flache Liste in Point2-Objekte
points = [
Point2.new(m.dimensions[i], m.dimensions[i + 1])
for i in range(0, len(m.dimensions), 2)
]
michalal7 marked this conversation as resolved.
Show resolved Hide resolved
return Polygon(points=points, offset=Transform2D.from_ros_msg(m.offset))

def to_ros_msg(self) -> msg.Shape2D:
m = super().to_ros_msg()
dimensions = []
for p in self.points:
dimensions.append(p.x())
dimensions.append(p.y())
m.dimensions = dimensions
return m

def to_marker(self, transform: Transform2D) -> Marker:
"""Convert to a visualization Marker for RViz."""
m = super().to_marker(transform)
m.type = Marker.LINE_STRIP
m.scale.x = 0.05 # Line thickness

# Initialisiere m.points als leere Liste
m.points = []

# Transformiere und füge Punkte hinzu
for pt in self.points:
transformed_pt: Point2 = transform * pt # Transformation anwenden
p = Point()
p.x = transformed_pt.x()
p.y = transformed_pt.y()
p.z = 0.0
m.points.append(p)

# Schließe die Polygon-Schleife
if len(m.points) > 0:
m.points.append(m.points[0])

return m
michalal7 marked this conversation as resolved.
Show resolved Hide resolved


_shape_supported_classes = [Rectangle, Circle, Polygon]
_shape_supported_classes_dict = {}
for t in _shape_supported_classes:
t_name = t.__name__.lower()
Expand Down
26 changes: 25 additions & 1 deletion code/mapping/src/mapping_data_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class MappingDataIntegrationNode(CompatibleNode):
lidar_data: Optional[PointCloud2] = None
hero_speed: Optional[CarlaSpeedometer] = None
lidar_marker_data: Optional[MarkerArray] = None
lidar_cluster_entities: Optional[List[Entity]] = None
radar_cluster_entities: Optional[List[Entity]] = None

def __init__(self, name, **kwargs):
super().__init__(name, **kwargs)
Expand All @@ -53,6 +55,18 @@ def __init__(self, name, **kwargs):
callback=self.lidar_marker_callback,
qos_profile=1,
)
self.new_subscription(
topic=self.get_param("~entity_topic", "/paf/hero/Lidar/cluster_entities"),
msg_type=MapMsg,
callback=self.lidar_cluster_entities_callback,
qos_profile=1,
)
self.new_subscription(
topic=self.get_param("~entity_topic", "/paf/hero/Radar/cluster_entities"),
msg_type=MapMsg,
callback=self.radar_cluster_entities_callback,
qos_profile=1,
)
michalal7 marked this conversation as resolved.
Show resolved Hide resolved

self.map_publisher = self.new_publisher(
msg_type=MapMsg,
Expand All @@ -68,6 +82,12 @@ def hero_speed_callback(self, data: CarlaSpeedometer):
def lidar_marker_callback(self, data: MarkerArray):
self.lidar_marker_data = data

def lidar_cluster_entities_callback(self, data: MapMsg):
self.lidar_cluster_entities = data.entities

def radar_cluster_entities_callback(self, data: MapMsg):
self.radar_cluster_entities = data.entities

michalal7 marked this conversation as resolved.
Show resolved Hide resolved
michalal7 marked this conversation as resolved.
Show resolved Hide resolved
def lidar_callback(self, data: PointCloud2):
self.lidar_data = data

Expand Down Expand Up @@ -193,7 +213,11 @@ def publish_new_map(self, timer_event=None):

stamp = rospy.get_rostime()
map = Map(
timestamp=stamp, entities=[hero_car] + self.entities_from_lidar_marker()
timestamp=stamp,
entities=[hero_car]
+ self.entities_from_lidar_marker()
+ (self.lidar_cluster_entities or [])
+ (self.radar_cluster_entities or []),
)
msg = map.to_ros_msg()
self.map_publisher.publish(msg)
Expand Down
15 changes: 14 additions & 1 deletion code/mapping/tests/mapping_common/test_shape.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from mapping_common import shape
from mapping_common.transform import Transform2D, Vector2
from mapping_common.transform import Transform2D, Vector2, Point2
import numpy as np


def test_rectangle_conversion():
Expand All @@ -15,3 +16,15 @@ def test_circle_conversion():
msg = s.to_ros_msg()
s_conv = shape.Shape2D.from_ros_msg(msg)
assert s == s_conv


def test_polygon_conversion():
data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
p_list = []
for i in range(0, 6, 2):
p_list.append(Point2.new(data[i], data[i + 1]))

polygon = shape.Polygon(p_list)
msg = polygon.to_ros_msg()
polygon_conv = shape.Shape2D.from_ros_msg(msg)
assert polygon == polygon_conv
26 changes: 26 additions & 0 deletions code/perception/src/lidar_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from cv_bridge import CvBridge
from tf.transformations import quaternion_from_matrix
from visualization_msgs.msg import Marker, MarkerArray
from shapely.geometry import Polygon as ShapelyPolygon
from mapping.ext_modules.mapping_common.shape import Polygon

# from mpl_toolkits.mplot3d import Axes3D
# from itertools import combinations
Expand Down Expand Up @@ -390,6 +392,30 @@ def generate_bounding_boxes(points_with_labels):
return bounding_boxes


def create_polygons_and_entities(label, bbox):
"""
Erstelle ein Shapely-Polygon, ein Shape2D-Polygon und ein Entity aus einer Bounding Box.

Args:
bbox (tuple): Bounding Box als (x_min, x_max, y_min, y_max).
entity_id (str): ID der zu erstellenden Entity.
frame_id (str): Referenzrahmen für die Pose. Default ist "map".

Returns:
tuple: (ShapelyPolygon, Shape2DPolygon, Entity)
"""
# Schritt 1: Shapely-Polygon aus Bounding Box
x_min, x_max, y_min, y_max = bbox
coordinates = [
(x_min, y_min),
(x_max, y_min),
(x_max, y_max),
(x_min, y_max),
(x_min, y_min), # Schließe das Polygon
]
shapely_polygon = ShapelyPolygon(coordinates)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix documentation and complete the implementation.

The function has several issues:

  1. Documentation:
    • Docstring is in German
    • Parameters don't match function signature (entity_id and frame_id are documented but not used)
    • Return type hint is missing
  2. Implementation:
    • Function is incomplete (only creates Shapely polygon)
    • Unused imports: Polygon from mapping.ext_modules.mapping_common.shape
    • Unused variable: shapely_polygon

Apply this diff to fix the issues:

-def create_polygons_and_entities(label, bbox):
+def create_polygons_and_entities(label: int, bbox: tuple) -> tuple[ShapelyPolygon, Polygon, Entity]:
     """
-    Erstelle ein Shapely-Polygon, ein Shape2D-Polygon und ein Entity aus einer Bounding Box.
+    Create a Shapely polygon, a Shape2D polygon, and an Entity from a bounding box.
 
     Args:
-        bbox (tuple): Bounding Box als (x_min, x_max, y_min, y_max).
-        entity_id (str): ID der zu erstellenden Entity.
-        frame_id (str): Referenzrahmen für die Pose. Default ist "map".
+        label (int): Label for the entity.
+        bbox (tuple): Bounding box as (x_min, x_max, y_min, y_max).
 
     Returns:
-        tuple: (ShapelyPolygon, Shape2DPolygon, Entity)
+        tuple[ShapelyPolygon, Polygon, Entity]: A tuple containing:
+            - shapely_polygon: Shapely polygon for geometric operations
+            - shape2d_polygon: Shape2D polygon for visualization
+            - entity: Entity object for publishing
     """
-    # Schritt 1: Shapely-Polygon aus Bounding Box
     x_min, x_max, y_min, y_max = bbox
     coordinates = [
         (x_min, y_min),
         (x_max, y_min),
         (x_max, y_max),
         (x_min, y_max),
-        (x_min, y_min),  # Schließe das Polygon
+        (x_min, y_min),  # Close the polygon
     ]
     shapely_polygon = ShapelyPolygon(coordinates)
+    
+    # Create Shape2D polygon
+    points = [Point2.new(x, y) for x, y in coordinates[:-1]]  # Exclude last point
+    shape2d_polygon = Polygon(points=points)
+    
+    # Create entity
+    entity = Entity(
+        id=str(label),
+        pose=Transform2D.identity(),
+        shape=shape2d_polygon
+    )
+    
+    return shapely_polygon, shape2d_polygon, entity

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.2)

416-416: Local variable shapely_polygon is assigned to but never used

Remove assignment to unused variable shapely_polygon

(F841)


def create_bounding_box_marker(label, bbox, bbox_type="aabb"):
"""
Creates an RViz Marker for visualizing a 3D bounding box using Marker.CUBE.
Expand Down