vla-jepa relative actions

This commit is contained in:
Maxime Ellerbach
2026-07-21 08:31:00 +00:00
parent a12a0b23ca
commit e8b8acc138
2 changed files with 26 additions and 0 deletions
@@ -56,6 +56,14 @@ class VLAJEPAConfig(PreTrainedConfig):
action_dim: int = 7 action_dim: int = 7
state_dim: int = 8 state_dim: int = 8
# Relative actions: converts absolute actions to relative (action -= state) during
# preprocessing, and reverses it at postprocessing. Requires `state_dim` (OBS_STATE).
use_relative_actions: bool = False
# Joint names to keep absolute (not converted to relative). Empty list = all dims relative.
relative_exclude_joints: list[str] = field(default_factory=lambda: ["gripper"])
# Populated at runtime from dataset metadata by make_policy (used to build the exclude mask).
action_feature_names: list[str] | None = None
num_action_tokens_per_timestep: int = 8 num_action_tokens_per_timestep: int = 8
num_embodied_action_tokens_per_instruction: int = 32 num_embodied_action_tokens_per_instruction: int = 32
num_inference_timesteps: int = 4 num_inference_timesteps: int = 4
@@ -20,6 +20,7 @@ import torch
from lerobot.policies.vla_jepa.configuration_vla_jepa import VLAJEPAConfig from lerobot.policies.vla_jepa.configuration_vla_jepa import VLAJEPAConfig
from lerobot.processor import ( from lerobot.processor import (
AbsoluteActionsProcessorStep,
AddBatchDimensionProcessorStep, AddBatchDimensionProcessorStep,
DeviceProcessorStep, DeviceProcessorStep,
EnvTransition, EnvTransition,
@@ -28,6 +29,7 @@ from lerobot.processor import (
PolicyProcessorPipeline, PolicyProcessorPipeline,
ProcessorStep, ProcessorStep,
ProcessorStepRegistry, ProcessorStepRegistry,
RelativeActionsProcessorStep,
RenameObservationsProcessorStep, RenameObservationsProcessorStep,
TransitionKey, TransitionKey,
UnnormalizerProcessorStep, UnnormalizerProcessorStep,
@@ -112,10 +114,21 @@ def make_vla_jepa_pre_post_processors(
PolicyProcessorPipeline[PolicyAction, PolicyAction], PolicyProcessorPipeline[PolicyAction, PolicyAction],
]: ]:
features = {**config.input_features, **config.output_features} features = {**config.input_features, **config.output_features}
# Shared relative-action step (OpenPI order: raw -> relative -> normalize -> model ->
# unnormalize -> absolute). The SAME instance is passed to AbsoluteActionsProcessorStep
# below so its cached raw state (set during preprocessing) flows to postprocessing.
relative_step = RelativeActionsProcessorStep(
enabled=config.use_relative_actions,
exclude_joints=getattr(config, "relative_exclude_joints", []),
action_names=getattr(config, "action_feature_names", None),
)
input_steps = [ input_steps = [
RenameObservationsProcessorStep(rename_map={}), RenameObservationsProcessorStep(rename_map={}),
AddBatchDimensionProcessorStep(), AddBatchDimensionProcessorStep(),
DeviceProcessorStep(device=config.device), DeviceProcessorStep(device=config.device),
relative_step,
NormalizerProcessorStep( NormalizerProcessorStep(
features=features, features=features,
norm_map=config.normalization_mapping, norm_map=config.normalization_mapping,
@@ -136,6 +149,11 @@ def make_vla_jepa_pre_post_processors(
stats=dataset_stats, stats=dataset_stats,
) )
) )
# Reverse the relative conversion on the unnormalized action, before gripper binarization.
# gripper is kept absolute by relative_exclude_joints, so the two steps touch disjoint dims.
output_steps.append(
AbsoluteActionsProcessorStep(enabled=config.use_relative_actions, relative_step=relative_step)
)
if config.binarize_gripper_action: if config.binarize_gripper_action:
output_steps.append( output_steps.append(
BinarizeGripperProcessorStep(gripper_dim=config.gripper_dim, threshold=config.gripper_threshold) BinarizeGripperProcessorStep(gripper_dim=config.gripper_dim, threshold=config.gripper_threshold)