Skip to main content
Robots have multiple sensors emitting data at different rates and latencies. A camera might run at 30fps, while lidar scans at 10Hz, and each has different processing delays. For perception tasks like projecting 2D detections into 3D pointclouds, we need to match data from these streams by timestamp. align_timestamped solves this by buffering messages and matching them within a time tolerance. output

Basic Usage

Below we set up replay of real camera and lidar data from the Unitree Go2 robot. You can check it if you’re interested. Streams would normally come from an actual robot into your module via In inputs. detection/module3D.py is a good example of this. Assume we have them. Let’s align them.
skip session=align
skip session=align output=assets/alignment_timeline.png
output If we loosen up our match tolerance, we might get multiple pairs matching the same lidar frame.
skip session=align
skip session=align output=assets/alignment_timeline2.png
output

Combine Frame Alignment with a Quality Filter

More on quality filtering here.
skip session=align
skip session=align output=assets/alignment_timeline3.png
output We are very picky but data is high quality. Best frame, with closest lidar match in this window.

How It Works

The primary stream (first argument) drives emissions. When a primary message arrives:
  1. Immediate match: If matching secondaries already exist in buffers, emit immediately
  2. Deferred match: If secondaries are missing, buffer the primary and wait
When secondary messages arrive:
  1. Add to buffer for future primary matches
  2. Check buffered primaries - if this completes a match, emit
output

Parameters

Usage in Modules

Every module In port exposes an .observable() method that returns a backpressured stream of incoming messages. This makes it easy to align inputs from multiple sensors. From detection/module3D.py, projecting 2D detections into 3D pointclouds:
skip
The 2D detection stream (camera + ML model) is the primary, matched with raw pointcloud data from lidar. The longer buffer_size=20.0 accounts for variable ML inference times.