Skip to main content
Install the package with uv sync (see Installation), then import from its submodules (e.g. almond_axol.robot, almond_axol.teleop). The Axol SDK is organized into focused modules:
The browser front-ends live outside the Python package, in web/: the VR teleop interface and the web control panel, both built to web/app/dist and served by axol serve. End-to-end data flow for teleoperation:
End-to-end data flow for LeRobot data collection:

Async context managers

Axol, Sim, VRTeleop, and VRServer all open and close hardware resources in __aenter__ / __aexit__. Always use them with async with.

Joint arrays

Every method that reads or writes joint state uses np.ndarray of shape (8,) in Joint enum order: SHOULDER_1, SHOULDER_2, SHOULDER_3, ELBOW, WRIST_1, WRIST_2, WRIST_3, GRIPPER. Arm joints are in radians; the gripper is normalized to [0.0 = closed, 1.0 = fully open].

Telemetry vs. one-shot reads

start_telemetry(hz) launches a background polling loop and populates .positions and .torques as non-blocking cached properties — read them in a tight control loop without await. The cache is empty until the first poll lands (motors can take a moment to answer, especially MyActuator motors rebooting after a control-mode change), so call await wait_for_telemetry() once after start_telemetry() to block until every motor has reported before the first .positions read. Direct calls like await get_positions() issue individual CAN reads and are suitable for diagnostics but too slow for real-time control. start_telemetry is not required if you are already running a motion_control loop — every impedance command sent to the arm motors returns a position and torque reading, which is used to populate the same cache automatically.