Transform tree inspectionΒΆ

tree() returns a one-shot snapshot of topology and edge poses. subscribe_tree() streams updates as poses or topology changes.

By default each yield is a full FrameTree; call changes() on the same subscription for raw TreeDelta batches (added / updated). The first delta is always a snapshot, so skip it to see incremental changes. Scoped trees omit external ancestor edges by default, so root nodes are pose-less unless include_ancestors=True.

import time

from olo import Client
from olo.spatial import Transform

with Client() as client:
    robot = client.robot()

    # Namespace-scoped TF tree.
    robot_frames = robot.frames
    base_frame = robot_frames.base_frame()
    if base_frame is None:
        raise RuntimeError("No base frame detected for this robot")

    # Read the scoped robot tree and the full TF tree.
    print(robot_frames.tree())
    print(client.spatial.tree())

    # Each iteration prints the whole tree. Publish between reads to drive updates.
    with robot_frames.subscribe_tree() as stream:
        for _, tree in zip(range(3), stream):
            print(f"Received tree with {len(tree.flatten())} frames")
            robot_frames.publish_static(
                Transform(frame_id=base_frame, child_frame_id=f"frame_{time.time_ns()}")
            )

    # Delta streams expose raw added and updated edges.
    with robot_frames.subscribe_tree() as stream:
        changes = stream.changes()

        # Skip the initial snapshot.
        next(changes)
        robot_frames.publish_static(
            Transform(frame_id=base_frame, child_frame_id=f"frame_{time.time_ns()}")
        )

        # Parse the next incremental response.
        delta = next(changes)
        added = [edge.child_frame_id for edge in delta.added]
        print(f"Added: {added}")
import { connect } from "olo/web";
import { Transform } from "olo/spatial";

const client = connect();
const robot = await client.robot();

// Namespace-scoped TF tree.
const robotFrames = robot.frames;
const baseFrame = await robotFrames.baseFrame();
if (baseFrame === undefined) {
  throw new Error("No base frame detected for this robot");
}

// Read the scoped robot tree and the full TF tree.
console.log(`${await robotFrames.tree()}`);
console.log(`${await client.spatial.tree()}`);

{
  // Each iteration prints the whole tree. Publish between reads to drive updates.
  const stream = robotFrames.subscribeTree();
  let count = 0;
  for await (const tree of stream) {
    console.log(`Received tree with ${tree.flatten().length} frames`);
    await robotFrames.publishStatic(
      new Transform({ frameId: baseFrame, childFrameId: `frame_${Date.now()}` }),
    );
    if (++count === 3) stream.close();
  }
}

{
  // Delta streams expose raw added and updated edges.
  const stream = robotFrames.subscribeTree();
  const changes = stream.changes();

  // Skip the initial snapshot.
  await changes.next();
  await robotFrames.publishStatic(
    new Transform({ frameId: baseFrame, childFrameId: `frame_${Date.now()}` }),
  );

  // Parse the next incremental response.
  const { value: delta } = await changes.next();
  const added = delta === undefined ? [] : delta.added.map((edge) => edge.childFrameId);
  console.log(`Added: ${added.join(", ")}`);
  stream.close();
}