Buckets:
| <meta charset="utf-8" /><meta name="hf:doc:metadata" content="{"title":"Bring Your Own Hardware","local":"bring-your-own-hardware","sections":[{"title":"Prerequisites","local":"prerequisites","sections":[],"depth":2},{"title":"Choose your motors","local":"choose-your-motors","sections":[],"depth":2},{"title":"Step 1: Subclass the Robot Interface","local":"step-1-subclass-the-robot-interface","sections":[],"depth":2},{"title":"Step 2: Define Observation and Action Features","local":"step-2-define-observation-and-action-features","sections":[{"title":"observation_features","local":"observationfeatures","sections":[],"depth":3},{"title":"action_features","local":"actionfeatures","sections":[],"depth":3}],"depth":2},{"title":"Step 3: Handle Connection and Disconnection","local":"step-3-handle-connection-and-disconnection","sections":[{"title":"is_connected","local":"isconnected","sections":[],"depth":3},{"title":"connect()","local":"connect","sections":[],"depth":3},{"title":"disconnect()","local":"disconnect","sections":[],"depth":3}],"depth":2},{"title":"Step 4: Support Calibration and Configuration","local":"step-4-support-calibration-and-configuration","sections":[{"title":"is_calibrated","local":"iscalibrated","sections":[],"depth":3},{"title":"calibrate()","local":"calibrate","sections":[],"depth":3},{"title":"configure()","local":"configure","sections":[],"depth":3}],"depth":2},{"title":"Step 5: Implement Sensors Reading and Action Sending","local":"step-5-implement-sensors-reading-and-action-sending","sections":[{"title":"get_observation()","local":"getobservation","sections":[],"depth":3},{"title":"send_action()","local":"sendaction","sections":[],"depth":3}],"depth":2},{"title":"Adding a Teleoperator","local":"adding-a-teleoperator","sections":[],"depth":2},{"title":"Using Your Own LeRobot Devices 🔌","local":"using-your-own-lerobot-devices-","sections":[{"title":"The 4 Core Conventions","local":"the-4-core-conventions","sections":[{"title":"1 . Create an Installable Package with a Specific Prefix","local":"1--create-an-installable-package-with-a-specific-prefix","sections":[],"depth":4},{"title":"2 . Follow the SomethingConfig / Something Naming Pattern","local":"2--follow-the-somethingconfig--something-naming-pattern","sections":[],"depth":4},{"title":"3 . Place Your Files in a Predictable Structure","local":"3--place-your-files-in-a-predictable-structure","sections":[],"depth":4},{"title":"4 . Expose Classes in __init__.py","local":"4--expose-classes-in-initpy","sections":[],"depth":4}],"depth":3},{"title":"Putting It All Together: A Complete Example","local":"putting-it-all-together-a-complete-example","sections":[{"title":"Directory Structure","local":"directory-structure","sections":[],"depth":4},{"title":"File Contents","local":"file-contents","sections":[],"depth":4}],"depth":3},{"title":"Installation and Usage","local":"installation-and-usage","sections":[],"depth":3},{"title":"Looking for an example ?","local":"looking-for-an-example-","sections":[],"depth":3}],"depth":2},{"title":"Wrapping Up","local":"wrapping-up","sections":[],"depth":2}],"depth":1}"> | |
| <link href="/docs/lerobot/pr_3313/en/_app/immutable/assets/0.e3b0c442.css" rel="modulepreload"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/entry/start.d3f1c0f3.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/scheduler.eb244325.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/singletons.1f33814c.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/index.3c23fb4b.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/paths.17f05d75.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/entry/app.04bb7687.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/preload-helper.b00aacbc.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/index.3fe63ad3.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/nodes/0.07fbe93e.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/each.e59479a4.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/nodes/28.865e8f6f.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/CopyLLMTxtMenu.d0c64540.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/MermaidChart.svelte_svelte_type_style_lang.6453902c.js"> | |
| <link rel="modulepreload" href="/docs/lerobot/pr_3313/en/_app/immutable/chunks/CodeBlock.48dd2cc2.js"><!-- HEAD_svelte-u9bgzb_START --><meta name="hf:doc:metadata" content="{"title":"Bring Your Own Hardware","local":"bring-your-own-hardware","sections":[{"title":"Prerequisites","local":"prerequisites","sections":[],"depth":2},{"title":"Choose your motors","local":"choose-your-motors","sections":[],"depth":2},{"title":"Step 1: Subclass the Robot Interface","local":"step-1-subclass-the-robot-interface","sections":[],"depth":2},{"title":"Step 2: Define Observation and Action Features","local":"step-2-define-observation-and-action-features","sections":[{"title":"observation_features","local":"observationfeatures","sections":[],"depth":3},{"title":"action_features","local":"actionfeatures","sections":[],"depth":3}],"depth":2},{"title":"Step 3: Handle Connection and Disconnection","local":"step-3-handle-connection-and-disconnection","sections":[{"title":"is_connected","local":"isconnected","sections":[],"depth":3},{"title":"connect()","local":"connect","sections":[],"depth":3},{"title":"disconnect()","local":"disconnect","sections":[],"depth":3}],"depth":2},{"title":"Step 4: Support Calibration and Configuration","local":"step-4-support-calibration-and-configuration","sections":[{"title":"is_calibrated","local":"iscalibrated","sections":[],"depth":3},{"title":"calibrate()","local":"calibrate","sections":[],"depth":3},{"title":"configure()","local":"configure","sections":[],"depth":3}],"depth":2},{"title":"Step 5: Implement Sensors Reading and Action Sending","local":"step-5-implement-sensors-reading-and-action-sending","sections":[{"title":"get_observation()","local":"getobservation","sections":[],"depth":3},{"title":"send_action()","local":"sendaction","sections":[],"depth":3}],"depth":2},{"title":"Adding a Teleoperator","local":"adding-a-teleoperator","sections":[],"depth":2},{"title":"Using Your Own LeRobot Devices 🔌","local":"using-your-own-lerobot-devices-","sections":[{"title":"The 4 Core Conventions","local":"the-4-core-conventions","sections":[{"title":"1 . Create an Installable Package with a Specific Prefix","local":"1--create-an-installable-package-with-a-specific-prefix","sections":[],"depth":4},{"title":"2 . Follow the SomethingConfig / Something Naming Pattern","local":"2--follow-the-somethingconfig--something-naming-pattern","sections":[],"depth":4},{"title":"3 . Place Your Files in a Predictable Structure","local":"3--place-your-files-in-a-predictable-structure","sections":[],"depth":4},{"title":"4 . Expose Classes in __init__.py","local":"4--expose-classes-in-initpy","sections":[],"depth":4}],"depth":3},{"title":"Putting It All Together: A Complete Example","local":"putting-it-all-together-a-complete-example","sections":[{"title":"Directory Structure","local":"directory-structure","sections":[],"depth":4},{"title":"File Contents","local":"file-contents","sections":[],"depth":4}],"depth":3},{"title":"Installation and Usage","local":"installation-and-usage","sections":[],"depth":3},{"title":"Looking for an example ?","local":"looking-for-an-example-","sections":[],"depth":3}],"depth":2},{"title":"Wrapping Up","local":"wrapping-up","sections":[],"depth":2}],"depth":1}"><!-- HEAD_svelte-u9bgzb_END --> <p></p> <div class="items-center shrink-0 min-w-[100px] max-sm:min-w-[50px] justify-end ml-auto flex" style="float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"><div class="inline-flex rounded-md max-sm:rounded-sm"><button class="inline-flex items-center gap-1 h-7 max-sm:h-7 px-2 max-sm:px-1.5 text-sm font-medium text-gray-800 border border-r-0 rounded-l-md max-sm:rounded-l-sm border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-live="polite"><span class="inline-flex items-center justify-center rounded-md p-0.5 max-sm:p-0 hover:text-gray-800 dark:hover:text-gray-200"><svg class="sm:size-3.5 size-3" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg></span> <span>Copy page</span></button> <button class="inline-flex items-center justify-center w-6 max-sm:w-5 h-7 max-sm:h-7 disabled:pointer-events-none text-sm text-gray-500 hover:text-gray-700 dark:hover:text-white rounded-r-md max-sm:rounded-r-sm border border-l transition border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-haspopup="menu" aria-expanded="false" aria-label="Open copy menu"><svg class="transition-transform text-gray-400 overflow-visible sm:size-3.5 size-3 rotate-0" width="1em" height="1em" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L6 6L11 1" stroke="currentColor"></path></svg></button></div> </div> <h1 class="relative group"><a id="bring-your-own-hardware" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#bring-your-own-hardware"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Bring Your Own Hardware</span></h1> <p data-svelte-h="svelte-sl443g">This tutorial will explain how to integrate your own robot design into the LeRobot ecosystem and have it access all of our tools (data collection, control pipelines, policy training and inference).</p> <p data-svelte-h="svelte-yp6pog">To that end, we provide the <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/robots/robot.py" rel="nofollow"><code>Robot</code></a> base class in the LeRobot which specifies a standard interface for physical robot integration. Let’s see how to implement it.</p> <h2 class="relative group"><a id="prerequisites" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#prerequisites"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Prerequisites</span></h2> <ul data-svelte-h="svelte-11itqwk"><li>Your own robot which exposes a communication interface (e.g. serial, CAN, TCP)</li> <li>A way to read sensor data and send motor commands programmatically, e.g. manufacturer’s SDK or API, or your own protocol implementation.</li> <li>LeRobot installed in your environment. Follow our <a href="./installation">Installation Guide</a>.</li></ul> <h2 class="relative group"><a id="choose-your-motors" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#choose-your-motors"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Choose your motors</span></h2> <p data-svelte-h="svelte-9dsyh9">If you’re using Feetech or Dynamixel motors, LeRobot provides built-in bus interfaces:</p> <ul data-svelte-h="svelte-1cfhvoe"><li><a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/feetech/feetech.py" rel="nofollow"><code>FeetechMotorsBus</code></a> – for controlling Feetech servos</li> <li><a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/dynamixel/dynamixel.py" rel="nofollow"><code>DynamixelMotorsBus</code></a> – for controlling Dynamixel servos</li></ul> <p data-svelte-h="svelte-1dzoibz">Please refer to the <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/motors_bus.py" rel="nofollow"><code>MotorsBus</code></a> abstract class to learn about its API. | |
| For a good example of how it can be used, you can have a look at our own <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/robots/so_follower/so101_follower/so101_follower.py" rel="nofollow">SO101 follower implementation</a></p> <p data-svelte-h="svelte-12o83v0">Use these if compatible. Otherwise, you’ll need to find or write a Python interface (not covered in this tutorial):</p> <ul data-svelte-h="svelte-1d5xfjp"><li>Find an existing SDK in Python (or use bindings to C/C++)</li> <li>Or implement a basic communication wrapper (e.g., via pyserial, socket, or CANopen)</li></ul> <p data-svelte-h="svelte-1e113uy">You’re not alone—many community contributions use custom boards or firmware!</p> <p data-svelte-h="svelte-145ejj4">For Feetech and Dynamixel, we currently support these servos: - Feetech: - STS & SMS series (protocol 0): <code>sts3215</code>, <code>sts3250</code>, <code>sm8512bl</code> - SCS series (protocol 1): <code>scs0009</code> - Dynamixel (protocol 2.0 only): <code>xl330-m077</code>, <code>xl330-m288</code>, <code>xl430-w250</code>, <code>xm430-w350</code>, <code>xm540-w270</code>, <code>xc430-w150</code></p> <p data-svelte-h="svelte-wbam8h">If you are using Feetech or Dynamixel servos that are not in this list, you can add those in the <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/feetech/tables.py" rel="nofollow">Feetech table</a> or <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/dynamixel/tables.py" rel="nofollow">Dynamixel table</a>. Depending on the model, this will require you to add model-specific information. In most cases though, there shouldn’t be a lot of additions to do.</p> <p data-svelte-h="svelte-tm2a0s">In the next sections, we’ll use a <code>FeetechMotorsBus</code> as the motors interface for the examples. Replace it and adapt to your motors if necessary.</p> <h2 class="relative group"><a id="step-1-subclass-the-robot-interface" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#step-1-subclass-the-robot-interface"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Step 1: Subclass the Robot Interface</span></h2> <p data-svelte-h="svelte-rc24jv">You’ll first need to specify the config class and a string identifier (<code>name</code>) for your robot. If your robot has special needs that you’d like to be able to change easily, it should go here (e.g. port/address, baudrate).</p> <p data-svelte-h="svelte-1p0w0g0">Here, we’ll add the port name and one camera by default for our robot:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> dataclasses <span class="hljs-keyword">import</span> dataclass, field | |
| <span class="hljs-keyword">from</span> lerobot.cameras <span class="hljs-keyword">import</span> CameraConfig | |
| <span class="hljs-keyword">from</span> lerobot.cameras.opencv <span class="hljs-keyword">import</span> OpenCVCameraConfig | |
| <span class="hljs-keyword">from</span> lerobot.robots <span class="hljs-keyword">import</span> RobotConfig | |
| <span class="hljs-meta">@RobotConfig.register_subclass(<span class="hljs-params"><span class="hljs-string">"my_cool_robot"</span></span>)</span> | |
| <span class="hljs-meta">@dataclass</span> | |
| <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyCoolRobotConfig</span>(<span class="hljs-title class_ inherited__">RobotConfig</span>): | |
| port: <span class="hljs-built_in">str</span> | |
| cameras: <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, CameraConfig] = field( | |
| default_factory={ | |
| <span class="hljs-string">"cam_1"</span>: OpenCVCameraConfig( | |
| index_or_path=<span class="hljs-number">2</span>, | |
| fps=<span class="hljs-number">30</span>, | |
| width=<span class="hljs-number">480</span>, | |
| height=<span class="hljs-number">640</span>, | |
| ), | |
| } | |
| )<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-1ieof37"><a href="./cameras">Cameras tutorial</a> to understand how to detect and add your camera.</p> <p data-svelte-h="svelte-dxku0t">Next, we’ll create our actual robot class which inherits from <code>Robot</code>. This abstract class defines a contract you must follow for your robot to be usable with the rest of the LeRobot tools.</p> <p data-svelte-h="svelte-nb93rz">Here we’ll create a simple 5-DoF robot with one camera. It could be a simple arm but notice that the <code>Robot</code> abstract class does not assume anything on your robot’s form factor. You can let you imagination run wild when designing new robots!</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> lerobot.cameras <span class="hljs-keyword">import</span> make_cameras_from_configs | |
| <span class="hljs-keyword">from</span> lerobot.motors <span class="hljs-keyword">import</span> Motor, MotorNormMode | |
| <span class="hljs-keyword">from</span> lerobot.motors.feetech <span class="hljs-keyword">import</span> FeetechMotorsBus | |
| <span class="hljs-keyword">from</span> lerobot.robots <span class="hljs-keyword">import</span> Robot | |
| <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyCoolRobot</span>(<span class="hljs-title class_ inherited__">Robot</span>): | |
| config_class = MyCoolRobotConfig | |
| name = <span class="hljs-string">"my_cool_robot"</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, config: MyCoolRobotConfig</span>): | |
| <span class="hljs-built_in">super</span>().__init__(config) | |
| self.bus = FeetechMotorsBus( | |
| port=self.config.port, | |
| motors={ | |
| <span class="hljs-string">"joint_1"</span>: Motor(<span class="hljs-number">1</span>, <span class="hljs-string">"sts3250"</span>, MotorNormMode.RANGE_M100_100), | |
| <span class="hljs-string">"joint_2"</span>: Motor(<span class="hljs-number">2</span>, <span class="hljs-string">"sts3215"</span>, MotorNormMode.RANGE_M100_100), | |
| <span class="hljs-string">"joint_3"</span>: Motor(<span class="hljs-number">3</span>, <span class="hljs-string">"sts3215"</span>, MotorNormMode.RANGE_M100_100), | |
| <span class="hljs-string">"joint_4"</span>: Motor(<span class="hljs-number">4</span>, <span class="hljs-string">"sts3215"</span>, MotorNormMode.RANGE_M100_100), | |
| <span class="hljs-string">"joint_5"</span>: Motor(<span class="hljs-number">5</span>, <span class="hljs-string">"sts3215"</span>, MotorNormMode.RANGE_M100_100), | |
| }, | |
| calibration=self.calibration, | |
| ) | |
| self.cameras = make_cameras_from_configs(config.cameras)<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="step-2-define-observation-and-action-features" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#step-2-define-observation-and-action-features"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Step 2: Define Observation and Action Features</span></h2> <p data-svelte-h="svelte-l9j1d3">These two properties define the <em>interface contract</em> between your robot and tools that consume it (such as data collection or learning pipelines).</p> <blockquote class="warning" data-svelte-h="svelte-5ox74y"><p>Note that these properties must be callable even if the robot is not yet connected, so avoid relying on runtime hardware state to define them.</p></blockquote> <h3 class="relative group"><a id="observationfeatures" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#observationfeatures"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>observation_features</span></h3> <p data-svelte-h="svelte-u7pcb4">This property should return a dictionary describing the structure of sensor outputs from your robot. The keys match what <code>get_observation()</code> returns, and the values describe either the shape (for arrays/images) or the type (for simple values).</p> <p data-svelte-h="svelte-1t42iyr">Example for our 5-DoF arm with one camera:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">_motors_ft</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-built_in">type</span>]: | |
| <span class="hljs-keyword">return</span> { | |
| <span class="hljs-string">"joint_1.pos"</span>: <span class="hljs-built_in">float</span>, | |
| <span class="hljs-string">"joint_2.pos"</span>: <span class="hljs-built_in">float</span>, | |
| <span class="hljs-string">"joint_3.pos"</span>: <span class="hljs-built_in">float</span>, | |
| <span class="hljs-string">"joint_4.pos"</span>: <span class="hljs-built_in">float</span>, | |
| <span class="hljs-string">"joint_5.pos"</span>: <span class="hljs-built_in">float</span>, | |
| } | |
| <span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">_cameras_ft</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-built_in">tuple</span>]: | |
| <span class="hljs-keyword">return</span> { | |
| cam: (self.cameras[cam].height, self.cameras[cam].width, <span class="hljs-number">3</span>) <span class="hljs-keyword">for</span> cam <span class="hljs-keyword">in</span> self.cameras | |
| } | |
| <span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">observation_features</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">dict</span>: | |
| <span class="hljs-keyword">return</span> {**self._motors_ft, **self._cameras_ft}<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-1rkp57y">In this case, observations consist of a simple dict storing each motor’s position and a camera image.</p> <h3 class="relative group"><a id="actionfeatures" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#actionfeatures"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>action_features</span></h3> <p data-svelte-h="svelte-12yyl6o">This property describes the commands your robot expects via <code>send_action()</code>. Again, keys must match the expected input format, and values define the shape/type of each command.</p> <p data-svelte-h="svelte-1h5avx7">Here, we simply use the same joints proprioceptive features (<code>self._motors_ft</code>) as with <code>observation_features</code>: the action sent will simply the goal position for each motor.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">action_features</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">dict</span>: | |
| <span class="hljs-keyword">return</span> self._motors_ft<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="step-3-handle-connection-and-disconnection" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#step-3-handle-connection-and-disconnection"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Step 3: Handle Connection and Disconnection</span></h2> <p data-svelte-h="svelte-h4v7d7">These methods should handle opening and closing communication with your hardware (e.g. serial ports, CAN interfaces, USB devices, cameras).</p> <h3 class="relative group"><a id="isconnected" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#isconnected"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>is_connected</span></h3> <p data-svelte-h="svelte-1gpnath">This property should simply reflect that communication with the robot’s hardware is established. When this property is <code>True</code>, it should be possible to read and write to the hardware using <code>get_observation()</code> and <code>send_action()</code>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">is_connected</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">bool</span>: | |
| <span class="hljs-keyword">return</span> self.bus.is_connected <span class="hljs-keyword">and</span> <span class="hljs-built_in">all</span>(cam.is_connected <span class="hljs-keyword">for</span> cam <span class="hljs-keyword">in</span> self.cameras.values())<!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="connect" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#connect"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>connect()</span></h3> <p data-svelte-h="svelte-1iphpc4">This method should establish communication with the hardware. Moreover, if your robot needs calibration and is not calibrated, it should start a calibration procedure by default. If your robot needs some specific configuration, this should also be called here.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">connect</span>(<span class="hljs-params">self, calibrate: <span class="hljs-built_in">bool</span> = <span class="hljs-literal">True</span></span>) -> <span class="hljs-literal">None</span>: | |
| self.bus.connect() | |
| <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.is_calibrated <span class="hljs-keyword">and</span> calibrate: | |
| self.calibrate() | |
| <span class="hljs-keyword">for</span> cam <span class="hljs-keyword">in</span> self.cameras.values(): | |
| cam.connect() | |
| self.configure()<!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="disconnect" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#disconnect"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>disconnect()</span></h3> <p data-svelte-h="svelte-lfuhuo">This method should gracefully terminate communication with the hardware: free any related resources (threads or processes), close ports, etc.</p> <p data-svelte-h="svelte-1seywak">Here, we already handle this in our <code>MotorsBus</code> and <code>Camera</code> classes so we just need to call their own <code>disconnect()</code> methods:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">disconnect</span>(<span class="hljs-params">self</span>) -> <span class="hljs-literal">None</span>: | |
| self.bus.disconnect() | |
| <span class="hljs-keyword">for</span> cam <span class="hljs-keyword">in</span> self.cameras.values(): | |
| cam.disconnect()<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="step-4-support-calibration-and-configuration" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#step-4-support-calibration-and-configuration"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Step 4: Support Calibration and Configuration</span></h2> <p data-svelte-h="svelte-1e7cuec">LeRobot supports saving and loading calibration data automatically. This is useful for joint offsets, zero positions, or sensor alignment.</p> <blockquote data-svelte-h="svelte-9w4yye"><p>Note that depending on your hardware, this may not apply. If that’s the case, you can simply leave these methods as no-ops:</p></blockquote> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">is_calibrated</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">bool</span>: | |
| <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">calibrate</span>(<span class="hljs-params">self</span>) -> <span class="hljs-literal">None</span>: | |
| <span class="hljs-keyword">pass</span><!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="iscalibrated" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#iscalibrated"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>is_calibrated</span></h3> <p data-svelte-h="svelte-10g0kc3">This should reflect whether your robot has the required calibration loaded.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-meta">@property</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">is_calibrated</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">bool</span>: | |
| <span class="hljs-keyword">return</span> self.bus.is_calibrated<!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="calibrate" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#calibrate"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>calibrate()</span></h3> <p data-svelte-h="svelte-15x8ibh">The goal of the calibration is twofold:</p> <ul data-svelte-h="svelte-1ka4i7r"><li>Know the physical range of motion of each motors in order to only send commands within this range.</li> <li>Normalize raw motors positions to sensible continuous values (e.g. percentages, degrees) instead of arbitrary discrete value dependant on the specific motor used that will not replicate elsewhere.</li></ul> <p data-svelte-h="svelte-7etria">It should implement the logic for calibration (if relevant) and update the <code>self.calibration</code> dictionary. If you are using Feetech or Dynamixel motors, our bus interfaces already include methods to help with this.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">calibrate</span>(<span class="hljs-params">self</span>) -> <span class="hljs-literal">None</span>: | |
| self.bus.disable_torque() | |
| <span class="hljs-keyword">for</span> motor <span class="hljs-keyword">in</span> self.bus.motors: | |
| self.bus.write(<span class="hljs-string">"Operating_Mode"</span>, motor, OperatingMode.POSITION.value) | |
| <span class="hljs-built_in">input</span>(<span class="hljs-string">f"Move <span class="hljs-subst">{self}</span> to the middle of its range of motion and press ENTER...."</span>) | |
| homing_offsets = self.bus.set_half_turn_homings() | |
| <span class="hljs-built_in">print</span>( | |
| <span class="hljs-string">"Move all joints sequentially through their entire ranges "</span> | |
| <span class="hljs-string">"of motion.\nRecording positions. Press ENTER to stop..."</span> | |
| ) | |
| range_mins, range_maxes = self.bus.record_ranges_of_motion() | |
| self.calibration = {} | |
| <span class="hljs-keyword">for</span> motor, m <span class="hljs-keyword">in</span> self.bus.motors.items(): | |
| self.calibration[motor] = MotorCalibration( | |
| <span class="hljs-built_in">id</span>=m.<span class="hljs-built_in">id</span>, | |
| drive_mode=<span class="hljs-number">0</span>, | |
| homing_offset=homing_offsets[motor], | |
| range_min=range_mins[motor], | |
| range_max=range_maxes[motor], | |
| ) | |
| self.bus.write_calibration(self.calibration) | |
| self._save_calibration() | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"Calibration saved to"</span>, self.calibration_fpath)<!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="configure" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#configure"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>configure()</span></h3> <p data-svelte-h="svelte-1gxojkw">Use this to set up any configuration for your hardware (servos control modes, controller gains, etc.). This should usually be run at connection time and be idempotent.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">configure</span>(<span class="hljs-params">self</span>) -> <span class="hljs-literal">None</span>: | |
| <span class="hljs-keyword">with</span> self.bus.torque_disabled(): | |
| self.bus.configure_motors() | |
| <span class="hljs-keyword">for</span> motor <span class="hljs-keyword">in</span> self.bus.motors: | |
| self.bus.write(<span class="hljs-string">"Operating_Mode"</span>, motor, OperatingMode.POSITION.value) | |
| self.bus.write(<span class="hljs-string">"P_Coefficient"</span>, motor, <span class="hljs-number">16</span>) | |
| self.bus.write(<span class="hljs-string">"I_Coefficient"</span>, motor, <span class="hljs-number">0</span>) | |
| self.bus.write(<span class="hljs-string">"D_Coefficient"</span>, motor, <span class="hljs-number">32</span>)<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="step-5-implement-sensors-reading-and-action-sending" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#step-5-implement-sensors-reading-and-action-sending"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Step 5: Implement Sensors Reading and Action Sending</span></h2> <p data-svelte-h="svelte-1qfd9tp">These are the most important runtime functions: the core I/O loop.</p> <h3 class="relative group"><a id="getobservation" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#getobservation"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>get_observation()</span></h3> <p data-svelte-h="svelte-adteeq">Returns a dictionary of sensor values from the robot. These typically include motor states, camera frames, various sensors, etc. In the LeRobot framework, these observations are what will be fed to a policy in order to predict the actions to take. The dictionary keys and structure must match <code>observation_features</code>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_observation</span>(<span class="hljs-params">self</span>) -> <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]: | |
| <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.is_connected: | |
| <span class="hljs-keyword">raise</span> ConnectionError(<span class="hljs-string">f"<span class="hljs-subst">{self}</span> is not connected."</span>) | |
| <span class="hljs-comment"># Read arm position</span> | |
| obs_dict = self.bus.sync_read(<span class="hljs-string">"Present_Position"</span>) | |
| obs_dict = {<span class="hljs-string">f"<span class="hljs-subst">{motor}</span>.pos"</span>: val <span class="hljs-keyword">for</span> motor, val <span class="hljs-keyword">in</span> obs_dict.items()} | |
| <span class="hljs-comment"># Capture images from cameras</span> | |
| <span class="hljs-keyword">for</span> cam_key, cam <span class="hljs-keyword">in</span> self.cameras.items(): | |
| obs_dict[cam_key] = cam.async_read() | |
| <span class="hljs-keyword">return</span> obs_dict<!-- HTML_TAG_END --></pre></div> <h3 class="relative group"><a id="sendaction" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#sendaction"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>send_action()</span></h3> <p data-svelte-h="svelte-1elar01">Takes a dictionary that matches <code>action_features</code>, and sends it to your hardware. You can add safety limits (clipping, smoothing) and return what was actually sent.</p> <p data-svelte-h="svelte-xmm0bj">For simplicity, we won’t be adding any modification of the actions in our example here.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">send_action</span>(<span class="hljs-params">self, action: <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]</span>) -> <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]: | |
| goal_pos = {key.removesuffix(<span class="hljs-string">".pos"</span>): val <span class="hljs-keyword">for</span> key, val <span class="hljs-keyword">in</span> action.items()} | |
| <span class="hljs-comment"># Send goal position to the arm</span> | |
| self.bus.sync_write(<span class="hljs-string">"Goal_Position"</span>, goal_pos) | |
| <span class="hljs-keyword">return</span> action<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="adding-a-teleoperator" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#adding-a-teleoperator"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Adding a Teleoperator</span></h2> <p data-svelte-h="svelte-1s7uvme">For implementing teleoperation devices, we also provide a <a href="https://github.com/huggingface/lerobot/blob/main/src/lerobot/teleoperators/teleoperator.py" rel="nofollow"><code>Teleoperator</code></a> base class. This class is very similar to the <code>Robot</code> base class and also doesn’t assume anything on form factor.</p> <p data-svelte-h="svelte-19bs1hk">The main differences are in the I/O functions: a teleoperator allows you to produce action via <code>get_action</code> and can receive feedback actions via <code>send_feedback</code>. Feedback could be anything controllable on the teleoperation device that could help the person controlling it understand the consequences of the actions sent. Think motion/force feedback on a leader arm, vibrations on a gamepad controller for example. To implement a teleoperator, you can follow this same tutorial and adapt it for these two methods.</p> <h2 class="relative group"><a id="using-your-own-lerobot-devices-" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#using-your-own-lerobot-devices-"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Using Your Own LeRobot Devices 🔌</span></h2> <p data-svelte-h="svelte-b4gh3p">You can easily extend <code>lerobot</code> with your own custom hardware—be it a camera, robot, or teleoperation device—by creating a separate, installable Python package. If you follow a few simple conventions, the <code>lerobot</code> command-line tools (like <code>lerobot-teleop</code> and <code>lerobot-record</code>) will <strong>automatically discover and integrate your creations</strong> without requiring any changes to the <code>lerobot</code> source code.</p> <p data-svelte-h="svelte-1nx86ge">This guide outlines the conventions your plugin must follow.</p> <h3 class="relative group"><a id="the-4-core-conventions" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-4-core-conventions"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>The 4 Core Conventions</span></h3> <p data-svelte-h="svelte-wkwf1l">To ensure your custom device is discoverable, you must adhere to the following four rules.</p> <h4 class="relative group"><a id="1--create-an-installable-package-with-a-specific-prefix" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#1--create-an-installable-package-with-a-specific-prefix"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>1 . Create an Installable Package with a Specific Prefix</span></h4> <p data-svelte-h="svelte-ngx3o7">Your project must be a standard, installable Python package. Crucially, the name of your package (as defined in <code>pyproject.toml</code> or <code>setup.py</code>) must begin with one of these prefixes:</p> <ul data-svelte-h="svelte-1if2wr2"><li><code>lerobot_robot_</code> for a robot.</li> <li><code>lerobot_camera_</code> for a camera.</li> <li><code>lerobot_teleoperator_</code> for a teleoperation device.</li></ul> <p data-svelte-h="svelte-1g5440j">This prefix system is how <code>lerobot</code> automatically finds your plugin in the Python environment.</p> <h4 class="relative group"><a id="2--follow-the-somethingconfig--something-naming-pattern" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#2--follow-the-somethingconfig--something-naming-pattern"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>2 . Follow the SomethingConfig / Something Naming Pattern</span></h4> <p data-svelte-h="svelte-hvi6c3">Your device’s implementation class must be named after its configuration class, simply by removing the <code>Config</code> suffix.</p> <ul data-svelte-h="svelte-one2g8"><li><strong>Config Class:</strong> <code>MyAwesomeTeleopConfig</code></li> <li><strong>Device Class:</strong> <code>MyAwesomeTeleop</code></li></ul> <h4 class="relative group"><a id="3--place-your-files-in-a-predictable-structure" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#3--place-your-files-in-a-predictable-structure"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>3 . Place Your Files in a Predictable Structure</span></h4> <p data-svelte-h="svelte-1oiom90">The device class (<code>MyAwesomeTeleop</code>) must be located in a predictable module relative to its configuration class (<code>MyAwesomeTeleopConfig</code>). <code>lerobot</code> will automatically search in these locations:</p> <ul data-svelte-h="svelte-1631zmm"><li>In the <strong>same module</strong> as the config class.</li> <li>In a <strong>submodule named after the device</strong> (e.g., <code>my_awesome_teleop.py</code>).</li></ul> <p data-svelte-h="svelte-8jrjeo">The recommended and simplest structure is to place them in separate, clearly named files within the same directory.</p> <h4 class="relative group"><a id="4--expose-classes-in-initpy" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#4--expose-classes-in-initpy"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>4 . Expose Classes in __init__.py</span></h4> <p data-svelte-h="svelte-5us40e">Your package’s <code>__init__.py</code> file should import and expose both the configuration and the device classes, making them easily accessible.</p> <h3 class="relative group"><a id="putting-it-all-together-a-complete-example" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#putting-it-all-together-a-complete-example"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Putting It All Together: A Complete Example</span></h3> <p data-svelte-h="svelte-1vhmw5s">Let’s create a new teleoperator called <code>my_awesome_teleop</code>.</p> <h4 class="relative group"><a id="directory-structure" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#directory-structure"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Directory Structure</span></h4> <p data-svelte-h="svelte-pqm0z">Here is what the project folder should look like. The package name, <code>lerobot_teleoperator_my_awesome_teleop</code>, follows <strong>Convention #1</strong>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START -->lerobot_teleoperator_my_awesome_teleop/ | |
| ├── pyproject.toml # (<span class="hljs-built_in">or</span> setup.<span class="hljs-keyword">py</span>) lists lerobot <span class="hljs-keyword">as</span> <span class="hljs-keyword">a</span> dependency | |
| └── lerobot_teleoperator_my_awesome_teleop/ | |
| ├── __init__.<span class="hljs-keyword">py</span> | |
| ├── config_my_awesome_teleop.<span class="hljs-keyword">py</span> | |
| └── my_awesome_teleop.<span class="hljs-keyword">py</span><!-- HTML_TAG_END --></pre></div> <h4 class="relative group"><a id="file-contents" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#file-contents"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>File Contents</span></h4> <ul><li><p data-svelte-h="svelte-1bkxmuc"><strong><code>config_my_awesome_teleop.py</code></strong>: Defines the configuration class. Note the <code>Config</code> suffix (<strong>Convention #2</strong>).</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> dataclasses <span class="hljs-keyword">import</span> dataclass | |
| <span class="hljs-keyword">from</span> lerobot.teleoperators.config <span class="hljs-keyword">import</span> TeleoperatorConfig | |
| <span class="hljs-meta">@TeleoperatorConfig.register_subclass(<span class="hljs-params"><span class="hljs-string">"my_awesome_teleop"</span></span>)</span> | |
| <span class="hljs-meta">@dataclass</span> | |
| <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyAwesomeTeleopConfig</span>(<span class="hljs-title class_ inherited__">TeleoperatorConfig</span>): | |
| <span class="hljs-comment"># Your configuration fields go here</span> | |
| port: <span class="hljs-built_in">str</span> = <span class="hljs-string">"192.168.1.1"</span><!-- HTML_TAG_END --></pre></div></li> <li><p data-svelte-h="svelte-xwteld"><strong><code>my_awesome_teleop.py</code></strong>: Implements the device. The class name <code>MyAwesomeTeleop</code> matches its config class name (<strong>Convention #2</strong>). This file structure adheres to <strong>Convention #3</strong>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> lerobot.teleoperators.teleoperator <span class="hljs-keyword">import</span> Teleoperator | |
| <span class="hljs-keyword">from</span> .config_my_awesome_teleop <span class="hljs-keyword">import</span> MyAwesomeTeleopConfig | |
| <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyAwesomeTeleop</span>(<span class="hljs-title class_ inherited__">Teleoperator</span>): | |
| config_class = MyAwesomeTeleopConfig | |
| name = <span class="hljs-string">"my_awesome_teleop"</span> | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, config: MyAwesomeTeleopConfig</span>): | |
| <span class="hljs-built_in">super</span>().__init__(config) | |
| self.config = config | |
| <span class="hljs-comment"># Your device logic (e.g., connect) goes here</span><!-- HTML_TAG_END --></pre></div></li> <li><p data-svelte-h="svelte-iyngrk"><strong><code>__init__.py</code></strong>: Exposes the key classes (<strong>Convention #4</strong>).</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> .config_my_awesome_teleop <span class="hljs-keyword">import</span> MyAwesomeTeleopConfig | |
| <span class="hljs-keyword">from</span> .my_awesome_teleop <span class="hljs-keyword">import</span> MyAwesomeTeleop<!-- HTML_TAG_END --></pre></div></li></ul> <h3 class="relative group"><a id="installation-and-usage" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#installation-and-usage"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Installation and Usage</span></h3> <ol><li><p data-svelte-h="svelte-1x64a52"><strong>Install your new plugin in your Python environment.</strong> You can install your local plugin package using <code>pip</code>’s editable mode or from PyPi.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-comment"># Locally</span> | |
| <span class="hljs-comment"># Navigate to your plugin's root directory and install it</span> | |
| <span class="hljs-built_in">cd</span> lerobot_teleoperator_my_awesome_teleop | |
| pip install -e . | |
| <span class="hljs-comment"># From PyPi</span> | |
| pip install lerobot_teleoperator_my_awesome_teleop<!-- HTML_TAG_END --></pre></div></li> <li><p data-svelte-h="svelte-rb903a"><strong>Use it directly from the command line.</strong> Now, you can use your custom device by referencing its type.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START -->lerobot-teleoperate --teleop.type=my_awesome_teleop \ | |
| <span class="hljs-comment"># other arguments</span><!-- HTML_TAG_END --></pre></div></li></ol> <p data-svelte-h="svelte-1u6sxd">And that’s it! Your custom device is now fully integrated.</p> <h3 class="relative group"><a id="looking-for-an-example-" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#looking-for-an-example-"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Looking for an example ?</span></h3> <p data-svelte-h="svelte-4e719u">Check out these two packages from the community:</p> <ul data-svelte-h="svelte-1rdiovu"><li><a href="https://github.com/SpesRobotics/lerobot-robot-xarm" rel="nofollow">https://github.com/SpesRobotics/lerobot-robot-xarm</a></li> <li><a href="https://github.com/SpesRobotics/lerobot-teleoperator-teleop" rel="nofollow">https://github.com/SpesRobotics/lerobot-teleoperator-teleop</a></li></ul> <h2 class="relative group"><a id="wrapping-up" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#wrapping-up"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Wrapping Up</span></h2> <p data-svelte-h="svelte-ta7vyn">Once your robot class is complete, you can leverage the LeRobot ecosystem:</p> <ul data-svelte-h="svelte-16m8r0w"><li>Control your robot with available teleoperators or integrate directly your teleoperating device</li> <li>Record training data and visualize it</li> <li>Integrate it into RL or imitation learning pipelines</li></ul> <p data-svelte-h="svelte-o90m39">Don’t hesitate to reach out to the community for help on our <a href="https://discord.gg/s3KuuzsPFb" rel="nofollow">Discord</a> 🤗</p> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/huggingface/lerobot/blob/main/docs/source/integrate_hardware.mdx" target="_blank"><svg class="mr-1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M31,16l-7,7l-1.41-1.41L28.17,16l-5.58-5.59L24,9l7,7z"></path><path d="M1,16l7-7l1.41,1.41L3.83,16l5.58,5.59L8,23l-7-7z"></path><path d="M12.419,25.484L17.639,6.552l1.932,0.518L14.351,26.002z"></path></svg> <span data-svelte-h="svelte-zjs2n5"><span class="underline">Update</span> on GitHub</span></a> <p></p> | |
| <script> | |
| { | |
| __sveltekit_9kza6s = { | |
| assets: "/docs/lerobot/pr_3313/en", | |
| base: "/docs/lerobot/pr_3313/en", | |
| env: {} | |
| }; | |
| const element = document.currentScript.parentElement; | |
| const data = [null,null]; | |
| Promise.all([ | |
| import("/docs/lerobot/pr_3313/en/_app/immutable/entry/start.d3f1c0f3.js"), | |
| import("/docs/lerobot/pr_3313/en/_app/immutable/entry/app.04bb7687.js") | |
| ]).then(([kit, app]) => { | |
| kit.start(app, element, { | |
| node_ids: [0, 28], | |
| data, | |
| form: null, | |
| error: null | |
| }); | |
| }); | |
| } | |
| </script> | |
Xet Storage Details
- Size:
- 102 kB
- Xet hash:
- 335847dd2448eb7c477a8cc2b212c32b5ebbadb6fa75000811eb480adddafe7a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.