@@ -365,10 +365,36 @@ def pointcloud_callback(self, msg: PointCloud2, sub_key: str) -> None:
365365 channels = ["x" , "y" , "z" ] + additional_channels
366366 try :
367367 points = rnp .numpify (msg )
368- except :
368+ except Exception as e :
369+ self .get_logger ().warn (f"Failed to numpify point cloud: { e } " )
369370 return
370- if points ['x' ].size == 0 :
371+
372+ # Check if points is empty - handle both structured array and dict cases
373+ if points is None :
371374 return
375+
376+ # Handle different data structures from rnp.numpify
377+ if isinstance (points , dict ):
378+ # If it's a dict, check if it has data
379+ if not points or (len (points ) == 0 ):
380+ return
381+ # Check for x,y,z keys or xyz field in dict
382+ if 'xyz' in points :
383+ # Handle combined xyz field (common in Livox lidars)
384+ xyz_data = points ['xyz' ]
385+ if len (xyz_data ) == 0 :
386+ return
387+ elif 'x' in points :
388+ # Handle separate x,y,z fields
389+ if len (points ['x' ]) == 0 :
390+ return
391+ else :
392+ self .get_logger ().warn (f"Point cloud dict missing 'x' or 'xyz' field. Available fields: { list (points .keys ())} " )
393+ return
394+ else :
395+ # It's a structured numpy array
396+ if points .size == 0 :
397+ return
372398 frame_sensor_id = msg .header .frame_id
373399 transform_sensor_to_map = self .safe_lookup_transform (
374400 self .map_frame ,
@@ -379,15 +405,50 @@ def pointcloud_callback(self, msg: PointCloud2, sub_key: str) -> None:
379405 q = transform_sensor_to_map .transform .rotation
380406 t_np = np .array ([t .x , t .y , t .z ], dtype = np .float32 )
381407 R = quaternion_matrix ([q .x , q .y , q .z , q .w ])[:3 , :3 ].astype (np .float32 )
382- pts = rnp .point_cloud2 .get_xyz_points (points )
383- # TODO: This is probably expensive. Consider modifying rnp or input_pointcloud()
384- # Append additional channels to pts
385- for channel in additional_channels :
386- if channel in points .dtype .names :
387- data = points [channel ].flatten ()
388- if data .ndim == 1 :
389- data = data [:, np .newaxis ]
390- pts = np .hstack ((pts , data ))
408+
409+ # Extract xyz points based on data structure
410+ if isinstance (points , dict ):
411+ # If points is a dict, manually construct xyz array
412+ if 'xyz' in points :
413+ # Handle combined xyz field (common in Livox lidars)
414+ xyz_array = np .array (points ['xyz' ])
415+ if xyz_array .ndim == 2 and xyz_array .shape [1 ] == 3 :
416+ pts = xyz_array
417+ elif xyz_array .ndim == 1 :
418+ # Reshape if flattened
419+ pts = xyz_array .reshape (- 1 , 3 )
420+ else :
421+ # Try to extract x, y, z from the structure
422+ pts = xyz_array [:, :3 ] if xyz_array .shape [1 ] >= 3 else xyz_array
423+ elif 'x' in points and 'y' in points and 'z' in points :
424+ # Handle separate x,y,z fields
425+ x = np .array (points ['x' ]).flatten ()
426+ y = np .array (points ['y' ]).flatten ()
427+ z = np .array (points ['z' ]).flatten ()
428+ pts = np .column_stack ((x , y , z ))
429+ else :
430+ # Fallback: try to use any available method
431+ self .get_logger ().warn (f"Unexpected point cloud structure, attempting fallback" )
432+ return
433+
434+ # Append additional channels
435+ for channel in additional_channels :
436+ if channel in points :
437+ data = np .array (points [channel ]).flatten ()
438+ if data .ndim == 1 :
439+ data = data [:, np .newaxis ]
440+ pts = np .hstack ((pts , data ))
441+ else :
442+ # Use standard method for structured arrays
443+ pts = rnp .point_cloud2 .get_xyz_points (points )
444+ # TODO: This is probably expensive. Consider modifying rnp or input_pointcloud()
445+ # Append additional channels to pts
446+ for channel in additional_channels :
447+ if hasattr (points , 'dtype' ) and hasattr (points .dtype , 'names' ) and channel in points .dtype .names :
448+ data = points [channel ].flatten ()
449+ if data .ndim == 1 :
450+ data = data [:, np .newaxis ]
451+ pts = np .hstack ((pts , data ))
391452 self ._map .input_pointcloud (pts , channels , R , t_np , 0 , 0 )
392453 self ._pointcloud_process_counter += 1
393454
0 commit comments