219 lines
13 KiB
Python
219 lines
13 KiB
Python
|
# Mimicking the humanoidv4 reward
|
||
|
|
||
|
import numpy as np
|
||
|
import placo
|
||
|
from gymnasium import utils
|
||
|
from gymnasium.envs.mujoco import MujocoEnv
|
||
|
from gymnasium.spaces import Box
|
||
|
|
||
|
from mini_bdx.walk_engine import WalkEngine
|
||
|
|
||
|
|
||
|
def mass_center(model, data):
|
||
|
mass = np.expand_dims(model.body_mass, axis=1)
|
||
|
xpos = data.xipos
|
||
|
return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy()
|
||
|
|
||
|
|
||
|
class BDXEnv(MujocoEnv, utils.EzPickle):
|
||
|
"""
|
||
|
## Action space
|
||
|
|
||
|
| Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit |
|
||
|
| ---- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | -------- |------------- |
|
||
|
| 0 | Set position of right_hip_yaw | -0.58TODO | 0.58TODO | right_hip_yaw | cylinder | pos (rad) |
|
||
|
| 1 | Set position of right_hip_roll | -0.58TODO | 0.58TODO | right_hip_roll | cylinder | pos (rad) |
|
||
|
| 2 | Set position of right_hip_pitch | -0.58TODO | 0.58TODO | right_hip_pitch | cylinder | pos (rad) |
|
||
|
| 3 | Set position of right_knee_pitch | -0.58TODO | 0.58TODO | right_knee_pitch | cylinder | pos (rad) |
|
||
|
| 4 | Set position of right_ankle_pitch | -0.58TODO | 0.58TODO | right_ankle_pitch | cylinder | pos (rad) |
|
||
|
| 5 | Set position of left_hip_yaw | -0.58TODO | 0.58TODO | left_hip_yaw | cylinder | pos (rad) |
|
||
|
| 6 | Set position of left_hip_roll | -0.58TODO | 0.58TODO | left_hip_roll | cylinder | pos (rad) |
|
||
|
| 7 | Set position of left_hip_pitch | -0.58TODO | 0.58TODO | left_hip_pitch | cylinder | pos (rad) |
|
||
|
| 8 | Set position of left_knee_pitch | -0.58TODO | 0.58TODO | left_knee_pitch | cylinder | pos (rad) |
|
||
|
| 9 | Set position of left_ankle_pitch | -0.58TODO | 0.58TODO | left_ankle_pitch | cylinder | pos (rad) |
|
||
|
| 9 | Set position of head_pitch1 | -0.58TODO | 0.58TODO | left_ankle_pitch | cylinder | pos (rad) |
|
||
|
| 9 | Set position of head_pitch2 | -0.58TODO | 0.58TODO | left_ankle_pitch | cylinder | pos (rad) |
|
||
|
| 9 | Set position of head_yaw | -0.58TODO | 0.58TODO | left_ankle_pitch | cylinder | pos (rad) |
|
||
|
|
||
|
|
||
|
## Observation space
|
||
|
OBSOLETE
|
||
|
|
||
|
| Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit |
|
||
|
| --- | -------------------------------------------------------- | ---- | --- | -------------------------------- | -------- | ------------------------ |
|
||
|
| 0 | Rotation right_hip_yaw | -Inf | Inf | right_hip_yaw | cylinder | angle (rad) |
|
||
|
| 1 | Rotation right_hip_roll | -Inf | Inf | right_hip_roll | cylinder | angle (rad) |
|
||
|
| 2 | Rotation right_hip_pitch | -Inf | Inf | right_hip_pitch | cylinder | angle (rad) |
|
||
|
| 3 | Rotation right_knee_pitch | -Inf | Inf | right_knee_pitch | cylinder | angle (rad) |
|
||
|
| 4 | Rotation right_ankle_pitch | -Inf | Inf | right_ankle_pitch | cylinder | angle (rad) |
|
||
|
| 5 | Rotation left_hip_yaw | -Inf | Inf | left_hip_yaw | cylinder | angle (rad) |
|
||
|
| 6 | Rotation left_hip_roll | -Inf | Inf | left_hip_roll | cylinder | angle (rad) |
|
||
|
| 7 | Rotation left_hip_pitch | -Inf | Inf | left_hip_pitch | cylinder | angle (rad) |
|
||
|
| 8 | Rotation left_knee_pitch | -Inf | Inf | left_knee_pitch | cylinder | angle (rad) |
|
||
|
| 9 | Rotation left_ankle_pitch | -Inf | Inf | left_ankle_pitch | cylinder | angle (rad) |
|
||
|
| 10 | Rotation head_pitch1 | -Inf | Inf | head_pitch1 | cylinder | angle (rad) |
|
||
|
| 11 | Rotation head_pitch2 | -Inf | Inf | head_pitch2 | cylinder | angle (rad) |
|
||
|
| 12 | Rotation head_yaw | -Inf | Inf | head_yaw | cylinder | angle (rad) |
|
||
|
| 13 | Velocity of right_hip_yaw | -Inf | Inf | right_hip_yaw | cylinder | speed (rad/s) |
|
||
|
| 14 | Velocity of right_hip_roll | -Inf | Inf | right_hip_roll | cylinder | speed (rad/s) |
|
||
|
| 15 | Velocity of right_hip_pitch | -Inf | Inf | right_hip_pitch | cylinder | speed (rad/s) |
|
||
|
| 16 | Velocity of right_knee_pitch | -Inf | Inf | right_knee_pitch | cylinder | speed (rad/s) |
|
||
|
| 17 | Velocity of right_ankle_pitch | -Inf | Inf | right_ankle_pitch | cylinder | speed (rad/s) |
|
||
|
| 18 | Velocity of left_hip_yaw | -Inf | Inf | left_hip_yaw | cylinder | speed (rad/s) |
|
||
|
| 19 | Velocity of left_hip_roll | -Inf | Inf | left_hip_roll | cylinder | speed (rad/s) |
|
||
|
| 20 | Velocity of left_hip_pitch | -Inf | Inf | left_hip_pitch | cylinder | speed (rad/s) |
|
||
|
| 21 | Velocity of left_knee_pitch | -Inf | Inf | left_knee_pitch | cylinder | speed (rad/s) |
|
||
|
| 22 | Velocity of left_ankle_pitch | -Inf | Inf | left_ankle_pitch | cylinder | speed (rad/s) |
|
||
|
| 23 | Velocity of head_pitch1 | -Inf | Inf | head_pitch1 | cylinder | speed (rad/s) |
|
||
|
| 24 | Velocity of head_pitch2 | -Inf | Inf | head_pitch2 | cylinder | speed (rad/s) |
|
||
|
| 25 | Velocity of head_yaw | -Inf | Inf | head_yaw | cylinder | speed (rad/s) |
|
||
|
| 26 | roll angular velocity | -Inf | Inf | | | |
|
||
|
| 27 | pitch angular velocity | -Inf | Inf | | | |
|
||
|
| 28 | yaw angular velocity | -Inf | Inf | | | |
|
||
|
| 29 | current x linear velocity | -Inf | Inf | | | |
|
||
|
| 30 | current y linear velocity | -Inf | Inf | | | |
|
||
|
| 31 | current z linear velocity | -Inf | Inf | | | |
|
||
|
|
||
|
| 32 | left foot in contact with the floor | -Inf | Inf | | | |
|
||
|
| 33 | right foot in contact with the floor | -Inf | Inf | | | |
|
||
|
"""
|
||
|
|
||
|
metadata = {
|
||
|
"render_modes": [
|
||
|
"human",
|
||
|
"rgb_array",
|
||
|
"depth_array",
|
||
|
],
|
||
|
"render_fps": 100,
|
||
|
}
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
utils.EzPickle.__init__(self, **kwargs)
|
||
|
observation_space = Box(low=-np.inf, high=np.inf, shape=(41,), dtype=np.float64)
|
||
|
|
||
|
self.left_foot_in_contact = 0
|
||
|
self.right_foot_in_contact = 0
|
||
|
|
||
|
self._healthy_z_range = (0.1, 0.2)
|
||
|
|
||
|
self._forward_reward_weight = 1.25
|
||
|
self._ctrl_cost_weight = 0.3
|
||
|
self.healthy_reward = 5.0
|
||
|
|
||
|
MujocoEnv.__init__(
|
||
|
self,
|
||
|
"/home/antoine/MISC/mini_BDX/mini_bdx/robots/bdx/scene.xml",
|
||
|
5, # frame_skip TODO set to 1 to test
|
||
|
observation_space=observation_space,
|
||
|
**kwargs,
|
||
|
)
|
||
|
|
||
|
def check_contact(self, body1_name, body2_name):
|
||
|
body1_id = self.data.body(body1_name).id
|
||
|
body2_id = self.data.body(body2_name).id
|
||
|
|
||
|
for i in range(self.data.ncon):
|
||
|
contact = self.data.contact[i]
|
||
|
|
||
|
if (
|
||
|
self.model.geom_bodyid[contact.geom1] == body1_id
|
||
|
and self.model.geom_bodyid[contact.geom2] == body2_id
|
||
|
) or (
|
||
|
self.model.geom_bodyid[contact.geom1] == body2_id
|
||
|
and self.model.geom_bodyid[contact.geom2] == body1_id
|
||
|
):
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
def control_cost(self):
|
||
|
control_cost = self._ctrl_cost_weight * np.sum(np.square(self.data.ctrl))
|
||
|
return control_cost
|
||
|
|
||
|
@property
|
||
|
def is_healthy(self):
|
||
|
min_z, max_z = self._healthy_z_range
|
||
|
is_healthy = min_z < self.data.qpos[2] < max_z
|
||
|
|
||
|
return is_healthy
|
||
|
|
||
|
@property
|
||
|
def terminated(self):
|
||
|
rot = np.array(self.data.body("base").xmat).reshape(3, 3)
|
||
|
Z_vec = rot[:, 2]
|
||
|
upright = np.array([0, 0, 1])
|
||
|
return not self.is_healthy or np.dot(upright, Z_vec) <= 0
|
||
|
|
||
|
def step(self, a):
|
||
|
xy_position_before = mass_center(self.model, self.data)
|
||
|
self.do_simulation(a, self.frame_skip)
|
||
|
xy_position_after = mass_center(self.model, self.data)
|
||
|
xy_velocity = (xy_position_after - xy_position_before) / self.dt
|
||
|
x_velocity, y_velocity = xy_velocity
|
||
|
|
||
|
self.right_foot_in_contact = self.check_contact("foot_module", "floor")
|
||
|
self.left_foot_in_contact = self.check_contact("foot_module_2", "floor")
|
||
|
|
||
|
ctrl_cost = self.control_cost()
|
||
|
moving_cost = 1.0 * (abs(x_velocity) + np.abs(x_velocity))
|
||
|
|
||
|
forward_reward = self._forward_reward_weight * x_velocity
|
||
|
healthy_reward = self.healthy_reward
|
||
|
|
||
|
# rewards = forward_reward + healthy_reward
|
||
|
rewards = healthy_reward
|
||
|
reward = rewards - ctrl_cost - moving_cost
|
||
|
|
||
|
# print("healthy", healthy_reward)
|
||
|
# print("ctrl", ctrl_cost)
|
||
|
# print("moving", moving_cost)
|
||
|
# print(("total reward", reward))
|
||
|
# print("-")
|
||
|
|
||
|
ob = self._get_obs()
|
||
|
|
||
|
if self.render_mode == "human":
|
||
|
self.render()
|
||
|
|
||
|
return (ob, reward, self.terminated, False, {})
|
||
|
|
||
|
def reset_model(self):
|
||
|
noise_low = -1e-2
|
||
|
noise_high = 1e-2
|
||
|
|
||
|
qpos = self.init_qpos + self.np_random.uniform(
|
||
|
low=noise_low, high=noise_high, size=self.model.nq
|
||
|
)
|
||
|
qvel = self.init_qvel + self.np_random.uniform(
|
||
|
low=noise_low, high=noise_high, size=self.model.nv
|
||
|
)
|
||
|
self.set_state(qpos, qvel)
|
||
|
|
||
|
observation = self._get_obs()
|
||
|
return observation
|
||
|
|
||
|
def _get_obs(self):
|
||
|
|
||
|
position = (
|
||
|
self.data.qpos.flat.copy()
|
||
|
) # position/rotation of trunk + position of all joints
|
||
|
velocity = (
|
||
|
self.data.qvel.flat.copy()
|
||
|
) # positional/angular velocity of trunk + of all joints
|
||
|
|
||
|
# com_inertia = self.data.cinert.flat.copy()
|
||
|
# com_velocity = self.data.cvel.flat.copy()
|
||
|
|
||
|
# actuator_forces = self.data.qfrc_actuator.flat.copy()
|
||
|
# external_contact_forces = self.data.cfrc_ext.flat.copy()
|
||
|
|
||
|
obs = np.concatenate(
|
||
|
[
|
||
|
position,
|
||
|
velocity,
|
||
|
[self.left_foot_in_contact, self.right_foot_in_contact],
|
||
|
]
|
||
|
)
|
||
|
# print("OBS SIZE", len(obs))
|
||
|
return obs
|