Rerun Quaternion 编码类型全解析:四元数在 3D 旋转数据中的表示、序列化与实战用法
Rerun Quaternion 编码类型全解析四元数在 3D 旋转数据中的表示、序列化与实战用法【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun本文围绕 Rerun 数据模型中的Quaternion编码类型展开说明它如何在 Rerun 中表示 3D 旋转、其 Arrow 内存布局FixedSizeList(4 x non-null Float32)、跨 Python / Rust / C 三种 SDK 的构造与使用方式并结合RotationQuat组件、Rotation3D辅助类型与Transform3D、Boxes3D等 Archetype 的源码实现给出可直接运行的实战示例。Quaternion是 Rerun 数据模型中的一种核心编码类型Encoding用于以 4 个浮点数紧凑地表达 3D 旋转。它本身不是可以直接记录到数据存储中的组件而是作为RotationQuat组件的底层表示存在广泛服务于Transform3D、Boxes3D、Capsules3D、Ellipsoids3D、GaussianSplats3D、InstancePoses3D、Volume3D、VoxelGridMap等 Archetype 的旋转表达。本文将以 Rerun 仓库crates、rerun_py、rerun_cpp 等目录中的类型定义与序列化实现为依据完整解析该类型的设计与用法。类型定义一个由 4 个实数表示的四元数Rerun 对Quaternion的权威定义位于类型定义文件它是一份被re_types_builder解析、用于生成 Rust / Python / C 三种语言绑定的类型说明书/// A Quaternion represented by 4 real numbers. /// /// Note: although the x,y,z,w components of the quaternion will be passed through to the /// datastore as provided, when used in the Viewer Quaternions will always be normalized. #[rerun::rerun_type] #[arrow(transparent)] #[cpp(no_field_ctors)] #[python(array_aliases npt.NDArray[Any] | npt.ArrayLike | Sequence[Sequence[float]])] #[rust(derive(Copy, PartialEq, PartialOrd, bytemuck::Pod, bytemuck::Zeroable))] #[rust(repr C)] #[rust(tuple_struct)] #[rerun(state stable)] pub struct Quaternion { pub xyzw: [f32; 4], }这段定义透露了几个关键信息字段顺序约定为 XYZW四元数的四个分量按x, y, z, w顺序存储其中w是标量实部。#[arrow(transparent)]该类型在 Arrow 序列化时采用透明表示——即直接暴露其内部字段[f32; 4]没有额外的嵌套包装层。状态为stable该类型处于稳定状态属于公开的稳定 API 面。派生Copy、PartialOrd、bytemuck::Pod/Zeroable等 trait说明它在 Rust 中是一个可零拷贝、可字节级转换的轻量 POD 结构。由该定义自动生成的 Rust 结构体位于 crates/store/re_sdk_types/src/encodings/quaternion.rs#[repr(C)] pub struct Quaternion(pub [f32; 4usize]);可以看到生成代码以repr(C)的元组结构体形式保存一个长度为 4 的f32数组正是4 个实数这一语义的最直接体现。归一化语义原样入库Viewer 中归一化原文档中特别强调了一条重要行为约定这也是使用四元数时最容易踩的坑尽管x,y,z,w四个分量会原样写入数据存储datastore但在 Viewer 中使用时四元数总是会被归一化。这意味着存储层不强制归一化你记录什么值数据存储中就保存什么值不做任何预处理或校验。可视化层归一化Viewer 在渲染、计算变换时会先把四元数归一化到单位长度再使用因此未归一化的输入在可视化时会被纠正。归一化失败的后果如果四元数的模长为 0例如[0, 0, 0, 0]无法归一化此时该旋转会被视为无效变换。这一点在RotationQuat组件的文档docs/content/reference/types/components/rotation_quat.md中有着一致的表述If normalization fails the rotation is treated as an invalid transform.从源码看Viewer 侧确实通过归一化路径消费四元数。例如在 crates/store/re_sdk_types/src/encodings/quaternion_ext.rs 中Quaternion向glam::Quat的转换就显式调用了try_normalize()归一化失败如零向量时返回错误并拒绝构造#[cfg(feature glam)] impl TryFromQuaternion for glam::Quat { type Error (); fn try_from(q: Quaternion) - ResultSelf, () { glam::Vec4::from(q.0) .try_normalize() .map(Self::from_vec4) .ok_or(()) } }这从实现层面印证了文档中Viewer 中总会归一化、归一化失败视为无效的约定。Arrow 数据表示FixedSizeList(4 x non-null Float32)原文档给出了该编码类型的 Arrow 数据类型FixedSizeList(4 x non-null Float32)其序列化实现可在生成代码 crates/store/re_sdk_types/src/encodings/quaternion.rs 中看到impl ::re_types_core::ArrowDataType for Quaternion { fn arrow_data_type() - arrow::datatypes::DataType { use arrow::datatypes::*; DataType::FixedSizeList( std::sync::Arc::new(Field::new(item, DataType::Float32, false)), 4, ) } }要点解读外层是一个定长列表FixedSizeList长度为 4元素类型为Float32元素字段不可为 nullfalse。定长列表的优点是内存紧凑4 个f32连续排布无需额外的 offsets 数组适合表示在数量上永远恰好是 4 个的向量/四元数。反序列化时代码会先校验value_length() 4不匹配则抛出datatype_mismatch错误见 quaternion.rs随后将内部 Float32 缓冲区直接按bytemuck::try_cast_slice零拷贝转换为[f32; 4]切片。Python 侧同样采用 pyarrow 的FixedSizeListArray生成该表示。在 rerun_py/rerun_sdk/rerun/encodings/quaternion_ext.py 中staticmethod def native_to_pa_array_override(data: QuaternionArrayLike, data_type: pa.DataType) - pa.Array: quaternions flat_np_float32_array_from_array_like(data, 4) return pa.FixedSizeListArray.from_arrays(quaternions, typedata_type)输入先被规整为形状为(N, 4)的float32ndarray再封装成定长列表数组——这也解释了 Python 类型别名中npt.NDArray[Any] | npt.ArrayLike | Sequence[Sequence[float]]的由来批量的四元数本质就是一个(N, 4)的数组。核心常量与构造方法XYZW 与 WXYZ 的约定原文档正文只说明了4 个实数与归一化语义而实际使用中还需掌握构造与分量顺序约定。Rerun 在手写扩展层 crates/store/re_sdk_types/src/encodings/quaternion_ext.rs 中提供了以下内容成员含义值 / 说明IDENTITY单位四元数无旋转[0.0, 0.0, 0.0, 1.0]INVALID无效变换四元数[0.0, 0.0, 0.0, 0.0]from_xyzw([f32; 4])按 x,y,z,w 顺序构造内部存储顺序即输入顺序from_wxyz([f32; 4])按 w,x,y,z 顺序构造自动重排为[x, y, z, w]xyzw() - [f32; 4]读取四个分量返回 x,y,z,w 顺序Default实现直接返回IDENTITY即默认四元数 无旋转impl Default for Quaternion { fn default() - Self { Self::IDENTITY } }顺序约定提醒数学社区与许多数学库如 Eigen、部分 ROS 资料习惯使用w, x, y, zWXYZ顺序而 Rerun 内部统一使用x, y, z, wXYZW顺序。为此 Rerun 在 Rust 与 C 中都提供了from_wxyz便捷构造避免手工重排出错。C 端实现见 rerun_cpp/src/rerun/encodings/quaternion.hpp同时提供了x()/y()/z()/w()分量访问器与从float*指针构造的版本。三种语言 SDK 的实战用法Pythonrr.QuaternionPython 侧的扩展实现在 rerun_py/rerun_sdk/rerun/encodings/quaternion_ext.py提供关键字构造、identity()与invalid()工厂方法import rerun as rr rr.init(rerun_example_quaternion, spawnTrue) # 关键字构造按 XYZW 顺序 q rr.Quaternion(xyzw[0.0, 0.0, 0.382683, 0.923880]) # 绕 Z 轴 45° # 单位四元数 identity rr.Quaternion.identity() # 无效四元数归一化失败时表示无效变换 invalid rr.Quaternion.invalid()Rustrerun::QuaternionRust 侧直接使用生成的rerun::Quaternion配合常量与构造方法。完整可运行示例见 docs/snippets/all/archetypes/boxes3d_batch.rslet rec rerun::RecordingStreamBuilder::new(rerun_example_box3d_batch).spawn()?; rec.log( batch, rerun::Boxes3D::from_centers_and_half_sizes( [(2.0, 0.0, 0.0), (-2.0, 0.0, 0.0), (0.0, 0.0, 2.0)], [(2.0, 2.0, 1.0), (1.0, 1.0, 0.5), (2.0, 0.5, 1.0)], ) .with_quaternions([ rerun::Quaternion::IDENTITY, rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]), // 45 degrees around Z ]), )?;注意 Rust 批量 APIwith_quaternions接收一个四元数数组其长度需要与centers/half_sizes的数量对齐。Crerun::encodings::QuaternionC 头文件 rerun_cpp/src/rerun/encodings/quaternion.hpp 提供同名结构体与丰富构造器#include rerun.hpp auto q rerun::encodings::Quaternion::from_xyzw(0.0f, 0.0f, 0.382683f, 0.923880f); auto q2 rerun::encodings::Quaternion::from_wxyz(0.923880f, 0.0f, 0.0f, 0.382683f); // 等价 auto identity rerun::encodings::Quaternion::IDENTITY;from_wxyz的重载同时支持四个标量、std::arrayfloat, 4与const float*指针三种入参形式便于与既有数学库互操作。四元数如何进入 Rerun 数据模型从 Encoding 到 Component 到 Archetype原文档末尾给出了Quaternion的唯一直接消费者RotationQuat组件docs/content/reference/types/components/rotation_quat.md。其完整的引用链如下1. Encoding → ComponentRust 生成代码中RotationQuat是一个透明的包装组件内部持有encodings::Quaternion#[repr(transparent)] pub struct RotationQuat(pub crate::encodings::Quaternion); impl ::re_types_core::WrapperComponent for RotationQuat { type Encoding crate::encodings::Quaternion; fn name() - ComponentType { rerun.components.RotationQuat.into() } fn into_inner(self) - Self::Encoding { self.0 } }它实现了Deref/DerefMut到Quaternion因此组件可直接复用编码类型的所有方法与常量。2. Component → Rotation3D 辅助类型在 crates/store/re_sdk_types/src/rotation3d.rs 中定义了一个非组件的辅助枚举Rotation3D用于填充Transform3Dpub enum Rotation3D { Quaternion(components::RotationQuat), // 四元数表达 AxisAngle(components::RotationAxisAngle), // 轴角表达 }它提供从components::RotationQuat、encodings::Quaternion以及启用glamfeature 时glam::Quat的From转换Rotation3D::IDENTITY也定义为以四元数形式表示的单位旋转。3. Rotation3D → Transform3D Archetypetransform3d_ext.rs 中的with_rotation方法接收任意实现了IntoRotation3D的类型四元数与轴角可以无缝混用pub fn with_rotation(self, rotation: impl IntoRotation3D) - Self { match rotation.into() { Rotation3D::Quaternion(quaternion) self.with_quaternion(quaternion), Rotation3D::AxisAngle(rotation_axis_angle) self.with_rotation_axis_angle(rotation_axis_angle), } }于是在 Python 中可以用rr.Transform3D(rotationrr.Quaternion(xyzw[...]))或rr.Transform3D(rotationrr.RotationAxisAngle(...))表达同一种旋转——四元数只是 Rerun 支持的两种旋转编码之一。4. 更广的消费面根据RotationQuat组件的文档使用四元数旋转的 Archetype 还包括Boxes3D、Capsules3D、Cylinders3D、Ellipsoids3D、GaussianSplats3D、GridMap、InstancePoses3D、Volume3D、VoxelGridMap等。以 Python 侧的批量包围盒示例 docs/snippets/all/archetypes/boxes3d_batch.py 为例rr.log( batch, rr.Boxes3D( centers[[2, 0, 0], [-2, 0, 0], [0, 0, 2]], half_sizes[[2.0, 2.0, 1.0], [1.0, 1.0, 0.5], [2.0, 0.5, 1.0]], quaternions[ rr.Quaternion.identity(), rr.Quaternion(xyzw[0.0, 0.0, 0.382683, 0.923880]), # 45 degrees around Z ], ... ), )可见一个未旋转的盒子与一个绕 Z 轴旋转 45° 的盒子可以同时记录在同一个 Archetype 实例中。与外部数学库的互操作Quaternion在设计上充分考虑了与常见 Rust 数学库的双向转换见 quaternion_ext.rsglam启用glamfeature 时Quaternion → glam::Quat使用TryFrom归一化失败返回错误glam::Quat → Quaternion使用From直接取to_array()的 XYZW 顺序。mint启用mintfeature 时与mint::Quaternionf32双向From转换。mint 是 Rust 生态中用于跨数学库互操作的标准类型约定这意味着用户完全可以在自己的代码中使用任意数学库计算四元数再在记录前转换为rerun::Quaternion。此外仓库的re_sdk_types测试crates/store/re_sdk_types/tests/types/mint_conversions.rs也覆盖了包括四元数在内的 mint 转换路径可作进一步参考。实践要点与注意事项综合文档与源码使用Quaternion时建议注意以下几点分量顺序统一为 XYZWRerun 内部约定是x, y, z, w若你的数据源是 WXYZ 顺序如部分数学库请使用from_wxyz/from_wxyz构造器或自行重排避免静默错位。记录时无需归一化存储层会原样保存你提供的 4 个浮点数Viewer 负责归一化但为了一致性与可读性建议记录前自行归一化。零模长四元数 无效变换[0, 0, 0, 0]即Quaternion::invalid()无法归一化会被 Viewer 视为无效旋转。这也是IDENTITY[0,0,0,1]与INVALID两个常量需要区分的原因。内存表示紧凑FixedSizeList(4 x non-null Float32)意味着每个四元数固定占用 16 字节4 × f32批量记录时适合按(N, 4)的数组一次性传入Python 侧会通过flat_np_float32_array_from_array_like自动规整。二选一的旋转表达Transform3D中四元数与轴角RotationAxisAngle通过Rotation3D枚举统一收口同一变换只能选择其中一种表达二者共用with_rotation接口。Quaternion编码类型虽小却是 Rerun 3D 数据模型中旋转语义的基石它定义了四元数的存储顺序XYZW、Arrow 表示定长 4 元 Float32 列表、归一化行为原样入库、Viewer 归一化、失败视为无效以及跨语言的一致性 API。理解这一定义是正确使用Transform3D、Boxes3D等一切涉及旋转的 Archetype 的前提。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考