Download payload/src/b1k/shared/correction_rules.py from davidwdw/fa-b1k-solution-code-metadata-d12eb4b63fc6: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/davidwdw/fa-b1k-solution-code-metadata-d12eb4b63fc6/resolve/main/payload/src/b1k/shared/correction_rules.py
- Command line
-
hf download hf://davidwdw/fa-b1k-solution-code-metadata-d12eb4b63fc6/payload/src/b1k/shared/correction_rules.py
-
curl -L -o correction_rules.py https://huggingface.co/davidwdw/fa-b1k-solution-code-metadata-d12eb4b63fc6/resolve/main/payload/src/b1k/shared/correction_rules.py
12.1 kB
| """ | |
| Evaluation tricks for improving success rates on BEHAVIOR-1K tasks. | |
| This module contains: | |
| 1. Correction rules: Task-specific problem resolution (stage reset, gripper recovery) | |
| 2. Gripper variation check: Disable compression when grippers have large movement | |
| Enable via --apply-eval-tricks flag (default: False) | |
| """ | |
| import logging | |
| import numpy as np | |
| from typing import Tuple, Optional | |
| logger = logging.getLogger(__name__) | |
| # ============================================================================ | |
| # GLOBAL CONSTANTS | |
| # ============================================================================ | |
| # Gripper thresholds (normalized [-1, 1] range) | |
| # Grippers are normalized from raw [0, 0.1] to [-1, 1]: | |
| # -1.0 = fully closed (raw 0.0) | |
| # -0.9 = mostly closed (raw 0.005) | |
| # 0.0 = half open (raw 0.05) | |
| # 0.9 = mostly open (raw 0.095) | |
| # 1.0 = fully open (raw 0.1) | |
| OPEN_THRESHOLD = 0.90 | |
| CLOSED_THRESHOLD = -0.98 # very strict threshold to avoid false positives | |
| # Gripper variation threshold for compression check | |
| GRIPPER_VARIATION_THRESHOLD = 0.2 | |
| # State indices | |
| LEFT_GRIPPER_IDX = 14 | |
| RIGHT_GRIPPER_IDX = 22 | |
| LEFT_ARM_START_IDX = 7 # Left arm: state[7:14] | |
| RIGHT_ARM_START_IDX = 15 # Right arm: state[15:22] | |
| LEFT_ARM_END_IDX = 14 | |
| RIGHT_ARM_END_IDX = 22 | |
| # Base positions for arms (7 joints each) | |
| BASE_POSITION_LEFT_ARM = np.array([-0.2, 0.0, 0.1, -1.4, 0.1, 0.7, 0.0]) | |
| BASE_POSITION_RIGHT_ARM = np.array([-0.2, 0.0, 0.1, -1.4, 0.1, 0.7, 0.0]) | |
| # ============================================================================ | |
| # GRIPPER CORRECTION POLICY | |
| # ============================================================================ | |
| # Tasks where grippers should ALWAYS be open (any closure is an error) | |
| ALWAYS_OPEN_LEFT_GRIPPER_TASKS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 47, 48} | |
| ALWAYS_OPEN_RIGHT_GRIPPER_TASKS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 34, 35, 36, 37, 42, 43, 44, 47, 48, 49} | |
| # Tasks where closure is not allowed BEFORE specified stage | |
| # Format: {task_id: {'left': min_stage, 'right': min_stage}} | |
| MIN_STAGE_FOR_CLOSURE = { | |
| 0: {'left': 2, 'right': 2}, # Radio: allow closing from stage 2+ | |
| 30: {'right': 6}, # setting_the_fire | |
| 31: {'right': 8}, # clean_boxing_gloves | |
| 32: {'right': 5}, # wash_a_baseball_cap | |
| 33: {'right': 11}, # wash_dog_toys | |
| 40: {'right': 4}, # make_microwave_popcorn | |
| 41: {'left': 14, 'right': 14}, # cook_cabbage | |
| 45: {'right': 10}, # cook_hot_dogs | |
| 46: {'left': 8, 'right': 8}, # cook_bacon | |
| 49: {'left': 14}, # make_pizza | |
| } | |
| # Tasks where right gripper is ALWAYS allowed to close (exceptions) | |
| RIGHT_GRIPPER_ALWAYS_ALLOWED = {38, 39} # spraying_for_bugs, spraying_fruit_trees | |
| # ============================================================================ | |
| # CORRECTION RULES - ADD NEW RULES HERE | |
| # ============================================================================ | |
| def task0_stage4_reset_to_stage2( | |
| task_id: int, | |
| stage: int, | |
| state: np.ndarray, | |
| actions: np.ndarray | |
| ) -> Optional[Tuple[np.ndarray, int]]: | |
| # Probably not very important for the final solution | |
| """ | |
| Task 0 (Radio), Stage 4: Reset to stage 2 with arm/gripper recovery. | |
| PRIMARY: task_id=0 AND stage=4 β reset to stage 2 (always) | |
| SECONDARY (conditional): | |
| - If both grippers are fully open or fully closed: | |
| β Open closed grippers | |
| Args: | |
| task_id: Current task ID | |
| stage: Current stage | |
| state: Current state (23-dim) | |
| actions: Predicted actions (horizon, 23) | |
| Returns: | |
| (corrected_actions, corrected_stage) if rule applies, None otherwise | |
| """ | |
| # Check primary condition | |
| if task_id != 0 or stage < 2: | |
| return None | |
| # PRIMARY: Always reset stage | |
| if stage == 4: | |
| logger.info(f"π§ CORRECTION RULE: Task 0 (Radio), Stage 4 β Stage 2") | |
| corrected_stage = 2 | |
| else: | |
| corrected_stage = stage | |
| # SECONDARY: Check gripper states | |
| left_gripper = state[LEFT_GRIPPER_IDX] | |
| right_gripper = state[RIGHT_GRIPPER_IDX] | |
| left_closed = left_gripper < CLOSED_THRESHOLD | |
| right_closed = right_gripper < CLOSED_THRESHOLD | |
| left_open = left_gripper > OPEN_THRESHOLD | |
| right_open = right_gripper > OPEN_THRESHOLD | |
| # Check if any gripper is in middle position (not fully open or closed) | |
| left_middle = not (left_open or left_closed) | |
| right_middle = not (right_open or right_closed) | |
| change_action = False | |
| # Copy current state to all actions as baseline | |
| corrected_actions = np.tile(state, (actions.shape[0], 1)) | |
| if left_closed and not right_middle: | |
| corrected_actions[:, LEFT_GRIPPER_IDX] = 1.0 | |
| change_action = True | |
| logger.info(f" - Opening left gripper β 1.0") | |
| if right_closed and not left_middle: | |
| corrected_actions[:, RIGHT_GRIPPER_IDX] = 1.0 | |
| change_action = True | |
| logger.info(f" - Opening right gripper β 1.0") | |
| if not change_action: | |
| if stage == corrected_stage: | |
| return None | |
| corrected_actions = actions | |
| return corrected_actions, corrected_stage | |
| def general_gripper_correction( | |
| task_id: int, | |
| stage: int, | |
| state: np.ndarray, | |
| actions: np.ndarray | |
| ) -> Optional[Tuple[np.ndarray, int]]: | |
| """ | |
| General gripper correction rule for all tasks. | |
| Applies task-specific policies: | |
| 1. Always-open tasks: Open gripper if closed (any stage) | |
| 2. Stage-restricted tasks: Open gripper if closed before allowed stage | |
| 3. Exceptions: Skip tasks where gripper is intentionally closed (e.g., spray bottles) | |
| Works in conjunction with task-specific rules: | |
| - Task 0: This rule handles stages β€1, task-specific rule handles stage 4 | |
| Args: | |
| task_id: Current task ID | |
| stage: Current stage | |
| state: np.ndarray, | |
| actions: Predicted actions (horizon, 23) | |
| Returns: | |
| (corrected_actions, corrected_stage) if rule applies, None otherwise | |
| """ | |
| # Get gripper states | |
| left_gripper = state[LEFT_GRIPPER_IDX] | |
| right_gripper = state[RIGHT_GRIPPER_IDX] | |
| left_closed = left_gripper < CLOSED_THRESHOLD | |
| right_closed = right_gripper < CLOSED_THRESHOLD | |
| # Determine if correction is needed | |
| left_needs_opening = False | |
| right_needs_opening = False | |
| # Check left gripper | |
| if left_closed: | |
| # Always open if task requires it | |
| if task_id in ALWAYS_OPEN_LEFT_GRIPPER_TASKS: | |
| left_needs_opening = True | |
| # Check stage restriction | |
| elif task_id in MIN_STAGE_FOR_CLOSURE: | |
| min_stage_left = MIN_STAGE_FOR_CLOSURE[task_id].get('left') | |
| if min_stage_left is not None and stage < min_stage_left: | |
| left_needs_opening = True | |
| # Check right gripper | |
| if right_closed: | |
| # Exception: Always allow for spray bottle tasks | |
| if task_id in RIGHT_GRIPPER_ALWAYS_ALLOWED: | |
| pass # Never correct these tasks | |
| # Always open if task requires it | |
| elif task_id in ALWAYS_OPEN_RIGHT_GRIPPER_TASKS: | |
| right_needs_opening = True | |
| # Check stage restriction | |
| elif task_id in MIN_STAGE_FOR_CLOSURE: | |
| min_stage_right = MIN_STAGE_FOR_CLOSURE[task_id].get('right') | |
| if min_stage_right is not None and stage < min_stage_right: | |
| right_needs_opening = True | |
| # Apply correction if needed | |
| if left_needs_opening or right_needs_opening: | |
| logger.info(f"π§ GENERAL GRIPPER CORRECTION: Task {task_id}, Stage {stage}") | |
| # Copy current state to all actions | |
| corrected_actions = np.tile(state, (actions.shape[0], 1)) | |
| if left_needs_opening: | |
| logger.info(f" Left gripper closed ({left_gripper:.4f}) β Opening to 1.0") | |
| corrected_actions[:, LEFT_GRIPPER_IDX] = 1.0 | |
| if right_needs_opening: | |
| logger.info(f" Right gripper closed ({right_gripper:.4f}) β Opening to 1.0") | |
| corrected_actions[:, RIGHT_GRIPPER_IDX] = 1.0 | |
| # Keep stage unchanged | |
| return corrected_actions, stage | |
| return None # No correction needed | |
| # ============================================================================ | |
| # RULE REGISTRY - ORGANIZE BY TASK_ID | |
| # ============================================================================ | |
| # Task-specific rules (these run BEFORE the general rule) | |
| # Dictionary mapping task_id -> list of rule functions | |
| TASK_SPECIFIC_RULES = { | |
| 0: [task0_stage4_reset_to_stage2], # Radio has special stage 4 handling | |
| # Add more task-specific rules here as needed | |
| } | |
| # General rules that apply to ALL tasks (run after task-specific rules) | |
| GENERAL_RULES = [ | |
| general_gripper_correction, # Applies to all tasks with gripper policies | |
| ] | |
| # ============================================================================ | |
| # RULE APPLICATION | |
| # ============================================================================ | |
| def apply_correction_rules( | |
| task_id: int, | |
| stage: int, | |
| state: np.ndarray, | |
| actions: np.ndarray | |
| ) -> Tuple[np.ndarray, int]: | |
| """ | |
| Apply correction rules for the given task_id. | |
| Rules are applied in order until one matches: | |
| 1. Task-specific rules (e.g., radio's special stage 4 handling) | |
| 2. General rules (e.g., gripper correction policies) | |
| Only the first matching rule is applied per call. | |
| Args: | |
| task_id: Current task ID | |
| stage: Current stage | |
| state: Current state (23-dim) | |
| actions: Predicted actions (horizon, 23) | |
| Returns: | |
| (actions, stage) - corrected or original if no rule applies | |
| """ | |
| # Try task-specific rules first | |
| task_specific_rules = TASK_SPECIFIC_RULES.get(task_id, []) | |
| for rule in task_specific_rules: | |
| result = rule(task_id, stage, state, actions) | |
| if result is not None: | |
| corrected_actions, corrected_stage = result | |
| return corrected_actions, corrected_stage | |
| # Try general rules (apply to all tasks) | |
| for rule in GENERAL_RULES: | |
| result = rule(task_id, stage, state, actions) | |
| if result is not None: | |
| corrected_actions, corrected_stage = result | |
| return corrected_actions, corrected_stage | |
| # No rules matched, return original | |
| return actions, stage | |
| # ============================================================================ | |
| # GRIPPER VARIATION CHECK | |
| # ============================================================================ | |
| def check_gripper_variation( | |
| actions: np.ndarray, | |
| num_actions_to_check: int | |
| ) -> Tuple[bool, float, float]: | |
| """ | |
| Check if gripper actions have high variation. | |
| High variation in gripper positions suggests complex manipulation that | |
| should not be compressed via interpolation. | |
| Args: | |
| actions: Predicted actions (horizon, 23) | |
| num_actions_to_check: Number of actions to check (e.g., actions_to_execute) | |
| Returns: | |
| (has_high_variation, left_variation, right_variation) | |
| """ | |
| # Check only the actions we plan to execute | |
| actions_to_check = actions[:num_actions_to_check] | |
| # Extract gripper actions | |
| left_gripper_actions = actions_to_check[:, LEFT_GRIPPER_IDX] | |
| right_gripper_actions = actions_to_check[:, RIGHT_GRIPPER_IDX] | |
| left_variation = float(np.max(left_gripper_actions) - np.min(left_gripper_actions)) | |
| right_variation = float(np.max(right_gripper_actions) - np.min(right_gripper_actions)) | |
| has_high_variation = (left_variation > GRIPPER_VARIATION_THRESHOLD or | |
| right_variation > GRIPPER_VARIATION_THRESHOLD) | |
| return has_high_variation, left_variation, right_variation | |