@@ -24,20 +24,21 @@ class YoloNode():
2424 and return only detections from the current frame.
2525 """
2626
27- def __init__ (
28- self ,
29- ):
30- self .latest_image = None
31- self .latest_lidar = None
32- self .bridge = CvBridge ()
27+ def __init__ (self ):
28+ self .latest_image = None
29+ self .latest_lidar = None
30+ self .bridge = CvBridge ()
3331 self .camera_name = 'front'
34- self .camera_front = (self .camera_name == 'front' )
32+ self .camera_front = (self .camera_name == 'front' )
3533 self .score_threshold = 0.4
3634 self .debug = True
35+ # self.undistort_map1 = None
36+ # self.undistort_map2 = None
3737 self .initialize ()
3838
3939 def initialize (self ):
40- # --- Determine the correct RGB topic for this camera ---
40+ """Initialize the YOLO node with camera calibration and ROS connections."""
41+ # # --- Determine the correct RGB topic for this camera ---
4142 rgb_topic_map = {
4243 'front' : '/oak/rgb/image_raw' ,
4344 'front_right' : '/camera_fr/arena_camera_node/image_raw' ,
@@ -50,29 +51,31 @@ def initialize(self):
5051
5152 # Initialize YOLO node
5253 rospy .init_node ('yolo_box_publisher' )
54+
5355 # Create bounding box publisher
5456 self .pub = rospy .Publisher ('/yolo_boxes' , BoundingBoxArray , queue_size = 1 )
5557 rospy .loginfo ("YOLO node initialized and waiting for messages." )
5658
59+ # Set camera intrinsic parameters based on camera
5760 if self .camera_front :
5861 self .K = np .array ([[684.83331299 , 0. , 573.37109375 ],
5962 [0. , 684.60968018 , 363.70092773 ],
6063 [0. , 0. , 1. ]])
64+ self .D = np .array ([0.0 , 0.0 , 0.0 , 0.0 , 0.0 ])
6165 else :
6266 self .K = np .array ([[1.17625545e+03 , 0.00000000e+00 , 9.66432645e+02 ],
6367 [0.00000000e+00 , 1.17514569e+03 , 6.08580326e+02 ],
6468 [0.00000000e+00 , 0.00000000e+00 , 1.00000000e+00 ]])
69+ self .D = np .array ([- 2.70136325e-01 , 1.64393255e-01 , - 1.60720782e-03 ,
70+ - 7.41246708e-05 , - 6.19939758e-02 ])
6571
66- if self .camera_front :
67- self .D = np .array ([0.0 , 0.0 , 0.0 , 0.0 , 0.0 ])
68- else :
69- self .D = np .array ([- 2.70136325e-01 , 1.64393255e-01 , - 1.60720782e-03 , - 7.41246708e-05 ,
70- - 6.19939758e-02 ])
71-
72+ # Transformation matrix from LiDAR to vehicle frame
7273 self .T_l2v = np .array ([[0.99939639 , 0.02547917 , 0.023615 , 1.1 ],
7374 [- 0.02530848 , 0.99965156 , - 0.00749882 , 0.03773583 ],
7475 [- 0.02379784 , 0.00689664 , 0.999693 , 1.95320223 ],
7576 [0. , 0. , 0. , 1. ]])
77+
78+ # Transformation matrix from LiDAR to camera frame
7679 if self .camera_front :
7780 self .T_l2c = np .array ([
7881 [0.001090 , - 0.999489 , - 0.031941 , 0.149698 ],
@@ -86,12 +89,14 @@ def initialize(self):
8689 [0.68884317 , - 0.7061996 , - 0.16363744 , - 1.04767285 ],
8790 [0. , 0. , 0. , 1. ]]
8891 )
92+
93+ # Compute inverse transformation (camera to LiDAR)
8994 self .T_c2l = np .linalg .inv (self .T_l2c )
9095 self .R_c2l = self .T_c2l [:3 , :3 ]
9196 self .camera_origin_in_lidar = self .T_c2l [:3 , 3 ]
9297
93- # Initialize the YOLO detector
94- self .detector = YOLO ('yolov8n.pt' ) # 'GEMstack/knowledge/detection/cone.pt')
98+ # Initialize the YOLO detector and move to GPU if available
99+ self .detector = YOLO ('yolov8n.pt' )
95100 self .detector .to ('cuda' )
96101
97102 # Subscribe to the RGB and LiDAR streams
@@ -103,28 +108,31 @@ def initialize(self):
103108 self .sync .registerCallback (self .synchronized_callback )
104109
105110 def synchronized_callback (self , image_msg , lidar_msg ):
111+ """Process synchronized RGB and LiDAR messages to detect pedestrians."""
106112 rospy .loginfo ("Received synchronized RGB and LiDAR messages" )
113+
114+ # Convert image message to OpenCV format
107115 try :
108116 self .latest_image = self .bridge .imgmsg_to_cv2 (image_msg , "bgr8" )
109117 except Exception as e :
110- rospy .logerr ("Failed to convert image: {}" . format ( e ) )
118+ rospy .logerr (f "Failed to convert image: { e } " )
111119 self .latest_image = None
120+
121+ # Convert LiDAR message to numpy array
112122 self .latest_lidar = pc2_to_numpy (lidar_msg , want_rgb = False )
113123
114124 # Gate guards against data not being present for both sensors:
115125 if self .latest_image is None or self .latest_lidar is None :
116- return {}
117- lastest_image = self .latest_image .copy ()
126+ return {} # Skip
127+
128+ latest_image = self .latest_image .copy ()
118129
130+ # Optionally downsample LiDAR points
119131 downsample = False
120132 if downsample :
121133 lidar_down = downsample_points (self .latest_lidar , voxel_size = 0.1 )
122134 else :
123135 lidar_down = self .latest_lidar .copy ()
124-
125- # if self.start_time is None:
126- # self.start_time = current_time
127- # time_elapsed = current_time - self.start_time
128136
129137 if self .camera_front == False :
130138 start = time .time ()
@@ -137,10 +145,12 @@ def synchronized_callback(self, image_msg, lidar_msg):
137145 # --- Begin modifications for three-angle detection ---
138146 img_normal = undistorted_img
139147 else :
140- img_normal = lastest_image .copy ()
141- undistorted_img = lastest_image .copy ()
142- orig_H , orig_W = lastest_image .shape [:2 ]
148+ img_normal = latest_image .copy ()
149+ undistorted_img = latest_image .copy ()
150+ orig_H , orig_W = latest_image .shape [:2 ]
143151 self .current_K = self .K
152+
153+ # Run YOLO detection on the image
144154 results_normal = self .detector (img_normal , conf = 0.4 , classes = [0 ])
145155 combined_boxes = []
146156
@@ -159,6 +169,7 @@ def synchronized_callback(self, image_msg, lidar_msg):
159169 # projected_pts[:, 1]: v-coordinate in the image (vertical pixel position)
160170 # projected_pts[:, 2:5]: original X, Y, Z coordinates in the LiDAR frame
161171
172+
162173 # Create empty list of bounding boxes to fill and publish later
163174 boxes = BoundingBoxArray ()
164175 boxes .header .frame_id = 'currentVehicleFrame'
@@ -181,8 +192,8 @@ def synchronized_callback(self, image_msg, lidar_msg):
181192 bottom = int (cy + h / 2 )
182193
183194 # Find LiDAR points that project to this box
184- mask = (projected_pts [:, 0 ] >= left ) & (projected_pts [:, 0 ] <= right ) & \
185- (projected_pts [:, 1 ] >= top ) & (projected_pts [:, 1 ] <= bottom )
195+ mask = (( projected_pts [:, 0 ] >= left ) & (projected_pts [:, 0 ] <= right ) &
196+ (projected_pts [:, 1 ] >= top ) & (projected_pts [:, 1 ] <= bottom ) )
186197 roi_pts = projected_pts [mask ]
187198
188199 # Ignore regions with too few points
@@ -220,31 +231,36 @@ def synchronized_callback(self, image_msg, lidar_msg):
220231 # yaw, pitch, roll = R.from_matrix(R_vehicle).as_euler('zyx', degrees=False)
221232 yaw = np .arctan2 (R_vehicle [1 , 0 ], R_vehicle [0 , 0 ])
222233
223- refined_center = refined_center_vehicle
224-
225- boxes = add_bounding_box ( boxes = boxes ,
234+ # Add the bounding box
235+ boxes = add_bounding_box (
236+ boxes = boxes ,
226237 frame_id = 'currentVehicleFrame' ,
227238 stamp = lidar_msg .header .stamp ,
228239 x = refined_center_vehicle [0 ],
229240 y = refined_center_vehicle [1 ],
230241 z = refined_center_vehicle [2 ],
231- l = dims [2 ], # length
232- w = dims [1 ], # width
233- h = dims [0 ], # height
242+ l = dims [2 ], # length
243+ w = dims [1 ], # width
244+ h = dims [0 ], # height
234245 yaw = yaw ,
235246 conf_score = float (conf_scores [i ]),
236- label = 0 # person/pedestrian class
247+ label = 0 # person/pedestrian class
237248 )
238249
239- rospy .loginfo (f"Person detected at ({ refined_center_vehicle [0 ]:.2f} , { refined_center_vehicle [1 ]:.2f} , { refined_center_vehicle [2 ]:.2f} ) with score { conf_scores [i ]:.2f} " )
250+ rospy .loginfo (f"Person detected at ({ refined_center_vehicle [0 ]:.2f} , "
251+ f"{ refined_center_vehicle [1 ]:.2f} , { refined_center_vehicle [2 ]:.2f} ) "
252+ f"with score { conf_scores [i ]:.2f} " )
240253
241254 # Publish the bounding boxes
242255 rospy .loginfo (f"Publishing { len (boxes .boxes )} person bounding boxes" )
243256 self .pub .publish (boxes )
244257
245258 def undistort_image (self , image , K , D ):
259+ """Undistort an image using the camera calibration parameters."""
246260 h , w = image .shape [:2 ]
247261 newK , _ = cv2 .getOptimalNewCameraMatrix (K , D , (w , h ), 1 , (w , h ))
262+
263+ # Initialize undistortion maps if not already done
248264 if self .undistort_map1 is None or self .undistort_map2 is None :
249265 self .undistort_map1 , self .undistort_map2 = cv2 .initUndistortRectifyMap (K , D , R = None ,
250266 newCameraMatrix = newK , size = (w , h ),
@@ -262,4 +278,4 @@ def undistort_image(self, image, K, D):
262278 node = YoloNode ()
263279 rospy .spin ()
264280 except rospy .ROSInterruptException :
265- pass
281+ pass
0 commit comments