Rerun Radius 组件深度解析:场景单位(Scene Units)与 UI Points 双模式尺寸语义
2026/9/17 3:40:45 网站建设 项目流程

Rerun Radius 组件深度解析:场景单位(Scene Units)与 UI Points 双模式尺寸语义

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

Radius 是 Rerun 数据类型体系中负责表达"尺寸"的核心组件,用于描述点、线、箭头、包围盒、圆柱体、胶囊体、椭球体等图元的半径。它最独特的设计在于:同一个Float32标量,通过符号位同时编码"场景单位"与"UI Points"两套度量体系,让开发者既能绘制随视角缩放的真实尺寸物体,也能绘制恒定屏幕大小的标记。本文将以 radius.md 为主线,结合仓库内的类型定义、自动生成的 SDK 绑定与官方示例,完整讲解 Radius 的编码原理、边界值语义、跨语言 API 用法及其在 16 个 Archetype 中的实际应用。

Radius 组件的定位与定义

Radius 的官方定义只有一句话:"The radius of something, e.g. a point."(某物体的半径,例如一个点的半径)。它属于rerun.components.Radius,状态为stable(稳定),意味着其语义与序列化格式已固化,可放心在长期项目中依赖。

该组件的规范源头位于类型定义文件 radius.def.rs:

#[rerun::rerun_type] #[python(aliases = "float")] #[python(array_aliases = "float | npt.ArrayLike")] #[rust(derive(Copy, PartialEq, PartialOrd, bytemuck::Pod, bytemuck::Zeroable))] #[rust(repr = "transparent")] #[rerun(state = "stable")] pub struct Radius { pub value: rerun::encodings::Float32, }

这份.def.rs是 Rerun 代码生成管线的"单一事实来源"(由re_types_builder解析),Rust、Python、C++ 三端 SDK 的Radius类型都由它统一生成,从而保证三端语义完全一致。值得注意的是:

  • #[rust(repr = "transparent")]bytemuck::Pod / Zeroable意味着 Radius 在内存中与Float32布局完全一致,可零拷贝地与 Arrow 数组互转;
  • #[python(aliases = "float")]允许 Python 侧直接传普通float字面量;
  • #[python(array_aliases = "float | npt.ArrayLike")]允许传 NumPy 数组批量指定半径。

双单位编码:一个Float32表达两种度量体系

Radius 最重要的设计决策是用值的符号区分单位,这是理解该组件的关键:

内部存储中,正值表示场景单位(scene units),负值被解释为 UI Points

这意味着 Radius 根本不需要额外的判别字段(tag/discriminator),序列化时就是一个裸Float32,紧凑且高效。Rust 侧的手写扩展实现位于 radius_ext.rs,通过两个构造函数和两个访问器完成符号语义的封装:

/// Creates a new radius in scene units. /// Values passed must be finite positive. pub fn new_scene_units(radius_in_scene_units: f32) -> Self { debug_assert!(0.0 <= radius_in_scene_units, "Bad radius: {radius_in_scene_units}"); Self(Float32(radius_in_scene_units)) } /// Creates a new radius in ui points. /// Values passed must be finite positive. pub fn new_ui_points(radius_in_ui_points: f32) -> Self { debug_assert!(0.0 <= radius_in_ui_points, "Bad radius: {radius_in_ui_points}"); Self(Float32(-radius_in_ui_points)) } pub fn scene_units(&self) -> Option<f32> { self.0.is_sign_positive().then_some(*self.0) } pub fn ui_points(&self) -> Option<f32> { self.0.is_sign_negative().then_some(-*self.0) }

两种度量的物理含义截然不同,对应两种典型的使用诉求:

度量体系存储值行为特征典型用途
场景单位(Scene Units)正值与视图缩放联动,随相机拉近拉远而等比放大缩小,是真实世界尺寸机器人的点云、路标点、包围盒、圆柱半径等需要表达真实几何尺寸的对象
UI Points负值与视图缩放无关,恒定屏幕尺寸;但对应用 UI 缩放敏感交互标记、关键点、类别标签的锚点等需要始终清晰可见的 UI 元素

关于 UI Points 的缩放行为,官方文档明确给出了换算基准:

  • 100% UI 缩放时,1 UI Point = 1 像素
  • Viewer 的 UI 缩放默认跟随操作系统缩放,例如全高清(Full HD)屏幕通常为 100%,4K 屏幕通常为 200%。

