LeRobot documentation
プロセッサの紹介
プロセッサの紹介
ロボティクスにおいて、ロボットや人間が生成するデータと、機械学習モデルが期待するデータの間には根本的な不一致があります。 ロボットはカメラ画像や関節位置などの生センサーデータを出力しますが、モデルがこれらを処理するには、正規化、バッチ処理、デバイスへの配置が必要です。 人間からの言語指示は数値表現にトークン化されなければならず、また、異なるロボットは異なる座標系を使用しているため、標準化が必要です。
この課題はモデルの出力にも及びます。 モデルはエンドエフェクタの位置を出力する場合がありますが、ロボットには関節空間のコマンドが必要です。また、テレオペレーターが相対的な動きを生成する一方で、ロボットは絶対的なコマンドを期待している場合があります。 モデルの予測値は多くの場合正規化されており、実世界のスケールへの変換が必要です。
クロスドメイン翻訳は、さらなる複雑さを伴います。 あるロボット構成で収集した学習データは、異なるハードウェアでのデプロイメントのために適応させる必要があります。特定のカメラ構成で学習されたモデルは、新しい配置でも動作する必要があります。また、命名規則が異なるデータセットは、調和させる必要があります。
ここで登場するのがプロセッサです。 プロセッサはこれらのギャップを埋める万能の翻訳者として機能し、センサーからモデル、そしてアクチュエータへのシームレスなデータフローを確保します。 プロセッサは、生データ(環境データ)をモデルが処理できる入力に変換し、その逆の変換を行うために必要な前処理および後処理のすべてのステップを処理します。
これは、お気に入りのポリシーを次のように使用できることを意味します。
import torch
from lerobot.datasets import LeRobotDataset
from lerobot.policies import make_pre_post_processors
from lerobot.policies.your_policy import YourPolicy
from lerobot.processor import RobotProcessorPipeline, PolicyProcessorPipeline
dataset = LeRobotDataset("hf_user/dataset", episodes=[0])
sample = dataset[10]
model = YourPolicy.from_pretrained(
"hf_user/model",
)
model.eval()
model.to("cuda")
preprocessor, postprocessor = make_pre_post_processors(model.config, pretrained_path="hf_user/model", dataset_stats=dataset.meta.stats)
preprocessed_sample = preprocessor(sample)
action = model.select_action(preprocessed_sample)
postprocessed_action = postprocessor(action)プロセッサとは何ですか?
ロボティクスでは、データは多様な形式で存在します。カメラからの画像、センサーからの関節位置、ユーザーからのテキスト指示などです。各データタイプは、モデルが効果的に利用できるよう、特定の変換を必要とします。モデルには、データが以下の状態である必要があります。
- 正規化: ニューラルネットワークの処理に適した範囲にスケーリング
- バッチ化: バッチ処理に適した適切な次元で整理されています
- トークン化済み: 数値表現に変換されたテキスト
- デバイス配置済み: 適切なハードウェア(CPU/GPU)に移動
- 型変換: 適切なデータ型にキャスト
Processorsは、パイプラインとして連結可能な組み合わせ可能で再利用可能なステップを通じて、これらの変換を処理します。モジュール化されたアセンブリラインと捉え、各ステーションがデータに対して特定の変換を実行すると考えてください。
コアコンセプト
EnvTransition: 汎用的なデータコンテナ
EnvTransitionは、すべてのプロセッサを流れる基本的なデータ構造です。
これは、ロボットと環境の完全な相互作用を表す型付きの辞書です。
- 観察: すべてのセンサーデータ(画像、状態、身体感覚)
- ACTION: 実行するアクション、または実行されたアクション
- REWARD: 強化学習のシグナル
- DONE/TRUNCATED: エピソード境界のインジケーター
- INFO: 任意のメタデータ
- COMPLEMENTARY_DATA: タスクの説明、インデックス、パディングフラグ、ステップ間データ
バッチコンバーターは、COMPLEMENTARY_DATA に index、episode_index、frame_index などのインデックスメタデータを保持します。
batch_to_transition / transition_to_batch の往復変換では、frame_index は元のスカラーまたはテンソルの値を保持します。
入力に frame_index が含まれない場合、コンバーターはこれを追加しません。
ProcessorStep: 基本構成要素
ProcessorStep は、トランジションを処理する単一の変換単位です。これは抽象基底クラスであり、2 つの必須メソッドを持ちます。
from lerobot.processor import ProcessorStep, EnvTransition
class MyProcessorStep(ProcessorStep):
"""Example processor step - inherit and implement abstract methods."""
def __call__(self, transition: EnvTransition) -> EnvTransition:
"""Transform the transition - REQUIRED abstract method."""
# Your processing logic here
return transition
def transform_features(self, features):
"""Declare how this step transforms feature shapes/types - REQUIRED abstract method."""
return features # Most processors return features unchanged__call__はプロセッサのステップの中核です。EnvTransitionを受け取り、変更されたEnvTransitionを返します。
transform_features は、このステップが特徴の形状や型をどのように変換するかを宣言するために使用されます。
DataProcessorPipeline: 汎用オーケストレーター
DataProcessorPipeline[TInput, TOutput] は複数の ProcessorStep インスタンスを連鎖させます:
from lerobot.processor import RobotProcessorPipeline, PolicyProcessorPipeline
# For robot hardware (unbatched data)
robot_processor = RobotProcessorPipeline[RobotAction, RobotAction](
steps=[step1, step2, step3],
name="robot_pipeline"
)
# For model training/inference (batched data)
policy_processor = PolicyProcessorPipeline[dict[str, Any], dict[str, Any]](
steps=[step1, step2, step3],
name="policy_pipeline"
)RobotProcessorPipeline と PolicyProcessorPipeline
重要な違いは、処理するデータ構造にあります。
| アスペクト | RobotProcessorPipeline | PolicyProcessorPipeline |
|---|---|---|
| 入力 | dict[str, Any] - 個々のロボットの値 | dict[str, Any] - バッチ化されたテンソル |
| 出力 | dict[str, Any] - 個々のロボットコマンド | torch.Tensor - ポリシーの予測 |
| ユースケース | リアルタイムロボット制御 | モデルの学習/推論 |
| データ形式 | バッチなし、非一様 | バッチ化、同質 |
| 例 | {"joint_1": 0.5} | {"observation.state": tensor([[0.5]])} |
ロボットハードウェアインターフェースには、RobotProcessorPipelineを使用します:
# Robot data structures: dict[str, Any] for observations and actions
robot_obs: dict[str, Any] = {
"joint_1": 0.5, # Individual joint values
"joint_2": -0.3,
"camera_0": image_array # Raw camera data
}
robot_action: dict[str, Any] = {
"joint_1": 0.2, # Target joint positions
"joint_2": 0.1,
"gripper": 0.8
}モデルの学習とバッチ処理には、PolicyProcessorPipelineを使用します:
# Policy data structures: batch dicts and tensors
policy_batch: dict[str, Any] = {
"observation.state": torch.tensor([[0.5, -0.3]]), # Batched states
"observation.images.camera0": torch.tensor(...), # Batched images
"action": torch.tensor([[0.2, 0.1, 0.8]]) # Batched actions
}
policy_action: torch.Tensor = torch.tensor([[0.2, 0.1, 0.8]]) # Model output tensorコンバーター関数
LeRobotは、lerobot.processor.converters内で異なるデータ形式を橋渡しするコンバーター関数を提供しています。これらの関数は、ロボットハードウェアのデータ構造、ポリシーモデルの形式、そしてプロセッサーパイプラインを流れる内部のEnvTransition表現の間で、重要な変換を処理します。
| カテゴリ | 関数 | 説明 |
|---|---|---|
| ロボットハードウェアコンバーター | robot_action_to_transition | Robot dict → EnvTransition |
observation_to_transition | ロボットの観測 → EnvTransition | |
transition_to_robot_action | EnvTransition → Robot dict | |
| ポリシー/トレーニング コンバーター | batch_to_transition | Batch dict → EnvTransition |
transition_to_batch | EnvTransition → バッチ辞書 | |
policy_action_to_transition | Policy テンソル → EnvTransition | |
transition_to_policy_action | EnvTransition → Policy テンソル | |
| ユーティリティ | create_transition | デフォルト値でトランジションを構築する |
identity_transition | パススルーコンバーター |
重要なポイントは、ロボットハードウェアコンバーターは個々の値や辞書で動作するのに対し、ポリシー/トレーニングコンバーターはバッチ化されたテンソルとモデルの出力で動作する点です。コンバーター関数は構造上の違いを自動的に処理するため、プロセッサーのステップではデータ形式の互換性を気にすることなく、コアの変換に集中できます。
プロセッサの例
以下の例は、ポリシーの学習と推論のための実世界のプロセッサ設定を示しています。
ポリシーの学習と推論用のプロセッサの例を以下に示します。
# Training data preprocessing (optimized order for GPU performance)
training_preprocessor = PolicyProcessorPipeline[dict[str, Any], dict[str, Any]](
steps=[
RenameObservationsProcessorStep(rename_map={}), # Standardize keys
AddBatchDimensionProcessorStep(), # Add batch dims
TokenizerProcessorStep(tokenizer_name="...", ...), # Tokenize language
DeviceProcessorStep(device="cuda"), # Move to GPU first
NormalizerProcessorStep(features=..., stats=...), # Normalize on GPU
]
)
# Model output postprocessing
training_postprocessor = PolicyProcessorPipeline[torch.Tensor, torch.Tensor](
steps=[
DeviceProcessorStep(device="cpu"), # Move to CPU
UnnormalizerProcessorStep(features=..., stats=...), # Denormalize
],
to_transition=policy_action_to_transition,
to_output=transition_to_policy_action,
)プロセッサを備えたロボットとポリシーの相互作用
最も一般的な実世界のシナリオでは、両方のパイプラインタイプが組み合わされます。ロボットハードウェアが観測データを生成し、それにはポリシーの処理が必要であり、ポリシーの出力にはロボット互換の後処理が必要です。
# Real deployment: Robot sensors → Model → Robot commands
with torch.no_grad():
while not done:
raw_obs = robot.get_observation() # dict[str, Any]
# Add your robot observation to policy observation processor
policy_input = policy_preprocessor(raw_obs) # Batched dict
policy_output = policy.select_action(policy_input) # Policy tensor
policy_action = policy_postprocessor(policy_output)
# Add your robot action to policy action processor
robot.send_action(policy_action)Feature Contracts: 形状と型の変換
プロセッサはデータを単に変換するだけでなく、データ構造自体を変更することもできます。transform_features() メソッドはこれらの変更を宣言し、これはデータセットの記録やポリシーの作成において重要です。
フィーチャー契約が重要な理由
データセットやポリシーを構築する際、LeRobotは以下を知る必要があります:
- 処理後に存在するデータフィールド
- 各フィールドが持つ shape と type
- モデルの構成方法(期待されるデータ構造向け)
# Example: A processor that adds velocity to observations
class VelocityProcessor(ObservationProcessorStep):
def observation(self, obs):
new_obs = obs.copy()
if "observation.state" in obs:
# concatenate computed velocity field to the state
new_obs["observation.state"] = self._compute_velocity(obs["observation.state"])
return new_obs
def transform_features(self, features):
"""Declare the new velocity field we're adding."""
state_feature = features[PipelineFeatureType.OBSERVATION].get("observation.state")
if state_feature:
double_shape = (state_feature.shape[0] * 2,) if state_feature.shape else (2,)
features[PipelineFeatureType.OBSERVATION]["observation.state"] = PolicyFeature(
type=FeatureType.STATE, shape=double_shape
)
return features機能仕様関数
create_initial_features() と aggregate_pipeline_dataset_features() は、データセット作成における重要な問題を解決します。それは、データが処理される前に最終的なデータ構造を正確に決定することです。
プロセッサのパイプラインは、新しい特徴量(速度フィールドなど)を追加したり、テンソルの形状を変更したり(画像のクロッピングなど)、キーの名前を変更したりするため、データセットは適切なストレージを割り当て、スキーマを定義するために、事前に完全な出力仕様を知る必要があります。
これらの関数は連携して動作し、ロボットのハードウェア仕様から開始(create_initial_features())し、その後、パイプライン全体の変換をシミュレート(aggregate_pipeline_dataset_features())することで、LeRobotDataset.create() に渡される最終的な特徴量辞書を計算します。これにより、プロセッサが出力するものと、データセットが保存することを期待するものの間で、完全な整合性が確保されます。
from lerobot.datasets import aggregate_pipeline_dataset_features
# Start with robot's raw features
initial_features = create_initial_features(
observation=robot.observation_features, # {"joint_1.pos": float, "camera_0": (480,640,3)}
action=robot.action_features # {"joint_1.pos": float, "gripper.pos": float}
)
# Apply processor pipeline to compute final features
final_features = aggregate_pipeline_dataset_features(
pipeline=my_processor_pipeline,
initial_features=initial_features,
use_videos=True
)
# Use for dataset creation
dataset = LeRobotDataset.create(
repo_id="my_dataset",
features=final_features, # Knows exactly what data to expect
...
)一般的なプロセッサのステップ
LeRobotには多くの登録済みのプロセッサステップが用意されています。以下は、最もよく使用されるコアプロセッサです:
必須プロセッサ
normalizer_processor: データセットの統計量(平均/標準偏差または最小値/最大値)を使用して、観測値/アクションを正規化しますdevice_processor: オプションのdtype変換を伴って、テンソルをCPU/GPUに移動しますto_batch_processor: モデルとの互換性を確保するために、トランジションにバッチ次元を追加しますrename_observations_processor: マッピング辞書を使用して観測キーの名前を変更しますtokenizer_processor: 自然言語のタスク説明をトークンおよびアテンションマスクにトークン化します
次のステップ
- 独自のProcessorを実装する - カスタムのProcessorステップを作成する
- パイプラインのデバッグ - パイプラインのトラブルシューティングと最適化
- ロボットおよびテレオペレーター向けプロセッサ - 実環境での統合パターン
概要
Processorsは、ロボティクスにおけるデータ変換の問題を解決するために、以下を提供します:
- モジュラー変換: 組み合わせ可能で再利用可能な処理ステップ
- 型安全性: コンパイル時チェックを備えた汎用パイプライン
- パフォーマンスの最適化: GPU による高速化
- Robot/Policyの区別: 異なるデータ構造用のパイプラインを分離
- 包括的なエコシステム: 一般的なタスク向けの登録済みプロセッサが30以上
重要なポイント: RobotProcessorPipelineはバッチ化されていないロボットハードウェアデータを処理し、PolicyProcessorPipelineはバッチ化されたモデルデータを処理します。データ構造に合ったツールを選択してください!