Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Humanoids pr #1165

Merged
merged 18 commits into from
Mar 1, 2023
Merged
2 changes: 1 addition & 1 deletion habitat-lab/habitat/articulated_agents/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Robot Design
==============================

Habitat supports different kinds of agents that can interact in the environment, including robots or humanoids. These agents are represented as articulated objects, consisting of multiple rigid parts (links) connected by joints to perform rotational or translational motion. This readme file details the design of these articulated agent modules, which robots inherit from. We provide the implementation of different robots under the `robots` folder.
Habitat supports different kinds of agents that can interact in the environment, including robots or humanoids. These agents are represented as articulated objects, consisting of multiple rigid parts (links) connected by joints to perform rotational or translational motion. This readme file details the design of these articulated agent modules, which robots inherit from. We provide the implementation of different robots under the `robots` folder, and the implementation of different humanoids under `humanoids`.
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(
fixed_based: bool = True,
base_type="mobile",
sim_obj=None,
maintain_link_order=False,
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
**kwargs,
):
r"""Constructor"""
Expand All @@ -42,6 +43,7 @@ def __init__(
self._limit_robo_joints = limit_robo_joints
self._base_type = base_type
self.sim_obj = sim_obj
self._maintain_link_order = maintain_link_order
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved

# NOTE: the follow members cache static info for improved efficiency over querying the API
# maps joint ids to motor settings for convenience
Expand Down Expand Up @@ -71,7 +73,9 @@ def reconfigure(self) -> None:
if self.sim_obj is None or not self.sim_obj.is_alive:
ao_mgr = self._sim.get_articulated_object_manager()
self.sim_obj = ao_mgr.add_articulated_object_from_urdf(
self.urdf_path, fixed_base=self._fixed_base
self.urdf_path,
fixed_base=self._fixed_base,
maintain_link_order=self._maintain_link_order,
)
# set correct gains for wheels
if (
Expand Down
18 changes: 18 additions & 0 deletions habitat-lab/habitat/articulated_agents/humanoids/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Humanoid Design
==============================

Habitat also supports humanoid avatars. These avatars are represented as articulated objects connected via joints with rotational motion. The current version only includes a kinematic humanoid, which can be controlled by specifying the joint rotations and object transformation.

---

## Humanoid Component Design

1. **Humanoid**
- `kinematic_humanoid.py` is built upon `mobile_manipulator.py` and can be controlled by setting the joint and object transforms, via the function `set_joint_transform`.

1. **Mobile Manipulator Parameters**
- Parameters such as the camera's transformation, end-effector position, control gain, and more are specified in each robot class (e.g., here, `kinematic_humanoid.py`).

1. **Humanoid Articulated Object**

The humanoid is represented as an articulated object made out of capsules and spheres. It contains 20 links and 19 joints, and is designed to match the morphology of the SMPL skeleton, as referenced [here](https://files.is.tue.mpg.de/black/talks/SMPL-made-simple-FAQs.pdf). Note that the current model does not include the hand or foot joint, hence the 20 links instead of 24, and that the root joint is represented via the object transform, hence the 19 joints.
13 changes: 13 additions & 0 deletions habitat-lab/habitat/articulated_agents/humanoids/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3

# Copyright (c) Meta Platforms, Inc. and its affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from habitat.articulated_agents.humanoids.kinematic_humanoid import (
KinematicHumanoid,
)

__all__ = [
"KinematicHumanoid",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (c) Meta Platforms, Inc. and its affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from typing import List

import magnum as mn
import numpy as np

import habitat_sim.physics as phy
from habitat.articulated_agents.mobile_manipulator import (
ArticulatedAgentCameraParams,
MobileManipulator,
MobileManipulatorParams,
)


class KinematicHumanoid(MobileManipulator):
def _get_humanoid_params(self):
return MobileManipulatorParams(
arm_joints=[], # For now we do not add arm_joints
gripper_joints=[],
wheel_joints=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this! Do we expect that humans have wheels?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I found I could not ignore this parameter, even if it was optional. I think that in order to be able to ignore it, we need to set a default argument to wheels, here of None. I can do that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm mobile_manipulator.py was probably designed with Fetch in mind, so I wouldn't change the default to None. Why not set it as False here instead of None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have here a check of wheel_joints existing as a param or being None, this is why I was proposing to set the default to None. If we set it as False, or [], we would need to change all these checks as well. I can do that if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the type of this variable? This should indicate what the no wheel option should be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional[List[int]]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I faced the same issue before, my solution at that time was to use inheritance, which is not the best way to do this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on how you used inheritance? I think the best would be to make it List[int] (no optional) with a default argument [] and whenever we are checking this param, we replace the None/False checks fro empty list.

arm_init_params=None,
gripper_init_params=None,
gripper_closed_state=np.array([]),
gripper_open_state=np.array([]),
gripper_state_eps=None,
wheel_mtr_pos_gain=None,
wheel_mtr_vel_gain=None,
wheel_mtr_max_impulse=None,
ee_offset=[mn.Vector3(), mn.Vector3()],
ee_links=[15, 9],
ee_constraint=np.zeros((2, 2, 3)),
cameras={
"robot_head": ArticulatedAgentCameraParams(
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
cam_offset_pos=mn.Vector3(0.0, 0.5, 0.25),
cam_look_at_pos=mn.Vector3(0.0, 0.5, 0.75),
attached_link_id=-1,
),
"robot_third": ArticulatedAgentCameraParams(
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
cam_offset_pos=mn.Vector3(-1.2, 2.0, -1.2),
cam_look_at_pos=mn.Vector3(1, 0.0, 0.75),
attached_link_id=-1,
),
},
arm_mtr_pos_gain=0.3,
arm_mtr_vel_gain=0.3,
arm_mtr_max_impulse=10.0,
base_offset=mn.Vector3(0, -0.9, 0),
base_link_names={
"base_link",
},
ee_count=2,
)

def __init__(
self, urdf_path, sim, limit_robo_joints=False, fixed_base=False
):
super().__init__(
self._get_humanoid_params(),
urdf_path,
sim,
limit_robo_joints,
fixed_base,
maintain_link_order=True,
)

def reconfigure(self) -> None:
"""Instantiates the human in the scene. Loads the URDF, sets initial state of parameters, joints, motors, etc..."""
super().reconfigure()
self.sim_obj.motion_type = phy.MotionType.KINEMATIC
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved

def set_joint_transform(
self, joint_list: List[float], transform: mn.Matrix4
) -> None:
"""Sets the joints and base transform of the humanoid"""
# TODO: should this go into articulated agent?
self.sim_obj.joint_positions = joint_list
self.sim_obj.transformation = transform
7 changes: 6 additions & 1 deletion habitat-lab/habitat/articulated_agents/manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(
limit_robo_joints: bool = True,
fixed_based: bool = True,
sim_obj=None,
maintain_link_order=False,
akshararai marked this conversation as resolved.
Show resolved Hide resolved
**kwargs,
):
r"""Constructor"""
Expand All @@ -38,6 +39,7 @@ def __init__(
self._limit_robo_joints = limit_robo_joints
self._fixed_base = fixed_based
self.sim_obj = sim_obj
self._maintain_link_order = maintain_link_order
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved

# Adapt Manipulator params to support multiple end effector indices
# NOTE: the follow members cache static info for improved efficiency over querying the API
Expand Down Expand Up @@ -78,7 +80,9 @@ def reconfigure(self) -> None:
if self.sim_obj is None or not self.sim_obj.is_alive:
ao_mgr = self._sim.get_articulated_object_manager()
self.sim_obj = ao_mgr.add_articulated_object_from_urdf(
self.urdf_path, fixed_base=self._fixed_base
self.urdf_path,
fixed_base=self._fixed_base,
maintain_link_order=self._maintain_link_order,
)
if self._limit_robo_joints:
# automatic joint limit clamping after each call to sim.step_physics()
Expand Down Expand Up @@ -177,6 +181,7 @@ def update(self) -> None:
sens_obj.node.transformation = (
orthonormalize_rotation_shear(cam_transform)
)

if self._fix_joint_values is not None:
self.arm_joint_pos = self._fix_joint_values

Expand Down
8 changes: 5 additions & 3 deletions habitat-lab/habitat/articulated_agents/mobile_manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ class MobileManipulatorParams:
arm_mtr_vel_gain: float
arm_mtr_max_impulse: float

wheel_mtr_pos_gain: float
wheel_mtr_vel_gain: float
wheel_mtr_max_impulse: float
wheel_mtr_pos_gain: Optional[float]
wheel_mtr_vel_gain: Optional[float]
wheel_mtr_max_impulse: Optional[float]

base_offset: mn.Vector3
base_link_names: Set[str]
Expand All @@ -112,6 +112,7 @@ def __init__(
sim: Simulator,
limit_robo_joints: bool = True,
fixed_base: bool = True,
maintain_link_order: bool = True,
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
base_type="mobile",
):
r"""Constructor
Expand Down Expand Up @@ -141,6 +142,7 @@ def __init__(
fixed_based=fixed_base,
sim_obj=self.sim_obj,
base_type=base_type,
maintain_link_order=maintain_link_order,
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
)

def reconfigure(self) -> None:
Expand Down
2 changes: 2 additions & 0 deletions habitat-lab/habitat/tasks/rearrange/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ def _try_register_rearrange_task():
HabitatSimActions.extend_action_space("empty")
if not HabitatSimActions.has_action("rearrange_stop"):
HabitatSimActions.extend_action_space("rearrange_stop")
if not HabitatSimActions.has_action("changejoint_action"):
HabitatSimActions.extend_action_space("changejoint_action")
42 changes: 42 additions & 0 deletions habitat-lab/habitat/tasks/rearrange/actions/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,45 @@ def step(self, ee_pos, **kwargs):
self._sim.viz_ids["ee_target"] = self._sim.visualize_position(
global_pos, self._sim.viz_ids["ee_target"]
)


@registry.register_task_action
class HumanoidJointAction(RobotAction):
def __init__(self, *args, sim: RearrangeSim, **kwargs):
super().__init__(*args, sim=sim, **kwargs)
self._sim: RearrangeSim = sim
self.num_joints = 19

def reset(self, *args, **kwargs):
super().reset()

@property
def action_space(self):
num_joints = self.num_joints
num_dim_transform = 16
# The action space is the number of joints plus 16 for a 4x4 transformtion matrix for the base
return spaces.Dict(
{
"human_joints_trans": spaces.Box(
shape=(4 * (num_joints + num_dim_transform),),
low=-1,
high=1,
dtype=np.float32,
)
}
)

def step(self, **kwargs):
new_pos_transform = kwargs["human_joints_trans"]
xavierpuigf marked this conversation as resolved.
Show resolved Hide resolved
new_joints = new_pos_transform[:-16]
new_pos_transform = new_pos_transform[-16:]
if np.array(new_pos_transform).sum() != 0:
akshararai marked this conversation as resolved.
Show resolved Hide resolved
vecs = [
mn.Vector4(new_pos_transform[i * 4 : (i + 1) * 4])
for i in range(4)
]
new_transform = mn.Matrix4(*vecs)
self.cur_articulated_agent.set_joint_transform(
new_joints, new_transform
)
return self._sim.step(HabitatSimActions.changejoint_action)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this does. Why is this needed? What does self._sim need to update for the changejoint_action ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably misunderstood the code, but don't we need to call sim.step on every step() function, since functions calling step expect observations from the environment? I think this makes sense when we call this function via the high level policy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could cause the simulator to step twice per high-level step. The action should only step the simulator if the action is the last action to execute. See how it's done in the other actions.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import magnum as mn
import numpy as np

from habitat.articulated_agents.humanoids.kinematic_humanoid import (
KinematicHumanoid,
)
from habitat.articulated_agents.mobile_manipulator import MobileManipulator

# flake8: noqa
Expand Down Expand Up @@ -120,7 +123,6 @@ def post_obj_load_reconfigure(self):
"""
Called at the end of the simulator reconfigure method. Used to set the starting configurations of the robots if specified in the task config.
"""

for agent_data in self._all_agent_data:
agent_data.articulated_agent.params.arm_init_params = (
agent_data.start_js
Expand Down