因此同一个半径值,在 4K 高分屏上会被自动放大一倍(以像素计),从而在不同 DPI 的屏幕上保持一致的视觉尺寸——这正是"UI Points"相对裸像素的优势。

边界值与特殊值:符号语义的严谨落地

由于 Radius 用符号位承载语义,零值(0.0)与负零(-0.0)就成了必须明确区分的边界情况。实现中特意使用is_sign_positive()/is_sign_negative()而非>= 0/< 0比较,正是为了把 IEEE 754 中的-0.0正确归类为 UI Points(负零是有效的负号值)。这一细节由 radius_ext.rs 中的单元测试scene_point_distinction完整覆盖:

let radius = Radius(Float32(1.0)); // scene_units() == Some(1.0), ui_points() == None let radius = Radius(Float32(-1.0)); // scene_units() == None, ui_points() == Some(1.0) let radius = Radius(Float32(f32::INFINITY)); // scene_units() == Some(inf) let radius = Radius(Float32(f32::NEG_INFINITY)); // ui_points() == Some(inf) let radius = Radius(Float32(0.0)); // scene_units() == Some(0.0) let radius = Radius(Float32(-0.0)); // scene_units() == None, ui_points() == Some(0.0)

可以看到,正负无穷也被符号位正确分流。此外,radius_ext.rs还提供了两个常用常量和默认值:

pub const ZERO: Self = Self(Float32(0.0)); // 零半径(场景单位) pub const ONE_UI_POINTS: Self = Self(Float32(-1.0)); // 1 个 UI Point 的半径 impl Default for Radius { fn default() -> Self { Self::new_ui_points(1.5) } }

默认半径为 1.5 个 UI Points:当用户未显式指定半径时,图元会以约 1.5 像素(100% 缩放下)的屏幕尺寸绘制,保证"开了点但没给半径"也能立即看见。两个构造函数内部都用debug_assert!校验入参必须为非负,防止把负值误解为"反向半径"造成歧义。

数据编码与 Arrow 表示

Radius 的序列化链路非常直接:

  • Rerun 编码(encoding)Float32
  • Arrow 数据类型(datatype)
Float32

即在数据帧(Dataframe)与磁盘存储中,一个 Radius 就是 4 字节单精度浮点数,无任何额外开销。自动生成的 Rust 绑定见 radius.rs,其核心结构为:

#[repr(transparent)] pub struct Radius(pub crate::encodings::Float32); impl ::re_types_core::WrapperComponent for Radius { type Encoding = crate::encodings::Float32; fn name() -> ComponentType { "rerun.components.Radius".into() } }

它实现了Deref / DerefMutFrom<T: Into<Float32>>Copy / PartialEq / PartialOrd等 trait,因此在实际使用中 Radius 与普通f32几乎可以互换:任何能转成Float32的类型都能直接Into<Radius>,配合with_radii这类批量写入接口使用非常顺滑。

跨语言 API 使用指南

Radius 在 Rust、Python、C++ 三端均有对称的 API,下面是三端对照表(以官方示例 points3d_ui_radius 为参照):

语义RustPythonC++
场景单位Radius::new_scene_units(0.3)或直接传0.3f32rr.Radius.scene_units(0.3)或直接传0.3rerun::Radius::scene_units(0.3f)
UI PointsRadius::new_ui_points(40.0)rr.Radius.ui_points(40.0)rerun::Radius::ui_points(40.0f)
常量(1 UI Point)Radius::ONE_UI_POINTS

注:Python 与 C++ 的构造器名为ui_points/scene_units(见 geo_points_simple.py、line_strips3d_ui_radius.rs 等示例),Rust 端则为new_ui_points/new_scene_units,命名略有差异但语义一致。

Rust 示例:同帧混合两种单位

