Skip to content

Commit 47a74ce

Browse files
Idate96claude
andcommitted
fix: Improve TF2 error handling with proper exception catching
Enhanced transform lookup error handling to gracefully handle missing frames and connectivity issues: - Added tf2_py import for specific exception types - Expanded exception handling in lookup_transform_catch_exception to catch LookupException and ConnectivityException - Added None checks after transform lookups in callbacks to skip processing when transforms are unavailable - All warnings are throttled to avoid log spam 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 4c96fab commit 47a74ce

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

elevation_mapping_cupy/scripts/elevation_mapping_node.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from sensor_msgs_py import point_cloud2
1313
from tf_transformations import quaternion_matrix
1414
import tf2_ros
15+
import tf2_py as tf2
1516
import message_filters
1617
from cv_bridge import CvBridge
1718
from rclpy.duration import Duration
@@ -323,11 +324,40 @@ def safe_lookup_transform(self, target_frame, source_frame, time):
323324
time
324325
)
325326
except tf2_ros.ExtrapolationException:
326-
return self._tf_buffer.lookup_transform(
327-
target_frame,
328-
source_frame,
329-
rclpy.time.Time()
327+
# Time is in the future/past, try with latest available
328+
try:
329+
return self._tf_buffer.lookup_transform(
330+
target_frame,
331+
source_frame,
332+
rclpy.time.Time()
333+
)
334+
except (tf2.LookupException, tf2.ConnectivityException) as e:
335+
self.get_logger().warning(
336+
f"Transform from '{source_frame}' to '{target_frame}' not available: {e}",
337+
throttle_duration_sec=5.0
338+
)
339+
return None
340+
except tf2.LookupException as e:
341+
# Frame doesn't exist
342+
self.get_logger().warning(
343+
f"Frame '{target_frame}' or '{source_frame}' does not exist: {e}",
344+
throttle_duration_sec=5.0
345+
)
346+
return None
347+
except tf2.ConnectivityException as e:
348+
# No transform path between frames
349+
self.get_logger().warning(
350+
f"No transform path from '{source_frame}' to '{target_frame}': {e}",
351+
throttle_duration_sec=5.0
330352
)
353+
return None
354+
except Exception as e:
355+
# Catch any other unexpected TF2 errors
356+
self.get_logger().warning(
357+
f"Unexpected TF2 error for transform from '{source_frame}' to '{target_frame}': {e}",
358+
throttle_duration_sec=5.0
359+
)
360+
return None
331361

332362
def image_callback(self, camera_msg: Image, camera_info_msg: CameraInfo, sub_key: str) -> None:
333363
self._last_t = camera_msg.header.stamp
@@ -348,6 +378,9 @@ def image_callback(self, camera_msg: Image, camera_info_msg: CameraInfo, sub_key
348378
camera_msg.header.frame_id,
349379
camera_msg.header.stamp
350380
)
381+
if transform_camera_to_map is None:
382+
# Transform not available, skip this image
383+
return
351384
t = transform_camera_to_map.transform.translation
352385
q = transform_camera_to_map.transform.rotation
353386
t_np = np.array([t.x, t.y, t.z], dtype=np.float32)
@@ -401,6 +434,9 @@ def pointcloud_callback(self, msg: PointCloud2, sub_key: str) -> None:
401434
frame_sensor_id,
402435
msg.header.stamp
403436
)
437+
if transform_sensor_to_map is None:
438+
# Transform not available, skip this pointcloud
439+
return
404440
t = transform_sensor_to_map.transform.translation
405441
q = transform_sensor_to_map.transform.rotation
406442
t_np = np.array([t.x, t.y, t.z], dtype=np.float32)
@@ -460,6 +496,9 @@ def pose_update(self) -> None:
460496
self.base_frame,
461497
self._last_t
462498
)
499+
if transform is None:
500+
# Transform not available, skip pose update
501+
return
463502
t = transform.transform.translation
464503
q = transform.transform.rotation
465504
trans = np.array([t.x, t.y, t.z], dtype=np.float32)

0 commit comments

Comments
 (0)