Spaces:
Running on Zero
Running on Zero
| import numpy as np | |
| import gymnasium as gym | |
| import torch | |
| from torchvision.transforms import functional as F | |
| from envs.wrappers.timeout import Timeout | |
| MUJOCO_TASKS = { | |
| 'mujoco-ant': 'Ant-v4', | |
| 'mujoco-inverted-pendulum': 'InvertedPendulum-v4', | |
| 'mujoco-reacher': 'Reacher-v4', | |
| 'mujoco-pusher': 'Pusher-v4', | |
| 'mujoco-halfcheetah': 'HalfCheetah-v4', | |
| 'mujoco-hopper': 'Hopper-v4', | |
| 'mujoco-walker': 'Walker2d-v4', | |
| } | |
| class MuJoCoWrapper(gym.Wrapper): | |
| def __init__(self, env, cfg): | |
| super().__init__(env) | |
| self.env = env | |
| self.cfg = cfg | |
| if cfg.obs == 'rgb': | |
| self.observation_space = gym.spaces.Dict({ | |
| 'rgb': gym.spaces.Box( | |
| low=0, high=255, shape=(3, self.cfg.render_size, self.cfg.render_size), dtype=np.uint8), | |
| 'state': env.observation_space, | |
| }) | |
| action_dim = env.action_space.shape[0] | |
| self.action_space = gym.spaces.Box( | |
| low=np.full(action_dim, -1), | |
| high=np.full(action_dim, +1), | |
| dtype=np.float32, | |
| ) | |
| self.action_scale = self.env.action_space.high | |
| self.action_repeat = 1 if cfg.task in { | |
| 'mujoco-reacher', 'mujoco-pusher', 'mujoco-halfcheetah' | |
| } else 2 | |
| self._cumulative_reward = 0 | |
| self._terminated = False | |
| def _extract_info(self, info): | |
| info = { | |
| 'terminated': info.get('terminated', False), | |
| 'truncated': info.get('truncated', False), | |
| 'success': float(info.get('success', 0.)), | |
| } | |
| if self.cfg.task == 'mujoco-inverted-pendulum': | |
| # range is [0, 1000], normalize to [0, 1] | |
| info['score'] = np.clip(self._cumulative_reward, 0, 1000) / 1000 | |
| elif self.cfg.task == 'mujoco-reacher': | |
| # range is [-50, 0], normalize to [0, 1] | |
| info['score'] = 1 + np.clip(self._cumulative_reward, -50, 0) / 50 | |
| elif self.cfg.task == 'mujoco-pusher': | |
| # Pusher-v4 over 100 steps: dense penalty = -dist(obj,goal) - 0.1*dist(arm,obj) - 0.001*||a||^2. | |
| # Cumulative range roughly [-150, 0]; expert ~[-50, -20]. | |
| info['score'] = 1 + np.clip(self._cumulative_reward, -150, 0) / 150 | |
| elif self.cfg.task == 'mujoco-halfcheetah': | |
| # range is [0, 15000], normalize to [0, 1] | |
| info['score'] = np.clip(self._cumulative_reward, 0, 15000) / 15000 | |
| elif self.cfg.task in {'mujoco-ant', 'mujoco-hopper', 'mujoco-walker'}: | |
| # range is [0, 5000], normalize to [0, 1] | |
| info['score'] = np.clip(self._cumulative_reward, 0, 5000) / 5000 | |
| else: | |
| raise NotImplementedError(f'Score calculation for {self.cfg.task} not implemented.') | |
| return info | |
| def get_observation(self, obs): | |
| if self.cfg.obs == 'rgb': | |
| return {'state': obs, 'rgb': self.render().transpose(2, 0, 1)} | |
| return obs.astype(np.float32) | |
| def reset(self): | |
| obs, info = self.env.reset() | |
| self._cumulative_reward = 0 | |
| self._terminated = False | |
| return self.get_observation(obs), self._extract_info(info) | |
| def step(self, action): | |
| action = action * self.action_scale | |
| reward = 0. | |
| for _ in range(self.action_repeat): | |
| obs, _reward, terminated, truncated, info = self.env.step(action.copy()) | |
| if 'pendulum' in self.cfg.task and (terminated or self._terminated): | |
| self._terminated = True | |
| _reward = 0. | |
| elif 'hopper' in self.cfg.task or 'walker' in self.cfg.task: | |
| _reward = max(0, _reward) if self.env.unwrapped.is_healthy else -1 | |
| reward += _reward | |
| self._cumulative_reward += reward | |
| info['terminated'] = False | |
| info['truncated'] = truncated | |
| return self.get_observation(obs), reward, False, truncated, self._extract_info(info) | |
| def unwrapped(self): | |
| return self.env.unwrapped | |
| def render(self, **kwargs): | |
| frame = self.env.render().copy() | |
| h, w = self.cfg.render_size, self.cfg.render_size | |
| if frame.shape[0] > h or frame.shape[1] > w: | |
| frame = torch.from_numpy(frame).permute(2, 0, 1) | |
| frame = F.resize(frame, (h, w)) | |
| frame = frame.permute(1, 2, 0).numpy() | |
| return frame | |
| def make_env(cfg): | |
| """ | |
| Make MuJoCo environment. | |
| """ | |
| if not cfg.task in MUJOCO_TASKS: | |
| raise ValueError('Unknown task:', cfg.task) | |
| if cfg.task in {'mujoco-ant', 'mujoco-hopper', 'mujoco-walker'}: | |
| env = gym.make( | |
| MUJOCO_TASKS[cfg.task], | |
| terminate_when_unhealthy=False, | |
| render_mode='rgb_array', | |
| ) | |
| else: | |
| env = gym.make( | |
| MUJOCO_TASKS[cfg.task], | |
| render_mode='rgb_array', | |
| ) | |
| env = MuJoCoWrapper(env, cfg) | |
| env = Timeout(env, max_episode_steps={ | |
| 'mujoco-reacher': 50, | |
| 'mujoco-pusher': 100, | |
| 'mujoco-halfcheetah': 1000, | |
| }.get(cfg.task, 500)) | |
| return env | |