들어가며

요즘 Rerun을 써보려 한다. 처음에는 “point cloud나 image를 예쁘게 띄워주는 viewer인가?” 정도로 생각했는데, 공식 문서와 예시를 보니 조금 다르게 봐야 할 것 같다.

Rerun은 단순 plotting library라기보다, robotics와 computer vision에서 나오는 multi-rate, multimodal data를 시간축 위에 정리해서 보고, 저장하고, 다시 query하기 위한 도구에 가깝다.

공식 README는 Rerun을 대략 다음처럼 설명한다.

log, query, visualize, stream to training
multimodal robotics data
shared columnar storage

내가 보기에는 여기서 중요한 단어가 세 개이다.

  • multimodal: image, point cloud, transform, scalar, tensor, text, mesh 같은 데이터가 같이 들어간다.
  • time-aware: frame index, timestamp, simulation time 같은 timeline 위에서 데이터를 맞춰 볼 수 있다.
  • queryable: viewer에서 보는 것으로 끝나지 않고, dataframe이나 dataset export 쪽으로 이어질 수 있다.

이 글은 Rerun 사용법을 처음부터 끝까지 다루는 tutorial은 아니다. 대신 “왜 써볼 만한가”를 코드 읽기/연구 workflow 관점에서 쉬운 설명으로 정리해보려 한다.


Rerun의 핵심 개념

내가 이해한 Rerun은 다음 한 문장으로 요약할 수 있다.

robotics나 vision pipeline에서 생기는 image, point cloud, pose, detection, scalar, mesh 같은 데이터를 entity path와 timeline에 맞춰 log하고, viewer에서 live/offline으로 보고, 필요하면 나중에 dataframe으로 query할 수 있게 해주는 도구.

matplotlib, Open3D visualizer, print, rosbag, TensorBoard, RViz가 각각 맡던 일부 역할이 Rerun에서는 하나의 recording model 위에 올라온다.

물론 Rerun이 이 도구들을 모두 대체한다는 뜻은 아니다. 더 정확히는 다음 문제에 잘 맞는다.

내 algorithm이 매 frame마다 무엇을 보고, 무엇을 예측하고, 어떤 intermediate state를 만들었는지 시간축 위에서 같이 보고 싶다.

이 문제는 text log만으로 해결하기 어렵다. SLAM, tracking, calibration, detection, segmentation, mapping 쪽 코드를 읽다 보면 대부분 같은 문제를 만난다.

RGB image는 이렇게 들어왔고
depth는 조금 늦게 들어왔고
pose estimate는 이만큼 drift했고
point cloud는 map frame에서 여기쯤 있고
detection box는 이상한 곳에 떠 있고
loss scalar는 이 frame부터 튀었다

이걸 각자 따로 저장하면 디버깅이 금방 지저분해진다. Rerun은 이 데이터를 같은 시간축과 entity tree 위에 얹는 방식으로 문제를 푼다.


입력/출력 파이프라인

먼저 Rerun SDK와 Viewer가 image, point cloud, transform 같은 robotics/vision data를 입력으로 받아 recording stream, .rrd file, interactive visualization을 만드는 흐름을 살펴보자. 처음 볼 때 헷갈리는 질문은 “Rerun은 viewer인가, logger인가, database인가?”이다.

정답은 셋 다 조금씩 맞다. Rerun은 SDK로 데이터를 log하고, Viewer로 시각화하고, .rrd recording이나 catalog/server를 통해 나중에 query할 수 있게 한다.

모듈별 입력/출력은 다음과 같다.

구성 입력 출력 역할
Rerun SDK image, point cloud, transform, scalar, tensor 등 recording stream 코드에서 데이터를 log
entity path /world/camera/image 같은 path scene hierarchy 데이터의 위치와 의미를 구조화
timeline frame index, sensor timestamp, sim time 등 time-indexed data multi-rate data를 시간축에 정렬
Rerun Viewer live stream 또는 .rrd file interactive visualization 2D/3D/plot/text를 같이 보기
.rrd recording logged data 저장 가능한 log file offline replay와 공유
dataframe/query API recording/catalog table-like data 학습/평가용 데이터 추출

전체 흐름은 이렇게 보면 된다.

robotics / vision pipeline
  -> rr.log()로 intermediate state 기록
  -> entity path로 scene 구조 정리
  -> rr.set_time()으로 frame/timestamp 지정
  -> Viewer에서 live debugging
  -> .rrd로 저장해 offline replay
  -> 필요하면 dataframe query로 dataset/export

