mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-18 18:20:08 +00:00
e963e5a0c4
* refactor: RL stack refactoring — RLAlgorithm, RLTrainer, DataMixer, and SAC restructuring * chore: clarify torch.compile disabled note in SACAlgorithm * fix(teleop): keyboard EE teleop not registering special keys and losing intervention state Fixes #2345 Co-authored-by: jpizarrom <jpizarrom@gmail.com> * fix: remove leftover normalization calls from reward classifier predict_reward Fixes #2355 * fix: add thread synchronization to ReplayBuffer to prevent race condition between add() and sample() * refactor: update SACAlgorithm to pass action_dim to _init_critics and fix encoder reference * perf: remove redundant CPU→GPU→CPU transition move in learner * Fix: add kwargs in reward classifier __init__() * fix: include IS_INTERVENTION in complementary_info sent to learner for offline replay buffer * fix: add try/finally to control_loop to ensure image writer cleanup on exit * fix: use string key for IS_INTERVENTION in complementary_info to avoid torch.load serialization error * fix: skip tests that require grpc if not available * fix(tests): ensure tensor stats comparison accounts for reshaping in normalization tests * fix(tests): skip tests that require grpc if not available * refactor(rl): expose public API in rl/__init__ and use relative imports in sub-packages * fix(config): update vision encoder model name to lerobot/resnet10 * fix(sac): clarify torch.compile status * refactor(rl): update shutdown_event type hints from 'any' to 'Any' for consistency and clarity * refactor(sac): simplify optimizer return structure * perf(rl): use async iterators in OnlineOfflineMixer.get_iterator * refactor(sac): decouple algorithm hyperparameters from policy config * update losses names in tests * fix docstring * remove unused type alias * fix test for flat dict structure * refactor(policies): rename policies/sac → policies/gaussian_actor * refactor(rl/sac): consolidate hyperparameter ownership and clean up discrete critic * perf(observation_processor): add CUDA support for image processing * fix(rl): correctly wire HIL-SERL gripper penalty through processor pipeline (cherry picked from commit9c2af818ff) * fix(rl): add time limit processor to environment pipeline (cherry picked from commitcd105f65cb) * fix(rl): clarify discrete gripper action mapping in GripperVelocityToJoint for SO100 (cherry picked from commit494f469a2b) * fix(rl): update neutral gripper action (cherry picked from commit9c9064e5be) * fix(rl): merge environment and action-processor info in transition processing (cherry picked from commit30e1886b64) * fix(rl): mirror gym_manipulator in actor (cherry picked from commitd2a046dfc5) * fix(rl): postprocess action in actor (cherry picked from commitc2556439e5) * fix(rl): improve action processing for discrete and continuous actions (cherry picked from commitf887ab3f6a) * fix(rl): enhance intervention handling in actor and learner (cherry picked from commitef8bfffbd7) * Revert "perf(observation_processor): add CUDA support for image processing" This reverts commit38b88c414c. * refactor(rl): make algorithm a nested config so all SAC hyperparameters are JSON-addressable * refactor(rl): add make_algorithm_config function for RLAlgorithmConfig instantiation * refactor(rl): add type property to RLAlgorithmConfig for better clarity * refactor(rl): make RLAlgorithmConfig an abstract base class for better extensibility * refactor(tests): remove grpc import checks from test files for cleaner code * fix(tests): gate RL tests on the `datasets` extra * refactor: simplify docstrings for clarity and conciseness across multiple files * fix(rl): update gripper position key and handle action absence during reset * fix(rl): record pre-step observation so (obs, action, next.reward) align in gym_manipulator dataset * refactor: clean up import statements * chore: address reviewer comments * chore: improve visual stats reshaping logic and update docstring for clarity * refactor: enforce mandatory config_class and name attributes in RLAlgorithm * refactor: implement NotImplementedError for abstract methods in RLAlgorithm and DataMixer * refactor: replace build_algorithm with make_algorithm for SACAlgorithmConfig and update related tests * refactor: add require_package calls for grpcio and gym-hil in relevant modules * refactor(rl): move grpcio guards to runtime entry points * feat(rl): consolidate HIL-SERL checkpoint into HF-style components Make `RLAlgorithmConfig` and `RLAlgorithm` `HubMixin`s, add abstract `state_dict()` / `load_state_dict()` for critic ensemble, target nets and `log_alpha`, and persist them as a sibling `algorithm/` component next to `pretrained_model/`. Replace the pickled `training_state.pt` with an enriched `training_step.json` carrying `step` and `interaction_step`, so resume restores actor + critics + target nets + temperature + optimizers + RNG + counters from HF-standard files. * refactor(rl): move actor weight-sync wire format from policy to algorithm * refactor(rl): update type hints for learner and actor functions * refactor(rl): hoist grpcio guard to module top in actor/learner * chore(rl): manage import pattern in actor (#3564) * chore(rl): manage import pattern in actor * chore(rl): optional grpc imports in learner; quote grpc ServicerContext types --------- Co-authored-by: Khalil Meftah <khalil.meftah@huggingface.co> * update uv.lock * chore(doc): update doc --------- Co-authored-by: jpizarrom <jpizarrom@gmail.com> Co-authored-by: Steven Palma <imstevenpmwork@ieee.org>
197 lines
6.4 KiB
Python
197 lines
6.4 KiB
Python
#!/usr/bin/env python
|
|
|
|
# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import pytest
|
|
|
|
from lerobot.configs.types import FeatureType, NormalizationMode, PolicyFeature
|
|
from lerobot.policies.gaussian_actor.configuration_gaussian_actor import (
|
|
ActorLearnerConfig,
|
|
ActorNetworkConfig,
|
|
ConcurrencyConfig,
|
|
CriticNetworkConfig,
|
|
GaussianActorConfig,
|
|
PolicyConfig,
|
|
)
|
|
from lerobot.utils.constants import ACTION, OBS_IMAGE, OBS_STATE
|
|
|
|
|
|
def test_gaussian_actor_config_default_initialization():
|
|
config = GaussianActorConfig()
|
|
|
|
assert config.normalization_mapping == {
|
|
"VISUAL": NormalizationMode.MEAN_STD,
|
|
"STATE": NormalizationMode.MIN_MAX,
|
|
"ENV": NormalizationMode.MIN_MAX,
|
|
"ACTION": NormalizationMode.MIN_MAX,
|
|
}
|
|
assert config.dataset_stats == {
|
|
OBS_IMAGE: {
|
|
"mean": [0.485, 0.456, 0.406],
|
|
"std": [0.229, 0.224, 0.225],
|
|
},
|
|
OBS_STATE: {
|
|
"min": [0.0, 0.0],
|
|
"max": [1.0, 1.0],
|
|
},
|
|
ACTION: {
|
|
"min": [0.0, 0.0, 0.0],
|
|
"max": [1.0, 1.0, 1.0],
|
|
},
|
|
}
|
|
|
|
# Basic parameters
|
|
assert config.device == "cpu"
|
|
assert config.storage_device == "cpu"
|
|
|
|
# Architecture specifics
|
|
assert config.vision_encoder_name is None
|
|
assert config.freeze_vision_encoder is True
|
|
assert config.image_encoder_hidden_dim == 32
|
|
assert config.shared_encoder is True
|
|
assert config.num_discrete_actions is None
|
|
assert config.image_embedding_pooling_dim == 8
|
|
assert config.state_encoder_hidden_dim == 256
|
|
assert config.latent_dim == 256
|
|
|
|
# Training parameters
|
|
assert config.online_steps == 1000000
|
|
assert config.online_buffer_capacity == 100000
|
|
assert config.offline_buffer_capacity == 100000
|
|
assert config.async_prefetch is False
|
|
assert config.online_step_before_learning == 100
|
|
|
|
# Dataset stats defaults
|
|
expected_dataset_stats = {
|
|
OBS_IMAGE: {
|
|
"mean": [0.485, 0.456, 0.406],
|
|
"std": [0.229, 0.224, 0.225],
|
|
},
|
|
OBS_STATE: {
|
|
"min": [0.0, 0.0],
|
|
"max": [1.0, 1.0],
|
|
},
|
|
ACTION: {
|
|
"min": [0.0, 0.0, 0.0],
|
|
"max": [1.0, 1.0, 1.0],
|
|
},
|
|
}
|
|
assert config.dataset_stats == expected_dataset_stats
|
|
|
|
# Actor network configuration
|
|
assert config.actor_network_kwargs.hidden_dims == [256, 256]
|
|
assert config.actor_network_kwargs.activate_final is True
|
|
|
|
# Policy configuration
|
|
assert config.policy_kwargs.use_tanh_squash is True
|
|
assert config.policy_kwargs.std_min == 1e-5
|
|
assert config.policy_kwargs.std_max == 10.0
|
|
assert config.policy_kwargs.init_final == 0.05
|
|
|
|
# Discrete critic network configuration
|
|
assert config.discrete_critic_network_kwargs.hidden_dims == [256, 256]
|
|
assert config.discrete_critic_network_kwargs.activate_final is True
|
|
assert config.discrete_critic_network_kwargs.final_activation is None
|
|
|
|
# Actor learner configuration
|
|
assert config.actor_learner_config.learner_host == "127.0.0.1"
|
|
assert config.actor_learner_config.learner_port == 50051
|
|
assert config.actor_learner_config.policy_parameters_push_frequency == 4
|
|
|
|
# Concurrency configuration
|
|
assert config.concurrency.actor == "threads"
|
|
assert config.concurrency.learner == "threads"
|
|
|
|
assert isinstance(config.actor_network_kwargs, ActorNetworkConfig)
|
|
assert isinstance(config.policy_kwargs, PolicyConfig)
|
|
assert isinstance(config.actor_learner_config, ActorLearnerConfig)
|
|
assert isinstance(config.concurrency, ConcurrencyConfig)
|
|
|
|
|
|
def test_critic_network_kwargs():
|
|
config = CriticNetworkConfig()
|
|
assert config.hidden_dims == [256, 256]
|
|
assert config.activate_final is True
|
|
assert config.final_activation is None
|
|
|
|
|
|
def test_actor_network_kwargs():
|
|
config = ActorNetworkConfig()
|
|
assert config.hidden_dims == [256, 256]
|
|
assert config.activate_final is True
|
|
|
|
|
|
def test_policy_kwargs():
|
|
config = PolicyConfig()
|
|
assert config.use_tanh_squash is True
|
|
assert config.std_min == 1e-5
|
|
assert config.std_max == 10.0
|
|
assert config.init_final == 0.05
|
|
|
|
|
|
def test_actor_learner_config():
|
|
config = ActorLearnerConfig()
|
|
assert config.learner_host == "127.0.0.1"
|
|
assert config.learner_port == 50051
|
|
assert config.policy_parameters_push_frequency == 4
|
|
|
|
|
|
def test_concurrency_config():
|
|
config = ConcurrencyConfig()
|
|
assert config.actor == "threads"
|
|
assert config.learner == "threads"
|
|
|
|
|
|
def test_gaussian_actor_config_custom_initialization():
|
|
config = GaussianActorConfig(
|
|
device="cpu",
|
|
latent_dim=128,
|
|
state_encoder_hidden_dim=128,
|
|
num_discrete_actions=3,
|
|
)
|
|
|
|
assert config.device == "cpu"
|
|
assert config.latent_dim == 128
|
|
assert config.state_encoder_hidden_dim == 128
|
|
assert config.num_discrete_actions == 3
|
|
|
|
|
|
def test_validate_features():
|
|
config = GaussianActorConfig(
|
|
input_features={OBS_STATE: PolicyFeature(type=FeatureType.STATE, shape=(10,))},
|
|
output_features={ACTION: PolicyFeature(type=FeatureType.ACTION, shape=(3,))},
|
|
)
|
|
config.validate_features()
|
|
|
|
|
|
def test_validate_features_missing_observation():
|
|
config = GaussianActorConfig(
|
|
input_features={"wrong_key": PolicyFeature(type=FeatureType.STATE, shape=(10,))},
|
|
output_features={ACTION: PolicyFeature(type=FeatureType.ACTION, shape=(3,))},
|
|
)
|
|
with pytest.raises(
|
|
ValueError, match="You must provide either 'observation.state' or an image observation"
|
|
):
|
|
config.validate_features()
|
|
|
|
|
|
def test_validate_features_missing_action():
|
|
config = GaussianActorConfig(
|
|
input_features={OBS_STATE: PolicyFeature(type=FeatureType.STATE, shape=(10,))},
|
|
output_features={"wrong_key": PolicyFeature(type=FeatureType.ACTION, shape=(3,))},
|
|
)
|
|
with pytest.raises(ValueError, match="You must provide 'action' in the output features"):
|
|
config.validate_features()
|