# Dataset Factory # Simple factory to create different types of datasets from typing import Dict, Any, List, Optional from omegaconf import OmegaConf import torch def create_dataset(config: OmegaConf, val: bool = False): """ Create dataset based on config. Args: config: Configuration object val: Whether to create validation dataset Returns: Dataset instance """ dataset_type = config.dataset.get('type', 'robotwin') # Default to robotwin if dataset_type == 'robotwin': from .robotwin2.robotwin_agilex_dataset import RobotWinTaskDataset # Get all parameters from config params = {} # Add common parameters if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'video_action_freq_ratio': config.common.video_action_freq_ratio, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), 'state_dim': config.common.state_dim, 'action_dim': config.common.action_dim, }) # Add dataset-specific parameters if hasattr(config.dataset, 'dataset_dir'): params['dataset_dir'] = config.dataset.dataset_dir if hasattr(config.dataset, 'data_mode'): params['data_mode'] = config.dataset.data_mode if hasattr(config.dataset, 'task_mode'): params['task_mode'] = config.dataset.task_mode if hasattr(config.dataset, 'task_name'): params['task_name'] = config.dataset.task_name if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.dataset, 'image_aug'): params['image_aug'] = config.dataset.image_aug and not val # No aug for validation if hasattr(config.dataset, 'randomized_limit_per_task'): params['randomized_limit_per_task'] = config.dataset.randomized_limit_per_task # Add VLM checkpoint path if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path # Add any additional parameters from dataset.params if hasattr(config.dataset, 'params'): additional_params = OmegaConf.to_object(config.dataset.params) params.update(additional_params) # Set validation flag params['val'] = val return RobotWinTaskDataset(**params) elif dataset_type == 'ac_one': from .ac_one.ac_one_dataset import ACOneDataset # Get all parameters from config params = {} # Add common parameters if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'video_action_freq_ratio': config.common.video_action_freq_ratio, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), }) # Add dataset-specific parameters if hasattr(config.dataset, 'dataset_dir'): params['dataset_dir'] = config.dataset.dataset_dir if hasattr(config.dataset, 'task_mode'): params['task_mode'] = config.dataset.task_mode if hasattr(config.dataset, 'task_name'): params['task_name'] = config.dataset.task_name if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.dataset, 'val_episodes'): params['val_episodes'] = config.dataset.val_episodes if hasattr(config.dataset, 'image_aug'): params['image_aug'] = config.dataset.image_aug and not val # No aug for validation # Add VLM checkpoint path if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path # Add any additional parameters from dataset.params if hasattr(config.dataset, 'params'): additional_params = OmegaConf.to_object(config.dataset.params) params.update(additional_params) # Set validation flag params['val'] = val return ACOneDataset(**params) elif dataset_type == 'latent_action': from .latent_action.latent_action_dataset import LatentActionDataset params = {} # Common parameters if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), }) if hasattr(config.dataset, 'dataset_dir'): dataset_dir = list(config.dataset.dataset_dir) params['dataset_dir'] = [str(p) for p in dataset_dir] if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.dataset, 'image_aug'): params['image_aug'] = config.dataset.image_aug and not val # Optional VLM checkpoint path if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path # Optional additional params if hasattr(config.dataset, 'params'): additional_params = OmegaConf.to_object(config.dataset.params) params.update(additional_params) params['val'] = val return LatentActionDataset(**params) elif dataset_type == 'aloha_agilex_2': from .aloha_agilex_2.aloha_agilex2_dataset import AlohaAgilex2Dataset # Get all parameters from config params = {} # Add common parameters if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'video_action_freq_ratio': config.common.video_action_freq_ratio, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), }) # Add dataset-specific parameters if hasattr(config.dataset, 'dataset_dir'): params['dataset_dir'] = config.dataset.dataset_dir if hasattr(config.dataset, 'task_mode'): params['task_mode'] = config.dataset.task_mode if hasattr(config.dataset, 'task_name'): params['task_name'] = config.dataset.task_name if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.dataset, 'val_episodes'): params['val_episodes'] = config.dataset.val_episodes if hasattr(config.dataset, 'image_aug'): params['image_aug'] = config.dataset.image_aug and not val # No aug for validation # Add VLM checkpoint path if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path # Add any additional parameters from dataset.params if hasattr(config.dataset, 'params'): additional_params = OmegaConf.to_object(config.dataset.params) params.update(additional_params) # Set validation flag params['val'] = val return AlohaAgilex2Dataset(**params) elif dataset_type == "manifeel_zarr": from .manifeel.manifeel_zarr_dataset import ManiFeelZarrDataset params = {} if hasattr(config, "common"): params.update({ "global_downsample_rate": config.common.global_downsample_rate, "video_action_freq_ratio": config.common.video_action_freq_ratio, "num_video_frames": config.common.num_video_frames, "video_size": (config.common.video_height, config.common.video_width), }) if hasattr(config.dataset, "dataset_dir"): params["dataset_dir"] = config.dataset.dataset_dir if hasattr(config.dataset, "max_episodes"): params["max_episodes"] = config.dataset.max_episodes if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path params["val"] = val return ManiFeelZarrDataset(**params) elif dataset_type == 'libero': from .libero_dataset import LiberoDataset params = {} if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'video_action_freq_ratio': config.common.video_action_freq_ratio, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), 'state_dim': config.common.state_dim, 'action_dim': config.common.action_dim, }) if hasattr(config.dataset, 'dataset_dir'): params['dataset_dir'] = config.dataset.dataset_dir elif hasattr(config.dataset, 'params') and hasattr(config.dataset.params, 'root'): params['dataset_dir'] = config.dataset.params.root if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path return LiberoDataset(**params) elif dataset_type == 'lerobot': from .lerobot.lerobot_dataset import LeRobotMotusDataset # Get all parameters from config params = {} # Add common parameters if hasattr(config, 'common'): params.update({ 'global_downsample_rate': config.common.global_downsample_rate, 'video_action_freq_ratio': config.common.video_action_freq_ratio, 'num_video_frames': config.common.num_video_frames, 'video_size': (config.common.video_height, config.common.video_width), }) # Add dataset-specific parameters if hasattr(config.dataset, 'dataset_dir'): params['dataset_dir'] = config.dataset.dataset_dir if hasattr(config.dataset, 'task_mode'): params['task_mode'] = config.dataset.task_mode if hasattr(config.dataset, 'task_name'): params['task_name'] = config.dataset.task_name if hasattr(config.dataset, 'max_episodes'): params['max_episodes'] = config.dataset.max_episodes if hasattr(config.dataset, 'image_aug'): params['image_aug'] = config.dataset.image_aug and not val # Add VLM checkpoint path if hasattr(config.model, 'vlm') and hasattr(config.model.vlm, 'checkpoint_path'): params['vlm_checkpoint_path'] = config.model.vlm.checkpoint_path # Add any additional parameters from dataset.params if hasattr(config.dataset, 'params'): additional_params = OmegaConf.to_object(config.dataset.params) params.update(additional_params) # Set validation flag params['val'] = val return LeRobotMotusDataset(**params) # Example: Add more dataset types here # elif dataset_type == 'bridge': # from .bridge_dataset import BridgeDataset # return BridgeDataset(**params) else: raise ValueError(f"Unknown dataset type: {dataset_type}. Available types: robotwin, aloha_agilex_1, ac_one, aloha_agilex_2, table30") def _process_vlm_inputs_batch(vlm_inputs: List[Dict[str, Any]]) -> Dict[str, torch.Tensor]: """Process and batch VLM inputs with padding.""" # Extract components input_ids_list = [vlm_input['input_ids'] for vlm_input in vlm_inputs] pixel_values_list = [vlm_input.get('pixel_values') for vlm_input in vlm_inputs] image_grid_thw_list = [vlm_input.get('image_grid_thw') for vlm_input in vlm_inputs] attention_mask_list = [vlm_input.get('attention_mask') for vlm_input in vlm_inputs] # Pad input_ids to same length (simplified like model implementation) max_seq_len = max(ids.shape[1] for ids in input_ids_list) padded_input_ids = [] padded_attention_masks = [] for ids, mask in zip(input_ids_list, attention_mask_list): if ids.shape[1] < max_seq_len: padding_size = max_seq_len - ids.shape[1] # Pad input_ids padding = torch.zeros(ids.shape[0], padding_size, dtype=ids.dtype, device=ids.device) padded_ids = torch.cat([ids, padding], dim=1) # Pad attention_mask if mask is not None: mask_padding = torch.zeros(mask.shape[0], padding_size, dtype=mask.dtype, device=mask.device) padded_mask = torch.cat([mask, mask_padding], dim=1) else: padded_mask = None else: padded_ids = ids padded_mask = mask padded_input_ids.append(padded_ids) padded_attention_masks.append(padded_mask) # Batch everything return { 'input_ids': torch.cat(padded_input_ids, dim=0), 'pixel_values': torch.cat([pv for pv in pixel_values_list if pv is not None], dim=0) if pixel_values_list and any(pv is not None for pv in pixel_values_list) else None, 'image_grid_thw': torch.cat([igt for igt in image_grid_thw_list if igt is not None], dim=0) if image_grid_thw_list and any(igt is not None for igt in image_grid_thw_list) else None, 'attention_mask': torch.cat([mask for mask in padded_attention_masks if mask is not None], dim=0) if any(mask is not None for mask in padded_attention_masks) else None, } def _process_language_embeddings_batch(language_embeddings: List[torch.Tensor], text_len: int = 512) -> torch.Tensor: """Process and batch language embeddings with padding.""" padded_embeddings = [] for emb in language_embeddings: if emb.shape[0] <= text_len: padded = torch.cat([emb, emb.new_zeros(text_len - emb.shape[0], emb.shape[1])]) else: padded = emb[:text_len] padded_embeddings.append(padded) # Stack to [B, seq_len, dim] return torch.stack(padded_embeddings, dim=0) def collate_fn(batch: List[Optional[Dict[str, Any]]]) -> Optional[Dict[str, Any]]: """ Universal collate function for all datasets. Args: batch: List of sample dictionaries (may contain None) Returns: Batched dictionary or None if all samples are None """ # Filter out None samples batch = [sample for sample in batch if sample is not None] if len(batch) == 0: return None # Stack tensors(支持无 initial_state 的样本) first_frames = torch.stack([sample['first_frame'] for sample in batch]) video_frames = torch.stack([sample['video_frames'] for sample in batch]) action_sequences = torch.stack([sample['action_sequence'] for sample in batch]) has_initial_state = all(('initial_state' in sample and sample['initial_state'] is not None) for sample in batch) initial_states = torch.stack([sample['initial_state'] for sample in batch]) if has_initial_state else None # Process VLM inputs with padding in collate_fn vlm_inputs = [sample.get('vlm_inputs') for sample in batch] processed_vlm_inputs = None if vlm_inputs and all(vlm_input is not None for vlm_input in vlm_inputs): processed_vlm_inputs = _process_vlm_inputs_batch(vlm_inputs) # Process language embeddings with padding in collate_fn language_embeddings = [sample.get('language_embedding') for sample in batch if 'language_embedding' in sample] processed_language_embeddings = None if language_embeddings and any(emb is not None for emb in language_embeddings): processed_language_embeddings = _process_language_embeddings_batch(language_embeddings) result = { 'first_frame': first_frames, # [B, C, H, W] 'video_frames': video_frames, # [B, F, C, H, W] 'action_sequence': action_sequences, # [B, F, D] 'vlm_inputs': processed_vlm_inputs, 'language_embedding': processed_language_embeddings, } if initial_states is not None: result['initial_state'] = initial_states return result# This line is no-op - we'll use sed instead