여기서 핵심은 rr.log() 하나가 단순 출력이 아니라는 점이다. rr.log("world/lidar", rr.Points3D(points))라고 쓰면, 그 데이터는 viewer에만 잠깐 뜨는 것이 아니라 recording 안의 특정 entity path와 timeline에 붙는다.


기본 Mental Model

Rerun을 이해할 때는 세 단어를 먼저 잡으면 된다.

1. Entity path

Rerun에서 데이터는 path에 log된다.

rr.log("world/camera/image", rr.Image(image))
rr.log("world/lidar/points", rr.Points3D(points))
rr.log("world/trajectory", rr.LineStrips3D([trajectory]))

이 path는 그냥 이름이 아니라 scene hierarchy이다. world/camera/image라고 하면, world 아래 camera가 있고 그 아래 image가 있는 식으로 생각하면 된다.

robotics에서는 이게 특히 자연스럽다.

world
  camera_front
    image
    detections
  lidar
    points
  robot
    base_link
    trajectory
  map
    mesh

즉 Rerun을 잘 쓰려면 data를 어떻게 log할지보다, 먼저 entity path를 어떻게 설계할지 생각하는 것이 중요하다.

2. Timeline

Rerun은 시간축을 안다. 예를 들어 frame index를 기준으로 보고 싶으면 이렇게 쓴다.

rr.set_time("frame", sequence=frame_id)

real timestamp나 simulation time을 쓰고 싶으면 다른 timeline을 만들 수 있다.

이게 중요한 이유는 robotics data가 보통 multi-rate이기 때문이다.

  • camera는 30 Hz
  • LiDAR는 10 Hz
  • IMU는 200 Hz
  • pose estimate는 algorithm loop마다
  • scalar metric은 evaluation step마다

단순 viewer에서는 “현재 값”만 보기 쉽다. Rerun은 같은 recording 안에서 시간축을 scrub하면서, 각 sensor와 intermediate output이 어떻게 변했는지 같이 볼 수 있다.

3. Archetype

Rerun에는 Points3D, Image, Transform3D, Mesh3D, Scalars, Boxes2D, LineStrips3D 같은 loggable type이 있다. 문서에서는 이런 high-level data type을 archetype이라고 부른다.

예를 들면 point cloud는 이렇게 log한다.

rr.log("world/lidar", rr.Points3D(points, colors=colors))

mesh는 이렇게 log한다.

rr.log(
    "world/map",
    rr.Mesh3D(
        vertex_positions=vertices,
        triangle_indices=triangles,
    ),
)

중요한 점은 내가 viewer rendering code를 직접 짜지 않아도 된다는 것이다. 내가 할 일은 “이 데이터가 무엇인지”를 Rerun type으로 알려주는 것이다.


Rerun의 장점

Rerun의 장점은 여러 개가 있지만, 내가 보기에는 다음 여섯 가지가 크다.

1. 디버깅 단위가 text가 아니라 scene이 된다

SLAM이나 perception code를 디버깅할 때 text log만 보면 한계가 빨리 온다.

frame=128
num_points=74521
tracking_valid=True
loss=0.041

이런 log는 필요하지만 충분하지 않다. 실제로 보고 싶은 것은 보통 이것이다.

  • scan이 map에 잘 붙었는가.
  • camera pose가 뒤집히지 않았는가.
  • detection box가 image와 3D point cloud에서 같은 object를 가리키는가.
  • mesh가 어느 frame부터 찢어졌는가.
  • 특정 scalar가 튀는 frame에서 geometry도 이상한가.

Rerun은 이 정보를 같은 viewer에서 볼 수 있게 해준다. 즉 debugging의 기본 단위가 line log에서 scene/time으로 올라간다.

2. 2D와 3D를 같이 본다

OpenCV window로 image를 보고, Open3D로 point cloud를 보고, matplotlib으로 scalar plot을 보는 식으로 나누면 각 데이터를 머릿속에서 맞춰야 한다.

Rerun은 image, point cloud, 3D transform, mesh, scalar plot을 같은 recording 안에 넣을 수 있다. 공식 examples도 robotics, 3D reconstruction, SLAM, nuScenes, KISS-ICP, ROS bridge, MCAP, LeRobot 같은 use case를 다룬다.

이건 robotics와 spatial AI 쪽에서 꽤 중요하다. 대부분의 bug는 한 modality 안에만 있지 않다.

image feature는 맞는데 depth projection이 틀림
point cloud는 맞는데 camera extrinsic이 틀림
trajectory는 그럴듯한데 map frame이 뒤집힘

