Skip to main content
LeRobot-compatible wrappers for the Axol hardware. Requires the lerobot extra. These classes implement the LeRobot Robot, Teleoperator, and Camera interfaces so the Axol works with any LeRobot training or data-collection pipeline without modification. The collect-data and run-policy CLI commands are built on top of this layer.

Use with the stock LeRobot CLIs

almond-axol is published to PyPI, and the companion lerobot_robot_axol plugin registers these wrappers through LeRobot’s plugin auto-discovery — so the stock LeRobot CLIs (lerobot-teleoperate, lerobot-record, lerobot-replay, …) drive the Axol with no changes to LeRobot itself. Installing the plugin pulls in almond-axol[lerobot]; then select the devices by type:
Teleoperation still uses the VR headset — open the VR app and connect to the robot machine. For the highest-rate data collection (out-of-process ZED capture, on-Jetson NVENC dataset encoding, headset video streaming), prefer the SDK’s own collect-data / run-policy, which are built on these same classes.
Requires LeRobot ≥ 0.6.1, Linux, and Python 3.13 on the robot machine; the zed camera additionally needs pyzed (zed.install). See the plugin README for adding it to an existing one-command-install axol environment.

AxolRobot

LeRobot Robot wrapping the async Axol hardware driver. A background thread runs a dedicated asyncio event loop so motor telemetry keeps streaming while the synchronous get_observation() and send_action() calls block on the calling thread.

AxolRobotConfig fields

Key methods

AxolVRTeleop

LeRobot Teleoperator wrapping VRTeleop. Runs the VR WebSocket server and IK subprocess on a background thread so get_action() is non-blocking and safe to call from any thread.

AxolVRTeleopConfig fields

Key methods

ZedCamera

LeRobot Camera for a locally attached ZED camera. Opens the camera by serial number via the ZED SDK and grabs frames in a background thread. Requires pyzed (zed.install).
Each grabbed frame carries two timestamps on the local perf_counter clock: capture_perf_ts (when the sensor exposed the frame, derived from TIME_REFERENCE.IMAGE) and receive_perf_ts (when this process retrieved it). Since capture is local, both live on the same clock — no cross-machine sync needed. The retrieve-vs-capture skew is sampled on connect() and a warning is logged if the mean falls outside the expected range.

ZedStereoCamera

Wrapper for a stereo ZED X. Opens one SDK connection and, on every grab, retrieves both eyes — so the camera is grabbed once. The eyes are exposed as left_view / right_view, which present the same read API as ZedCamera so the rest of the pipeline can treat them as ordinary cameras.
When an AxolRobot camera config has stereo=True, the observation keys depend on eyes (see AxolRobotConfig.observation_cameras()): "both" expands camera X into X_left / X_right (the head camera records both eyes in the dataset), while "left" / "right" exposes a single eye under the plain name X — indistinguishable downstream from a mono camera (the wrist convention). Stereo is auto-promoted from the serial by AxolRobotConfig.apply_detected_stereo()collect-data / run-policy call it so a stereo ZED X opens correctly without setting stereo by hand.

ZedCameraConfig fields