尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Rerun 数据记录与摄入指南:用 DNA 算盘示例打通 Log and Ingest 全流程

Rerun 数据记录与摄入指南:用 DNA 算盘示例打通 Log and Ingest 全流程 Rerun 数据记录与摄入指南用 DNA 算盘示例打通 Log and Ingest 全流程【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读本文基于 Rerun 官方入门教程《Log and Ingest》data-in.md以「DNA 算盘」DNA abacus这一经典示例为主线从初始化 SDK、记录首批点云到引入时间轴实现动画、叠加层级空间变换逐步带你掌握 Rerun 的核心数据模型Archetypes、Components、Entity Paths、Component Batches与「Latest-at」查询语义。读完本文你将能够在 Python、Rust、C 三种语言中完成从白纸一张到动态交互式 3D 场景的完整数据记录流程并掌握通过网络gRPC、RRD 文件等途径投喂数据的多种方式。前置准备安装 SDK 与建立项目在开始记录数据之前需要完成两件事安装 SDK按官方指引安装对应语言的 Rerun SDKPython 使用pip install rerun-sdkRust/C 分别通过 Cargo 与 CMake 接入详见 install-rerun.md。建立项目为你的语言创建项目骨架详见 project-setup.md。本教程的完整代码位于仓库中对应语言的示例目录可直接对照运行Pythonexamples/python/dna/dna.pyRustexamples/rust/dna/src/main.rsCexamples/cpp/dna/main.cpp此外教程中逐步讲解用到的分片代码也以可复用片段的形式存放在 docs/snippets/all/tutorials/dna.py以及同目录的dna.rs、dna.cpp中便于单独抽取验证。初始化 SDK命名记录并唤起 Viewer创建一个新文件或项目导入 SDK 相关工具并初始化一条 recording。初始化会为这条记录命名一个稳定的ApplicationId然后拉起一个 Rerun Viewer 并把记录连接到它Pythonimport rerun as rr rr.init(rerun_example_dna_abacus, spawnTrue)Rustlet rec rerun::RecordingStreamBuilder::new(rerun_example_dna_abacus).spawn()?;Cconst auto rec rerun::RecordingStream(rerun_example_dna_abacus); rec.spawn().exit_on_failure();稳定的ApplicationId会让 Viewer 在多次运行间为该数据集保留 UI 状态例如蓝图布局在迭代调试时非常省事。关于 Application ID 的更多规则非空 ASCII 字符串、最长 180 字符、仅允许字母数字与_、-、.、空格、括号、冒号等字符非法字符会被归一化为连字符可参考 recordings.md。默认情况下spawn会在另一个进程中启动 Viewer并把数据自动通过管道送进去。作为对比还有多种把数据送往 Viewer 的方式本教程末尾会讲到但实验阶段spawn默认行为已经足够。说明spawn每次会随机生成一个新的 recording ID如果需要把多个进程/多台机器产生的数据合并成同一条逻辑记录分布式记录可以在初始化时覆盖 recording ID详见 recordings.md。记录第一批点云两个螺旋DNA 形状的核心结构可以很方便地用两团螺旋状点云来描述Pythonimport numpy as np from math import tau from rerun.utilities import bounce_lerp, build_color_spiral NUM_POINTS 100 points1, colors1 build_color_spiral(NUM_POINTS) points2, colors2 build_color_spiral(NUM_POINTS, angular_offsettau * 0.5) rr.log(dna/structure/left, rr.Points3D(points1, colorscolors1, radii0.08)) rr.log(dna/structure/right, rr.Points3D(points2, colorscolors2, radii0.08))Rustconst NUM_POINTS: usize 100; let (points1, colors1) color_spiral(NUM_POINTS, 2.0, 0.02, 0.0, 0.1); let (points2, colors2) color_spiral(NUM_POINTS, 2.0, 0.02, TAU * 0.5, 0.1); rec.log_static( dna/structure/left, rerun::Points3D::new(points1.iter().copied()) .with_colors(colors1) .with_radii([0.08]), )?; rec.log_static( dna/structure/right, rerun::Points3D::new(points2.iter().copied()) .with_colors(colors2) .with_radii([0.08]), )?;Cstd::vectorrerun::Position3D points1, points2; std::vectorrerun::Color colors1, colors2; color_spiral(NUM_POINTS, 2.0f, 0.02f, 0.0f, 0.1f, points1, colors1); color_spiral(NUM_POINTS, 2.0f, 0.02f, TAU * 0.5f, 0.1f, points2, colors2); rec.log_static( dna/structure/left, rerun::Points3D(points1).with_colors(colors1).with_radii({0.08f}) ); rec.log_static( dna/structure/right, rerun::Points3D(points2).with_colors(colors2).with_radii({0.08f}) );运行程序后Viewer 中即可看到这两个螺旋。注意如果 Viewer 已经在运行Rerun 会直接连接到现有会话并把数据替换成这条新的recording逻辑记录按 recording ID 与 application ID 归并多个物理来源可合并为同一条逻辑记录。在继续之前建议先在 Viewer 中动手交互一下场景、熟悉各个菜单可参考 navigating-the-viewer.md 与 viewer 参考 的完整讲解。幕后原理这几行代码到底做了什么这两行简单的调用背后蕴含了大量 Rerun 的核心概念。逐一拆解如下。Archetypes原型记录几何图元最省事的方式就是用 SDK 的log方法配合内置的原型类这里就是Points3D。Archetype 负责把一组组件components打包成能被 Viewer 正确识别与渲染的批次。Rerun 中大多数原型类都以复数形式命名如rr.Points3D、rr.LineStrips3D原因正是它们天然面向组件批次参见 batches.md。Components组件在底层Rerun SDK 记录的其实是独立的组件比如位置Position3D、颜色Color、半径Radius等。Archetype 只是构建这类组件集合的高层便捷方式。例如rr.log(my_point, rr.Points2D([32.7, 45.9], colors[255, 0, 0]))内部会构建Points2D:positions类型Position2D与Points2D:colors类型Color两个组件并与my_point实体关联Viewer 再通过组件元数据反查对应原型并决定如何渲染详见 entity-component.md。对于高级用法你既可以往 Archetype 上附加自定义组件Python 中可用rr.AnyValues辅助对象也可以完全绕过 Archetype直接记录自定义的组件集合参见 custom-data.md。Entities hierarchies实体与层级注意我们传入的两个字符串dna/structure/left与dna/structure/right。这些是entity paths实体路径唯一标识场景中的每个实体。每个实体由一条路径加一个或多个组件构成。实体路径天然形成层级结构——例如本例中dna/structure之下挂着left、right、scaffolding、scaffolding/beads——这种层级在数据如何被可视化与如何施加变换transform上起着关键作用后续的旋转动画正是依赖这一点。Component batches组件批次最后注意我们是一下子把一整批点与一整批颜色一次性记录进去的。组件批次在 Rerun 中是一等公民带有大量性能收益与专属特性。你现在看到的正是其中一个特性我们只为所有点记录了一个半径radii0.08它却同时作用于全部 100 个点——这被称为clamping夹取。其底层规则是Points3D以Position3D为主组件主组件的批次长度决定了场景中可见的实例数对于其他组件实例数多了会被 Viewer 忽略少了则最后一个实例会被重复补齐。同一时间点对同一组件重复记录时最后一次记录的批次生效此前批次仍留在存储中详见 batches.md。小结这两次函数调用包含的信息量远不止表面——消化完 Archetype、Component、Entity Path、Component Batch 这四个概念后记录其他任何实体都只是依葫芦画瓢。下面我们就把场景里的其余元素全部补上。补全场景脚手架与珠子用 3D 线段条表示脚手架DNA 的骨架可以用一批 3D 线带line strips来表示——每条线带连接左右螺旋的对应点Pythonrr.log( dna/structure/scaffolding, rr.LineStrips3D( np.stack((points1, points2), axis1), colors[128, 128, 128] ), )Rustlet lines: Vec[glam::Vec3; 2] std::iter::zip(points1, points2) .map(|(p1, p2)| (p1, p2).into()) .collect_vec(); rec.log_static( dna/structure/scaffolding, rerun::LineStrips3D::new(lines.iter().cloned()) .with_colors([rerun::Color::from_rgb(128, 128, 128)]), )?;Cstd::vectorrerun::LineStrip3D lines; for (size_t i 0; i points1.size(); i) { lines.emplace_back(rerun::LineStrip3D({points1[i].xyz, points2[i].xyz})); } rec.log_static( dna/structure/scaffolding, rerun::LineStrips3D(lines).with_colors(rerun::Color(128, 128, 128)) );记录珠子第一版静态位置剩下的就是珠子。先把珠子放在两条螺旋之间的某处基于每个点对应的随机偏移量做一次插值颜色随偏移量从暗到亮渐变Pythonoffsets np.random.rand(NUM_POINTS) beads [ bounce_lerp(points1[n], points2[n], offsets[n]) for n in range(NUM_POINTS) ] colors [ [int(bounce_lerp(80, 230, offsets[n] * 2))] for n in range(NUM_POINTS) ] rr.log( dna/structure/scaffolding/beads, rr.Points3D(beads, radii0.06, colorsnp.repeat(colors, 3, axis-1)), )这里虽然数组操作变复杂了但本质没有新东西仍然是往 Archetype 里填数据、喂给 Rerun API。bounce_lerp是一个在两点间往返插值的辅助函数仓库中 Python 侧位于rerun.utilitiesRust 侧位于rerun::demo_util见 dna.py 与 main.rs。让珠子动起来引入时间轴时间与时间线Timelines到目前为止我们完全搁置了 Rerun 的核心概念之一Time and Timelines。即便如此打开 Viewer 底部的时间面板Timeline View参考 timeline.md你会发现 Rerun 已经默默替我们按每次 log 调用的发生时刻记录了时间——这条自动时间线名为log_time另一个自动时间线log_tick是序号时间线默认关闭可通过RERUN_LOG_TICK环境变量或set_log_tick_enabled开启log_time则可用RERUN_LOG_TIME或set_log_time_enabled关闭。但记录时间在这里帮不上忙我们不能让珠子按记录时刻运动否则珠子移动速度会受记录进程性能影响而忽快忽慢为此需要引入一条自定义时间线使用一个由我们自己掌控的确定性时钟。Rerun 对时间的支持很丰富并发或互不重叠的多条时间线、乱序插入、甚至完全活在时间线之外的数据静态数据都行。具体做法是把记录珠子的部分替换成一个在不同时间戳下循环记录珠子的循环Pythontime_offsets np.random.rand(NUM_POINTS) for i in range(400): time i * 0.01 rr.set_time(stable_time, durationtime) times np.repeat(time, NUM_POINTS) time_offsets beads [ bounce_lerp(points1[n], points2[n], times[n]) for n in range(NUM_POINTS) ] colors [ [int(bounce_lerp(80, 230, times[n] * 2))] for n in range(NUM_POINTS) ] rr.log( dna/structure/scaffolding/beads, rr.Points3D(beads, radii0.06, colorsnp.repeat(colors, 3, axis-1)), )Rustlet mut rng rand::rng(); let offsets (0..NUM_POINTS).map(|_| rng.random::f32()).collect_vec(); for i in 0..400 { let time i as f32 * 0.01; rec.set_duration_secs(stable_time, time as f64); let times offsets.iter().map(|offset| time offset).collect_vec(); let beads std::iter::zip(lines, times) .map(|([p1, p2], time)| bounce_lerp(p1, p2, time)) .collect_vec(); let colors times .iter() .map(|time| bounce_lerp(80.0, 230.0, time * 2.0) as u8) .map(|c| rerun::Color::from_rgb(c, c, c)) .collect_vec(); rec.log( dna/structure/scaffolding/beads, rerun::Points3D::new(beads) .with_colors(colors) .with_radii([0.06]), )?; }Cstd::default_random_engine gen; std::uniform_real_distributionfloat dist(0.0f, 1.0f); std::vectorfloat offsets(NUM_POINTS); std::generate(offsets.begin(), offsets.end(), [] { return dist(gen); }); std::vectorrerun::Position3D beads_positions(lines.size()); std::vectorrerun::Color beads_colors(lines.size()); for (int t 0; t 400; t) { auto time std::chrono::durationfloat(t) * 0.01f; rec.set_time_duration(stable_time, time); for (size_t i 0; i lines.size(); i) { float time_offset time.count() offsets[i]; auto c static_castuint8_t(bounce_lerp(80.0f, 230.0f, time_offset * 2.0f)); beads_positions[i] rerun::Position3D( bounce_lerp(lines[i].points[0].x(), lines[i].points[1].x(), time_offset), bounce_lerp(lines[i].points[0].y(), lines[i].points[1].y(), time_offset), bounce_lerp(lines[i].points[0].z(), lines[i].points[1].z(), time_offset) ); beads_colors[i] rerun::Color(c, c, c); } rec.log( dna/structure/scaffolding/beads, rerun::Points3D(beads_positions).with_colors(beads_colors).with_radii({0.06f}) ); }调用set_timePythonset_duration_secsRustset_time_durationC会创建一条名为stable_time的新时间线并确保其后所有 log 调用都被打上该时间戳。记录数据时你可以随意添加任意多条时间线与时间戳。Rerun 支持三种时间索引类型都以i64编码序号sequential、时间戳自 Unix 纪元起的纳秒、时长纳秒。Python 端对应的set_time_sequence/set_time_nanos/set_time_secs可查阅 timelines.md若你手里已有一段随时间变化的传感器数据更高效的做法是用send_columns一次性批量发送参见 send-columns.md。⚠️ 警告直接运行上面的代码结果会很意外——珠子确实在动画了但此前记录的所有东西都消失了原因在于Viewer 默认切换到了显示你的自定义时间线stable_time而原始数据螺旋、脚手架此前只记录在默认时间线log_time上。理解 Latest-at 语义修复把结构数据记录在时间零点解决办法是在记录原始结构之前先把自定义时间线设为时间零点Pythonrr.set_time(stable_time, duration0) # ...然后记录 left / right / scaffoldingRustrec.set_duration_secs(stable_time, 0f64); // ...然后记录 left / right / scaffolding示例中用 log_static 记录Crec.set_time_duration(stable_time, 0s); // ...然后记录 left / right / scaffolding示例中用 log_static 记录这一修复背后的原理这个修复实际上引出了 Rerun 中另一个非常重要的概念latest-at取最新语义。注意dna/structure/left与dna/structure/right只在时间零点被记录过一次但当我们在远超零点的时刻查询场景时它们依然可见——因为Rerun 总是以最新数据的口径来推理对于给定实体在给定时刻它会取该实体在该时刻的所有最新组件。换句话说查询某时刻某实体的状态得到的是该时刻或之前最后一次记录的组件值数据会一直延续到被更新的记录覆盖为止。这也解释了为什么把静态结构放在stable_time0就能与动态珠子共存。顺便一提如果某些数据希望属于所有时间线包括尚未创建的可以直接用staticTrueRust/C 中为log_static记录为静态数据——本示例最终版本即采用这种方式例如 dna.py 中的rr.log(helix/structure/left, ..., staticTrue)。静态数据会遮蔽同一实体上同类型的时序数据参见 timelines.md。变换空间让算盘转起来现在只剩最后一步原场景中算盘会沿其主轴旋转。与时间一样Rerun 把层级空间变换也作为一等公民对待——transforms.md 中有完整讲解。现在我们只需要把两者结合起来在每个时间戳为脚手架记录一个旋转变换即可。既可以扩充上面的循环来顺带记录变换也可以像下面这样单独加一个循环Pythonfor i in range(400): time i * 0.01 rr.set_time(stable_time, durationtime) rr.log( dna/structure, rr.Transform3D( rotationrr.RotationAxisAngle( axis[0, 0, 1], radianstime / 4.0 * tau ) ), )Rustfor i in 0..400 { let time i as f32 * 0.01; rec.set_duration_secs(stable_time, time as f64); rec.log( dna/structure, rerun::archetypes::Transform3D::from_rotation(rerun::RotationAxisAngle::new( glam::Vec3::Z, rerun::Angle::from_radians(time / 4.0 * TAU), )), )?; }Cfor (int t 0; t 400; t) { auto time std::chrono::durationfloat(t) * 0.01f; rec.set_time_duration(stable_time, time); rec.log( dna/structure, rerun::archetypes::Transform3D(rerun::RotationAxisAngle( {0.0f, 0.0f, 1.0f}, rerun::Angle::radians(time.count() / 4.0f * TAU) )) ); }注意这里把Transform3D记录在父实体dna/structure上——因为实体路径的层级结构作用于父实体的变换会自动级联应用到其所有子实体left、right、scaffolding乃至scaffolding/beads。至此一个完整动画的 DNA 算盘就诞生了。记录与可视化数据的其他方式spawn非常适合单机实验但如果记录过程发生在一台无头headless计算机上呢Rerun 为此提供了多种方案。通过网络记录数据gRPC任何时候你都可以直接运行rerun命令启动一个 Viewer——这个 Viewer 本身就是一个服务器随时准备通过 gRPC 接收数据默认监听0.0.0.0:9876。在记录端把前面的spawn调用替换成connect_grpc调用即可把数据发送到任意 gRPC 地址Pythonimport rerun as rr rr.init(rerun_example_dna_abacus) rr.connect_grpc() # 连接到默认 URL 上正在运行的 viewer # …其余数据记录逻辑与 spawn 版一致…对应完整片段见 docs/snippets/all/tutorials/dna_connect_grpc.py同目录还有dna_connect_grpc.rs与dna_connect_grpc.cpp。运行rerun --help可查看更多启动选项。保存 / 加载 RRD 文件有时走网络不可行——比如想分享数据、把它附在 bug 报告里。Rerun 对此也有内置支持每个 SDK 都提供save方法Python 的rr.save、Rust 与 C 的RecordingStream::save把所有已记录数据流式写入磁盘随后用rerun path/to/recording.rrd即可查看生成的文件。此外你也可以在可视化过程中直接从 Viewer 里保存当前记录或其中一部分。⚠️ 关于 RRD 向后兼容Rerun 0.23 及之后保存的 RRD 文件可以用更新的 Rerun 版本打开但目前只保证相邻次版本之间的兼容例如 Rerun 0.24 可以打开 0.23 的 RRD。更详细内容参见发布说明文档 CHANGELOG.md。Rust 独有进程内嵌 ViewerRust SDK 可以通过rerun::native_viewer::show把 Viewer 直接宿主在你的应用进程内——它期望的是内存中的一条完整 recording而不是实时数据流。使用它需要在Cargo.toml中启用native_viewerfeature该 Viewer 会阻塞主线程直到被关闭具体见 Rust API 文档。直接打开各类文件Viewer 本身也支持直接打开已有文件RRD、MCAP、图片、视频、点云等共有三种加载方式CLI 参数如rerun myfile.jpeg、拖拽、以及 Viewer 内的打开对话框详见 open-any-file.md。内置支持的文件类型包括原生rrd、3D 模型gltf/glb/obj/stl、常见图片格式png/jpeg/gif/tiff/webp等、点云ply、文本md/txt以及 LeRobot 数据集目录。若要在 SDK 内把文件内容作为数据记录进去可调用log_file_from_path/log_file_from_contents方法——注意此时文件由运行 SDK 的进程加载而非 Viewer 进程。总结与下一步至此我们已经完成了一次 Rerun 记录Log and Ingest的全流程速览从初始化 SDK、记录点云与线带到引入自定义时间线实现动画、借助层级实体路径施加空间变换再到掌握 Latest-at 语义以及 gRPC / RRD / 内嵌 Viewer 等多种数据通路。这还只是冰山一角但它应该给了你充足的出发点去动手实验。接下来可以浏览 reference/types.md 了解更多编码encodings的简单用法深入学习如何查询已记录的数据或者在仓库中示例例如 examples/python、examples/rust、examples/cpp 下的其他真实用例。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表