# 独自のハードウェアを持ち込む

このチュートリアルでは、独自のロボットデザインをLeRobotエコシステムに統合し、当社のすべてのツール（データ収集、制御パイプライン、ポリシーの学習と推論）にアクセスする方法を説明します。

その目的のため、LeRobotでは物理ロボット統合のための標準インターフェースを指定する[`Robot`](https://github.com/huggingface/lerobot/blob/main/src/lerobot/robots/robot.py)ベースクラスを提供しています。その実装方法を見ていきましょう。

## 前提条件

- 通信インターフェース（例：シリアル、CAN、TCP）を公開する独自のロボット
- センサーデータを読み取り、モーターコマンドをプログラムで送信するための方法。例えば、メーカーのSDKやAPI、または独自のプロトコル実装など。
- 環境にLeRobotがインストールされています。[インストールガイド](./installation)に従ってください。

## モーターを選択

Feetech または Dynamixel モーターを使用している場合、LeRobot はどちらのモーターファミリーも駆動できる組み込みバスインターフェース [`SerialMotorsBus`](https://github.com/huggingface/lerobot/blob/main/src/lerobot/motors/motors_bus.py) を提供しています。各モーターのモデルがどのファミリーに属するかを指定します。API については、そのドキュメントを参照してください。この機能には `serial-motors` エクストラが必要で、これにより rustypot がインストールされます: `pip install 'lerobot[serial-motors]'`、またはソースチェックアウトから `pip install -e ".[serial-motors]"` を実行してください。使用例として、私たちの [SO101 フォロー実装](https://github.com/huggingface/lerobot/blob/main/src/lerobot/robots/so_follower/so_follower.py) をご覧ください。

互換性がある場合はこれらを使用してください。互換性がない場合は、Python インターフェースを見つけるか、自分で作成する必要があります（このチュートリアルでは扱いません）：

- Pythonで既存のSDKを見つける（またはC/C++へのバインディングを使用する）
- または、基本的な通信ラッパーを実装します（例：pyserial、socket、または CANopen を使用）

あなただけではありません。コミュニティの貢献の多くが、カスタムボードやファームウェアを使用しています！

FeetechとDynamixelについては、現在これらのサーボに対応しています:

- Feetech STS & SMS シリーズ: `sts3215`、`sts3250`、`sm8512bl`
- Feetech SCSシリーズ: `scs0009`
- Dynamixel X シリーズ（プロトコル 2.0）: `xl330-m077`、`xl330-m288`、`xc330-t181`、`xc330-t288`、`xl430-w250`、`xl430-w250-2`、`xc430-w150`、`xm430-w350`、`xm540-w270`、`xh540-w150`

モデルはバス、STS/SMSおよびSCSサーボ、またはXL430とXL330を共有できます。各モーターは、そのモーター固有のモデル定義、バイトオーダー、符号エンコーディングを介して読み書きされます。ファミリー間の違いは、同じ定義から生じます。すなわち、どの動作モード値が位置制御を意味するか（`bus.set_operating_mode("position")`）、ホーミングオフセットが位置に足し算されるのか引き算されるのか、どのレジスタが存在するかなどです。

モデルをサポートしているからといって、すべてのキャリブレーションヘルパーがそのモデルで動作するわけではありません。Feetech SCS0009には動作モードもホーミングオフセットもないため、`set_operating_mode()`および`set_half_turn_homings()`はこれを拒否します。以下のキャリブレーション手順は、STSのように両方を持つサーボ向けです。SCSサーボは範囲のみでキャリブレーションされます。

このリストにない Feetech または Dynamixel サーボを使用している場合は、バスが読み取る制御テーブルを保持している [rustypot](https://github.com/pollen-robotics/rustypot) に追加してください。既存の制御テーブルを共有するモデルの場合は、モデル番号の登録だけで十分ですが、新しいモデルの場合はサーボの定義が必要です（[サーボ貢献ガイド](https://github.com/pollen-robotics/rustypot/blob/v1.13.1/src/servo/README.md)を参照）。LeRobot はインストール済みの rustypot Python パッケージからモデルを読み取るため、新しいモデルを含む必要があります。具体的には、そのモデルを含む rustypot のリリース版、またはソースからビルドした rustypot です。

次のセクションでは、例としてFeetechモーターを使用します。必要に応じて、モデルを変更し、お使いのモーターに合わせて調整してください。

## ステップ1: `Robot`インターフェースをサブクラス化

まず、ロボット用の設定クラスと文字列識別子（`name`）を指定する必要があります。ロボットに特別な要件があり、それを簡単に変更できるようにしたい場合は、ここで指定してください（例：ポート/アドレス、ボーレート）。

ここでは、ロボット用にポート名と1つのカメラをデフォルトで追加します。

```python
from dataclasses import dataclass, field

from lerobot.cameras import CameraConfig
from lerobot.cameras.opencv import OpenCVCameraConfig
from lerobot.robots import RobotConfig

@RobotConfig.register_subclass("my_cool_robot")
@dataclass
class MyCoolRobotConfig(RobotConfig):
    port: str
    cameras: dict[str, CameraConfig] = field(
        default_factory=lambda: {
            "cam_1": OpenCVCameraConfig(
                index_or_path=2,
                fps=30,
                width=480,
                height=640,
            ),
        }
    )
```

カメラの検出と追加の方法を理解するための[カメラのチュートリアル](./cameras)。

次に、`Robot`を継承する実際のロボットクラスを作成します。この抽象クラスは、LeRobotの他のツールでロボットを使用するために従う必要がある契約を定義しています。

ここでは、1つのカメラを備えたシンプルな5自由度のロボットを作成します。単純なアームであっても構いませんが、`Robot`抽象クラスはロボットの形状について何も仮定していません。新しいロボットを設計する際は、想像力を存分に発揮してください。

```python
from typing import Any

from lerobot.cameras import make_cameras_from_configs
from lerobot.motors import Motor, MotorCalibration, MotorNormMode, SerialMotorsBus
from lerobot.robots import Robot

class MyCoolRobot(Robot):
    config_class = MyCoolRobotConfig
    name = "my_cool_robot"

    def __init__(self, config: MyCoolRobotConfig):
        super().__init__(config)
        self.config = config
        self.bus = SerialMotorsBus(
            port=self.config.port,
            motors={
                "joint_1": Motor(1, "sts3250", MotorNormMode.RANGE_M100_100),
                "joint_2": Motor(2, "sts3215", MotorNormMode.RANGE_M100_100),
                "joint_3": Motor(3, "sts3215", MotorNormMode.RANGE_M100_100),
                "joint_4": Motor(4, "sts3215", MotorNormMode.RANGE_M100_100),
                "joint_5": Motor(5, "sts3215", MotorNormMode.RANGE_M100_100),
            },
            calibration=self.calibration,
        )
        self.cameras = make_cameras_from_configs(config.cameras)
```

## ステップ2: 観測とアクションの特徴を定義する

これら2つのプロパティは、ロボットとそれを消費するツール（データ収集や学習パイプラインなど）の間の_インターフェース契約_を定義します。

> [!WARNING]
> これらのプロパティは、ロボットがまだ接続されていない場合でも呼び出せる必要があるため、それらの定義にランタイムのハードウェア状態に依存しないようにしてください。

### `observation_features`

このプロパティは、ロボットからのセンサー出力の構造を記述する辞書を返す必要があります。キーは`get_observation()`が返すものと一致し、値は配列/画像の場合は形状、単純な値の場合は型を記述します。

カメラ1台を備えた5自由度アームの例:

```python
@property
def _motors_ft(self) -> dict[str, type]:
    return {
        "joint_1.pos": float,
        "joint_2.pos": float,
        "joint_3.pos": float,
        "joint_4.pos": float,
        "joint_5.pos": float,
    }

@property
def _cameras_ft(self) -> dict[str, tuple]:
    return {
        cam: (self.cameras[cam].height, self.cameras[cam].width, 3) for cam in self.cameras
    }

@property
def observation_features(self) -> dict:
    return {**self._motors_ft, **self._cameras_ft}
```

この場合、観測値は各モーターの位置とカメラ画像を格納するシンプルな辞書（dict）で構成されています。

### `action_features`

このプロパティは、`send_action()`を介してロボットが期待するコマンドを記述します。同様に、キーは期待される入力形式と一致する必要があり、値は各コマンドの形状/型を定義します。

ここでは、`observation_features` と同様に、同じ関節の固有受容性特徴量 (`self._motors_ft`) を使用します。送信されるアクションは、各モーターの目標位置そのものになります。

```python
@property
def action_features(self) -> dict:
    return self._motors_ft
```

## ステップ3: 接続と切断の処理

これらのメソッドは、ハードウェア（シリアルポート、CANインターフェース、USBデバイス、カメラなど）との通信の開始と終了を処理する必要があります。

### `is_connected`

このプロパティは、ロボットのハードウェアとの通信が確立されていることを示すだけで構いません。このプロパティが`True`の場合、`get_observation()`および`send_action()`を使用してハードウェアの読み書きが可能である必要があります。

```python
@property
def is_connected(self) -> bool:
    return self.bus.is_connected and all(cam.is_connected for cam in self.cameras.values())
```

### `connect()`

このメソッドは、ハードウェアとの通信を確立する必要があります。さらに、ロボットにキャリブレーションが必要で、まだキャリブレーションが実行されていない場合、デフォルトでキャリブレーション手順を開始する必要があります。ロボットに特定の構成が必要な場合、その構成もここで呼び出す必要があります。

```python
def connect(self, calibrate: bool = True) -> None:
    self.bus.connect()
    if not self.is_calibrated and calibrate:
        self.calibrate()

    for cam in self.cameras.values():
        cam.connect()

    self.configure()
```

### `disconnect()`

このメソッドは、ハードウェアとの通信を正常に終了する必要があります。関連するリソース（スレッドやプロセス）を解放し、ポートを閉じるなどの処理を実行してください。

ここでは、`SerialMotorsBus`クラスと`Camera`クラスでこの処理をすでに実装しているため、それぞれの`disconnect()`メソッドを呼び出すだけで済みます。

```python
def disconnect(self) -> None:
    self.bus.disconnect()
    for cam in self.cameras.values():
        cam.disconnect()
```

## ステップ4:キャリブレーションと設定のサポート

LeRobotはキャリブレーションデータの保存と読み込みを自動的にサポートしています。これは、関節オフセット、ゼロ位置、またはセンサーの整列に役立ちます。

> ハードウェアによって、この点が適用されない場合があります。その場合は、これらのメソッドを何もしない（no-op）としてそのままにしてください。

```python
@property
def is_calibrated(self) -> bool:
    return True

def calibrate(self) -> None:
    pass
```

### `is_calibrated`

これは、ロボットに必要なキャリブレーションが読み込まれているかどうかを反映するはずです。

```python
@property
def is_calibrated(self) -> bool:
    return self.bus.is_calibrated
```

### `calibrate()`

キャリブレーションの目的は2つあります:

- 各モーターの物理的な可動範囲を把握し、この範囲内でのみコマンドを送信するようにしてください。
- 使用しているモーターに依存する、他の環境では再現できない任意の離散値ではなく、パーセンテージや角度などの妥当な連続値に、生のモーター位置を正規化します。

キャリブレーション（関連する場合）のロジックを実装し、`self.calibration`辞書を更新する必要があります。FeetechまたはDynamixelモーターを使用している場合、`SerialMotorsBus`はすでに手動でキャリブレーションを実行しています：

1. `_keep_calibration_file()` は、この ID のキャリブレーションファイルを再利用するかどうかを尋ねます。保持を選択した場合、モーターに書き込まれ、キャリブレーションはそこで終了します。
2. `record_calibration()` はモーターを解放し、指定した動作モードに設定します。
3. 各関節を可動域の中央に保持するよう求めます。その姿勢が、各モーターの半回転になります。
4. 各関節を動かす範囲を記録し、ENTERキーを押すまで続行します。無限に回転する関節は`full_turn_motors`に格納され、エンコーダーの全範囲が割り当てられます。なお、5自由度アームには該当する関節はありません。
5. `write_calibration()` は結果をモーターに書き込み、`_save_calibration()` はそれをキャリブレーションファイルに保存します。

```python
def calibrate(self) -> None:
    if self._keep_calibration_file(self.bus):
        return

    self.calibration = self.bus.record_calibration("position", full_turn_motors=[])
    self.bus.write_calibration(self.calibration)
    self._save_calibration()
    print("Calibration saved to", self.calibration_fpath)
```

### `configure()`

これを使用して、ハードウェア（サーボの制御モード、コントローラーのゲインなど）の任意の構成を設定します。通常、接続時に実行され、冪等である必要があります。

```python
def configure(self) -> None:
    with self.bus.torque_disabled():
        self.bus.configure_motors()
        for motor in self.bus.motors:
            self.bus.set_operating_mode("position", motor)
            self.bus.write("P_Coefficient", motor, 16)
            self.bus.write("I_Coefficient", motor, 0)
            self.bus.write("D_Coefficient", motor, 32)
```

## ステップ5: センサーの読み取りとアクションの送信を実装する

これらは最も重要なランタイム関数であり、コアのI/Oループです。

### `get_observation()`

ロボットからセンサー値の辞書を返します。これには通常、モーターの状態、カメラのフレーム、各種センサーなどが含まれます。LeRobotフレームワークでは、これらの観測値がポリシーに与えられ、実行すべきアクションを予測するために使用されます。辞書のキーと構造は`observation_features`と一致する必要があります。

```python
def get_observation(self) -> dict[str, Any]:
    if not self.is_connected:
        raise ConnectionError(f"{self} is not connected.")

    # Read arm position
    obs_dict = self.bus.sync_read("Present_Position")
    obs_dict = {f"{motor}.pos": val for motor, val in obs_dict.items()}

    # Capture images from cameras
    for cam_key, cam in self.cameras.items():
        obs_dict[cam_key] = cam.async_read()

    return obs_dict
```

### `send_action()`

`action_features` と一致する辞書を受け取り、ハードウェアに送信します。安全制限（クリッピング、スムージング）を追加し、実際に送信された内容を返すことができます。

簡便さのため、ここでは例の中でアクションの修正は行いません。

```python
def send_action(self, action: dict[str, Any]) -> dict[str, Any]:
    goal_pos = {key.removesuffix(".pos"): val for key, val in action.items()}

    # Send goal position to the arm
    self.bus.sync_write("Goal_Position", goal_pos)

    return action
```

### 予測間の待機

通常のローカル RTC は、新しいロボット機能なしでも引き続き使用できます。リモートロールアウト、計画された非同期の言語一時停止、および回復可能なアクション飢餓には、保持ターゲットの待機契約が必要です。すべてのアクションフィールドを`send_action()`を通じて更新することがコントローラーにとって安全な場合のみ、`supports_position_hold = True`でオプトインしてください。通常、これらのフィールドは実際の位置ターゲットです。オンボードコントローラーのバランス維持が繰り返しの実行によって保たれる場合、コントローラーは SONIC の潜在運動トークンなどの、別途検証された保持コマンドでオプトインすることもできます。トルクイネーブルや`.pos`サフィックスだけでは、この契約は確立されません。速度/トルクおよび歩行コマンドは引き続き不適切です。

リセットコマンドなしで待機できるロボット（`send_action()` の呼び出し時のみ進行するシミュレータなど）は、`supports_command_hold = True` を設定し、`hold()` を実装します。これは位置ホールドの代わりに使用されます。

共有のロールアウト実装は、`send_action()` が返した最後の完全な有限アクション（グリップおよびドライバによるクリッピングを含む）を更新します。ドライバがアクションを変更する場合は、元の要求ではなく、実際に適用されたターゲットを返してください。その後の観測値の更新では、そのターゲットは置き換えられません。コマンドが適用される前に、検証済みの測定位置のスナップショットによって初期化されます。ロボットは保持されたターゲットに向かって安定する可能性があります。この契約は、即座の物理的な停止を保証するのではなく、軌道の進行を停止します。各デプロイメントにおいて、ハードウェア上でロードサポート、グリップ、およびコマンド更新の動作を検証してください。

holding が有効な場合、カスタムアクションプロセッサおよびテレオペレーション補正は、グリップを含む完全な位置コマンドを保持する必要があります。ドライバーは、すべてのアクションコンポーネントについて、有限の適用値を返す必要があります。コマンド適用後の不完全または無効な返却は契約違反とみなされ、ロールアウトは停止し、古いターゲットの再利用の代わりにホーミングを抑制します。同期ロールアウトでは、コマンドの失敗もホーミングを抑制します。

Remote rollout はこのドライバーの機能を自動的に確認します。追加のホールドモード CLI フラグは不要です。一般的な観測の失敗では、通常のロールアウトのクリーンアップパスが使用されます。初期位置への復帰には完全な観測が必要であり、カメラが利用できない状態が続いている場合は失敗する可能性があります。

| ロボットの実装                                    | ポジションターゲットの待機サポート                                                                                                                                                                                                                                            |
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SO フォロワー (SO-100/SO-101)、OMX、Koch                 | サポートされています。                                                                                                                                                                                                                                                                 |
| HopeJrのアームとハンド                                     | サポートされています。                                                                                                                                                                                                                                                                 |
| Rebot B601                                              | 既存の絶対位置アーム/グリップモード全体でサポートされています。                                                                                                                                                                                                         |
| YAM                                                     | サポートされています。`command_timeout_s` 以内にコマンドが届かない場合、アームは `idle_mode` で設定されたように、その位置を保持するか、浮遊します。                                                                                                                                                     |
| OpenArm                                                 | `use_velocity_and_torque=false` でのサポートに対応しています。混合アクションスキーマは対象外です。                                                                                                                                                                                         |
| BiSO、BiOpenArm、BiRebot B601、BiYAM                    | 両方の下層アームがサポートしている場合にサポートされます。適用されたターゲットには、左/右のプレフィックスが保持されます。                                                                                                                                                                      |
| Reachy2                                                 | `with_mobile_base=false`、`use_external_commands=false`、`max_relative_target=null` の場合のみサポートされます。Mobile-base のコマンドは速度であり、外部モードではターゲットが送信されません。また、クリッピングパスは現在、適用後の値ではなく元の値を返します。 |
| LeKiwi(ネットワーククライアントを含む)、EarthRover Mini Plus | サポートされていません: アクションにはモバイルベースの速度が含まれており、位置ターゲットとして保持することはできません。                                                                                                                                                                         |
| Unitree G1                                              | `SonicWholeBodyController`でのみサポートされています: waitingは、オンボードのデコーダーがバランス調整を継続している間、最後の完全な64次元モーショントークンを繰り返します。GR00T/Holosomaの移動および生の`.q`制御は引き続きサポートされていません。                                                      |

これらの宣言は、ドライバのコマンド契約を反映したものであり、すべての実装やコントローラ設定に対する物理的な検証ではありません。汎用的な`Robot`のデフォルト値は引き続きfalseです。機能チェックは、チェックポイントの互換性、コントローラのチューニング、負荷のサポート、ハードウェアの健全性を確立するものではありません。

## テレオペレーターの追加

テレオペレーションデバイスの実装のために、[`Teleoperator`](https://github.com/huggingface/lerobot/blob/main/src/lerobot/teleoperators/teleoperator.py)ベースクラスも提供しています。このクラスは`Robot`ベースクラスと非常に似ており、フォームファクターについても何も仮定していません。

主な違いは I/O 関数にあります。テレオペレーターでは、`get_action` を介してアクションを生成し、`send_feedback` を介してフィードバックアクションを受信できます。フィードバックとは、テレオペレーション装置上で制御可能であり、操作者が送信したアクションの結果を理解するのに役立つあらゆるものです。例えば、リーダーアームのモーション/フォースフィードバックや、ゲームパッドコントローラーの振動などが挙げられます。テレオペレーターを実装するには、このチュートリアルを参考にし、これら 2 つのメソッドに合わせて適応させることができます。

## ご自身の`LeRobot`デバイスを使用する 🔌

独自のカメラ、ロボット、またはテレオペレーションデバイスなど、カスタムハードウェアを個別のインストール可能な Python パッケージとして作成することで、`lerobot` を簡単に拡張できます。いくつかのシンプルな規約に従うことで、`lerobot` のコマンドラインツール（`lerobot-teleoperate` や `lerobot-record` など）は、`lerobot` のソースコードを変更することなく、**作成したものを自動的に検出し、統合します**。

このガイドでは、プラグインが従う必要がある規約を説明します。

### 4つのコア規約

カスタムデバイスが検出可能であることを保証するには、以下の4つのルールに従う必要があります。

#### 1\. 特定のプレフィックスを持つインストール可能なパッケージを作成する

プロジェクトは、標準的なインストール可能なPythonパッケージである必要があります。特に重要なのは、パッケージ名（`pyproject.toml`または`setup.py`で定義されるもの）が、以下のプレフィックスのいずれかで始まることです。

- ロボットには`lerobot_robot_`を使用します。
- カメラには`lerobot_camera_`を指定します。
- テレオペレーションデバイス用の`lerobot_teleoperator_`
- `lerobot-rollout`戦略用の`lerobot_strategy_`（[独自の戦略の持ち込み](./inference#bring-your-own-strategy)を参照）。

このプレフィックスシステムにより、`lerobot`はPython環境からプラグインを自動的に特定します。

#### 2\. `SomethingConfig`/`Something` の命名パターンに従う

デバイスの実装クラスは、設定クラス名から`Config`サフィックスを除去した名称にしてください。

- **設定クラス:** `MyAwesomeTeleopConfig`
- **デバイスクラス:** `MyAwesomeTeleop`

#### 3\. ファイルを予測可能な構造に配置する

デバイスクラス（`MyAwesomeTeleop`）は、その設定クラス（`MyAwesomeTeleopConfig`）に対して予測可能なモジュール内に配置する必要があります。`lerobot` は自動的に以下の場所を検索します：

- 設定クラスと同じ**モジュール**内。
- **デバイス名に由来するサブモジュール**（例: `my_awesome_teleop.py`）内。

推奨される最もシンプルな構成は、同じディレクトリ内に、明確な名前が付けられた別々のファイルとして配置することです。

#### 4\. `__init__.py` でクラスを公開する

パッケージの`__init__.py`ファイルでは、設定クラスとデバイスクラスをインポートして公開し、簡単にアクセスできるようにする必要があります。

### すべてを統合する：完全な例

新しいテレオペレーター`my_awesome_teleop`を作成しましょう。

#### ディレクトリ構造

プロジェクトフォルダは以下のようになるはずです。パッケージ名`lerobot_teleoperator_my_awesome_teleop`は、**規約 \#1**に従っています。

```
lerobot_teleoperator_my_awesome_teleop/
├── pyproject.toml # (or setup.py) lists lerobot as a dependency
└── lerobot_teleoperator_my_awesome_teleop/
    ├── __init__.py
    ├── config_my_awesome_teleop.py
    └── my_awesome_teleop.py
```

#### ファイルの内容

- **`config_my_awesome_teleop.py`**: 設定クラスを定義します。`Config`サフィックスに注意してください（**規約 \#2**）。

  ```python
  from dataclasses import dataclass

  from lerobot.teleoperators.config import TeleoperatorConfig

  @TeleoperatorConfig.register_subclass("my_awesome_teleop")
  @dataclass
  class MyAwesomeTeleopConfig(TeleoperatorConfig):
      # Your configuration fields go here
      port: str = "192.168.1.1"
  ```

- **`my_awesome_teleop.py`**: デバイスを実装します。クラス名`MyAwesomeTeleop`は、その設定クラス名と一致しています（**規約 \#2**）。このファイル構成は、**規約 \#3**に準拠しています。

  ```python
  from lerobot.teleoperators.teleoperator import Teleoperator

  from .config_my_awesome_teleop import MyAwesomeTeleopConfig

  class MyAwesomeTeleop(Teleoperator):
      config_class = MyAwesomeTeleopConfig
      name = "my_awesome_teleop"

      def __init__(self, config: MyAwesomeTeleopConfig):
          super().__init__(config)
          self.config = config

      # Your device logic (e.g., connect) goes here
  ```

- **`__init__.py`**: 主要なクラスを公開します(**Convention \#4**)。

  ```python
  from .config_my_awesome_teleop import MyAwesomeTeleopConfig
  from .my_awesome_teleop import MyAwesomeTeleop
  ```

### インストールと使用方法

1.  **新しいプラグインをPython環境にインストールします。** ローカルのプラグインパッケージは、`pip`のeditableモードまたはPyPiからインストールできます。

    ```bash
    # Locally
    # Navigate to your plugin's root directory and install it
    cd lerobot_teleoperator_my_awesome_teleop
    pip install -e .

    # From PyPi
    pip install lerobot_teleoperator_my_awesome_teleop
    ```

2.  **コマンドラインから直接使用します。** これにより、デバイスの型を参照してカスタムデバイスを使用できます。

    ```bash
    # Add the other arguments you need (robot type, ports, ids, ...)
    lerobot-teleoperate --teleop.type=my_awesome_teleop ...
    ```

これで完了です\! カスタムデバイスが完全に統合されました。

### 例をお探しですか?

コミュニティは、この方法で作られた数十のプラグインを維持管理しています。お使いのハードウェアに近いものを探すには、[サードパーティ製ロボット & テレオペレーター](./third_party_robots)および[サードパーティ製カメラ & センサー](./third_party_sensors)をご覧ください。あるいは、以下の2つの小さなパッケージから始めることもできます:

- https://github.com/SpesRobotics/lerobot-robot-xarm
- https://github.com/SpesRobotics/lerobot-teleoperator-teleop

## まとめ

ロボットクラスが完成したら、LeRobotエコシステムを活用できます。

- 利用可能なテレオペレーターでロボットを制御するか、テレオペレーションデバイスを直接統合してください
- トレーニングデータを記録して可視化
- RLまたは模倣学習のパイプラインに統合します

プラグインが公開されたら、[Third-Party Robots & Teleoperators](./third_party_robots)または[Third-Party Cameras & Sensors](./third_party_sensors)に追加するPRを開き、他のユーザーが探しやすくしてください。

[Discord](https://discord.gg/s3KuuzsPFb) のコミュニティに遠慮なく助けを求めてください 🤗

