11import rospy
22import numpy as np
3- import cv2
4- from cv_bridge import CvBridge
5- from sensor_msgs .msg import PointCloud2 , Image
3+ from sensor_msgs .msg import PointCloud2
64from typing import Dict
7- import sensor_msgs .point_cloud2 as pc2
8- import ros_numpy
9- from ultralytics import YOLO
10- from message_filters import Subscriber , ApproximateTimeSynchronizer
115from ..component import Component
126from ...state import AgentState , ObjectPose , ObjectFrameEnum , Obstacle , ObstacleMaterialEnum
137from ..interface .gem import GEMInterface
14- from scipy . spatial import ConvexHull
15- from .parking_utils import *
16- from .visualization_utils import *
8+ from . utils . detection_utils import *
9+ from .utils . parking_utils import *
10+ from .utils . visualization_utils import *
1711
1812
1913class ParkingSpotsDetector3D (Component ):
2014 def __init__ (self , vehicle_interface : GEMInterface ):
2115 # Init Variables
2216 self .vehicle_interface = vehicle_interface
17+ self .cone_pts_3D = []
18+ self .ordered_cone_ground_centers_2D = []
2319 self .goal_parking_spot = None
24- self .cone_pts_3d = []
25- self .visualization = False
20+ self .parking_obstacles_pose = []
21+ self .parking_obstacles_dim = []
22+ self .ground_threshold = - 0.15
23+ self .visualization = True
24+ self .T_l2v = np .array ([[0.99939639 , 0.02547917 , 0.023615 , 1.1 ],
25+ [- 0.02530848 , 0.99965156 , - 0.00749882 , 0.03773583 ],
26+ [- 0.02379784 , 0.00689664 , 0.999693 , 1.95320223 ],
27+ [0. , 0. , 0. , 1. ]])
28+
29+
30+ # Subscribers (note: we need top lidar only for visualization)
31+ self .lidar_sub = rospy .Subscriber ("/ouster/points" , PointCloud2 , self .callback , queue_size = 1 )
32+
33+ # Publishers (all topics are for visualization purposes)
34+ self .pub_lidar_top_vehicle_pc2 = rospy .Publisher ("lidar_top_vehicle/point_cloud" , PointCloud2 , queue_size = 10 )
35+ self .pub_vehicle_marker = rospy .Publisher ("vehicle/marker" , MarkerArray , queue_size = 10 )
36+ self .pub_cones_centers_pc2 = rospy .Publisher ("cones_detection/centers/point_cloud" , PointCloud2 , queue_size = 10 )
37+ self .pub_parking_spot_marker = rospy .Publisher ("parking_spot_detection/marker" , MarkerArray , queue_size = 10 )
38+ self .pub_polygon_marker = rospy .Publisher ("polygon_detection/marker" , MarkerArray , queue_size = 10 )
39+ self .pub_obstacles_marker = rospy .Publisher ("obstacle_detection/marker" , MarkerArray , queue_size = 10 )
40+
41+ def callback (self , top_lidar_msg ):
42+ # Top lidar points in ouster frame
43+ lidar_ouster_frame = pc2_to_numpy (top_lidar_msg )
44+
45+ # Visualize everything in vehicle frame
46+ if self .visualization :
47+ self .visualize (
48+ self .cone_pts_3D ,
49+ self .ordered_cone_ground_centers_2D ,
50+ self .goal_parking_spot ,
51+ self .parking_obstacles_pose ,
52+ self .parking_obstacles_dim ,
53+ lidar_ouster_frame
54+ )
2655
2756 def spin (self ):
2857 rospy .spin ()
@@ -35,32 +64,62 @@ def state_inputs(self) -> list:
3564
3665 def state_outputs (self ) -> list :
3766 return ['goal' , 'obstacles' ]
38-
39- def order_points_convex_hull (self , points_2d ):
40- points_np = np .array (points_2d )
41- hull = ConvexHull (points_np )
42- ordered = [points_np [i ] for i in hull .vertices ]
43- return ordered
44-
45- def detect_parking_spot (self , cone_3d_centers ):
46- # Initial variables
47- goal_parking_spot = None
48- parking_obstacles_pose = []
49- parking_obstacles_dim = []
5067
51- # Get 2D cone centers
52- cone_ground_centers = np .array (cone_3d_centers )
53- cone_ground_centers_2D = cone_ground_centers [:, :2 ]
54- ordered_cone_ground_centers_2D = self .order_points_convex_hull (cone_ground_centers_2D )
55- # print(f"-----cone_ground_centers_2D: {cone_ground_centers_2D}")
56- candidates = find_all_candidate_parking_spots (ordered_cone_ground_centers_2D )
57- # print(f"-----candidates: {candidates}")
58- if len (candidates ) > 0 :
59- parking_obstacles_pose , parking_obstacles_dim = get_parking_obstacles (ordered_cone_ground_centers_2D )
60- # print(f"-----parking_obstacles: {self.parking_obstacles_pose}")
61- goal_parking_spot = select_best_candidate (candidates , ordered_cone_ground_centers_2D )
62- # print(f"-----goal_parking_spot: {self.goal_parking_spot}")
63- return goal_parking_spot , parking_obstacles_pose , parking_obstacles_dim , ordered_cone_ground_centers_2D
68+
69+ def visualize (self ,
70+ cone_pts_3D ,
71+ ordered_cone_ground_centers_2D ,
72+ goal_parking_spot ,
73+ parking_obstacles_pose ,
74+ parking_obstacles_dim ,
75+ lidar_ouster_frame ):
76+ # Transform top lidar pointclouds to vehicle frame for visualization
77+ latest_lidar_vehicle = transform_points (lidar_ouster_frame , self .T_l2v )
78+ latest_lidar_vehicle = filter_ground_points (latest_lidar_vehicle , self .ground_threshold )
79+ ros_lidar_top_vehicle_pc2 = create_point_cloud (latest_lidar_vehicle , (255 , 0 , 0 ), "vehicle" )
80+ self .pub_lidar_top_vehicle_pc2 .publish (ros_lidar_top_vehicle_pc2 )
81+
82+ # Create vehicle marker
83+ ros_vehicle_marker = create_markers ([[0.0 , 0.0 , 0.0 , 0.0 ]],
84+ [[0.8 , 0.5 , 0.3 ]],
85+ (0.0 , 0.0 , 1.0 , 1 ),
86+ "markers" , "vehicle" )
87+ self .pub_vehicle_marker .publish (ros_vehicle_marker )
88+
89+ # Delete previous markers
90+ ros_delete_polygon_marker = delete_markers ("polygon" , 1 )
91+ self .pub_polygon_marker .publish (ros_delete_polygon_marker )
92+ ros_delete_parking_spot_markers = delete_markers ("parking_spot" , 1 )
93+ self .pub_parking_spot_marker .publish (ros_delete_parking_spot_markers )
94+ ros_delete_obstacles_markers = delete_markers ("obstacles" , 5 )
95+ self .pub_obstacles_marker .publish (ros_delete_obstacles_markers )
96+
97+ # Draw polygon first
98+ if len (ordered_cone_ground_centers_2D ) > 0 :
99+ ros_polygon_marker = create_polygon_marker (ordered_cone_ground_centers_2D , ref_frame = "vehicle" )
100+ self .pub_polygon_marker .publish (ros_polygon_marker )
101+
102+ # Create parking spot marker
103+ if goal_parking_spot :
104+ ros_parking_spot_marker = create_parking_spot_marker (goal_parking_spot , ref_frame = "vehicle" )
105+ self .pub_parking_spot_marker .publish (ros_parking_spot_marker )
106+
107+ # Create parking obstacles marker
108+ if parking_obstacles_pose and parking_obstacles_dim :
109+ ros_obstacles_marker = create_markers (parking_obstacles_pose ,
110+ parking_obstacles_dim ,
111+ (1.0 , 0.0 , 0.0 , 0.4 ),
112+ "obstacles" , "vehicle" )
113+ self .pub_obstacles_marker .publish (ros_obstacles_marker )
114+
115+ # Draw 3D cone centers
116+ if len (cone_pts_3D ) > 0 :
117+ cone_ground_centers = np .array (cone_pts_3D )
118+ cone_ground_centers [:, 2 ] = 0.0
119+ cone_ground_centers = [tuple (point ) for point in cone_ground_centers ]
120+ ros_cones_centers_pc2 = create_point_cloud (cone_ground_centers , color = (255 , 0 , 255 ))
121+ self .pub_cones_centers_pc2 .publish (ros_cones_centers_pc2 )
122+
64123
65124 def update (self , agents : Dict [str , AgentState ]):
66125 # Initial variables
@@ -76,8 +135,15 @@ def update(self, agents: Dict[str, AgentState]):
76135 cone_pts_3D .append (cone_pt_3D )
77136
78137 # Detect parking spot
79- if len (cone_pts_3D ) >= 4 :
80- goal_parking_spot , parking_obstacles_pose , parking_obstacles_dim , ordered_cone_ground_centers_2D = self .detect_parking_spot (cone_pts_3D )
138+ if len (cone_pts_3D ) == 4 :
139+ goal_parking_spot , parking_obstacles_pose , parking_obstacles_dim , ordered_cone_ground_centers_2D = detect_parking_spot (cone_pts_3D )
140+
141+ # Update local variables for visualization
142+ self .cone_pts_3D = cone_pts_3D
143+ self .ordered_cone_ground_centers_2D = ordered_cone_ground_centers_2D
144+ self .goal_parking_spot = goal_parking_spot
145+ self .parking_obstacles_pose = parking_obstacles_pose
146+ self .parking_obstacles_dim = parking_obstacles_dim
81147
82148 # Return if no goal parking spot is found
83149 if not goal_parking_spot :
0 commit comments