Rerun 数据清除(Clear/Tombstone)完全指南:清除语义、递归清除与最佳实践

发布时间:2026/9/16 19:52:09
Rerun 数据清除(Clear/Tombstone)完全指南:清除语义、递归清除与最佳实践 Rerun 数据清除Clear/Tombstone完全指南清除语义、递归清除与最佳实践【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读Rerun Viewer 在回放流式数据时会在当前时间点上为每个可见实体展示“最新值”但有些数据在录制全程中并非始终有效——目标丢失、检测失效、临时状态过期等场景都需要主动告知 Rerun“某个实体不再显示”。本篇指南以 docs/content/howto/logging-and-ingestion/clears.md 为核心结合仓库源码深入讲解 Rerun 的Clear归档类型tombstone 机制的语义、recursive参数、底层查询行为以及三种典型的清除与数据组织方案帮助你在目标跟踪、多频率数据对齐、短生命周期数据等场景中正确使用清除能力。为什么需要“清除数据”Rerun 的数据模型是流式的开发者不断向实体路径Entity Path写入数据而 Viewer 默认采用latest-at最新时刻查询语义——即在当前时间点上为每个可见实体展示其最近一次写入的组件值。这套机制天然适合构建连贯的时序视图只要某个实体在更早的时间点写入过数据它在之后的时间点上就会一直可见直到有更新的数据覆盖它。问题随之而来某些数据只在一段时间内有效之后既没有新值写入也不应该继续显示。例如目标跟踪中某个目标被判定为丢失旧的包围框不该继续显示每 10 帧才运行一次的检测结果在中间 9 帧里显示的仍是旧检测框某个临时传感器读数只在一秒内有效之后应消失。Rerun 为此提供了 tombstone墓碑机制通过向实体路径写入一个特殊的Clear归档类型明确标记“该实体在此时间点之后不再可见”。这也正是本指南标题中“Clears”的含义——它记录的是“清除动作发生的时间点”而不是“删除过去的数据”。方法一显式记录实体已被清除rr.Clear最直接的方式是向任意实体路径显式记录一个Clear。其语义是Clear被记录的时间点就是该实体在此之后不再在视图中显示的时间点。… for frame in sensors.read(): # Associate the following logs with frame frame.id rr.set_time(frame, sequenceframe.id) # Do the actual tracking update tracker.update(frame) if tracker.is_lost: # Clear everything on or below tracked/{tracker.id} # and that happened on or before frame frame.id rr.log(ftracked/{tracker.id}, rr.Clear(recursiveTrue)) else: # Log data to the main entity and a child entity rr.log(ftracked/{tracker.id}, rr.Rect2D(tracker.bounds)) rr.log(ftracked/{tracker.id}/cm, rr.Point2D(tracker.cm))这是典型的目标跟踪应用场景每一帧先rr.set_time(frame, sequenceframe.id)关联时间点再做跟踪更新一旦tracker.is_lost为真就向tracked/{tracker.id}写入rr.Clear(recursiveTrue)把该目标及其子树下的全部数据一次性清掉否则正常写入主实体包围框和子实体质心点。通过把Clear和普通日志放在同一个frame时间点上保证了清除动作与数据更新在时间轴上完全对齐。flat 与 recursive两种清除粒度Clear构造函数的唯一参数是recursivePython SDK 中为命名参数见 clear_ext.py。仓库同时提供了两个语义清晰的静态构造方法构造方式清除范围适用场景rr.Clear(recursiveFalse)/rr.Clear.flat()只清空该实体本身在该时间点的全部组件子实体不受影响单个实体失效但子树仍需保留rr.Clear(recursiveTrue)/rr.Clear.recursive()清空该实体本身以及其所有递归子实体的全部组件整个实体子树同时失效如目标丢失源码中的语义注释进一步说明了二者的行为差异flat()“This will empty all components of the associated entity at the logged timepoint. Children will be left untouched.”清空关联实体在该时间点的全部组件子实体保持不动recursive()“This will empty all components of the associated entity at the logged timepoint, as well as all components of all its recursive children.”同时清空所有递归子实体的全部组件Python 侧的实现在 clear_ext.pyRust 侧的等价实现位于 crates/store/re_types_core/src/archetypes/clear_ext.rs二者保持一致均委托给底层Clear::new(is_recursive)构造器crates/store/re_types_core/src/archetypes/clear.rs。底层原理一个只有 is_recursive 组件的归档类型从源码看Clear是整个 Rerun 中最“轻量”的归档类型之一它只包含一个必填组件ClearIsRecursive没有推荐组件、没有可选组件REQUIRED_COMPONENTS 1见 clear.rs。其类型定义来自crates/build/re_type_definitions/rerun/archetypes/clear.def.rs并被代码生成器同步产出 Python、Rust 等各语言绑定。用一句话概括它在数据层的行为也是 clear.py 与 clear.rs 中共同的 docstringClear 的存在意味着对该路径做 latest-at 查询时不会再返回在 Clear 之前写入该路径的任何组件而 Clear 之后写入的组件不受影响。这意味着两点关键推论过去的数据并未被物理删除如果做 range范围查询并覆盖 Clear 之前的时刻仍能取到那些组件。因此实践中当你在 Viewer 中使用“可见时间范围”visible time ranges时Clear 是无效的——它只作用于 latest-at 语义。标量曲线Scalar plots是例外时间序列视图会显式跟踪 Clear并用它来表示数据中的“空洞”即不连续的断线而不是简单地把曲线截断。从源码验证查询语义entity_db 测试与视图层仓库用两个层面的代码验证并消费上述语义数据层测试crates/store_app/re_entity_db/tests/clear.rs 是一个完整的 “clear pending clear” 测试套件636 行构造了parent、parent/child1、parent/deep/deep/down/child2、parent/child1/grandchild等多级实体路径在frame时间轴上写入点、颜色等组件后再写入Clear然后用latest_at查询验证清除之后该路径不再返回清除前写入的组件、清除后新写入的数据正常返回、递归清除会覆盖全部后代路径等行为。视图渲染层crates/viewer_support/re_view/src/clears.rs 中的collect_recursive_clears函数展示了实际消费逻辑它收集在给定时间范围内、对entity_path生效的每一个Clear的(time, row_id)。一个Clear生效的条件是——记录在该路径自身或记录在其任意祖先且is_recursive true同时还会用latest_at在可见窗口起始点做一次“引导查询”bootstrap把恰好落在可见窗口之前、但仍在生效期内的 Clear 也拾取进来。这些时间点最终被喂给需要渲染“间断”的视觉化器如折线图跨重置断开、状态时间线结束等这也解释了为什么标量曲线能表现出断线效果。方法二澄清数据含义——重新设计日志方式在某些情况下与其用Clear事后清除不如重新思考数据的组织方式让数据本身更准确地表达“它是什么”。文档给出了一个更新频率不匹配的典型反例… for frame in sensors.read(): # Associate the following logs with frame frame.id rr.set_time(frame, sequenceframe.id) # Log every image that comes in rr.log(input/image, rr.Image(frame.image)) if frame.id % 10 0: # Run detection every 10 frames detection detector.detect(frame) # Woops! These detections will not update at the # same frequency as the input data and thus look strange rr.log(input/detections, rr.Rect2D(detection.bounds))这里每帧都记录input/image但检测结果每 10 帧才更新一次导致input/detections的包围框长期“僵持”在旧值上观感上像是检测失效。虽然可以用rr.Clear修复但更本质的解法是把检测结果连同它实际使用的输入一起记录到另一个命名空间下让 Viewer 始终能看到“当前检测结果对应的输入帧是哪一帧”。修复示例class Detector: … def detect(self, frame): downscaled self.downscale(frame.image) # Log the downscaled image rr.log(detections/source, rr.Image(downscaled)) result self.model(downscaled) detection self.post_process(result) # Log the detections together with the downscaled image # Image and detections will update at the same frequency rr.log(downscaled/detections, rr.Rect2D(detection.bounds)) return detection … for frame in sensors.read(): # Associate the following logs with frame frame.id rr.set_time(frame, sequenceframe.id) # Log every image that comes in rr.log(input/image, rr.Image(frame.image)) if frame.id % 10 0: # Run detection every 10 frames # Logging of detections now happens inside the detector detected detector.detect(frame)这个方案在 Viewer 中会形成第二个视图downscaled/detections下的降采样图像与检测框以完全相同的频率一起更新二者永远保持时间一致杜绝了“检测框配错图”的歧义。核心思想是当两组数据天然存在依赖关系检测依赖输入时把它们放进同一时间节奏的实体路径下比事后清除更能表达真实语义。方法三时间跨段spans语义的现状与手动清除替代方案有些场景下你在记录数据时就已经知道这条数据的有效期例如一个只持续 1 秒的传感器读数。理想情况下你希望直接记录(from_timepoint, to_timepoint)或(timepoint, time-to-live)这样的时间跨段span让 Rerun 在有效期结束后自动隐藏数据。需要明确的是Rerun 目前尚不支持为日志数据关联 span 语义该能力在项目中是已知的未实现特性社区对此有持续的讨论与跟踪。因此在当前版本中唯一可靠的替代方案是——在数据失效的时间点上手动记录一个非递归的Clear# Associate the following data with start_time on the time timeline rr.set_time(time, durationstart_time) # Log the data as usual rr.log(short_lived, rr.Tensor(one_second_tensor)) # Associate the following clear with start_time 1.0 on the time timeline rr.set_time(time, durationstart_time 1.0) rr.log(short_lived, rr.Clear(recursiveFalse)) # or rr.Clear.flat() # Set the time back so other data isnt accidentally logged in the future. rr.set_time(time, durationstart_time)这段代码有三个值得注意的细节把 Clear 记录在start_time 1.0这个时间点即数据恰好失效的那一刻。由于 latest-at 语义任何晚于该时间点的查询都不会再看到short_lived实体上的旧张量。使用recursiveFalse或rr.Clear.flat()因为这里只想清除short_lived这一个实体而不应波及其子树。记录完 Clear 后把时间设回start_timerr.set_time设置的是“当前时间上下文”如果不清除它后续代码中未显式设置时间的日志都会被错误地关联到start_time 1.0导致数据被记录到“未来”。这是多时间点编程中极易踩坑、也极易被忽略的一行。实践要点与小结综合文档与源码把“清除数据”的正确姿势总结为一张决策清单场景推荐方案关键参数实体含子树永久失效如目标丢失rr.Clear(recursiveTrue)记录在失效时刻recursiveTrue单个实体失效、子树保留rr.Clear(recursiveFalse)或rr.Clear.flat()recursiveFalse数据频率不匹配导致观感异常重构日志命名空间让依赖数据同步更新无需 Clear已知有效期short-lived 数据在失效时刻手动记录非递归 Clear记录后把时间设回核心结论Clear是 tombstone 而非删除它只影响latest-at 查询过去的数据在 range 查询中仍然可读使用可见时间范围时 Clear 无效标量曲线除外表现为断线。递归清除通过is_recursive组件实现并在视图层由collect_recursive_clears收集生效的(time, row_id)用于渲染间断crates/viewer_support/re_view/src/clears.rs。数据层行为有完整测试背书crates/store_app/re_entity_db/tests/clear.rs可放心依赖该语义。在 span 语义落地前“在失效时间点手动 Clear 用后即恢复时间上下文”是处理短生命周期数据的标准工作流。若想深入验证建议阅读本指南的原始文档 docs/content/howto/logging-and-ingestion/clears.md、Python 侧构造器 clear_ext.py 与 Rust 侧实现 clear.rs并结合re_entity_db/tests/clear.rs中的测试用例逐条对照理解。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考