Frame system API reference

The RPCs for querying and transforming frame system poses live on the robot service, not on a dedicated FrameSystemService. New users often look for a separate service matching the concept; there is none. All four methods below belong to RobotService in api/proto/viam/robot/v1/robot.proto.

MethodPurpose
FrameSystemConfigReturn the list of frame parts that make up the machine’s frame system.
GetPoseReturn a component’s pose in any reference frame.
TransformPoseConvert a pose between reference frames.
TransformPCDConvert a point cloud between reference frames.

The SDK client classes surface these as methods on the robot or machine client object (machine.transform_pose, machine.get_pose, and so on). You do not instantiate a separate frame system client.

Service names

Inside RDK, the frame system is registered under two names: builtin (the default instance that ships with viam-server) and $framesystem (the name modules use to resolve a dependency on the frame system). SDK callers do not reference either; the RPCs on the robot/machine client hit the frame system transparently.

FrameSystemConfig

Returns the list of frame parts configured on the machine. Each part has a name, parent, translation, orientation, and optional geometry. This is what the CLI’s print-config command prints.

parts = await machine.get_frame_system_config()
for part in parts:
    print(part.name, "parent:", part.pose_in_parent_frame.reference_frame)
parts, err := machine.FrameSystemConfig(ctx)
if err != nil {
    logger.Fatal(err)
}
for _, part := range parts.Parts {
    logger.Infof("%s parent: %s", part.FrameConfig.Name(), part.FrameConfig.Parent())
}

GetPose

Returns a component’s pose in the specified destination frame.

ParameterDescription
component_nameThe component whose pose to return.
destination_frameThe reference frame to express the pose in. Default: "world".
supplemental_transformsOptional additional frame relationships not stored in the machine’s configuration.
extraOptional map for implementation-specific extras.

Returns a PoseInFrame.

The Python RobotClient does not expose GetPose. Call get_pose on the motion service client instead:

from viam.services.motion import MotionClient

motion_service = MotionClient.from_robot(machine, "builtin")
gripper_in_world = await motion_service.get_pose(
    component_name="my-gripper",
    destination_frame="world",
)
gripperInWorld, err := machine.GetPose(
    ctx,
    "my-gripper",
    referenceframe.World,
    nil, // supplemental_transforms
    nil, // extra
)

Python goes through the motion service, Go through the robot service

GetPose exists on two services in the proto: the robot service (current) and the motion service (older, deprecated, same parameter shape). The Go SDK surfaces the robot service version as RobotClient.GetPose; Go callers go through the robot service. The Python SDK never wrapped the robot service version, so Python callers use MotionClient.get_pose and hit the deprecated motion service path.

The two paths return equivalent results today. The underlying API surface will eventually consolidate, but until then Python callers stay on the motion-service path. The CLI’s print-status, get-pose, and set-pose commands also invoke the deprecated motion-service method internally.

TransformPose

Convert a pose expressed in one reference frame into the equivalent pose in another. Unlike GetPose, the starting pose can be any point you choose, not just a component origin.

ParameterDescription
sourceThe input PoseInFrame (pose plus its reference frame name).
destinationThe reference frame to express the pose in.
supplemental_transformsOptional additional frame relationships not stored in the machine’s configuration.

Returns a PoseInFrame in the destination frame.

from viam.proto.common import PoseInFrame, Pose

detected_in_camera = PoseInFrame(
    reference_frame="my-camera",
    pose=Pose(x=50, y=30, z=400),
)

detected_in_world = await machine.transform_pose(detected_in_camera, "world")
detectedInCamera := referenceframe.NewPoseInFrame("my-camera",
    spatialmath.NewPoseFromPoint(r3.Vector{X: 50, Y: 30, Z: 400}))

detectedInWorld, err := machine.TransformPose(ctx, detectedInCamera, "world", nil)

Supplemental transforms

Use supplemental_transforms to augment the machine’s configured frame system for a single call. A common case: an object the camera has detected whose pose you know relative to the camera, but which is not configured as a component. Pass the object’s frame relationship as a supplemental transform, then call TransformPose or GetPose to compute poses involving that object.

Supplemental transforms apply only to the current call. They do not modify the stored frame system configuration.

Python kwarg naming. In Python, MotionClient.get_pose takes supplemental_transforms; RobotClient.transform_pose and RobotClient.get_frame_system_config take additional_transforms. The proto field and all Go methods use supplemental_transforms. Pass the kwarg that matches the client class you call.

TransformPCD

Transform a point cloud from one reference frame to another. Useful for aligning point clouds from multiple cameras into a common frame, or for expressing lidar scans in world coordinates.

ParameterDescription
sourceThe source point cloud (bytes, PCD format).
source_frameThe reference frame the source point cloud is expressed in.
destinationThe reference frame to express the point cloud in.

Returns the transformed point cloud (bytes).

Point cloud transforms are computationally expensive for large clouds. Consider down-sampling before calling.

Common patterns

  • Verify frame configuration: transform a component’s origin to the world frame and compare against physical measurements.
  • Convert camera detections to world coordinates: transform a detection pose from the camera frame to the world frame before commanding an arm to that pose.
  • Compare poses across frames: transform each pose to a common frame (typically "world"), then compare.
  • Carry dynamic frame relationships: pass objects the machine picks up or detects as supplemental transforms so the planner and your code agree on their position.

What’s next