Rerun Graphs 图可视化实战:基于 Fjädra 力导向布局引擎绘制节点连线图与气泡图
2026/9/17 20:55:04 网站建设 项目流程

Rerun Graphs 图可视化实战:基于 Fjädra 力导向布局引擎绘制节点连线图与气泡图

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

导读

本文围绕 Rerun 仓库中的 graphs 示例 展开,讲解如何借助 Rerun 内置的力导向布局引擎Fjädra,仅通过GraphNodes/GraphEdges两类原语记录图数据,即可在 viewer 中自动完成节点排布与布局渲染。你将掌握 Rerun 力导向物理模拟的五大力(Center、Collision、Many-Body、Link、Position)的含义与调参方式,学会用rr.GraphNodesrr.GraphEdges记录树、格子、马尔可夫链等常见图结构,并通过 Blueprint 的GraphViewForceLinkForceManyBodyForceCollisionRadius等力参数配置不同形态的图可视化(节点连线图、气泡图)。

一、示例概览:Rerun 能画出哪些图

Rerun 的re_view_graph视图(对应 crate crates/views/re_view_graph)专门用于渲染节点连线图(node-link diagram)。graphs 示例演示了四种典型图结构:

  • 随机树(node_link / bubble_chart):以root为根,逐步随机挂载新节点的有向树,同时以节点连线图和气泡图两种形态展示;
  • 格子(lattice):10×10 的二维网格有向图,节点颜色按坐标渐变;
  • 马尔可夫链(markov_chain):三状态(sunny / rainy / cloudy)随机游走的时间序列动画,节点位置固定;
  • 描述面板(description):以 Markdown 文本展示示例说明。

示例的核心卖点在于:记录数据时完全不需要提供节点坐标(除马尔可夫链外),节点位置由 Rerun 内部的力导向布局引擎实时计算,用户既可以在选择面板(selection panel)里动态调整力参数,也可以通过 Blueprint 在代码中预设。

二、Rerun 的力导向布局引擎 Fjädra

Rerun 内置了一个与d3-force非常相似的物理模拟引擎,名为Fjädra(瑞典语"弹簧")。它被集成在re_view_graph视图中,负责把图数据(节点 + 边)转换为布局坐标。

从源码看,布局的核心实现在 crates/views/re_view_graph/src/layout/provider.rs 的ForceLayoutProvider中:

  • fjadra::Simulation承载所有节点与力的模拟状态;
  • 每个布局请求会把节点转换为fj::Node,把边转换为节点索引对(usize, usize),节点的半径取自其包围盒尺寸的一半(v.size.max_elem() as f64 / 2.0);
  • 每次视图刷新调用tick(1)推进模拟一步,直到is_finished()判定收敛;
  • 若所有节点都带固定位置(如马尔可夫链),则直接跳过模拟,仅按固定坐标摆放(见 provider.rs 中all_nodes_fixed()分支)。

力导向布局的优点在于灵活:同一条图数据,只需调整力的组合与参数,就能产出节点连线图、气泡图等差异巨大的可视化形态。

三、五大物理力详解

Fjädra 实现了五种与d3-force对应力。它们在 Rerun 中均有对应的 Blueprint archetype,定义于 crates/store/re_sdk_types/src/blueprint/archetypes,并在 provider.rs 的update_simulation中按开关逐一生效:

Blueprint 类型参数作用
居中力(Center)force_center.rsenabledstrength将整个图的质心拉向原点(注释原文:tries to move the center of mass of the graph to the origin)
碰撞半径力(Collision radius)force_collision_radius.rsenabledstrengthiterations依据节点半径解析节点间碰撞,避免节点互相重叠
多体力(Many-Body)force_many_body.rsenabledstrength在任意两节点间施加类似电荷的作用力,strength为正吸引、为负排斥
链接力(Link)force_link.rsenableddistanceiterations让存在边相连的节点趋向目标距离,行为类似弹簧
位置力(Position)force_position.rsenabledstrengthposition类似重力,把所有节点拉向指定位置

在 provider.rs 中可以看到每支力的具体装配方式:fj::Link::new(edges).distance(d).iterations(n)fj::ManyBody::new().strength(s)fj::Collide::new().radius(...).iterations(n).strength(s)fj::Center::new().strength(s),而 Position 力被拆分为fj::PositionXfj::PositionY两个方向分别施力。所有参数的默认值均来自ForceLayoutParams的查询回退逻辑(见 crates/views/re_view_graph/src/layout/params.rs),它通过ViewProperty::component_or_fallback从 Blueprint 中读取,未配置时自动使用各组件类型的默认值——这就是"不开蓝图也能自动出布局"的原因。

说明:这些 Force archetype 在源码注释中均标注为unstable(可能在不兼容的情况下变更),使用时建议跟随 Rerun SDK 版本固定行为。

四、运行 graphs 示例

在仓库根目录下安装并运行:

pip install -e examples/python/graphs python -m graphs

示例项目配置见 examples/python/graphs/pyproject.toml:依赖rerun-sdk,并注册了graphs命令入口(graphs = "graphs:main"),因此也可以直接用graphs命令启动。运行后 viewer 中会同时出现四个面板:Node-link diagram、Bubble chart、Lattice 与 Markov Chain(外加 Description 文本面板)。

五、核心代码:用 GraphNodes / GraphEdges 记录图数据

Rerun 记录图数据只依赖两个 archetype:

  • GraphNodes:节点列表,支持node_idspositions(可选坐标)、colorslabelsshow_labelsradii等字段,定义见 crates/store/re_sdk_types/src/archetypes/graph_nodes.rs;
  • GraphEdges:边列表,支持graph_type="directed"/"undirected"以及GraphType.Directed等枚举值。

