rerun-urdf

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rerun URDF ingestion

Rerun URDF 数据导入

A URDF gives you a robot's geometry and its kinematic tree. Ingesting it means two API calls on one
rerun.urdf.UrdfTree
: stream the static model, then drive it with forward kinematics from joint states. The transforms it produces are derived, so they are a layer, never base (the
URDF + joints (computed)
row of the
rerun-data-model
table).
This skill is the
UrdfTree
API and the two judgment calls it cannot make for you: how your joint values map to URDF joints, and how the disconnected frames in the scene connect to one root. The stream/lens plumbing (
LazyChunkStream
,
DeriveLens
,
Selector
, writing and optimizing RRDs) is in
rerun-chunk-processing
; reach for it, do not re-derive it. Nothing below is tied to a data format: where your joint names, joint values, and calibration come from is yours to wire in.
URDF 包含机器人的几何结构和运动学树。 导入URDF意味着对一个
rerun.urdf.UrdfTree
执行两次API调用:先流式传输静态模型,再根据关节状态通过正向运动学驱动模型。它生成的变换是派生的,因此属于图层,而非基础层(对应
rerun-data-model
表格中的
URDF + joints (computed)
行)。
本技能围绕
UrdfTree
API展开,同时涉及两个API无法自动完成的判断:关节值如何映射到URDF关节,以及场景中不连通的帧如何连接到同一个根节点。流/透镜相关的底层实现(
LazyChunkStream
DeriveLens
Selector
、RRD文件的写入与优化)位于
rerun-chunk-processing
中,直接使用即可,无需重复实现。以下内容不绑定特定数据格式:关节名称、关节值和校准数据的来源由您自行对接。

The API

API说明

python
from rerun.urdf import UrdfTree

urdf = UrdfTree.from_file_path(
    urdf_path,
    entity_path_prefix="robot",  # links log under /robot/<link>
    frame_prefix="",  # prepended to every frame name
    static_transform_entity_path="robot/tf_static",
)
  • entity_path_prefix
    namespaces the entity tree. One per robot instance.
  • frame_prefix
    namespaces the frame names (
    base_link
    ->
    arm_base_link
    ). Two robots in one recording need different prefixes or their roots collide and transforms cross-wire. Leave it empty for a single robot.
  • static_transform_entity_path
    is where the URDF's fixed-joint transforms log (defaults to
    /tf_static
    ).
The tree is also introspectable (full surface in
help(UrdfTree)
) For one-off, non-stream use there are
joint.compute_transform(value)
,
joint.compute_transform_columns(values)
(feeds
rr.send_columns
), and
urdf.log_urdf_to_recording()
to log the whole model through the classic logging API (the
animated_urdf
example,
https://github.com/rerun-io/rerun/tree/main/examples/python/animated_urdf
, is that style).
For pipelines, a
UrdfTree
does two things.
1. Stream the static model. Emits the visual meshes (
Asset3D
) and the fixed-joint transforms as chunks. This is the whole "log the URDF" step:
python
model = (
    urdf.stream(include_joint_transforms=True).drop(  # rest-pose joint transforms too
        content="/robot/**/collision_geometries/**"
    )  # unless you need collision meshes
)
Recolor a robot's meshes with a
MutateLens
on
Asset3D:albedo_factor
(see
rerun-chunk-processing
).
2. Solve forward kinematics. Given joint names and the matching joint values, it returns one
rerun.urdf.JointTransformBatch
per input row. Each batch is a list of per-joint entries with
parent_frame
,
child_frame
,
translation
, and
quaternion
:
python
batches = urdf.compute_joint_transform_batches(names, values, clamp=False)
python
from rerun.urdf import UrdfTree

urdf = UrdfTree.from_file_path(
    urdf_path,
    entity_path_prefix="robot",  # 连杆记录在/robot/<link>路径下
    frame_prefix="",  # 为每个帧名称添加前缀
    static_transform_entity_path="robot/tf_static",
)
  • entity_path_prefix
    用于为实体树添加命名空间,每个机器人实例对应一个前缀。
  • frame_prefix
    用于为帧名称添加命名空间(例如
    base_link
    变为
    arm_base_link
    )。若录制中包含两个机器人,需使用不同前缀,否则它们的根节点会冲突,导致变换交叉关联。单个机器人时可留空。
  • static_transform_entity_path
    是URDF中固定关节变换的记录路径(默认值为
    /tf_static
    )。
