diff --git a/dimos/visualization/rerun/bridge.py b/dimos/visualization/rerun/bridge.py index 85180fa40..9bba9dd82 100644 --- a/dimos/visualization/rerun/bridge.py +++ b/dimos/visualization/rerun/bridge.py @@ -36,10 +36,18 @@ from dimos.core.core import rpc from dimos.core.module import Module, ModuleConfig +from dimos.msgs.sensor_msgs import Image, PointCloud2 from dimos.protocol.pubsub.impl.lcmpubsub import LCM from dimos.protocol.pubsub.patterns import Glob, pattern_matches from dimos.utils.logging_config import setup_logger +# Message types with large payloads that need rate-limiting. +# Image (~1 MB/frame at 30 fps) and PointCloud2 (~600-800 KB/frame) +# cause viewer OOM if logged at full rate. Light messages +# (Path, PointStamped, Twist, TF, EntityMarkers …) pass through +# unthrottled so navigation overlays and user input are never dropped. +_HEAVY_MSG_TYPES: tuple[type, ...] = (Image, PointCloud2) + RERUN_GRPC_PORT = 9876 RERUN_WEB_PORT = 9090 @@ -254,10 +262,11 @@ def _on_message(self, msg: Any, topic: Any) -> None: # convert a potentially complex topic object into an str rerun entity path entity_path: str = self._get_entity_path(topic) - # Rate-limit per entity path to prevent viewer memory exhaustion. - # High-bandwidth streams (e.g. 30fps camera) would otherwise flood - # the viewer with data faster than it can evict, causing OOM. - if self.config.min_interval_sec > 0: + # Rate-limit heavy data types to prevent viewer memory exhaustion. + # High-bandwidth streams (e.g. 30fps camera, lidar) would otherwise + # flood the viewer faster than it can evict, causing OOM. Light + # messages (Path, PointStamped, TF, etc.) pass through unthrottled. + if self.config.min_interval_sec > 0 and isinstance(msg, _HEAVY_MSG_TYPES): now = time.monotonic() last = self._last_log.get(entity_path, 0.0) if now - last < self.config.min_interval_sec: