Spaces:
Sleeping
Sleeping
| """ | |
| Realism layer for the tabletop planning environment. | |
| Each function injects a specific real-world failure mode into the sim. | |
| These are the exact things that break in real robot deployments. | |
| Turn them on/off with RealismConfig to control environment difficulty. | |
| """ | |
| import random | |
| from dataclasses import dataclass | |
| class RealismConfig: | |
| # 1. Probabilistic action failure β gripper slips, push doesn't fully clear | |
| grasp_fail_prob: float = 0.15 # PICK fails this % of the time even if reachable | |
| clear_partial_prob: float = 0.20 # CLEAR_BLOCKER partially clears (needs 2nd attempt) | |
| # 2. Observation noise β perception isn't perfect | |
| reachability_noise: float = 0.10 # reachable object reported as blocked this % of time | |
| hidden_object_prob: float = 0.30 # objects not visible until SCAN_SCENE is called | |
| # 3. World dynamics β things change without the agent acting | |
| object_drift_prob: float = 0.05 # per step: a reachable object drifts to blocked | |
| new_obstacle_prob: float = 0.10 # per episode: a random obstacle appears mid-task | |
| # 4. Delayed/ambiguous reward β success isn't always obvious immediately | |
| reward_delay_steps: int = 0 # reward shows up N steps after the action (0 = instant) | |
| def easy(cls): | |
| """Close to current clean stub. Good for initial training.""" | |
| return cls( | |
| grasp_fail_prob=0.0, | |
| clear_partial_prob=0.0, | |
| reachability_noise=0.0, | |
| hidden_object_prob=0.0, | |
| object_drift_prob=0.0, | |
| new_obstacle_prob=0.0, | |
| ) | |
| def medium(cls): | |
| """Adds action noise and partial observability. Matches a well-calibrated real robot.""" | |
| return cls( | |
| grasp_fail_prob=0.15, | |
| clear_partial_prob=0.20, | |
| reachability_noise=0.10, | |
| hidden_object_prob=0.30, | |
| object_drift_prob=0.0, | |
| new_obstacle_prob=0.0, | |
| ) | |
| def hard(cls): | |
| """Dynamic world + all noise. Matches a real uncontrolled tabletop.""" | |
| return cls( | |
| grasp_fail_prob=0.20, | |
| clear_partial_prob=0.25, | |
| reachability_noise=0.15, | |
| hidden_object_prob=0.40, | |
| object_drift_prob=0.08, | |
| new_obstacle_prob=0.20, | |
| ) | |
| def apply_action_noise(action: str, result: str, config: RealismConfig) -> str: | |
| """ | |
| Given a successful action result, maybe flip it to a failure | |
| to simulate real-world execution uncertainty. | |
| """ | |
| if result != "SUCCESS": | |
| return result # already failed, don't double-fail | |
| if action == "PICK" and random.random() < config.grasp_fail_prob: | |
| return "FAILED_SLIP" # gripper closed but object slipped out | |
| if action == "CLEAR_BLOCKER" and random.random() < config.clear_partial_prob: | |
| return "PARTIAL_CLEAR" # blocker moved but not fully out of the way | |
| return result | |
| def apply_observation_noise(objects: dict, config: RealismConfig, scanned: bool) -> dict: | |
| """ | |
| Apply noise to what the agent can observe. | |
| hidden_object_prob: objects not yet visible without SCAN_SCENE | |
| reachability_noise: a reachable object sometimes appears blocked | |
| """ | |
| noisy = {} | |
| for name, obj in objects.items(): | |
| noisy_obj = dict(obj.__dict__) if hasattr(obj, '__dict__') else dict(obj) | |
| # Hidden until scanned | |
| if not scanned and random.random() < config.hidden_object_prob: | |
| noisy_obj['reachable'] = False | |
| noisy_obj['_hidden'] = True # agent doesn't know it's there at all | |
| # Reachability noise β sensor thinks it's blocked when it isn't | |
| elif obj.reachable and random.random() < config.reachability_noise: | |
| noisy_obj['reachable'] = False | |
| noisy_obj['_noisy'] = True | |
| noisy[name] = noisy_obj | |
| return noisy | |
| def apply_world_dynamics(objects: dict, step: int, config: RealismConfig) -> dict: | |
| """ | |
| Randomly change world state between steps. | |
| This simulates: objects sliding, another agent acting, vibration, etc. | |
| """ | |
| if random.random() < config.object_drift_prob: | |
| # Pick a reachable object and make it drift to blocked | |
| reachable = [name for name, obj in objects.items() | |
| if obj.reachable and not obj.is_held and obj.in_bin is None] | |
| if reachable: | |
| drifted = random.choice(reachable) | |
| objects[drifted].reachable = False | |
| # find something to mark as its blocker | |
| for other_name, other in objects.items(): | |
| if other_name != drifted and other.reachable and other.blocking is None: | |
| other.blocking = drifted | |
| break | |
| return objects | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Why each one maps to a real failure mode | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # | |
| # grasp_fail_prob | |
| # Real: grasping irregular objects with a parallel gripper fails ~15-25% | |
| # of the time in practice (varies by object shape, friction, placement). | |
| # The agent needs to learn: retry after slip, adjust approach angle. | |
| # | |
| # clear_partial_prob | |
| # Real: a push operation doesn't always fully clear a blocker β the object | |
| # might still partially occlude the target. Agent must verify and push again. | |
| # | |
| # reachability_noise | |
| # Real: depth cameras have noise, segmentation models misclassify objects. | |
| # An object within reach might be estimated as too far. Agent should | |
| # attempt approach anyway if it's uncertain. | |
| # | |
| # hidden_object_prob | |
| # Real: most grasping scenarios have partial observability β you can't see | |
| # everything from one camera angle. SCAN_SCENE maps to "move camera" or | |
| # "look from a different angle." The agent must learn when to gather info. | |
| # | |
| # object_drift_prob | |
| # Real: tables vibrate, gripper bumps adjacent objects, another process | |
| # moves something. Agent must re-verify state before acting on stale info. | |
| # | |
| # new_obstacle_prob | |
| # Real: in warehouse / home settings, new objects appear constantly. | |
| # The agent's plan from 5 steps ago may be invalid now. | |