FEATURED · 精选文章

Rerun 静态数据(Static Data)完全指南:跨时间线持久数据与内存优化实战

发布时间 / 2026/9/16 20:31:42
来源 / 创域科博编辑部
栏目 / 资讯中心
Rerun 静态数据(Static Data)完全指南:跨时间线持久数据与内存优化实战 Rerun 静态数据Static Data完全指南跨时间线持久数据与内存优化实战【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读在 Rerun 中默认记录的数据都挂靠在某个时间线timeline上随时间线索引的推进不断累积。而本指南所讲解的**静态数据Static Data**是一种特殊的存储方式它不属于任何时间线对所有时间线包括尚未创建的一律生效并且会遮蔽同一实体路径上任何时间性的同类型数据。读完本文你将掌握如何在 Rerun SDK 中写入静态数据log(..., staticTrue)与send_columns两种方式理解静态数据与时间性数据在语义、存储、垃圾回收上的本质差异并学会用静态数据做场景布置scene setting和显著降低内存占用同时了解.rrd录制文件与 Viewer 数据存储两种上下文下的真实物理存储行为。静态数据是什么不挂任何时间线的数据块Rerun 的底层数据模型是列式块chunks of columns。从源码结构看一个 Chunk 包含零个或多个时间列索引以及零个或多个组件列数据相关字段定义在 crates/store/re_chunk/src/chunk.rstimelines时间列每个时间列与row_ids等长为空即表示这是一个静态块源码注释明确写着 Empty if this is a static chunkcomponents组件列稀疏的ListArray每个组件对应实体路径上的一个描述符row_ids每一行的唯一行 ID静态数据查询依赖它。据此可以给出静态数据的精确定义静态数据就是存放在时间列集合为空的数据块chunk中的数据。因为数据块里没有任何时间索引静态数据天然不属于任何时间线——它属于所有时间线包括那些在写入时尚未创建的时间线。这也意味着只要向某个实体路径写入了静态数据该实体路径上同一组件的所有过去、现在与未来的时间性数据都会在语义上被丢弃shadowing转而以静态数据为准语义上丢弃不必然等于物理上删除具体见后文理解存储成本一节。从查询引擎的角度看静态数据也是被专门对待的re_chunk_store中的查询接口提供了针对静态数据的专用入口见 crates/store/re_chunk/src/earliest_at.rs 中的EarliestAtQuery::new_static()其注释说明 to query static data, use new_static时间查询与静态查询被明确区分。如何写入静态数据方式一log家族的static标志推荐最直观的方式是使用log家族方法在合适的地方传入static标志。以 Python 为例rr.log(skybox, generate_skybox_mesh(), staticTrue)完整代码见 docs/snippets/all/concepts/static/log_static.pyRust 与 C 的对应写法略有差异但语义完全一致// Rustlog_static 是专门的便捷方法 rec.log_static(skybox, generate_skybox_mesh())?;// C rec.log_static(skybox, generate_skybox_mesh());分别见 docs/snippets/all/concepts/static/log_static.rs 与 docs/snippets/all/concepts/static/log_static.cpp注意一个容易混淆的细节log_static这个名字可能让人以为它只记录当前时刻的值但实际上它记录的是不受任何时间线约束的值——没有时间列因此也没有时刻可言。方式二send_columns传空的时间列集合既然静态数据的本质是时间列集合为空那么用send_columnsAPI 时只要把时间列集合留空即可达到同样效果rr.send_columns(skybox, indexes[], columnsgenerate_skybox_mesh())// Rust rec.send_columns(skybox, std::iter::empty(), generate_skybox_mesh())?;// C rec.send_columns(skybox, {}, generate_skybox_mesh());分别见 docs/snippets/all/concepts/static/send_static.py、docs/snippets/all/concepts/static/send_static.rs、docs/snippets/all/concepts/static/send_static.cpp正如原文档所指出的这种用法在实际项目中很少用到它更多是数据模型在逻辑上的自然延伸——既然没有时间列就是静态数据那么send_columns当然也能产出一块静态数据。反过来理解也成立send_columns是比log更底层的 APIlog(..., staticTrue)在底层正是被转换为一个不含时间列的块。什么时候该用静态数据静态数据的适用场景可以归纳为两大类场景布置scene setting与内存节省memory savings。场景布置与数据采集无关的布景数据很多时候某些数据不属于正常的数据采集过程而是用来设定场景、决定数据该如何被展示。例如记录街道上行驶的汽车时你可能希望始终显示街道网格street mesh作为场景的一部分——这类数据就非常适合做成静态数据坐标系coordinate systems通常以静态方式存储标注上下文annotation context也典型地以静态方式存储。为什么不把这些布景数据放到每条时间线的开头因为时间线集合在运行时可能根本是未知的你无法预知会有哪些时间线例如frame、log_time、自定义时间线……也就无法保证在每条时间线开始处都完整地补上场景数据。静态数据的无时间线特性恰好绕开了这个难题写一次对所有时间线生效。内存节省静态数据可被覆盖时间性数据只会累积这是静态数据最具实际价值的一面的核心差异。时间性数据在 Rerun 中是只追加的不存在覆盖overwrite的概念数据集只会增长、不会收缩。为了应对无限增长Rerun Viewer 内置了垃圾回收机制在内存紧张时从存储中丢弃最旧的数据。看一个典型例子下面的代码把 10 张图像都记录在frame时间线的索引4上rr.set_time(frame, sequence4) for _ in range(10): rr.log(camera/image, camera.save_current_frame())见 docs/snippets/all/concepts/static/log_temporal_10x.py这 10 张图像全部会被真实存储并且都能在 Viewer 中被独立查看——即便它们共享同一个时间索引。而静态数据则截然不同静态数据永远不会被垃圾回收……但它可以被覆盖从语义上讲在任意时刻一个实体上的某个组件最多只能存在一份静态数据。下面的例子循环记录 10 次静态图像for _ in range(10): rr.log(camera/image, camera.save_current_frame(), staticTrue)见 docs/snippets/all/concepts/static/log_static_10x.py此时 Viewer 中只能看到最后一次按执行顺序log调用写入的数据——前面 9 份在语义上已被覆盖。实践中Rerun 数据存储会依赖这套语义在物理层面丢弃多余的静态数据从而大幅降低内存开销细节见下一节。换句话说如果某类数据你不需要保留历史值用静态数据记录它就等于在恒定内存下无限期地记录其新值——这正是只关心最新状态类应用状态位、位姿、环境网格等的理想存储方式。理解存储成本语义覆盖 ≠ 物理删除前面提到语义上只存了一份图像那么这份语义如何落地为物理存储取决于上下文。这里必须区分两种截然不同的环境**录制文件recordings**与Viewer 数据存储datastore。在录制文件.rrd中无语义的字节流物理上全部保留Rerun 录制文件.rrd本质上只是一串二进制消息流文件本身没有任何语义它不知道静态是什么意思也无法自行处理覆盖逻辑。因此如果直接把上面的静态 10 连拍示例写进文件例如rr.save()打开.rrd你会发现里面确实存了全部 10 张图像——语义覆盖没有在文件层生效。如果你希望录制文件本身只包含一份静态值有两种途径先把数据流式发送到 Viewer再从 Viewer 中保存录制使用Menu Save recording菜单或等价的 palette 命令。由于 Viewer 的数据存储原生理解覆盖语义保存出来的文件自然只含最终一份静态数据用 Rerun CLI 手动重新压实recompact录制文件让数据覆盖语义在重写过程中被正确应用rerun rrd optimize -o compacted.rrd myrecording.rrdrerun rrd optimize是 Rerun CLI 中与录制文件处理相关的子命令之一其他如rerun rrd merge、rerun rrd migrate同理凡是重写 RRD 的 CLI 工具都会按当前 Sorbet 版本重新产出数据块相关说明见 rrd-format 文档它会重写数据块并施加存储层的语义——包括静态数据的覆盖。在 Viewer 中数据存储原生理解静态语义内存恒定支撑 Viewer 的数据存储层原生理解这两套语义时间性数据 可垃圾回收/追加静态数据 可覆盖/不回收。这一点在实现层面有迹可循re_chunk_store的垃圾回收/时间范围删除逻辑会明确跳过静态块——例如 crates/store/re_chunk_store/src/drop_time_range.rs 中注释 Static chunks are unaffected而静态块被新值覆盖时会产生专门的存储事件见 crates/store/re_chunk_store/src/events.rs 中 A static chunk was overwritten by a newer value 相关的is_static判断。因此如果直接通过rr.connect_grpc()把上面的静态 10 连拍实时发送给 Viewer你会观察到Viewer 的内存占用保持恒定——新数据到达时旧静态数据被自动覆盖物理丢弃。这正是用静态数据在恒定内存下无限期记录最新值的实现保障。原文档还展示了一个非常直观的实战案例项目中的 face tracking 示例 通过把所有数据都记录为静态数据实现了无限时长的面部跟踪同时内存占用维持恒定该示例位于examples/python/face_tracking/其中即包含face_tracking.py主程序。小结与选型建议对比维度时间性数据Temporal静态数据Static所属时间线挂靠特定时间线与索引不属于任何时间线对所有时间线生效写入方式rr.log(...)/rr.send_columns(indexes[...])rr.log(..., staticTrue)/send_columns(indexes[])同实体同组件多份数据全部保留可独立查看语义上仅保留最新一份可被覆盖垃圾回收受 Viewer GC 机制管理内存紧张时丢弃最旧数据永不被 GC 回收但可被新静态值覆盖物理存储Viewer随数据量增长需要 GC自动物理丢弃多余静态数据内存恒定物理存储.rrd 文件全部写入全部写入需rerun rrd optimize或从 Viewer 保存才能压实使用建议场景布置数据街道网格、坐标系、标注上下文、背景/环境→ 一律用静态数据避免每条时间线开头都要补数据的困境见 transforms.md 与 annotation-context.md只关心最新状态的数据位姿、当前帧图像、状态标志→ 用静态数据实现恒定内存下的无限记录需要历史轨迹/回放的数据→ 必须用时间性数据不要试图用静态数据保存历史落地到文件时若希望.rrd文件同样只含最终静态值记得使用rerun rrd optimize或从 Viewer 内保存而不是直接rr.save()。延伸阅读时间线Timelines概念理解时间线是理解静态数据无时间线特性的前提坐标系Transforms 与 标注上下文Annotation context典型的静态数据应用场景限制 Viewer 内存占用时间性数据的垃圾回收机制说明Rerun CLI 参考rerun rrd optimize等录制文件处理命令RRD 文件格式录制文件为何无语义的底层原因源码深入chunk.rs静态块的字段定义、earliest_at.rs静态查询、drop_time_range.rsGC 跳过静态块、events.rs静态覆盖事件。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