fn main() -> Result<(), Box<dyn std::error::Error>> { let rec = rerun::RecordingStreamBuilder::new("rerun_example_points3d_ui_radius").spawn()?; // 两个蓝色点:场景单位半径 0.1 与 0.3,随视角缩放 rec.log( "scene_units", &rerun::Points3D::new([(0.0, 1.0, 0.0), (1.0, 1.0, 1.0)]) .with_radii([0.1, 0.3]) // 默认即场景单位 .with_colors([rerun::Color::from_rgb(0, 0, 255)]), )?; // 两个红色点:UI Points 半径 40 与 60,恒定屏幕尺寸 rec.log( "ui_points", &rerun::Points3D::new([(0.0, 0.0, 0.0), (1.0, 0.0, 1.0)]) .with_radii([ rerun::Radius::new_ui_points(40.0), rerun::Radius::new_ui_points(60.0), ]) .with_colors([rerun::Color::from_rgb(255, 0, 0)]), )?; Ok(()) }

Python 示例:完全等价的写法

import rerun as rr rr.init("rerun_example_points3d_ui_radius", spawn=True) # 场景单位:默认行为 rr.log( "scene_units", rr.Points3D([[0, 1, 0], [1, 1, 1]], radii=[0.1, 0.3], colors=[0, 0, 255]), ) # UI Points:通过 rr.Radius.ui_points 显式声明 rr.log( "ui_points", rr.Points3D( [[0, 0, 0], [1, 0, 1]], radii=rr.Radius.ui_points([40.0, 60.0]), # 批量传入数组 colors=[255, 0, 0], ), )

Python 侧由于array_aliases的存在,rr.Radius.ui_points(...)既能接收单个float也能接收npt.ArrayLike数组,与radii=[...]逐点对齐。

底层写入机制

在 Archetype 层面,radii字段通过with_radii统一写入。以 Points3D 为例,其实现位于 points3d.rs:

/// Optional radii for the points, effectively turning them into circles. pub fn with_radii( mut self, radii: impl IntoIterator<Item = impl Into<crate::components::Radius>>, ) -> Self { self.radii = try_serialize_field(Self::descriptor_radii(), radii); self }

注意impl Into<Radius>的泛型约束:传入的每个元素都会被自动转换为Radius,因此直接传f32字面量时默认按场景单位解释,只有显式调用new_ui_points/ui_points才会编码为 UI Points。在 Points3D 的字段体系中,radii属于Recommended(推荐)字段,与colors同级——这意味着绘图时给出半径是常见的最佳实践,但并非强制。

使用 Radius 的 Archetype 全景

根据组件参考文档,Radius被 16 个 Archetype 引用,覆盖了 Rerun 几乎所有的几何图元。按用途可归纳为五类:

1. 点与轨迹类

  • Points2D、Points3D:半径把点变成可见圆斑;
  • LineStrips2D、LineStrips3D:折线的线宽;
  • GraphNodes:图结构中的节点大小。

2. 箭头与刚体类

  • Arrows2D、Arrows3D:箭头杆身的粗细;
  • Capsules3D、Cylinders3D:胶囊体与圆柱的截面半径。

3. 包围盒与曲面类

  • Boxes2D、Boxes3D:圆角包围盒的圆角半径;
  • Ellipses2D、Ellipsoids3D:椭圆/椭球体的轴半径。

4. 地理空间类

  • GeoPoints、GeoLineStrings:地图上的点位与路径宽度(官方示例中即用Radius::ui_points指定恒定屏幕尺寸,见 geo_points_simple.py)。

5. 相机模型类

  • Pinhole:针孔相机模型相关的半径描述。

实践建议与易错点

综合官方文档与源码实现,使用 Radius 时有几个关键经验值得记录:

  1. 单位混用是最大陷阱with_radii([0.1, 0.3])直接传字面量 = 场景单位;要固定屏幕尺寸必须显式用ui_points系构造器。若在一个批次中混入两种语义,务必逐元素确认符号。
  2. 零半径与负零0.0是"场景单位下的零",-0.0则是"0 个 UI Points"。Rust 实现用is_sign_negative()精确区分二者,序列化往返不会丢失该语义。
  3. 默认值兜底:未指定半径时使用new_ui_points(1.5)(1.5 个 UI Points),保证图元始终可见,因此"忘设半径"不会导致画面空无一物。
  4. UI 缩放联动:UI Points 不随视图缩放,但会随应用 UI 缩放等比变化(100% 缩放时 1 UI Point = 1 像素)。若你的场景需要"无论 1080p 还是 4K 屏幕都保持相同屏幕尺寸",UI Points 是正确的选择;反之,需要"真实世界尺寸感"(如机器人导航中的障碍物半径),请使用场景单位。

通过本文对 radius.md、radius.def.rs、radius_ext.rs 及官方示例的交叉解读,可以看出 Radius 是 Rerun 中"小类型、深设计"的典型代表:一个 4 字节浮点数,以符号位承载双单位语义,再经由代码生成管线在 Rust/Python/C++ 三端提供一致的构造器、常量与默认值,最终支撑起从点云到地图、从包围盒到相机模型的整套可视化体系。

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

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

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

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

立即咨询