Skip to main content

The Problem: Everything Measures from Its Own Perspective

Imagine your robot has an RGB-D cameraβ€”a camera that captures both color images and depth (distance to each pixel). These are common in robotics: Intel RealSense, Microsoft Kinect, and similar sensors. The camera spots a coffee mug at pixel (320, 240), and the depth sensor says it’s 1.2 meters away. You want the robot arm to pick it upβ€”but the arm doesn’t understand pixels or camera-relative distances. It needs coordinates in its own workspace: β€œmove to position (0.8, 0.3, 0.1) meters from my base.” To convert camera measurements to arm coordinates, you need to know:
  • The camera’s intrinsic parameters (focal length, sensor size) to convert pixels to a 3D direction
  • The depth value to get the full 3D position relative to the camera
  • Where the camera is mounted relative to the arm, and at what angle
This chain of conversionsβ€”(pixels + depth) β†’ 3D point in camera frame β†’ robot coordinatesβ€”is what transforms handle. output Each arrow in this tree is a transform. To get the mug’s position in gripper coordinates, you chain transforms through their common parent: camera β†’ robot_base β†’ arm β†’ gripper.

What’s a Coordinate Frame?

A coordinate frame is simply a point of viewβ€”an origin point and a set of axes (X, Y, Z) from which you measure positions and orientations. Think of it like giving directions:
  • GPS says you’re at 37.7749Β° N, 122.4194Β° W
  • The coffee shop floor plan says β€œtable 5 is 3 meters from the entrance”
  • Your friend says β€œI’m two tables to your left”
These all describe positions in the same physical space, but from different reference points. Each is a coordinate frame. In a robot:
  • The camera measures in pixels, or in meters relative to its lens
  • The LIDAR measures distances from its own mounting point
  • The robot arm thinks in terms of its base or end-effector position
  • The world has a fixed coordinate system everything lives in
Each sensor, joint, and reference point has its own frame.

The Transform Class

The Transform class at geometry_msgs/Transform.py represents a spatial transformation with:
  • frame_id - The parent frame name
  • child_frame_id - The child frame name
  • translation - A Vector3 (x, y, z) offset
  • rotation - A Quaternion (x, y, z, w) orientation
  • ts - Timestamp for temporal lookups

Transform Operations

Transforms can be composed and inverted:

Converting to Matrix Form

For integration with libraries like NumPy or OpenCV:

Frame IDs in Modules

Modules in DimOS automatically get a frame_id property. This is controlled by two config options in core/module.py:
  • frame_id - The base frame name (defaults to the class name)
  • frame_id_prefix - Optional prefix for namespacing

The TF Service

Every module has access to self.tf, a transform service that:
  • Publishes transforms to the system
  • Looks up transforms between any two frames
  • Buffers historical transforms for temporal queries
The TF service is implemented in protocol/tf/tf.py and is lazily initialized on first access.

Multi-Module Transform Example

This example demonstrates how multiple modules publish and receive transforms. Three modules work together:
  1. RobotBaseModule - Publishes world -> base_link (robot’s position in the world)
  2. CameraModule - Publishes base_link -> camera_link (camera mounting position) and camera_link -> camera_optical (optical frame convention)
  3. PerceptionModule - Looks up transforms between any frames
skip ansi=false
You can view these transforms in 3D using the Rerun viewer (see Visualization). transforms Key points:
  • Automatic broadcasting: self.tf.publish() broadcasts via LCM to all modules
  • Chained lookups: TF finds paths through the tree automatically
  • Inverse lookups: Request transforms in either direction
  • Temporal buffering: Transforms are timestamped and buffered (default 10s) for sensor fusion
The transform tree from the example above, showing which module publishes each transform: output

Internals

Transform Buffer

self.tf on a module is a transform buffer. This is a standalone class that maintains a temporal buffer of transforms (default 10 seconds) allowing queries at past timestamps, you can use it directly:
This is essential for sensor fusion where you need to know where the camera was when an image was captured, not where it is now.

Further Reading

For a visual introduction to transforms and coordinate frames: For the mathematical foundations, the ROS documentation provides detailed background: See also: