用 Rerun 的 3D 原语构建实时模拟时钟:Rust 示例逐行拆解

发布时间:2026/9/17 14:29:46
用 Rerun 的 3D 原语构建实时模拟时钟:Rust 示例逐行拆解 用 Rerun 的 3D 原语构建实时模拟时钟Rust 示例逐行拆解【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun本篇技术指南以 examples/rust/clock 示例为主体讲解如何仅用 Rerun SDK 的Boxes3D、Points3D、Arrows3D三种原语搭建一个带时针、分针、秒针的模拟时钟并沿 Rerun 时间轴timeline实时动画。读完本文你将掌握log_static与log的用法差异、如何通过set_duration_secs驱动时间轴、如何用RerunArgs控制数据去向以及为什么这个示例同时是大量小事件场景下的绝佳基准测试。示例概览一个模拟时钟能教会你什么该示例位于仓库的 examples/rust/clock官方 README 用一句话概括了它的本质An example visualizing an analog clock with hour, minute and seconds hands using Rerun Arrow3D primitives.使用 Rerun Arrow3D 原语可视化带时针、分针、秒针的模拟时钟。尽管视觉上只是一个时钟它在教学上覆盖了 Rerun SDK 的三类核心能力静态场景声明——用ViewCoordinates声明坐标系、用Boxes3D声明时钟表盘这些只在时间轴 0 处记录一次之后不再变化逐帧动态数据——每根指针的端点用Points3D、指针本身用Arrows3D每帧每个时间步都重新记录时间轴驱动——通过set_duration_secs把sim_time推进到当前步数对应的秒数让 Viewer 能以真实时间轴播放动画。源码注释还点明了它的另一重身份This is a great benchmark for many small events.这是大量小事件场景的绝佳基准。因为每一帧都会产生 6 条 log 记录3 根指针 × 2 条端点 箭头默认跑 10000 步就是约 6 万个小数据事件非常适合压测 SDK 的日志管线与 Viewer 的更新性能。快速运行一条命令启动示例的依赖与构建方式非常轻量README 给出的启动命令只有一行cargo run --release在仓库根目录执行后程序会编译clockcrate启动一个新的 Rerun Viewer 进程并把时钟数据实时送入其中。也可以通过 cargo 的-p参数在任意目录下精确定位到该 crate源码头部注释同样给出了这条用法cargo run -p clock --release -- --help--help会打印出该程序支持的完整命令行参数具体清单见下文命令行参数一节。依赖构成examples/rust/clock/Cargo.toml 揭示了示例的依赖面非常精简[dependencies] rerun { path ../../../crates/top/rerun, features [web_viewer, clap, log_setup] } anyhow.workspace true clap { workspace true, features [derive] }值得注意的几点rerun走 path 依赖直接指向仓库内的 crates/top/rerun因此必须在本仓库内编译运行启用了三个 featureweb_viewer支持--serve启动网页版 Viewer、clap复用 SDK 内置的RerunArgs命令行参数解析、log_setup提供re_log::setup_logging()初始化日志edition 2024、rust-version 1.96对 Rust 工具链版本有要求主程序使用clapderive 宏来定义参数结构体。程序入口与参数结构src/main.rs 的入口部分展示了一个典型的 Rerun Rust 应用骨架#[derive(Debug, clap::Parser)] #[clap(author, version, about)] struct Args { #[command(flatten)] rerun: rerun::clap::RerunArgs, #[clap(long, default_value 10000)] steps: usize, } fn main() - anyhow::Result() { re_log::setup_logging(); use clap::Parser as _; let args Args::parse(); let (rec, _serve_guard) args.rerun.init(rerun_example_clock)?; run(rec, args) }其中args.rerun.init(rerun_example_clock)是核心一行它以rerun_example_clock作为 Application ID 创建 RecordingStream并根据命令行参数决定数据流向。init返回的_serve_guard在 web 模式下需要存活才能持续提供网页服务因此用_前缀接收并保活到 main 结束。命令行参数RerunArgs 全解析rerun::clap::RerunArgs定义于 crates/top/rerun/src/clap.rs它是一组开箱即用的 CLI 参数任何 Rerun 应用都能通过#[command(flatten)]直接复用。结合 clap.rs 的源码注释参数清单如下参数默认值作用--spawntrue启动一个新的 Rerun Viewer 进程并实时喂入数据--save PATH无将数据保存为.rrd文件而非立即可视化-o, --stdoutfalse把日志数据写到标准输出便于管道传给 Rerun Viewer--connect [URL]rerunhttp://127.0.0.1:9876/proxy连接远程 Rerun viewerscheme 必须是rerun://、rerunhttp://或rerunhttps://路径名必须是/proxy--servefalse启动基于网页的 Rerun Viewer需要web_viewerfeature--server-memory-limit LIMIT25%gRPC 服务器内存上限如16GB或50%达到上限后丢弃最旧数据用于缓冲以便迟到的 viewer 补看--newest-firstfalse新客户端连接时优先回放最新数据--bind IP0.0.0.0gRPC 服务器绑定的 IP 地址示例自身额外定义了--steps参数默认10000控制模拟时钟总共走的步数帧数。Args结构体还透传了 clap 自带的--help、--version等能力。静态场景坐标系与表盘时钟的表盘是静态的在整个动画过程中只记录一次因此使用log_static——它把数据关联到时间轴的初始状态时间 0与随时间变化的动态数据区分开rec.log_static(world, rerun::ViewCoordinates::RIGHT_HAND_Y_UP())?; rec.log_static( world/frame, rerun::Boxes3D::from_half_sizes([(LENGTH_S, LENGTH_S, 1.0)]), )?;ViewCoordinates声明坐标系统ViewCoordinates::RIGHT_HAND_Y_UP()声明场景采用右手坐标系、Y 轴朝上——这是 3D 可视化的惯例约定。通过把该组件 log 到world实体Viewer 会据此确定默认相机姿态与坐标系语义避免出现表盘躺在地上之类的视角错位。Boxes3D用半尺寸定义方形表盘Boxes3D::from_half_sizes([(LENGTH_S, LENGTH_S, 1.0)])创建了一个三维盒子LENGTH_S 20.0因此表盘是一个40.0 × 40.0 × 2.0的薄板。Rerun 的 3D 盒子原语使用**半尺寸half-sizes**定义即每个维度只给出一半长度盒子中心位于实体原点(0, 0, 0)。从源码结构看log_static的static语义与log的关键区别在于static 数据只与实体路径绑定不受时间步影响而后续指针的每一帧数据都属于某个具体时间点。两者可以共存于同一实体层级这里是world下由 Rerun 的时间轴机制统一组织。动态指针Points3D Arrows3D 的组合三根指针由统一的log_hand函数生成每一帧被调用 3 次秒针、分针、时针fn log_hand( rec: rerun::RecordingStream, name: str, step: usize, angle: f32, length: f32, width: f32, blue: u8, ) - anyhow::Result() { let pos tip(angle * TAU, length); let color color(angle, blue); rec.set_duration_secs(sim_time, step as f64); rec.log( format!(world/{name}_pt), rerun::Points3D::new([pos]).with_colors([color]), )?; rec.log( format!(world/{name}_hand), rerun::Arrows3D::from_vectors([pos]) .with_origins([(0.0, 0.0, 0.0)]) .with_colors([color]) .with_radii([width * 0.5]), )?; Ok(()) }指针尖端的坐标计算指针端点的位置由辅助函数tip计算fn tip(angle: f32, length: f32) - [f32; 3] { [length * angle.sin(), length * angle.cos(), 0.0] }传入的angle是归一化比例0.0 ~ 1.0乘以TAU即2π后得到弧度角指针长度作为半径把端点投影到 XZ 平面Z 恒为 0即表盘平面。各指针在 main.rs 中的角度由步数取模得出for step in 0..args.steps { log_hand(rec, seconds, step, (step % 60) as f32 / 60.0, LENGTH_S, WIDTH_S, 0)?; log_hand(rec, minutes, step, (step % 3600) as f32 / 3600.0, LENGTH_M, WIDTH_M, 128)?; log_hand(rec, hours, step, (step % 43200) as f32 / 43200.0, LENGTH_H, WIDTH_H, 255)?; }秒针步数对 60 取模 → 60 步走完一圈长度LENGTH_S 20.0表盘半径宽度WIDTH_S 0.25分针步数对 3600 取模 → 3600 步一圈对应 60 秒 × 60 帧/秒长度10.0宽度0.4时针步数对 43200 取模 → 43200 步一圈12 小时 × 3600长度4.0宽度0.6。长度与宽度常量定义在 main.rs三根指针呈明显的内外层次保证在表盘上互不遮挡、视觉可区分。指针的两种呈现点与箭头每根指针同时记录了两个实体world/{name}_pt用Points3D::new([pos]).with_colors([color])在指针尖端画一个彩色点充当指针端点标记world/{name}_hand用Arrows3D::from_vectors([pos])从原点(0, 0, 0)指向pos画一根箭头with_radii([width * 0.5])把箭头半径设为指针宽度的一半视觉上即指针杆。动态配色的实现color辅助函数让指针颜色随时间角度平滑变化fn color(angle: f32, blue: u8) - rerun::Color { let c (angle * 255.0) as u8; rerun::Color::from_unmultiplied_rgba(255 - c, c, blue, u8::max(128, blue)) }R、G 通道随角度在255→0与0→255之间渐变形成冷暖色过渡B 通道由调用方传入秒针0、分针128、时针255用于区分三根指针Alpha 取max(128, blue)保证半透明可见。时间轴驱动set_duration_secs 让动画流动整个动画的灵魂在于 main.rs 这一行rec.set_duration_secs(sim_time, step as f64);它做了两件事声明/推进一个名为sim_time的时间轴timeline把该时间轴的当前值设置为step秒。之后的rec.log(...)调用会把数据自动关联到sim_time step这一时刻。于是 Viewer 中会出现一条可拖拽/可播放的时间轴步进即可看到秒针绕表盘旋转Rerun 还会自动为sim_time推断出单调递增的时序支持连续播放与逐帧跳转。这正是 Rerun 数据模型的核心思想每个实体 每条时间轴 一组按时间索引的组件序列。静态数据用log_static挂在时间 0动态数据用log 时间轴推进二者互不干扰。作为基准测试的价值大量小事件场景源码头部注释明确写道This is a great benchmark for many small events.从数据结构看这一点不难理解默认steps 10000每步产生6 条 log秒/分/时针 × 端点 箭头总计约 6 万个Points3D/Arrows3D小数据事件在短时间内灌入 SDK 的日志管线这些事件体积极小单点、单向量、单颜色考验的是 Rerun 在海量小消息下的编码、缓冲、传输与 Viewer 端更新吞吐而非大数据块的写入性能。因此你可以用--steps调节事件总量配合--spawn / --save / --connect / --serve等数据通道快速实测不同环节的吞吐差异。例如# 1 万步直接可视化 cargo run --release # 100 万步把数据存成 rrd 文件验证大批量落盘性能 cargo run -p clock --release -- --steps 1000000 --save clock.rrd # 连接远程 viewer如局域网内的另一台机器 cargo run -p clock --release -- --connect rerunhttp://192.168.1.100:9876/proxy小结通过 examples/rust/clock 这个不到 100 行的示例可以完整走通 Rerun Rust SDK 的核心工作流RerunArgs::init(application_id)一键完成 RecordingStream 初始化并根据--spawn/--save/--stdout/--connect/--serve等参数自动决定数据去向log_static记录不随时间变化的场景坐标系、表盘logset_duration_secs把逐帧动态数据挂到sim_time时间轴上形成可播放动画Boxes3D/Points3D/Arrows3D三种原语足以组合出具备真实空间语义的 3D 场景。它是理解 Rerun实体-组件-时间轴数据模型的最小完整样例也是验证大量小事件吞吐的现成基准。以此为起点可以继续研读 examples/rust 下其他示例如 dna、log_file掌握更多原语与交互模式。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考