이런 문제는 2D/3D/time을 같이 봐야 빨리 잡힌다.

3. live와 offline이 같은 코드 경로를 쓴다

Rerun은 viewer를 띄워서 live stream으로 볼 수도 있고, .rrd file로 저장한 뒤 나중에 열 수도 있다.

기본 mental model은 이렇다.

import rerun as rr

rr.init("my_robot_debug")
rr.spawn()                 # live viewer
# rr.save("run.rrd")       # file로 저장

for frame_id, frame in enumerate(dataset):
    rr.set_time("frame", sequence=frame_id)
    rr.log("world/lidar", rr.Points3D(frame.points))

개발 중에는 live viewer로 보고, 실험 결과는 .rrd로 저장해서 다시 열 수 있다. 이게 좋다.

왜냐하면 실험이 끝난 뒤에도 같은 visualization 상태로 replay하면서 볼 수 있기 때문이다. 특정 frame에서 algorithm이 망가졌다면, 그 frame으로 돌아가서 image, point cloud, pose, scalar를 같이 보면 된다.

4. 코드에 붙이는 비용이 낮다

Rerun은 Python 기준으로 설치와 첫 로그가 단순하다.

pip install rerun-sdk

그리고 코드에서는 보통 이런 식으로 시작한다.

import rerun as rr

rr.init("example", spawn=True)
rr.set_time("frame", sequence=0)
rr.log("points", rr.Points3D([[0, 0, 0], [1, 1, 1]]))

이 정도면 기존 research code에 instrumentation을 넣기 쉽다. 거대한 ROS graph를 꾸리거나 custom viewer를 만들지 않아도, 일단 내 algorithm의 intermediate state를 눈으로 볼 수 있다.

5. 나중에 query할 수 있다

Rerun의 차별점 중 하나는 “보고 끝”이 아니라는 점이다. 공식 docs는 recording을 dataframe으로 query하거나, 여러 recording을 catalog/server에 등록해서 dataset처럼 다루는 workflow를 설명한다.

예를 들어 robot recording에서 필요한 entity path만 골라서 LeRobot dataset으로 export하는 예시도 있다.

이 관점이 중요하다. debugging을 위해 남긴 log가 나중에는 evaluation dataset이나 training dataset의 seed가 될 수 있다.

visual debugging log
  -> 실패 case collection
  -> dataframe query
  -> training/evaluation dataset

이 흐름이 자연스러운 도구는 생각보다 많지 않다.

6. ROS/RViz와 다른 층위의 도구이다

Rerun README도 RViz와의 차이를 따로 설명한다. 내가 이해한 차이는 이렇다.

RViz는 ROS graph의 현재 상태를 보는 데 강하다. Rerun은 time-aware recording을 남기고, 그 recording을 replay/query하는 데 더 초점이 있다.

즉 RViz는 이런 질문에 좋다.

지금 robot이 publish하는 topic들이 제대로 보이나?

Rerun은 이런 질문에 더 잘 맞는다.

어제 돌린 run의 1372번째 frame에서 왜 tracking이 튀었나?
그때 image, lidar, map, scalar가 각각 어떤 상태였나?
그 failure case만 모아서 다시 학습/평가에 쓸 수 있나?

두 도구는 경쟁 관계라기보다 workflow의 다른 층위를 담당한다고 보는 게 맞다.

Rerun vs RViz 비교를 내 식으로 다시 쓰면 다음과 같다.

항목 Rerun RViz2
ROS 의존성 없음. ROS2 bridge로 연결 가능 ROS 환경이 기본 전제
SDK Python / Rust / C++ 주로 C++ plugin, Python workflow는 상대적으로 불편
timeline frame/timestamp slider와 multi-timeline 중심 rosbag replay에 의존하는 편
원격/웹 시각화 remote/web viewer workflow 지원 별도 stack이 필요할 수 있음
녹화/공유 .rrd 단일 recording으로 공유 가능 .bag은 강력하지만 무겁고 ROS 의존성이 큼
URDF robot model 지원이 생겼지만 아직 제한적으로 보는 게 맞음 성숙한 지원
interactive marker 약함 강함
안정성/커뮤니티 빠르게 성장 중이고 API 변동 가능 오래 검증된 ROS 생태계
설치 pip install rerun-sdk로 시작 가능 전체 ROS 2 환경 필요

이 표를 보면 결론이 더 선명해진다.

