from typing import Any
import numpy as np
import sapien
import torch
from mani_skill.agents.robots.anymal.anymal_c import ANYmalC
from mani_skill.agents.robots.unitree_go.unitree_go2 import UnitreeGo2Simplified
from mani_skill.envs.sapien_env import BaseEnv
from mani_skill.sensors.camera import CameraConfig
from mani_skill.utils import sapien_utils
from mani_skill.utils.building import actors
from mani_skill.utils.building.ground import build_ground
from mani_skill.utils.registration import register_env
from mani_skill.utils.structs.pose import Pose
from mani_skill.utils.structs.types import GPUMemoryConfig, SceneConfig, SimConfig
[docs]class QuadrupedSpinEnv(BaseEnv):
[docs] SUPPORTED_ROBOTS = ["anymal_c", "unitree_go2_simplified_locomotion"]
[docs] default_qpos: torch.Tensor
def __init__(self, *args, robot_uids="anymal-c", **kwargs):
super().__init__(*args, robot_uids=robot_uids, **kwargs)
@property
[docs] def _default_sim_config(self):
return SimConfig(
gpu_memory_config=GPUMemoryConfig(max_rigid_contact_count=2**20),
scene_config=SceneConfig(
solver_position_iterations=4, solver_velocity_iterations=0
),
)
@property
[docs] def _default_sensor_configs(self):
pose = sapien_utils.look_at([-1.0, 1.0, 2], [0, 0.0, 0.5])
return [
CameraConfig(
"base_camera",
pose=pose,
width=128,
height=128,
fov=np.pi / 2,
near=0.01,
far=100,
)
]
@property
[docs] def _default_human_render_camera_configs(self):
pose = sapien_utils.look_at([-1.0, 1.0, 2], [0, 0.0, 0.5])
return [
CameraConfig(
"render_camera",
pose=pose,
width=512,
height=512,
fov=1,
near=0.01,
far=100,
# mount=self.agent.robot.links[0],
)
]
[docs] def _load_agent(self, options: dict):
super()._load_agent(options, sapien.Pose(p=[0, 0, 1]))
[docs] def _load_scene(self, options: dict):
self.ground = build_ground(self.scene, floor_width=400)
[docs] def _initialize_episode(self, env_idx: torch.Tensor, options: dict):
with torch.device(self.device):
len(env_idx)
keyframe = self.agent.keyframes["standing"]
self.agent.robot.set_pose(keyframe.pose)
self.agent.robot.set_qpos(keyframe.qpos)
[docs] def evaluate(self):
is_fallen = self.agent.is_fallen()
return {
"fail": is_fallen,
"is_fallen": is_fallen,
}
[docs] def compute_dense_reward(self, obs: Any, action: torch.Tensor, info: dict):
rotation_reward = self.agent.robot.root_angular_velocity[:, 2]
# various penalties:
lin_vel_z_l2 = torch.square(self.agent.robot.root_linear_velocity[:, 2])
ang_vel_xy_l2 = (
torch.square(self.agent.robot.root_angular_velocity[:, :2])
).sum(axis=1)
penalties = (
lin_vel_z_l2 * -2
+ ang_vel_xy_l2 * -0.05
+ self._compute_undesired_contacts() * -1
+ torch.linalg.norm(self.agent.robot.qpos - self.default_qpos, axis=1)
* -0.05
)
reward = 2 * rotation_reward + penalties
reward[info["fail"]] = -100
return reward
[docs] def compute_normalized_dense_reward(
self, obs: Any, action: torch.Tensor, info: dict
):
max_reward = 2.0
return self.compute_dense_reward(obs=obs, action=action, info=info) / max_reward
@register_env("AnymalC-Spin-v1", max_episode_steps=200)
[docs]class AnymalCSpinEnv(QuadrupedSpinEnv):
"""
**Task Description:**
Control the AnymalC robot to spin around in place as fast as possible and is rewarded by its angular velocity.
**Randomizations:**
- Robot is initialized in a stable rest/standing position
**Fail Conditions:**
- If the robot has fallen over, which is considered True when the main body (the center part) hits the ground
"""
[docs] _sample_video_link = "https://github.com/haosulab/ManiSkill/raw/main/figures/environment_demos/AnymalC-Spin-v1_rt.mp4"
def __init__(self, *args, robot_uids="anymal_c", **kwargs):
super().__init__(*args, robot_uids=robot_uids, **kwargs)
[docs] self.default_qpos = torch.from_numpy(ANYmalC.keyframes["standing"].qpos).to(
self.device
)