Revert "feat(visualization): allow remote viewer + compress rerun images (#2756)" (#2766)

This reverts commit f844c7a458.
This commit is contained in:
Steven Palma
2026-01-07 17:33:36 +01:00
committed by GitHub
parent f844c7a458
commit 4f7cd8d369
5 changed files with 11 additions and 69 deletions
+2 -12
View File
@@ -96,7 +96,6 @@ def visualize_dataset(
ws_port: int = 9087, ws_port: int = 9087,
save: bool = False, save: bool = False,
output_dir: Path | None = None, output_dir: Path | None = None,
display_compressed_images: bool = False,
) -> Path | None: ) -> Path | None:
if save: if save:
assert output_dir is not None, ( assert output_dir is not None, (
@@ -138,9 +137,8 @@ def visualize_dataset(
# display each camera image # display each camera image
for key in dataset.meta.camera_keys: for key in dataset.meta.camera_keys:
img = to_hwc_uint8_numpy(batch[key][i]) # TODO(rcadene): add `.compress()`? is it lossless?
img_entity = rr.Image(img).compress() if display_compressed_images else rr.Image(img) rr.log(key, rr.Image(to_hwc_uint8_numpy(batch[key][i])))
rr.log(key, entity=img_entity)
# display each dimension of action space (e.g. actuators command) # display each dimension of action space (e.g. actuators command)
if ACTION in batch: if ACTION in batch:
@@ -263,14 +261,6 @@ def main():
), ),
) )
parser.add_argument(
"--display-compressed-images",
type=bool,
required=True,
default=False,
help="If set, display compressed images in Rerun instead of uncompressed ones.",
)
args = parser.parse_args() args = parser.parse_args()
kwargs = vars(args) kwargs = vars(args)
repo_id = kwargs.pop("repo_id") repo_id = kwargs.pop("repo_id")
+2 -17
View File
@@ -185,12 +185,6 @@ class RecordConfig:
policy: PreTrainedConfig | None = None policy: PreTrainedConfig | None = None
# Display all cameras on screen # Display all cameras on screen
display_data: bool = False display_data: bool = False
# Display data on a remote Rerun server
display_ip: str | None = None
# Port of the remote Rerun server
display_port: int | None = None
# Whether to display compressed images in Rerun
display_compressed_images: bool = False
# Use vocal synthesis to read events. # Use vocal synthesis to read events.
play_sounds: bool = True play_sounds: bool = True
# Resume recording on an existing dataset. # Resume recording on an existing dataset.
@@ -267,7 +261,6 @@ def record_loop(
control_time_s: int | None = None, control_time_s: int | None = None,
single_task: str | None = None, single_task: str | None = None,
display_data: bool = False, display_data: bool = False,
display_compressed_images: bool = False,
): ):
if dataset is not None and dataset.fps != fps: if dataset is not None and dataset.fps != fps:
raise ValueError(f"The dataset fps should be equal to requested fps ({dataset.fps} != {fps}).") raise ValueError(f"The dataset fps should be equal to requested fps ({dataset.fps} != {fps}).")
@@ -378,9 +371,7 @@ def record_loop(
dataset.add_frame(frame) dataset.add_frame(frame)
if display_data: if display_data:
log_rerun_data( log_rerun_data(observation=obs_processed, action=action_values)
observation=obs_processed, action=action_values, compress_images=display_compressed_images
)
dt_s = time.perf_counter() - start_loop_t dt_s = time.perf_counter() - start_loop_t
precise_sleep(max(1 / fps - dt_s, 0.0)) precise_sleep(max(1 / fps - dt_s, 0.0))
@@ -393,12 +384,7 @@ def record(cfg: RecordConfig) -> LeRobotDataset:
init_logging() init_logging()
logging.info(pformat(asdict(cfg))) logging.info(pformat(asdict(cfg)))
if cfg.display_data: if cfg.display_data:
init_rerun(session_name="recording", ip=cfg.display_ip, port=cfg.display_port) init_rerun(session_name="recording")
display_compressed_images = (
True
if (cfg.display_data and cfg.display_ip is not None and cfg.display_port is not None)
else cfg.display_compressed_images
)
robot = make_robot_from_config(cfg.robot) robot = make_robot_from_config(cfg.robot)
teleop = make_teleoperator_from_config(cfg.teleop) if cfg.teleop is not None else None teleop = make_teleoperator_from_config(cfg.teleop) if cfg.teleop is not None else None
@@ -492,7 +478,6 @@ def record(cfg: RecordConfig) -> LeRobotDataset:
control_time_s=cfg.dataset.episode_time_s, control_time_s=cfg.dataset.episode_time_s,
single_task=cfg.dataset.single_task, single_task=cfg.dataset.single_task,
display_data=cfg.display_data, display_data=cfg.display_data,
display_compressed_images=display_compressed_images,
) )
# Execute a few seconds without recording to give time to manually reset the environment # Execute a few seconds without recording to give time to manually reset the environment
+1 -16
View File
@@ -108,12 +108,6 @@ class TeleoperateConfig:
teleop_time_s: float | None = None teleop_time_s: float | None = None
# Display all cameras on screen # Display all cameras on screen
display_data: bool = False display_data: bool = False
# Display data on a remote Rerun server
display_ip: str | None = None
# Port of the remote Rerun server
display_port: int | None = None
# Whether to display compressed images in Rerun
display_compressed_images: bool = False
def teleop_loop( def teleop_loop(
@@ -125,7 +119,6 @@ def teleop_loop(
robot_observation_processor: RobotProcessorPipeline[RobotObservation, RobotObservation], robot_observation_processor: RobotProcessorPipeline[RobotObservation, RobotObservation],
display_data: bool = False, display_data: bool = False,
duration: float | None = None, duration: float | None = None,
display_compressed_images: bool = False,
): ):
""" """
This function continuously reads actions from a teleoperation device, processes them through optional This function continuously reads actions from a teleoperation device, processes them through optional
@@ -137,7 +130,6 @@ def teleop_loop(
robot: The robot instance being controlled. robot: The robot instance being controlled.
fps: The target frequency for the control loop in frames per second. fps: The target frequency for the control loop in frames per second.
display_data: If True, fetches robot observations and displays them in the console and Rerun. display_data: If True, fetches robot observations and displays them in the console and Rerun.
display_compressed_images: If True, compresses images before sending them to Rerun for display.
duration: The maximum duration of the teleoperation loop in seconds. If None, the loop runs indefinitely. duration: The maximum duration of the teleoperation loop in seconds. If None, the loop runs indefinitely.
teleop_action_processor: An optional pipeline to process raw actions from the teleoperator. teleop_action_processor: An optional pipeline to process raw actions from the teleoperator.
robot_action_processor: An optional pipeline to process actions before they are sent to the robot. robot_action_processor: An optional pipeline to process actions before they are sent to the robot.
@@ -175,7 +167,6 @@ def teleop_loop(
log_rerun_data( log_rerun_data(
observation=obs_transition, observation=obs_transition,
action=teleop_action, action=teleop_action,
compress_images=display_compressed_images,
) )
print("\n" + "-" * (display_len + 10)) print("\n" + "-" * (display_len + 10))
@@ -200,12 +191,7 @@ def teleoperate(cfg: TeleoperateConfig):
init_logging() init_logging()
logging.info(pformat(asdict(cfg))) logging.info(pformat(asdict(cfg)))
if cfg.display_data: if cfg.display_data:
init_rerun(session_name="teleoperation", ip=cfg.display_ip, port=cfg.display_port) init_rerun(session_name="teleoperation")
display_compressed_images = (
True
if (cfg.display_data and cfg.display_ip is not None and cfg.display_port is not None)
else cfg.display_compressed_images
)
teleop = make_teleoperator_from_config(cfg.teleop) teleop = make_teleoperator_from_config(cfg.teleop)
robot = make_robot_from_config(cfg.robot) robot = make_robot_from_config(cfg.robot)
@@ -224,7 +210,6 @@ def teleoperate(cfg: TeleoperateConfig):
teleop_action_processor=teleop_action_processor, teleop_action_processor=teleop_action_processor,
robot_action_processor=robot_action_processor, robot_action_processor=robot_action_processor,
robot_observation_processor=robot_observation_processor, robot_observation_processor=robot_observation_processor,
display_compressed_images=display_compressed_images,
) )
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
+5 -20
View File
@@ -22,25 +22,13 @@ import rerun as rr
from .constants import OBS_PREFIX, OBS_STR from .constants import OBS_PREFIX, OBS_STR
def init_rerun( def init_rerun(session_name: str = "lerobot_control_loop") -> None:
session_name: str = "lerobot_control_loop", ip: str | None = None, port: int | None = None """Initializes the Rerun SDK for visualizing the control loop."""
) -> None:
"""
Initializes the Rerun SDK for visualizing the control loop.
Args:
session_name: Name of the Rerun session.
ip: Optional IP for connecting to a Rerun server.
port: Optional port for connecting to a Rerun server.
"""
batch_size = os.getenv("RERUN_FLUSH_NUM_BYTES", "8000") batch_size = os.getenv("RERUN_FLUSH_NUM_BYTES", "8000")
os.environ["RERUN_FLUSH_NUM_BYTES"] = batch_size os.environ["RERUN_FLUSH_NUM_BYTES"] = batch_size
rr.init(session_name) rr.init(session_name)
memory_limit = os.getenv("LEROBOT_RERUN_MEMORY_LIMIT", "10%") memory_limit = os.getenv("LEROBOT_RERUN_MEMORY_LIMIT", "10%")
if ip and port: rr.spawn(memory_limit=memory_limit)
rr.connect_grpc(url=f"rerun+http://{ip}:{port}/proxy")
else:
rr.spawn(memory_limit=memory_limit)
def _is_scalar(x): def _is_scalar(x):
@@ -52,7 +40,6 @@ def _is_scalar(x):
def log_rerun_data( def log_rerun_data(
observation: dict[str, Any] | None = None, observation: dict[str, Any] | None = None,
action: dict[str, Any] | None = None, action: dict[str, Any] | None = None,
compress_images: bool = False,
) -> None: ) -> None:
""" """
Logs observation and action data to Rerun for real-time visualization. Logs observation and action data to Rerun for real-time visualization.
@@ -61,7 +48,7 @@ def log_rerun_data(
to the Rerun viewer. It handles different data types appropriately: to the Rerun viewer. It handles different data types appropriately:
- Scalars values (floats, ints) are logged as `rr.Scalars`. - Scalars values (floats, ints) are logged as `rr.Scalars`.
- 3D NumPy arrays that resemble images (e.g., with 1, 3, or 4 channels first) are transposed - 3D NumPy arrays that resemble images (e.g., with 1, 3, or 4 channels first) are transposed
from CHW to HWC format, (optionally) compressed to JPEG and logged as `rr.Image` or `rr.EncodedImage`. from CHW to HWC format and logged as `rr.Image`.
- 1D NumPy arrays are logged as a series of individual scalars, with each element indexed. - 1D NumPy arrays are logged as a series of individual scalars, with each element indexed.
- Other multi-dimensional arrays are flattened and logged as individual scalars. - Other multi-dimensional arrays are flattened and logged as individual scalars.
@@ -70,7 +57,6 @@ def log_rerun_data(
Args: Args:
observation: An optional dictionary containing observation data to log. observation: An optional dictionary containing observation data to log.
action: An optional dictionary containing action data to log. action: An optional dictionary containing action data to log.
compress_images: Whether to compress images before logging to save bandwidth & memory in exchange for cpu and quality.
""" """
if observation: if observation:
for k, v in observation.items(): for k, v in observation.items():
@@ -89,8 +75,7 @@ def log_rerun_data(
for i, vi in enumerate(arr): for i, vi in enumerate(arr):
rr.log(f"{key}_{i}", rr.Scalars(float(vi))) rr.log(f"{key}_{i}", rr.Scalars(float(vi)))
else: else:
img_entity = rr.Image(arr).compress() if compress_images else rr.Image(arr) rr.log(key, rr.Image(arr), static=True)
rr.log(key, entity=img_entity, static=True)
if action: if action:
for k, v in action.items(): for k, v in action.items():
+1 -4
View File
@@ -41,10 +41,7 @@ def mock_rerun(monkeypatch):
def __init__(self, arr): def __init__(self, arr):
self.arr = arr self.arr = arr
def dummy_log(key, obj=None, **kwargs): def dummy_log(key, obj, **kwargs):
# Accept either positional `obj` or keyword `entity` and record remaining kwargs.
if obj is None and "entity" in kwargs:
obj = kwargs.pop("entity")
calls.append((key, obj, kwargs)) calls.append((key, obj, kwargs))
dummy_rr = SimpleNamespace( dummy_rr = SimpleNamespace(