Rerun은 RViz의 모든 역할을 대체하는 도구라기보다, RViz가 불편했던 “시간축 있는 실험 기록, 공유, replay, multi-modal debugging” 영역을 강하게 보완하는 도구이다.

따라서 ROS2 작업에서 바로 RViz를 버릴 필요는 없다. TF tree, URDF robot model, interactive marker, Nav2 같은 standard ROS workflow는 RViz가 여전히 편하다. 대신 algorithm 개발 중에 image, point cloud, pose, scalar, text log를 시간축 위에서 같이 보고 싶다면 Rerun을 병행해서 쓰는 쪽이 훨씬 현실적이다.


Rerun을 쓰기 좋은 상황

Rerun은 다음 상황에서 특히 좋아 보인다.

  1. multi-modal debugging image, depth, point cloud, pose, detection, scalar를 같이 봐야 하는 경우.

  2. time-dependent failure analysis “어느 frame부터 망가졌는지”를 찾아야 하는 경우.

  3. research code instrumentation custom viewer를 만들 시간은 없지만 intermediate result를 눈으로 보고 싶은 경우.

  4. robotics dataset curation 실패 case를 recording으로 남기고, 나중에 query해서 dataset으로 만들고 싶은 경우.

  5. demo algorithm 내부 state를 논문 figure보다 더 interactive하게 보여주고 싶은 경우.

  6. ROS 밖의 code ROS를 쓰지 않는 Python/C++/Rust pipeline에서도 RViz 비슷한 temporal visualization이 필요한 경우.


조심할 점

Rerun이 항상 정답은 아니다. 공식 README에도 몇 가지 한계가 적혀 있다. 특히 viewer는 entity가 너무 많거나 multi-million point cloud가 많으면 느려질 수 있다.

그리고 현재 Rerun은 활발히 개발 중인 프로젝트라 API가 바뀔 수 있다. GitHub release 기준으로 2026년 5월 18일에 0.32.1이 latest로 표시되어 있었고, README도 API가 evolving 중이라고 말한다. 실험 코드라면 괜찮지만, 장기 project라면 version pinning을 해두는 게 좋다.

0.23 전후: TCP에서 gRPC로 바뀐 지점

Rerun을 C++와 Python에서 같이 쓸 때 특히 조심해야 하는 version 경계가 있다. 처음에는 “0.22+”(~투투~)라고 기억하기 쉬운데, 공식 migration guide 기준으로는 0.23에서 SDK와 Viewer 사이의 통신 방식이 바뀐 것이 핵심이다.

통신 쪽을 잘 모른다면 이렇게 생각하면 쉽다.

내 code 안의 Rerun SDK
  -> 데이터를 보냄
  -> Rerun Viewer가 받아서 화면에 그림

여기서 SDK는 Python의 rerun-sdk일 수도 있고, C++에 link된 Rerun SDK일 수도 있다. Viewer는 우리가 terminal에서 실행하는 rerun app이다. 즉 Rerun visualization은 “내 code가 viewer에 데이터를 배달하는 구조”이다.

그런데 0.23 근처에서 이 배달 방식이 바뀌었다. 예전에는 SDK가 Viewer에 비교적 직접적인 raw TCP 방식으로 붙었다면, 이후에는 gRPC라는 더 구조화된 통신 방식과 rerun+http://.../proxy 형태의 주소를 쓰는 쪽으로 바뀌었다.

쉬운 비유로 말하면,

예전 방식:
  "127.0.0.1:9876으로 바로 보내"

새 방식:
  "rerun+http://127.0.0.1:9876/proxy라는 Rerun용 입구로 보내"

주소도 바뀌고, 문 앞에서 데이터를 받는 방식도 바뀐 것이다. 그래서 SDK와 Viewer 중 하나만 새 방식이고 다른 하나가 예전 방식이면, “분명 log를 보내는 것 같은데 viewer에는 안 뜨는” 식의 애매한 문제가 생길 수 있다.

예전 코드는 대략 이런 느낌이었다.

rec.connect_tcp("127.0.0.1:9876")
rec.connect_tcp("127.0.0.1:9876");

0.23 이후에는 SDK가 Viewer에 붙을 때 gRPC endpoint를 쓰는 쪽으로 바뀐다.

rec.connect_grpc("rerun+http://127.0.0.1:9876/proxy")
rec.connect_grpc("rerun+http://127.0.0.1:9876/proxy");

