rerun-chunk-processing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRerun chunk processing
Rerun Chunk 处理
The pipeline layer between raw data and an RRD: readers produce s,
streams transform them, terminal calls execute. This skill is the generic
mechanics only. Decide the data model first (), then pick the
importer skill for each source:
Chunkrerun-data-model| Source | Reader | Skill |
|---|---|---|
| MCAP file (ROS2, protobuf, Foxglove) | | |
| URDF robot model (+ joint states → FK) | | |
| Parquet table (trajectories, sensor logs) | | |
| mp4 camera video | | |
| LeRobot dataset directory | built-in importer, then | |
| Existing RRD | | here, below |
| Sidecar files (JSON calib, metadata) | | here, below |
The API is ; when
behavior matters, check the installed surface:
.
rerun.experimentalpython -c "from rerun.experimental import LazyChunkStream; help(LazyChunkStream)"原始数据与 RRD 之间的流水线层:reader 生成,流对其进行转换,最终调用执行处理。本技能仅涵盖通用机制。请先确定数据模型(),再为每个数据源选择对应的导入器技能:
Chunkrerun-data-model| 数据源 | Reader | 技能 |
|---|---|---|
| MCAP 文件(ROS2、protobuf、Foxglove) | | |
| URDF 机器人模型(+ 关节状态 → 正向运动学) | | |
| Parquet 表(轨迹、传感器日志) | | |
| MP4 相机视频 | | |
| LeRobot 数据集目录 | 内置导入器,之后使用 | |
| 现有 RRD 文件 | | 本文档下方介绍 |
| 辅助文件(JSON 校准文件、元数据) | | 本文档下方介绍 |
该 API 属于;若需确认具体行为,请查看已安装版本的 API 接口:
。
rerun.experimentalpython -c "from rerun.experimental import LazyChunkStream; help(LazyChunkStream)"Decision rule: where does each component come from?
决策规则:各组件的来源?
Default: a reader produces the chunks; lenses shape them. Walk this before
writing any conversion code — most "build it by hand" instincts are wrong here:
- Source a reader supports? Use the reader's ; never hand-parse and re-log. MCAP→
.stream(), URDF→McapReader, parquet→UrdfTree, mp4→ParquetReader, RRD→Mp4Reader, LeRobot dir→RrdReader.log_file_from_path - A decoder already emits the archetype? Foxglove gives ,
Transform3D,Pinhole(real sample bytes) ready-made — pass it through, do not re-derive. Only custom-protobuf topics arrive asVideoStreamand need a lens (see<Name>:message).rerun-mcap - Fix an existing component in place (swapped resolution, recolor, unit
convert)? ,
MutateLens.output_mode="forward_unmatched" - Derive a new component/entity (FK→, scalars from a message)?
/tf. To scatter one row into N (a joint batch → per-jointDeriveLens), use the two-lens pair: derive the batch with/tf(keeps the originals, e.g. the joint states), then a secondoutput_mode="forward_all"withDeriveLensandscatter=True(emits only the scattered rows). See theoutput_mode="drop_unmatched"example.robot_data_preprocessing - Genuine sidecar no reader or lens can produce (JSON calibration offsets,
hand-measured extrinsics, external metadata)? +
Chunk.from_columns.from_iter - Finish with →
LazyChunkStream.merge(...)→.collect(optimize=OptimizationProfile.OBJECT_STORE).write_rrd(application_id, recording_id)
Why this order: the pipeline stays lazy, columnar, multithreaded, and
-optimizable. A hand-built row loop or an out-of-lens
throws all of that away — that is the path we are deliberately avoiding.
OBJECT_STOREpa.array默认规则:由 reader 生成 Chunk;由 Lens 调整其形态。在编写任何转换代码前,请遵循以下步骤——大多数“手动构建”的直觉都是错误的:
- 数据源受 reader 支持? 使用该 reader 的方法;切勿手动解析并重新记录。MCAP→
.stream(),URDF→McapReader,Parquet→UrdfTree,MP4→ParquetReader,RRD→Mp4Reader,LeRobot 目录→RrdReader。log_file_from_path - 解码器已输出对应原型? Foxglove 会直接生成、
Transform3D、Pinhole(真实样本字节)——直接传递即可,无需重新推导。只有自定义 protobuf 主题会以VideoStream形式传入,需要使用 Lens 处理(参见<Name>:message)。rerun-mcap - 原地修复现有组件(分辨率互换、重着色、单位转换)?使用,设置
MutateLens。output_mode="forward_unmatched" - 推导新组件/实体(正向运动学→、从消息中提取标量)?使用
/tf。若要将一行数据拆分为多行(如关节批量数据→每个关节对应一条DeriveLens),请使用双 Lens 组合:先用/tf推导批量数据(保留原始数据,如关节状态),再用第二个output_mode="forward_all"设置DeriveLens和scatter=True(仅输出拆分后的行)。请参见output_mode="drop_unmatched"示例。robot_data_preprocessing - 真正的辅助数据(reader 或 Lens 无法生成的内容,如 JSON 校准偏移量、手动测量的外参、外部元数据)?使用+
Chunk.from_columns。from_iter - 最后执行→
LazyChunkStream.merge(...)→.collect(optimize=OptimizationProfile.OBJECT_STORE)。write_rrd(application_id, recording_id)
为何遵循此顺序:流水线保持惰性、列式、多线程,且可通过优化。手动构建的行循环或 Lens 外的组装会破坏所有这些优势——这正是我们刻意避免的路径。
OBJECT_STOREpa.arrayAnti-patterns (use a reader + lens instead)
反模式(改用 reader + Lens)
If you are writing the left, stop and use the right:
- -loop building rows/components → a lens with a
forPyArrow-compute callback.Selector(...).pipe(...) - +
rr.initper message for conversion → that is live logging; for ingestion, read with a reader andrr.log.write_rrd - +
chunk.to_record_batch()then rebuilding viapc.filter(row-thinning by hand) →Chunk.from_columns,stream.drop(content=...), or a.split(...)returning a filteredMutateLens.pa.array - /
pa.array/pa.RecordBatchassembled OUTSIDE a lens → move the transform inside anp.frombuffer/MutateLensselector callback.DeriveLens - hand-assembled from a custom parser → use the matching reader; it produces chunks directly.
rr.send_columns - Parsing MCAP/URDF with a non-Rerun library then re-logging → /
McapReader.UrdfTree - for data a reader already decodes (
Chunk.from_columnsintrinsics,Pinhole,VideoStreamfrom a transforms topic) → keep it in the reader stream; fix with aTransform3Dif needed.MutateLens
A wall of "missing-attribute" type errors (,
) usually means calls sit in module-level helpers instead
of inside lens callbacks. Refactor into a lens before suppressing
the checker — the errors are a smell that the hand-building should not exist.
pyarrow.computepc.filterpc.list_elementpc.*Selector.pipePorting an existing converter? Hand-built converters predate decoder
improvements and are not ground truth. Re-verify the decoder output (step 2) and
check every / for-loop against this list before copying.
Chunk.from_columns若你正在编写左侧的代码,请停止并改用右侧的方案:
- 循环构建行/组件 → 使用带有
forPyArrow 计算回调的 Lens。Selector(...).pipe(...) - 转换时使用+
rr.init逐消息记录 → 这是_实时_记录;对于数据摄入,请使用 reader 读取并调用rr.log。write_rrd - +
chunk.to_record_batch()再通过pc.filter重建(手动精简行)→ 使用Chunk.from_columns、stream.drop(content=...)或返回过滤后.split(...)的pa.array。MutateLens - 在 Lens 外组装/
pa.array/pa.RecordBatch→ 将转换逻辑移至np.frombuffer/MutateLens的 selector 回调中。DeriveLens - 手动组装的自定义解析器 → 使用匹配的 reader;它会直接生成 Chunk。
rr.send_columns - 使用非 Rerun 库解析 MCAP/URDF 后重新记录 → 使用/
McapReader。UrdfTree - 对 reader 已解码的数据使用(如
Chunk.from_columns内参、Pinhole、来自变换主题的VideoStream)→ 保留在 reader 流中;如需调整,使用Transform3D。MutateLens
大量 PyArrow 计算的“缺失属性”类型错误(、)通常意味着调用位于模块级辅助函数中,而非的 Lens 回调内。在抑制检查器错误前,请重构为 Lens——这些错误是手动构建逻辑不应存在的信号。
pc.filterpc.list_elementpc.*Selector.pipe迁移现有转换器? 手动构建的转换器早于解码器改进,并非最优方案。迁移前,请重新验证解码器输出(步骤 2),并对照此列表检查每个/for 循环。
Chunk.from_columnsCore model
核心模型
- is a lazy pipeline DAG, not a collection. Building filters, lenses, maps, splits, and merges reads no source data.
LazyChunkStream - Execution starts at terminal calls: ,
write_rrd(...),collect(), or iterating the stream.to_chunks() - Execution is streaming, multithreaded, and mostly GIL-free. Prefer stream/lens operations and PyArrow compute over Python row loops.
- Move semantics: builder calls (,
filter,drop,lenses,map) consume the input stream; reusing a consumed stream raises. Reassign after each step. Terminal calls do not consume, but each terminal call re-executes the whole pipeline;flat_maponce if that is too costly.collect() - is materialized in memory (
ChunkStore,stream.collect()).ChunkStore.from_chunksis manifest-indexed, loads chunks on demand (LazyStore, catalog segment stores). Both haveRrdReader(path).store(),schema(),summary(), andstream().write_rrd(...)
- 是惰性流水线有向无环图,而非集合。构建过滤器、Lens、映射、拆分和合并操作不会读取源数据。
LazyChunkStream - 执行从最终调用开始:、
write_rrd(...)、collect()或遍历流。to_chunks() - 执行过程是流式、多线程且基本无需 GIL。优先使用流/Lens 操作和 PyArrow 计算,而非 Python 行循环。
- 移动语义:构建器调用(、
filter、drop、lenses、map)会消耗输入流;复用已消耗的流会引发错误。每步操作后重新赋值。最终调用不会消耗流,但每次最终调用都会重新执行整个流水线;若成本过高,请仅调用一次flat_map。collect() - 在内存中实例化(
ChunkStore、stream.collect())。ChunkStore.from_chunks基于清单索引,按需加载 Chunk(LazyStore、目录段存储)。两者均提供RrdReader(path).store()、schema()、summary()和stream()方法。write_rrd(...)
Stream composition
流组合
python
from rerun.experimental import Chunk, LazyChunkStream, OptimizationProfile- keeps the matching portion of each chunk;
stream.filter(content=, has_timeline=, is_static=, components=)is its complement, same keyword filters.stream.drop(...)takes an entity-path glob or a list of them.content - applies
stream.map(fn);Chunk -> Chunkappliesstream.flat_map(fn). Escape hatches for chunk-level Python logic; prefer lenses for columnar work.Chunk -> Iterable[Chunk] - returns
stream.split(content=, ...); both branches share the same upstream.(matching, non_matching) - fans in any number of sources.
LazyChunkStream.merge(*streams) - wraps hand-built chunks.
LazyChunkStream.from_iter(chunks)
python
stream = source_stream() # any importer skill
stream = stream.drop(content="/video_raw/**")
stream = stream.lenses(fix_lens, content="/cam/**", output_mode="forward_unmatched")
merged = LazyChunkStream.merge(stream, sidecar_stream)
merged.write_rrd(out_path, application_id="my_app", recording_id=recording_id)python
from rerun.experimental import Chunk, LazyChunkStream, OptimizationProfile- 保留每个 Chunk 中匹配的部分;
stream.filter(content=, has_timeline=, is_static=, components=)是其互补操作,使用相同的关键字过滤器。stream.drop(...)接受实体路径通配符或路径列表。content - 应用
stream.map(fn)转换;Chunk -> Chunk应用stream.flat_map(fn)转换。这是 Chunk 级 Python 逻辑的逃生舱;对于列式处理,优先使用 Lens。Chunk -> Iterable[Chunk] - 返回
stream.split(content=, ...);两个分支共享同一上游流。(matching, non_matching) - 合并任意数量的源流。
LazyChunkStream.merge(*streams) - 包装手动构建的 Chunk。
LazyChunkStream.from_iter(chunks)
python
stream = source_stream() # 任意导入器技能
stream = stream.drop(content="/video_raw/**")
stream = stream.lenses(fix_lens, content="/cam/**", output_mode="forward_unmatched")
merged = LazyChunkStream.merge(stream, sidecar_stream)
merged.write_rrd(out_path, application_id="my_app", recording_id=recording_id)Hand-built chunks — sidecar only
手动构建 Chunk — 仅用于辅助数据
Use ONLY for data no reader or lens can emit — JSON/CSV
calibration, frame offsets, external metadata. If a reader
(//) decodes the topic or a lens can derive
it, that is the idiomatic path; do not hand-assemble it here. In the
example the only hand-built chunk is the JSON
offsets sidecar; the camera fix, FK→, meshes, and recolor are all
readers + lenses.
Chunk.from_columnsMcapReaderUrdfTreeParquetReaderrobot_data_preprocessing/tfChunk.from_columns(entity_path, indexes, columns)rr.send_columns(...).columns(...)indexespython
chunk = Chunk.from_columns(
"/tf_static/robot_offsets",
indexes=[], # static
columns=rr.Transform3D.columns(
translation=translations,
quaternion=quaternions_xyzw,
parent_frame=parents,
child_frame=children,
),
)
sidecar_stream = LazyChunkStream.from_iter([chunk])rr.AnyValues.columns(...)Chunkentity_pathnum_rowsis_statictimeline_namesto_record_batch()format()仅当 reader 或 Lens 无法生成数据时,才使用——如 JSON/CSV 校准数据、帧偏移量、外部元数据。若 reader(//)可解码该主题,或 Lens 可推导得出,则应使用标准路径;请勿在此手动组装。在示例中,唯一手动构建的 Chunk 是 JSON 偏移量辅助数据;相机修复、正向运动学→、网格和重着色均通过 reader + Lens 实现。
Chunk.from_columnsMcapReaderUrdfTreeParquetReaderrobot_data_preprocessing/tfChunk.from_columns(entity_path, indexes, columns)rr.send_columns(...).columns(...)indexespython
chunk = Chunk.from_columns(
"/tf_static/robot_offsets",
indexes=[], # 静态数据
columns=rr.Transform3D.columns(
translation=translations,
quaternion=quaternions_xyzw,
parent_frame=parents,
child_frame=children,
),
)
sidecar_stream = LazyChunkStream.from_iter([chunk])rr.AnyValues.columns(...)Chunkentity_pathnum_rowsis_statictimeline_namesto_record_batch()format()Lenses
Lens
Lenses reshape, fix, or derive components without iterating rows. Apply with
.
stream.lenses(lenses, output_mode=..., content=...)- modifies an existing component in place.
MutateLens(component, selector, keep_row_ids=False) - creates new columns, optionally at another entity. Chain
DeriveLens(component, output_entity=None, scatter=False)per output;.to_component(descriptor, selector)extracts a time column from the data itself..to_timeline(name, "sequence" | "duration_ns" | "timestamp_ns", selector)explodes one input row into N output rows (one per list element).scatter=True - Scope with whenever the same component name exists under multiple entities.
content=
Output modes, and the default is :
drop_unmatched- (default): only lens outputs survive. Right for derive-only intermediate streams; silently discards everything else if applied broadly.
drop_unmatched - : lens outputs plus the original components no lens consumed. Right for targeted fixes that preserve the rest of the stream.
forward_unmatched - : lens outputs plus all originals, including consumed ones. Can duplicate data.
forward_all
In-place fix (keep Arrow type and length intact):
python
stream = stream.lenses(
MutateLens(
"Pinhole:resolution",
Selector(".").pipe(
lambda res: pa.array(
[(h, w) for w, h in res.to_pylist()],
type=res.type,
)
),
),
content=["/external/cam_low", "/external/cam_high"],
output_mode="forward_unmatched",
)Derive with unit conversion (PyArrow compute, no Python loop):
python
DeriveLens("schemas.proto.JointState:message", output_entity="/joints_deg/waist").to_component(
rr.Scalars.descriptor_scalars(),
Selector(".joint_positions").pipe(lambda arr: pc.multiply(pc.list_element(arr, 0), 180.0 / math.pi)),
)Lens 可在不遍历行的情况下重塑、修复或推导组件。通过应用。
stream.lenses(lenses, output_mode=..., content=...)- 原地修改现有组件。
MutateLens(component, selector, keep_row_ids=False) - 创建新列,可选择输出到另一个实体。链式调用
DeriveLens(component, output_entity=None, scatter=False)定义每个输出;.to_component(descriptor, selector)从数据中提取时间列。.to_timeline(name, "sequence" | "duration_ns" | "timestamp_ns", selector)会将一行输入拆分为多行输出(每个列表元素对应一行)。scatter=True - 当同一组件名称存在于多个实体下时,请使用限定作用域。
content=
输出模式,默认值为:
drop_unmatched- (默认):仅保留 Lens 的输出。适用于仅推导的中间流;若广泛应用,会静默丢弃所有其他内容。
drop_unmatched - :保留 Lens 的输出以及未被 Lens 处理的原始组件。适用于针对性修复,同时保留流的其余部分。
forward_unmatched - :保留 Lens 的输出以及所有原始组件,包括已被处理的组件。可能会导致数据重复。
forward_all
原地修复(保持 Arrow 类型和长度不变):
python
stream = stream.lenses(
MutateLens(
"Pinhole:resolution",
Selector(".").pipe(
lambda res: pa.array(
[(h, w) for w, h in res.to_pylist()],
type=res.type,
)
),
),
content=["/external/cam_low", "/external/cam_high"],
output_mode="forward_unmatched",
)带单位转换的推导(使用 PyArrow 计算,无 Python 循环):
python
DeriveLens("schemas.proto.JointState:message", output_entity="/joints_deg/waist").to_component(
rr.Scalars.descriptor_scalars(),
Selector(".joint_positions").pipe(lambda arr: pc.multiply(pc.list_element(arr, 0), 180.0 / math.pi)),
)Selector grammar
Selector 语法
Selector("<query>")- current value;
.struct field.field - iterate list elements;
[]index a list[N] - suppress errors / skip missing optionals;
?assert non-null! - pipe one expression into another
|
.pipe(fn).execute(array).execute_per_row(array)Selector("<query>")- 当前值;
.结构体字段.field - 遍历列表元素;
[]列表索引[N] - 抑制错误/跳过缺失的可选值;
?断言非空! - 将一个表达式的输出传递给另一个表达式
|
.pipe(fn).execute(array).execute_per_row(array)Writing RRDs
写入 RRD
- executes and writes in one streaming pass.
stream.write_rrd(path, application_id=..., recording_id=...) - materializes, optimizes chunk layout, then writes. Memory scales with the materialized chunks.
stream.collect(optimize=OptimizationProfile.OBJECT_STORE).write_rrd(...) - Profiles: (large chunks, for storage/query/catalog) and
OBJECT_STORE(small chunks, low-latency viewer).LIVE - Multiple physical RRDs form one logical recording when they share a
; use this to separate base data, model/URDF data, and layers.
recording_id
Always use when the RRD is headed for a
Rerun catalog or Hub, unless explicitly asked otherwise.
OptimizationProfile.OBJECT_STORE- 执行并通过一次流式传递写入。
stream.write_rrd(path, application_id=..., recording_id=...) - 实例化数据、优化 Chunk 布局,然后写入。内存占用与实例化的 Chunk 规模成正比。
stream.collect(optimize=OptimizationProfile.OBJECT_STORE).write_rrd(...) - 配置文件:(大 Chunk,用于存储/查询/目录)和
OBJECT_STORE(小 Chunk,低延迟查看器)。LIVE - 多个物理 RRD 文件若共享同一,则构成一个逻辑录制文件;可用于分离基础数据、模型/URDF 数据和图层。
recording_id
当 RRD 要上传至 Rerun 目录或 Hub 时,请始终使用,除非另有明确要求。
OptimizationProfile.OBJECT_STOREChunk API vs logging API
Chunk API 与日志 API
- Logging (,
rr.log,rr.send_columns) is for live logging from user code; chunk processing is for ingestion, conversion, and postprocessing existing recordings.RecordingStream - Logging → chunks: write an RRD, read it back with .
RrdReaderlistsRrdReader(path)/recordings()(each ablueprints()withStoreEntry,kind,application_id);recording_idfor sequential passes,.stream(store=entry)for indexed access..store(store=entry) - Chunks → logging: accepts a
rerun.experimental.send_chunks(chunks, recording=...),Chunk,LazyChunkStream,LazyStore, or any iterable of chunks. The source store'sChunkStore/application_idare not preserved; the active recording's identity wins.recording_id
- 日志 API(、
rr.log、rr.send_columns)用于从用户代码进行实时日志记录;Chunk 处理用于数据摄入、转换和后处理现有录制文件。RecordingStream - 日志→Chunk:写入 RRD,再用读取。
RrdReader列出RrdReader(path)/recordings()(每个都是带有blueprints()、kind、application_id的recording_id);StoreEntry用于顺序遍历,.stream(store=entry)用于索引访问。.store(store=entry) - Chunk→日志:接受
rerun.experimental.send_chunks(chunks, recording=...)、Chunk、LazyChunkStream、LazyStore或任何 Chunk 可迭代对象。源存储的ChunkStore/application_id不会被保留;当前活动录制文件的标识优先。recording_id
Common gotchas
常见陷阱
- The default lens is
output_mode; forgetting to setdrop_unmatchedon a targeted fix silently drops the rest of the stream.forward_unmatched - Do not reuse a consumed ; reassign or
LazyChunkStreamdeliberately.split - Scope lenses with ; the same component name often exists under many entities.
content= - Preserve Arrow array type and length in transforms.
MutateLens - For catalog layers, the layer must equal the segment id.
recording_id - This is ; pin-check signatures when upgrading.
rerun.experimental
- Lens 默认输出模式为;若在针对性修复时忘记设置
drop_unmatched,会静默丢弃流的其余部分。forward_unmatched - 请勿复用已消耗的;请重新赋值或刻意使用
LazyChunkStream。split - 使用限定 Lens 的作用域;同一组件名称通常存在于多个实体下。
content= - 在转换中保持 Arrow 数组的类型和长度不变。
MutateLens - 对于目录图层,图层的必须等于段 ID。
recording_id - 本 API 属于;升级时请固定并检查签名。
rerun.experimental
References
参考资料
- End-to-end example (MCAP + URDF + JSON sidecar, lenses, merge, optimize):
https://github.com/rerun-io/rerun/tree/main/examples/python/robot_data_preprocessing - Docs: ,
https://rerun.io/docs/concepts/logging-and-ingestion/chunk-processing-apihttps://rerun.io/docs/concepts/query-and-transform/lenses
- 端到端示例(MCAP + URDF + JSON 辅助数据、Lens、合并、优化):
https://github.com/rerun-io/rerun/tree/main/examples/python/robot_data_preprocessing - 文档:,
https://rerun.io/docs/concepts/logging-and-ingestion/chunk-processing-apihttps://rerun.io/docs/concepts/query-and-transform/lenses