Skip to content

Commit cc27e89

Browse files
committed
add viz
1 parent 11d65d8 commit cc27e89

4 files changed

Lines changed: 166 additions & 40 deletions

File tree

GEMstack/onboard/perception/parking_detection.py

Lines changed: 105 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,57 @@
11
import rospy
22
import 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
64
from 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
115
from ..component import Component
126
from ...state import AgentState, ObjectPose, ObjectFrameEnum, Obstacle, ObstacleMaterialEnum
137
from ..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

1913
class 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:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import ros_numpy
2+
import numpy as np
3+
4+
def transform_points(points, transform):
5+
ones_column = np.ones((points.shape[0], 1))
6+
points_extended = np.hstack((points, ones_column))
7+
points_transformed = ((transform @ (points_extended.T)).T)
8+
return points_transformed[:, :3]
9+
10+
11+
def filter_ground_points(lidar_points, ground_threshold = 0):
12+
filtered_array = lidar_points[lidar_points[:, 2] > ground_threshold]
13+
return filtered_array
14+
15+
16+
def pc2_to_numpy(pc2_msg, want_rgb=False):
17+
# Convert the ROS message to a numpy structured array
18+
pc = ros_numpy.point_cloud2.pointcloud2_to_array(pc2_msg)
19+
# Stack x,y,z fields to a (N,3) array
20+
pts = np.stack((np.array(pc['x']).ravel(),
21+
np.array(pc['y']).ravel(),
22+
np.array(pc['z']).ravel()), axis=1)
23+
# Apply filtering (for example, x > 0 and z in a specified range)
24+
mask = (pts[:, 0] > 0) & (pts[:, 2] < -1.5) & (pts[:, 2] > -2.7)
25+
return pts[mask]

GEMstack/onboard/perception/parking_utils.py renamed to GEMstack/onboard/perception/utils/parking_utils.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import cv2
22
import math
33
import numpy as np
4+
from scipy.spatial import ConvexHull
45

56

67
GEM_E4_LENGTH = 3.6 # m
@@ -188,4 +189,38 @@ def get_parking_obstacles(vertices):
188189
filtered_positions = [segment_info[i][0] for i in range(len(segment_info)) if i not in indices_to_remove]
189190
filtered_dimensions = [segment_info[i][1] for i in range(len(segment_info)) if i not in indices_to_remove]
190191

191-
return filtered_positions, filtered_dimensions
192+
return filtered_positions, filtered_dimensions
193+
194+
# def order_points_all(points_2d):
195+
# points_np = np.array(points_2d)
196+
# hull = ConvexHull(points_np)
197+
# ordered = [points_np[i] for i in hull.vertices]
198+
# return ordered
199+
200+
def order_points_all(points_2d):
201+
points = np.array(points_2d)
202+
centroid = np.mean(points, axis=0)
203+
angles = np.arctan2(points[:,1] - centroid[1], points[:,0] - centroid[0])
204+
sorted_indices = np.argsort(angles)
205+
return points[sorted_indices]
206+
207+
208+
def detect_parking_spot(cone_3d_centers):
209+
# Initial variables
210+
goal_parking_spot = None
211+
parking_obstacles_pose = []
212+
parking_obstacles_dim = []
213+
214+
# Get 2D cone centers
215+
cone_ground_centers = np.array(cone_3d_centers)
216+
cone_ground_centers_2D = cone_ground_centers[:, :2]
217+
ordered_cone_ground_centers_2D = order_points_all(cone_ground_centers_2D)
218+
# print(f"-----cone_ground_centers_2D: {len(ordered_cone_ground_centers_2D)}")
219+
candidates = find_all_candidate_parking_spots(ordered_cone_ground_centers_2D)
220+
# print(f"-----candidates: {candidates}")
221+
if len(candidates) > 0:
222+
parking_obstacles_pose, parking_obstacles_dim = get_parking_obstacles(ordered_cone_ground_centers_2D)
223+
# print(f"-----parking_obstacles: {self.parking_obstacles_pose}")
224+
goal_parking_spot = select_best_candidate(candidates, ordered_cone_ground_centers_2D)
225+
# print(f"-----goal_parking_spot: {self.goal_parking_spot}")
226+
return goal_parking_spot, parking_obstacles_pose, parking_obstacles_dim, ordered_cone_ground_centers_2D
File renamed without changes.

0 commit comments

Comments
 (0)