즉 단순히 함수 이름만 바뀐 것이 아니다. 기존의 connect_tcp("127.0.0.1:9876") 감각에서, 이제는 connect_grpc("rerun+http://127.0.0.1:9876/proxy")처럼 Rerun이 정의한 주소 형식과 gRPC proxy endpoint를 맞춰야 한다. 또 0.23 release에서는 0.22 이전의 오래된 RRD encoding 지원도 정리되었다. 그래서 예전 .rrd file을 최신 Viewer에서 열 때도 version 문제가 날 수 있다.

내가 ERASOR2에 Rerun visualization을 붙이려다가 C++ 쪽 Rerun과 Python 쪽 Rerun이 충돌난 것처럼 보였던 것도 아마 이 계열의 문제였을 가능성이 있다. 예를 들어 이런 조합이면 충분히 꼬일 수 있다.

  • Python environment의 rerun-sdk는 최신인데, C++ code가 예전 Rerun SDK의 connect_tcp 기준으로 build되어 있는 경우.
  • C++ SDK는 새 버전인데, terminal에서 실행되는 rerun Viewer binary는 다른 environment에 설치된 예전 버전인 경우.
  • Python script가 rr.spawn()으로 Viewer를 띄우고, C++ binary는 다른 protocol이나 다른 endpoint로 붙으려는 경우.
  • 예전에 저장한 .rrd file을 최신 Viewer에서 열려고 하는데, file encoding이 0.22 이전 포맷인 경우.

이런 문제를 줄이려면 먼저 version을 맞추는 게 좋다.

python -c "import rerun as rr; print(rr.__version__)"
rerun --version

C++ 쪽은 CMake에서 어떤 Rerun SDK를 가져오는지, 또는 vendored SDK를 쓰는지 확인해야 한다. 그리고 code 안에서 transport API를 먼저 찾는다.

rg "connect_tcp|connect_grpc|rerun\+http|spawn|save" path/to/ERASOR2

혼합 language project에서는 처음부터 live streaming을 복잡하게 구성하기보다, 먼저 Python과 C++에서 각각 .rrd를 저장하고 같은 Viewer version으로 열어보는 방식이 더 단단하다. 그 다음 live visualization이 필요해지면 하나의 Viewer endpoint를 명시적으로 띄우고, Python과 C++ 모두 같은 Rerun version과 같은 connect_grpc("rerun+http://.../proxy") 규칙으로 붙이는 편이 좋다.

또 하나 중요한 점은 logging granularity이다. 너무 작은 log call을 너무 많이 만들면 chunk 수가 늘어나고 성능에 영향을 준다. Rerun docs는 chunk count와 compaction, SDK micro-batching을 성능 관점에서 따로 설명한다.

따라서 처음부터 이렇게 쓰는 건 피하는 게 좋다.

for point in points:
    rr.log("points", rr.Points3D([point]))

대신 한 frame의 point cloud는 한 번에 log하는 편이 낫다.

rr.log("points", rr.Points3D(points))

내가 기대하는 사용 방식

내가 Rerun을 써본다면 처음 목표는 거창한 data platform이 아니라, 다음 정도일 것 같다.

내 SLAM / mapping / perception code의
intermediate state를 frame별로 잘 남기고,
나중에 특정 failure frame을 다시 열어볼 수 있게 만들기.

처음부터 query API나 catalog까지 쓰지 않아도 된다. 일단 .rrd recording을 남기는 것만으로도 가치가 있다.

추천하는 시작 순서는 이렇다.

  1. pip install rerun-sdk
  2. 가장 작은 script에서 rr.init(..., spawn=True)로 viewer 띄우기.
  3. image 또는 point cloud 하나 log하기.
  4. frame timeline 추가하기.
  5. pose/trajectory/scalar 추가하기.
  6. .rrd 저장으로 offline replay 만들기.
  7. 필요해지면 dataframe query나 dataset export 보기.

정리

Rerun이 좋은 이유는 “예쁜 3D viewer”라서가 아니다. 더 정확히는,

robotics와 vision pipeline의 intermediate state를 시간축이 있는 multimodal recording으로 남기고, live로 보거나 나중에 다시 열고, 필요하면 query까지 이어갈 수 있기 때문

이라고 보는 게 맞다.

그래서 print, matplotlib, Open3D, RViz, rosbag, TensorBoard 사이에서 계속 왔다 갔다 하던 workflow를 어느 정도 정리해줄 수 있다. 특히 SLAM이나 neural mapping처럼 2D/3D/time/scalar가 한꺼번에 얽히는 code를 읽을 때는 꽤 잘 맞을 것 같다.


References