robogym/envs/dactyl/goals/full_unconstrained.py (71 lines of code) (raw):
import typing
import numpy as np
from robogym.envs.dactyl.common import cube_utils
from robogym.goal.goal_generator import GoalGenerator
from robogym.utils import rotation
class FullUnconstrainedGoal(GoalGenerator):
"""
Rotate any face, no orientation objectives for the Z axis.
"""
def __init__(
self,
mujoco_simulation,
success_threshold: dict,
face_geom_names: typing.List[str],
goal_directions: typing.Optional[typing.List[str]] = None,
round_target_face: bool = True,
):
"""
Create new FullUnconstrainedGoal object
:param mujoco_simulation: A SimulationInterface object for a mujoco simulation considered
:param success_threshold: Dictionary of threshold levels for cube orientation and face
rotation, for which we consider the cube "aligned" with the goal
:param face_geom_names: Names of 6 face geoms of the cube for which we measure the rotation
:param goal_directions: Whether to rotate faces only clockwise, counterclockwise or both
:param round_target_face: Whether target face rotations should be only round angles
(multiplies of pi/2) or not
:param p_face_flip: If the cube is aligned, what is the probability of flipping the cube
vs rotating the face
"""
super().__init__()
assert len(face_geom_names) == 6, "Only supports full cube for now"
self.mujoco_simulation = mujoco_simulation
self.success_threshold = success_threshold
self.face_geom_names = face_geom_names
if goal_directions is None:
self.goal_directions = ["cw", "ccw"]
else:
self.goal_directions = goal_directions
self.round_target_face = round_target_face
self.goal_candidates = list(range(len(self.face_geom_names)))
def next_goal(self, random_state, current_state):
""" Generate a new goal from current cube goal state """
cube_face = current_state["cube_face_angle"]
self.mujoco_simulation.clone_target_from_cube()
self.mujoco_simulation.target_model.soft_align_faces()
# Make the goal so that any face is rotated at random
face_to_shift = random_state.choice(self.goal_candidates)
# Rotate given face by a random angle and return both, new rotations and an angle
goal_face, delta_angle = cube_utils.rotated_face_with_angle(
cube_face,
face_to_shift,
random_state,
self.round_target_face,
directions=self.goal_directions,
)
self.mujoco_simulation.target_model.rotate_face(
face_to_shift // 2, face_to_shift % 2, delta_angle
)
return {
"cube_pos": np.zeros(3),
"cube_quat": np.zeros(4),
"cube_face_angle": goal_face,
"goal_type": "rotation",
}
def current_state(self):
""" Extract current cube goal state """
return {
"cube_pos": self.mujoco_simulation.get_qpos("cube_position"),
"cube_quat": self.mujoco_simulation.get_qpos("cube_rotation"),
"cube_face_angle": self.mujoco_simulation.get_face_angles("cube"),
}
def relative_goal(self, goal_state, current_state):
"""
Calculate a difference in the 'goal space' between current state and the target goal
"""
assert goal_state["goal_type"] == "rotation"
return {
# Cube pos does not count
"cube_pos": np.zeros(goal_state["cube_pos"].shape),
# Quaternion difference of a rotation
"cube_quat": np.zeros(goal_state["cube_quat"].shape),
# Angle differences
"cube_face_angle": rotation.normalize_angles(
goal_state["cube_face_angle"] - current_state["cube_face_angle"]
),
}
def goal_distance(self, goal_state, current_state):
""" Distance from the current goal to the target state. """
relative_goal = self.relative_goal(goal_state, current_state)
goal_distance = {
"cube_pos": 0.0,
"cube_quat": 0.0,
"cube_face_angle": np.linalg.norm(relative_goal["cube_face_angle"]),
}
return goal_distance
def goal_types(self) -> typing.Set[str]:
return {"rotation"}