Skip to content

MCAPLogger

SUMMARY

MCAPLogger captures every message published on a Zenoh (BabyROS) network into a single .mcap file. Use it to record robot runs, replay them for debugging, and convert them into training datasets.

MCAP is an open container format for robotics data — timestamped, multi-channel, seekable, and self-describing. MCAPLogger records every topic on the network with no per-topic configuration. Reading back is a single generator call.

Install

bash
pip install telekinesis-dataengine

Import

python
from telekinesis.dataengine import MCAPLogger

Writing

MCAPLogger is a context manager. Open it before publishers start; close it when the run ends. It subscribes to "**" by default, capturing every topic on the Zenoh session.

python
MCAPLogger(
    path,           # output .mcap file path
    key="**",       # Zenoh key expression to subscribe to
    depth=1024,     # internal subscriber queue depth
)

Parameters

ParameterTypeDescription
pathstr | PathOutput file path, e.g. "results/run.mcap".
keystrZenoh key expression. "**" captures all topics (default). Use a prefix like "camera/**" to filter.
depthintSubscriber queue depth. Increase for high-frequency topics.

Methods

MethodReturnsDescription
num_messages()intTotal messages written so far.
topics()list[str]List of topic strings seen during the recording.

Example

python
import babyros
from telekinesis.dataengine import MCAPLogger

# ------------------------------------------------
# 1. Start publishers on the Zenoh network
# ------------------------------------------------
camera_pub  = babyros.node.Publisher(topic="camera/rgb")
joint_pub   = babyros.node.Publisher(topic="robot/joint_states")

# ------------------------------------------------
# 2. Record everything to an MCAP file
# ------------------------------------------------
with MCAPLogger("results/run.mcap") as logger:
    for step in range(100):
        camera_pub.publish(frame)
        joint_pub.publish(joint_states)

# ------------------------------------------------
# 3. Inspect the recording
# ------------------------------------------------
print(f"Messages: {logger.num_messages()}")
print(f"Topics:   {logger.topics()}")

Reading

MCAPLogger.read() is a generator that yields (topic, object) pairs in recording order.

python
for topic, obj in MCAPLogger.read(path):
    print(topic, obj)

Parameters

ParameterTypeDescription
pathstr | PathPath to an existing .mcap file.

Returns

Each iteration yields a (topic: str, obj: Any) tuple. obj is the decoded Telekinesis datatype (e.g. Image, Points3D) or a plain dict if the original type is not available.

Example

python
from collections import defaultdict
from telekinesis.dataengine import MCAPLogger

# ------------------------------------------------
# 1. Read back a recording and count per topic
# ------------------------------------------------
counts = defaultdict(int)

for topic, obj in MCAPLogger.read("results/run.mcap"):
    counts[topic] += 1

for topic, count in counts.items():
    print(f"{topic}: {count} messages")

Full Pipeline Example

Record a multi-rate robot run, then read it back and inspect the contents.

python
import time
import threading
import babyros
from telekinesis.dataengine import MCAPLogger

# ------------------------------------------------
# 1. Define publishers
# ------------------------------------------------
imu_pub    = babyros.node.Publisher(topic="sensors/imu")
camera_pub = babyros.node.Publisher(topic="camera/rgb")
joints_pub = babyros.node.Publisher(topic="robot/joint_states")

def publish_loop(pub, make_msg, hz, stop_event):
    while not stop_event.is_set():
        pub.publish(make_msg())
        time.sleep(1.0 / hz)

stop = threading.Event()

# ------------------------------------------------
# 2. Record for 5 seconds
# ------------------------------------------------
with MCAPLogger("results/robot_run.mcap") as logger:
    threads = [
        threading.Thread(target=publish_loop, args=(imu_pub,    make_imu,    100, stop), daemon=True),
        threading.Thread(target=publish_loop, args=(camera_pub, make_frame,   30, stop), daemon=True),
        threading.Thread(target=publish_loop, args=(joints_pub, make_joints,   5, stop), daemon=True),
    ]
    for t in threads:
        t.start()

    time.sleep(5)
    stop.set()

print(f"Recorded {logger.num_messages()} messages across {len(logger.topics())} topics")

# ------------------------------------------------
# 3. Read back and count per topic
# ------------------------------------------------
from collections import defaultdict

counts = defaultdict(int)
for topic, obj in MCAPLogger.read("results/robot_run.mcap"):
    counts[topic] += 1

for topic, count in sorted(counts.items()):
    print(f"  {topic}: {count}")