该树支持内省(完整接口可查看
help(UrdfTree)
)。 对于一次性、非流式的使用场景,提供了
joint.compute_transform(value)
joint.compute_transform_columns(values)
(为
rr.send_columns
提供数据)和
urdf.log_urdf_to_recording()
方法,可通过传统日志API记录整个模型(示例
animated_urdf
,地址:
https://github.com/rerun-io/rerun/tree/main/examples/python/animated_urdf
,即采用这种方式)。
对于流水线场景,
UrdfTree
主要完成两项工作:
1. 流式传输静态模型。输出视觉网格(
Asset3D
)和固定关节变换的数据块。这就是完整的“记录URDF”步骤:
python
model = (
    urdf.stream(include_joint_transforms=True).drop(  # 同时包含静止姿态的关节变换
        content="/robot/**/collision_geometries/**"
    )  # 除非需要碰撞网格,否则可移除
)
可通过
MutateLens
修改
Asset3D:albedo_factor
来重新为机器人网格着色(详见
rerun-chunk-processing
)。
2. 求解正向运动学。给定关节名称和对应的关节值,它会为每一行输入返回一个
rerun.urdf.JointTransformBatch
。每个批次是一组关节条目,包含
parent_frame
child_frame
translation
quaternion
python
batches = urdf.compute_joint_transform_batches(names, values, clamp=False)

names, values: pyarrow arrays, one list per timestamp (names aligned to values)

names、values:pyarrow数组,每个时间戳对应一个列表(names与values对齐)

clamp=True clamps out-of-limit values and warns, useful while debugging units

clamp=True会限制超出范围的值并发出警告,调试单位时非常有用


You will almost always run this inside a stream so it stays columnar and lazy,
via the two-lens pattern: derive the batch, then `scatter=True` it into
`Transform3D.descriptor_translation/quaternion/parent_frame/child_frame`. The
full shape is the "Minimal shape" section below; the `robot_data_preprocessing`
example (References) is a complete working instance. The only URDF-specific
part is the `compute_joint_transform_batches` call inside the first lens;
everything else is generic stream mechanics (`rerun-chunk-processing`).

Merge the model stream and the FK stream, `collect(optimize=OBJECT_STORE)`, and
`write_rrd(..., recording_id=<segment_id>)`. The `recording_id` must equal the
base segment id or the layer never attaches; `application_id` is discarded on
registration.

您几乎总是会在流中运行此方法,以保持列存储和惰性计算,这通过双透镜模式实现:先派生批次,再通过`scatter=True`将其转换为`Transform3D.descriptor_translation/quaternion/parent_frame/child_frame`。完整结构见下文“最小实现结构”部分;参考示例`robot_data_preprocessing`(见参考链接)是一个完整的可运行实例。唯一与URDF相关的部分是第一个透镜中的`compute_joint_transform_batches`调用;其余均为通用的流机制(`rerun-chunk-processing`)。

合并模型流和FK流,调用`collect(optimize=OBJECT_STORE)`,再调用`write_rrd(..., recording_id=<segment_id>)`。`recording_id`必须与基础段ID一致,否则图层将无法关联;`application_id`在注册时会被忽略。

Mapping joint state to URDF joints (you supply this; the data will not)

将关节状态映射到URDF关节(需自行实现,数据本身不包含此映射)

compute_joint_transform_batches
is only as right as the
names
/
values
you hand it, and the mapping is not in the URDF. Three things go wrong silently:
  • Order. Build an explicit
    names
    array aligned to the
    values
    you read. Never assume your message's field order matches the URDF's
    <joint>
    order.
  • Count. The URDF's non-
    fixed
    joint count rarely equals your reported value count. A gripper sent as one value is often two prismatic joints in the URDF; a mimic joint may be omitted from the message. Reconcile explicitly, and use the API to do it: iterate
    urdf.joints()
    , partition by
    joint_type
    and
    mimic
    . A joint with
    mimic
    set derives its value from the driver joint as
    driver * multiplier + offset
    ; feed it that, not a message field. Confirm the count against
    urdf.joints()
    , not the message length.
  • Units. URDF joints are radians (revolute) and meters (prismatic). Convert if your source differs.
Get any of these wrong and FK runs and writes a confident, wrong pose.
compute_joint_transform_batches
的准确性完全取决于您传入的
names
/
values
,而这种映射并不包含在URDF中。以下三类问题会静默发生:
  • 顺序问题:构建与读取到的
    values
    对齐的显式
    names
    数组。永远不要假设消息的字段顺序与URDF中
    <joint>
    的顺序一致。
  • 数量问题:URDF中非
    fixed
    类型的关节数量很少与您上报的值数量一致。例如,作为单个值发送的夹具在URDF中通常对应两个棱柱关节;模拟关节可能未包含在消息中。需显式协调,可使用API完成:遍历
    urdf.joints()
    ,按
    joint_type
    mimic
    进行分区。设置了
    mimic
    的关节其值由驱动关节派生而来,公式为
    driver * multiplier + offset
    ;需传入该派生值,而非消息中的字段。请根据
    urdf.joints()
    确认数量,而非消息长度。
  • 单位问题:URDF关节的旋转单位为弧度(revolute类型),移动单位为米(prismatic类型)。若数据源单位不同,需进行转换。
以上任何一点出错,都会导致FK计算完成并输出一个看似正确但实际错误的姿态。

Make the joint states readable first

先确保关节状态数据源可读

Where the joint values come from is not this skill's problem, but a dead joint source produces an empty FK layer with no error: a source path that matched nothing, a decoder that yielded zero rows, or a reader that dropped the message silently. Whatever the source, confirm the joint-state stream yields rows before debugging FK. The importer skill for your source format covers its own empty-stream failure modes.
关节值的来源不属于本技能的范畴,但失效的关节数据源会生成空的FK图层且无任何错误提示:可能是源路径匹配失败、解码器输出零行,或读取器静默丢弃了消息。无论数据源是什么,在调试FK之前,请先确认关节状态流能输出数据。对应数据源格式的导入技能会处理其自身的空流故障模式。

How transforms compose (reason about this before logging anything)

变换的组合方式(记录前需先理清)

Rerun resolves a pose by chaining transforms from a frame up to a root. There are two ways an edge in that chain gets defined, and a URDF ingest mixes both:
  • By entity-path hierarchy. A
    Transform3D
    on
    /a/b
    with no frame names is the transform of
    /a/b
    relative to its parent path
    /a
    . Composition follows the entity tree.
  • By explicit frame graph. A
    Transform3D
    that carries
    parent_frame
    and
    child_frame
    defines an edge between two named frames, independent of where in the entity tree it is logged. URDF FK uses this: every joint transform names its parent and child link frames.
So a URDF ingest is a graph of named frames. An edge exists only if some
Transform3D
names that exact
parent_frame -> child_frame
pair. A frame with no incoming edge is a root. The viewer renders every root at the world origin, which is why two unconnected robots silently overlap instead of erroring.
Rerun通过将帧到根节点的变换链来解析姿态。变换链中的边有两种定义方式,URDF导入会混合使用这两种方式:
  • 通过实体路径层级
    /a/b
    上的
    Transform3D
    若未指定帧名称,则表示
    /a/b
    相对于其父路径
    /a
    的变换。组合遵循实体树结构。
  • 通过显式帧图。包含
    parent_frame
    child_frame
    Transform3D
    定义了两个命名帧之间的边,与它在实体树中的记录位置无关。URDF的FK计算使用这种方式:每个关节变换都会指定其父连杆和子连杆的帧名称。
因此,URDF导入的结果是一个命名帧构成的图。只有当某个
Transform3D
指定了确切的
parent_frame -> child_frame
对时,对应的边才存在。没有入边的帧即为根节点。查看器会将每个根节点渲染在世界原点,这就是为什么两个未连接的机器人会静默重叠而非报错。

Resolving the transform forest (the part the data cannot always give you)

解决变换森林问题(数据无法始终提供这部分信息)

A URDF is one tree rooted at its base link. FK and fixed joints supply every edge inside that tree. A real scene is a forest of roots the URDF never connects: a world or scene frame, each robot's base, every camera or sensor frame. The edges that join those roots come from calibration (extrinsics in a sidecar, a TF static publisher, a hand-eye result), not from the URDF, and some are simply absent. A single connected tree is not always solvable. Resolve it deliberately:
  1. Enumerate every frame. Iterate
    urdf.joints()
    and collect the
    parent_link
    /
    child_link
    pairs (the in-tree edges, frame-prefixed);
    urdf.root_link()
    is that URDF's root. Add every sensor/world frame the scene needs (from the
    rerun-data-model
    table). Decide the one intended root.
  2. Classify each edge by source. In-URDF edges (FK joints,
    fixed
    joints) come from the URDF plus joint values. Inter-root edges (root to each robot base, root to each fixed sensor, an arm link to a wrist-mounted camera) come from calibration and you must log them yourself.
  3. Compose fixed chains from the URDF when you need the transform between two links joined only by
    fixed
    joints (a camera bracket, a tool mount): walk parent links across
    fixed
    joints via
    urdf.joints()
    , turning each joint's
    origin_xyz
    /
    origin_rpy
    into a homogeneous matrix and multiplying along the chain. If the walk cannot reach the target link, stop and say so ("no fixed chain from A to B; stuck at C"). A broken chain is a wrong pose, not a missing one.
  4. Build the edge set and find the roots. Collect every
    parent_frame -> child_frame
    pair you will log (URDF + calibration). Walk parents from each frame; any frame that does not reach the intended root is an unconnected root and names a missing edge. This is a pure graph check you can run before writing the RRD.
  5. Resolve every missing edge, or fail loudly. For each one:
    • If calibration supplies the transform, log it (next section).
    • If the data does not, the tree is unsolvable. Do not leave the frame disconnected (it collapses onto the origin and reads as one merged scene). Either abort and name the missing edge, or log identity and emit a loud warning naming the assumed edge. State which you did.
  6. Match frame names across sources. FK-derived
    parent_frame
    /
    child_frame
    must equal the names
    urdf.stream()
    emits for the static geometry, and your calibration edges must use those same names, or links float off the mesh. The
    frame_prefix
    is what keeps them identical; reuse it everywhere.
A correct ingest has exactly one root, and a path from every frame to it.
URDF是以基础连杆为根的单棵树。FK和固定关节提供了树内的所有边。实际场景是一个森林,包含URDF从未连接的多个根节点:世界或场景帧、每个机器人的基础帧、每个相机或传感器帧。连接这些根节点的边来自校准数据(如附带文件中的外参、TF静态发布器、手眼标定结果),而非URDF,且部分边可能缺失。并非总能构建出单一的连通树。需有针对性地解决:
  1. 枚举所有帧。遍历
    urdf.joints()
    并收集
    parent_link
    /
    child_link
    对(树内边,已添加帧前缀);
    urdf.root_link()
    是该URDF的根节点。添加场景所需的所有传感器/世界帧(来自
    rerun-data-model
    表格)。确定一个目标根节点。
  2. 按来源分类每条边。URDF内的边(FK关节、
    fixed
    关节)来自URDF和关节值。根节点间的边(根节点到每个机器人基础帧、根节点到每个固定传感器、机械臂连杆到腕部相机)来自校准数据,需自行记录。
  3. 从URDF构建固定链。当需要仅由
    fixed
    关节连接的两个连杆之间的变换时(如相机支架、工具安装座):通过
    urdf.joints()
    遍历父连杆,将每个关节的
    origin_xyz
    /
    origin_rpy
    转换为齐次矩阵并沿链相乘。若无法遍历到目标连杆,请立即终止并提示(“无法从A到B构建固定链;在C处中断”)。断裂的链会导致错误的姿态,而非缺失姿态。
  4. 构建边集并查找根节点。收集所有将要记录的
    parent_frame -> child_frame
    对(URDF边 + 校准边)。从每个帧向上遍历父节点;任何无法到达目标根节点的帧都是未连接的根节点,意味着存在缺失的边。这是一个纯图检查,可在写入RRD文件前执行。
  5. 解决所有缺失的边,或明确报错。对于每条缺失的边:
    • 若校准数据提供了变换,则记录它(见下一节)。
    • 若数据中没有,则树无法求解。不要让帧处于未连接状态(它会坍缩到原点,导致场景看起来是合并的)。要么终止并指出缺失的边,要么记录单位变换并发出明确警告说明假设的边。请明确说明您采取的操作。
  6. 跨源匹配帧名称。FK派生的
    parent_frame
    /
    child_frame
    必须与
    urdf.stream()
    输出的静态几何结构的帧名称一致,且校准边必须使用相同的名称,否则连杆会与网格分离。
    frame_prefix
    是确保名称一致的关键,请在所有地方复用它。
正确的导入结果应只有一个根节点,且所有帧都能到达该根节点。

Logging a connection correctly

正确记录连接边

A connecting edge is a static
Transform3D
carrying the bridging frame names. Static (no time index) so it holds for the whole recording; the frame names, not the entity path, are what create the graph edge. This
Chunk.from_columns
is the rare sidecar exception — a calibration transform no reader or FK lens can produce; do not generalize it to transforms a reader emits (a
frame_transforms
topic →
Transform3D
) or that FK derives:
python
import rerun as rr
from rerun.experimental import Chunk, LazyChunkStream
连接边是一个静态
Transform3D
,包含桥接帧的名称。静态(无时间索引)意味着它在整个录制过程中有效;帧名称而非实体路径才是创建图边的关键。以下
Chunk.from_columns
是罕见的附加数据例外——校准变换无法由读取器或FK透镜生成;请勿将其推广到读取器输出的变换(如
frame_transforms
主题→
Transform3D
)或FK派生的变换:
python
import rerun as rr
from rerun.experimental import Chunk, LazyChunkStream

world -> this robot's base, from your calibration (translation + xyzw quaternion)

世界帧 -> 机器人基础帧,来自校准数据(平移 + xyzw四元数)

edge = Chunk.from_columns( "/world/robot_base", # any sensible bridging entity path indexes=[], # no index == static columns=rr.Transform3D.columns( translation=[translation], quaternion=[quaternion_xyzw], parent_frame=["world"], # must match the root frame name child_frame=["arm_base_link"], # must match the URDF root frame (prefixed) ), ) edges = LazyChunkStream.from_iter([edge]) # merge alongside model + FK streams

Log a fixed-chain result (step 3) the same way, with `parent_frame` and
`child_frame` set to the two link frames the chain spans. Merge all edge chunks
into the same recording as the model and FK streams so they share the graph.
edge = Chunk.from_columns( "/world/robot_base", # 任何合理的桥接实体路径 indexes=[], # 无索引 == 静态 columns=rr.Transform3D.columns( translation=[translation], quaternion=[quaternion_xyzw], parent_frame=["world"], # 必须匹配根帧名称 child_frame=["arm_base_link"], # 必须匹配URDF根帧(已添加前缀) ), ) edges = LazyChunkStream.from_iter([edge]) # 与模型流 + FK流合并

记录固定链结果(步骤3)的方式相同,将`parent_frame`和`child_frame`设置为链所跨越的两个连杆帧。将所有边数据块合并到与模型流和FK流相同的录制中,以确保它们共享同一个图。

Minimal shape (one robot, generic joint source)

最小实现结构(单个机器人,通用关节数据源)

python
import rerun as rr
from rerun.experimental import DeriveLens, LazyChunkStream, OptimizationProfile, Selector
from rerun.urdf import UrdfTree

urdf = UrdfTree.from_file_path(urdf_path, entity_path_prefix="robot", static_transform_entity_path="robot/tf_static")
model = urdf.stream(include_joint_transforms=True).drop(content="/robot/**/collision_geometries/**")

joints = source_joint_state_stream()  # your reader; one message column of names+values
fk = (
    joints
    .lenses(
        DeriveLens(JOINT_MSG_COMPONENT, output_entity="/tmp/batches").to_component(
            "rerun.urdf.JointTransformBatch",
            Selector(".").pipe(lambda msgs: urdf.compute_joint_transform_batches(read_names(msgs), read_values(msgs))),
        ),
        content=JOINT_SOURCE_PATH,
        output_mode="forward_all",
    )
    .lenses(
        DeriveLens("rerun.urdf.JointTransformBatch", output_entity="/robot/transforms", scatter=True)
        .to_component(rr.Transform3D.descriptor_translation(), Selector("[].translation"))
        .to_component(rr.Transform3D.descriptor_quaternion(), Selector("[].quaternion"))
        .to_component(rr.Transform3D.descriptor_parent_frame(), Selector("[].parent_frame"))
        .to_component(rr.Transform3D.descriptor_child_frame(), Selector("[].child_frame")),
        content="/tmp/batches",
        output_mode="drop_unmatched",
    )
    .filter(content="/robot/transforms")
)

LazyChunkStream.merge(model, fk).collect(optimize=OptimizationProfile.OBJECT_STORE).write_rrd(
    out_path,
    application_id="urdf",
    recording_id=segment_id,
)
read_names
,
read_values
,
JOINT_MSG_COMPONENT
, and
JOINT_SOURCE_PATH
are the only data-specific pieces, and the mapping section above is what makes them correct.
python
import rerun as rr
from rerun.experimental import DeriveLens, LazyChunkStream, OptimizationProfile, Selector
from rerun.urdf import UrdfTree

urdf = UrdfTree.from_file_path(urdf_path, entity_path_prefix="robot", static_transform_entity_path="robot/tf_static")
model = urdf.stream(include_joint_transforms=True).drop(content="/robot/**/collision_geometries/**")

joints = source_joint_state_stream()  # 您的读取器;包含名称+值的消息列
fk = (
    joints
    .lenses(
        DeriveLens(JOINT_MSG_COMPONENT, output_entity="/tmp/batches").to_component(
            "rerun.urdf.JointTransformBatch",
            Selector(".").pipe(lambda msgs: urdf.compute_joint_transform_batches(read_names(msgs), read_values(msgs))),
        ),
        content=JOINT_SOURCE_PATH,
        output_mode="forward_all",
    )
    .lenses(
        DeriveLens("rerun.urdf.JointTransformBatch", output_entity="/robot/transforms", scatter=True)
        .to_component(rr.Transform3D.descriptor_translation(), Selector("[].translation"))
        .to_component(rr.Transform3D.descriptor_quaternion(), Selector("[].quaternion"))
        .to_component(rr.Transform3D.descriptor_parent_frame(), Selector("[].parent_frame"))
        .to_component(rr.Transform3D.descriptor_child_frame(), Selector("[].child_frame")),
        content="/tmp/batches",
        output_mode="drop_unmatched",
    )
    .filter(content="/robot/transforms")
)

LazyChunkStream.merge(model, fk).collect(optimize=OptimizationProfile.OBJECT_STORE).write_rrd(
    out_path,
    application_id="urdf",
    recording_id=segment_id,
)
read_names
read_values
JOINT_MSG_COMPONENT
JOINT_SOURCE_PATH
是唯一与数据相关的部分,上文的映射部分说明了如何确保它们的正确性。

Gotchas that cause real failures

导致实际故障的常见陷阱

  1. Empty layer, no error: dead joint-state source (decoded to zero rows; see the importer skill for your format) or wrong
    JOINT_SOURCE_PATH
    /component name.
  2. Confident wrong pose: joint count, order, or units wrong.
  3. Layer writes but never attaches:
    recording_id != segment_id
    .
  4. Frames collide: two robots sharing a
    frame_prefix
    .
  5. Scene looks merged at the origin: unconnected roots logged as identity without a calibration edge.
  6. Catalog ingest rejects or misorders chunks:
    OBJECT_STORE
    optimization skipped.
  1. 图层为空但无错误:关节状态数据源失效(解码为零行;请查看对应格式的导入技能)或
    JOINT_SOURCE_PATH
    /组件名称错误。
  2. 姿态明显错误:关节数量、顺序或单位错误。
  3. 图层已写入但无法关联:
    recording_id != segment_id
  4. 帧冲突:两个机器人共享同一个
    frame_prefix
  5. 场景看起来在原点合并:未连接的根节点被记录为单位变换,且未添加校准边。
  6. 目录导入拒绝或乱序数据块:跳过了
    OBJECT_STORE
    优化。

References

参考链接

  • https://github.com/rerun-io/rerun/tree/main/examples/python/robot_data_preprocessing
    (FK two-lens pattern, two robots + scene URDFs, prefixes, recoloring, calibration offsets)
  • https://github.com/rerun-io/rerun/tree/main/examples/python/animated_urdf
    (classic logging API:
    log_urdf_to_recording
    , per-joint
    compute_transform
    )
  • rerun-data-model
    (the mapping table this skill consumes)
  • the importer skill for your joint-state source format (making the source readable)
  • rerun-chunk-processing
    (lens/stream, write/optimize mechanics)
  • https://github.com/rerun-io/rerun/tree/main/examples/python/robot_data_preprocessing
    (双透镜FK模式、两个机器人+场景URDF、前缀、重着色、校准偏移)
  • https://github.com/rerun-io/rerun/tree/main/examples/python/animated_urdf
    (传统日志API:
    log_urdf_to_recording
    、单关节
    compute_transform
  • rerun-data-model
    (本技能依赖的映射表格)
  • 关节状态数据源格式对应的导入技能(确保数据源可读)
  • rerun-chunk-processing
    (透镜/流机制、写入/优化方法)