5.1 记录格子图(静态)

rr.log( "lattice", rr.GraphNodes( nodes, colors=colors, # 按坐标渐变的颜色 labels=[f"({x}, {y})" for x, y in itertools.product(...)], ), static=True, # 静态数据,不随时间变化 ) rr.log("lattice", rr.GraphEdges(edges, graph_type="directed"), static=True)

要点:使用static=True表示该数据不随时间演化;节点 ID 使用字符串(如"0""1"),边的两端引用这些 ID。由于没有提供positions,节点坐标完全交由 Fjädra 计算。

5.2 记录随机树(随时间演化)

rr.set_time("frame", sequence=i) # 以 frame 为时间轴 rr.log( "node_link", # 节点连线图 rr.GraphNodes(nodes, labels=nodes, radii=radii, colors=colors), rr.GraphEdges(edges, graph_type=rr.GraphType.Directed), ) rr.log( "bubble_chart", # 气泡图:只记节点,不记边 rr.GraphNodes(nodes, labels=nodes, radii=radii, colors=colors), )

要点:同一批节点被记录到两个不同 entity 路径(node_linkbubble_chart),其中气泡图不记录边,仅靠节点的radii与碰撞力形成圆形打包(circle packing)效果;rr.set_time让每帧节点数递增,形成动态生长动画。完整实现见 examples/python/graphs/graphs.py 的log_trees()

5.3 记录马尔可夫链(固定坐标 + 状态动画)

rr.log( "markov_chain", rr.GraphNodes(state_names, labels=state_names, colors=colors, positions=positions), rr.GraphEdges(edges, graph_type="directed"), )

要点:positions为硬编码坐标[[0, 0], [150, 150], [300, 0]],因此无需任何力即可布局(viewer 检测到所有节点位置固定时直接跳过物理模拟)。每帧通过np.random.choice按转移矩阵采样下一个状态,并高亮当前状态节点,形成马尔可夫链动画(见log_markov_chain())。

六、用 Blueprint 预设布局:GraphView 与力参数

除了在运行时手动调节选择面板,还可以在代码中通过rr.send_blueprint精确配置每个图面板的布局方式。示例的log_blueprint()展示了四种典型配置:

rrb.GraphView( origin="node_link", name="Node-link diagram", force_link=ForceLink(distance=60), # 弹簧目标距离 60 force_many_body=ForceManyBody(strength=-60), # 节点间互相排斥 ), rrb.GraphView( origin="bubble_chart", name="Bubble chart", force_link=ForceLink(enabled=False), # 关闭链接力 force_many_body=ForceManyBody(enabled=False), # 关闭多体力 force_collision_radius=ForceCollisionRadius(enabled=True), # 仅靠碰撞力打包 defaults=[rr.GraphNodes.from_fields(show_labels=False)], # 全局关闭标签 ), rrb.GraphView( origin="lattice", name="Lattice", force_link=ForceLink(distance=60), force_many_body=ForceManyBody(strength=-60), defaults=[rr.GraphNodes.from_fields(show_labels=False, radii=10)], ), rrb.GraphView( origin="markov_chain", name="Markov Chain", # 节点坐标固定,无需任何力 ),

从这段代码可以提炼出可复用的调参思路:

  • 节点连线图:保留ForceLink(控制边长度)与ForceManyBody(控制节点疏密)。ForceLink(distance=60)使相邻节点趋向 60 的间距;ForceManyBody(strength=-60)让不相连节点互相排斥,防止过度拥挤;
  • 气泡图:关闭 Link 与 Many-Body 力,只保留ForceCollisionRadius。此时节点在碰撞力作用下自动挤压成紧密的圆形堆叠,节点的radii直接决定气泡大小——这就是"圆打包"布局的实现原理;
  • 固定布局图:节点自带positions时无需配置任何力;
  • defaults参数:可对 entity 内的节点统一附加属性(如show_labels=Falseradii=10),免去在数据端逐节点设置。

这些 Force archetype 的 Python 导入路径为rerun.blueprint.archetypes.force_*(对应 Rust 侧 crates/store/re_sdk_types/src/blueprint/archetypes),全部字段均为可选(0 required / 0 recommended / 若干 optional),意味着最小化配置只需设置关心的参数即可。

七、布局运行机制与注意事项

结合源码与示例,有几点值得注意:

  1. 边方向GraphEdgesgraph_type控制边是否带箭头;多条平行边在布局收敛后会以贝塞尔曲线扇出绘制(见 provider.rs 的SlotKind::Regular分支),自环边单独以顶部锚点的曲线渲染;
  2. 节点半径来源:布局中碰撞力使用的半径来自节点包围盒(size.max_elem() / 2),因此节点越大,碰撞力作用范围越大;
  3. 性能取舍iterations参数决定每步模拟内单支力的应用次数,源码注释明确"提高该参数可获得更好结果,但会延长计算时间"(见 force_link.rs);
  4. 数据体积控制:图数据量较大时,应使用static=True记录不变的部分(如格子图结构),随时间演化的部分(如树、马尔可夫链)再用rr.set_time逐帧记录,避免冗余存储。

八、延伸阅读

  • 示例源码与元数据:examples/python/graphs/graphs.py、examples/python/graphs/README.md、examples/python/graphs/pyproject.toml
  • 图视图实现:crates/views/re_view_graph/src/view.rs、布局模块 crates/views/re_view_graph/src/layout/mod.rs
  • 数据模型定义:crates/store/re_sdk_types/src/archetypes/graph_nodes.rs、crates/store/re_sdk_types/src/archetypes/graph_edges.rs
  • 力参数查询与默认值:crates/views/re_view_graph/src/layout/params.rs

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询