temporarily fix attachment (demo and test pass), and fix bugs in RigidContactAPI)

This commit is contained in:
Chengshu Li 2024-09-25 17:22:45 -07:00
parent c4383a8aee
commit 458d32a0c2
6 changed files with 66 additions and 32 deletions

View File

@ -37,7 +37,6 @@ def main(random_selection=False, headless=False, short_exec=False):
category="shelf_back_panel", category="shelf_back_panel",
model="gjsnrt", model="gjsnrt",
position=[0, 0, 0.01], position=[0, 0, 0.01],
fixed_base=True,
abilities={"attachable": {}}, abilities={"attachable": {}},
) )
) )
@ -99,7 +98,7 @@ def main(random_selection=False, headless=False, short_exec=False):
name=f"shelf_baseboard", name=f"shelf_baseboard",
category="shelf_baseboard", category="shelf_baseboard",
model="hlhneo", model="hlhneo",
position=[0, -0.97884506, base_z + delta_z * idx], position=[0, -10.97884506, base_z + delta_z * idx],
abilities={"attachable": {}}, abilities={"attachable": {}},
) )
) )
@ -119,13 +118,10 @@ def main(random_selection=False, headless=False, short_exec=False):
env.step([]) env.step([])
shelf_baseboard = env.scene.object_registry("name", "shelf_baseboard") shelf_baseboard = env.scene.object_registry("name", "shelf_baseboard")
shelf_baseboard.set_position_orientation(position=[0, -0.979, 0.26], orientation=[0, 0, 0, 1]) shelf_baseboard.set_position_orientation(position=[0, -0.979, 0.21], orientation=[0, 0, 0, 1])
shelf_baseboard.keep_still() shelf_baseboard.keep_still()
shelf_baseboard.set_linear_velocity(th.tensor([-0.2, 0, 0])) # Lower the mass of the baseboard - otherwise, the gravity will create enough torque to break the joint
shelf_baseboard.root_link.mass = 0.1
shelf_side_left = env.scene.object_registry("name", "shelf_side_left")
shelf_side_left.set_position_orientation(position=[-0.4, 0.0, 0.2], orientation=[0, 0, 0, 1])
shelf_side_left.keep_still()
input( input(
"\n\nShelf parts fall to their correct poses and get automatically attached to the back panel.\n" "\n\nShelf parts fall to their correct poses and get automatically attached to the back panel.\n"

View File

@ -316,7 +316,7 @@ class AttachedTo(
_, parent_local_quat = T.relative_pose_transform([0, 0, 0], child_quat, [0, 0, 0], parent_quat) _, parent_local_quat = T.relative_pose_transform([0, 0, 0], child_quat, [0, 0, 0], parent_quat)
# Disable collision between the parent and child objects # Disable collision between the parent and child objects
self._disable_collision_between_child_and_parent(child=self.obj, parent=other) # self._disable_collision_between_child_and_parent(child=self.obj, parent=other)
# Set the parent references # Set the parent references
self.parent = other self.parent = other
@ -341,6 +341,7 @@ class AttachedTo(
joint_frame_in_parent_frame_quat=parent_local_quat, joint_frame_in_parent_frame_quat=parent_local_quat,
joint_frame_in_child_frame_pos=th.zeros(3), joint_frame_in_child_frame_pos=th.zeros(3),
joint_frame_in_child_frame_quat=th.tensor([0.0, 0.0, 0.0, 1.0]), joint_frame_in_child_frame_quat=th.tensor([0.0, 0.0, 0.0, 1.0]),
exclude_from_articulation=True,
**kwargs, **kwargs,
) )

View File

@ -273,32 +273,38 @@ class ManipulationRobot(BaseRobot):
set of unique robot link prim_paths that it is in contact with set of unique robot link prim_paths that it is in contact with
""" """
arm = self.default_arm if arm == "default" else arm arm = self.default_arm if arm == "default" else arm
# Get robot contact links
# Get robot finger links
finger_paths = set([link.prim_path for link in self.finger_links[arm]])
# Get robot links
link_paths = set(self.link_prim_paths) link_paths = set(self.link_prim_paths)
if not return_contact_positions: if not return_contact_positions:
raw_contact_data = { raw_contact_data = {
(row, col) (row, col)
for row, col in GripperRigidContactAPI.get_contact_pairs(self.scene.idx, column_prim_paths=link_paths) for row, col in GripperRigidContactAPI.get_contact_pairs(self.scene.idx, column_prim_paths=finger_paths)
if row not in link_paths
}
else:
raw_contact_data = {
(row, col, point)
for row, col, force, point, normal, sep in GripperRigidContactAPI.get_contact_data(
self.scene.idx, column_prim_paths=finger_paths
)
if row not in link_paths if row not in link_paths
} }
# Translate that to robot contact data # Translate to robot contact data
robot_contact_links = {} robot_contact_links = dict()
for con_data in raw_contact_data: contact_data = set()
for con_data in raw_contact_data:
if not return_contact_positions:
other_contact, link_contact = con_data other_contact, link_contact = con_data
if other_contact not in robot_contact_links: contact_data.add(other_contact)
robot_contact_links[other_contact] = set() else:
robot_contact_links[other_contact].add(link_contact) other_contact, link_contact, point = con_data
contact_data.add((other_contact, point))
return {other for other, _ in raw_contact_data}, robot_contact_links
# Otherwise, we rely on the simpler, but more costly, get_contact_data API.
contacts = GripperRigidContactAPI.get_contact_data(self.scene.idx, column_prim_paths=link_paths)
contact_data = {(contact[0], contact[3]) for contact in contacts}
robot_contact_links = {}
for con_data in contacts:
other_contact, link_contact = con_data[:2]
if other_contact not in robot_contact_links: if other_contact not in robot_contact_links:
robot_contact_links[other_contact] = set() robot_contact_links[other_contact] = set()
robot_contact_links[other_contact].add(link_contact) robot_contact_links[other_contact].add(link_contact)
@ -867,7 +873,7 @@ class ManipulationRobot(BaseRobot):
if candidate_obj is None or link_name not in candidate_obj.links: if candidate_obj is None or link_name not in candidate_obj.links:
continue continue
candidate_link = candidate_obj.links[link_name] candidate_link = candidate_obj.links[link_name]
dist = th.norm(th.tensor(candidate_link.get_position_orientation()[0]) - th.tensor(gripper_center_pos)) dist = th.norm(candidate_link.get_position_orientation()[0] - gripper_center_pos)
candidate_data.append((prim_path, dist)) candidate_data.append((prim_path, dist))
if not candidate_data: if not candidate_data:
@ -1240,9 +1246,14 @@ class ManipulationRobot(BaseRobot):
force_data, _ = self._find_gripper_contacts(arm=arm, return_contact_positions=True) force_data, _ = self._find_gripper_contacts(arm=arm, return_contact_positions=True)
for c_link_prim_path, c_contact_pos in force_data: for c_link_prim_path, c_contact_pos in force_data:
if c_link_prim_path == ag_link.prim_path: if c_link_prim_path == ag_link.prim_path:
contact_pos = th.tensor(c_contact_pos) contact_pos = c_contact_pos
break break
assert contact_pos is not None
assert contact_pos is not None, (
f"contact_pos in self._find_gripper_contacts(return_contact_positions=True) is not found in "
f"self._find_gripper_contacts(return_contact_positions=False). This is likely because "
f"GripperRigidContactAPI.get_contact_pairs and get_contact_data return inconsistent results."
)
# Joint frame set at the contact point # Joint frame set at the contact point
# Need to find distance between robot and contact point in robot link's local frame and # Need to find distance between robot and contact point in robot link's local frame and
@ -1266,6 +1277,7 @@ class ManipulationRobot(BaseRobot):
body0=self.eef_links[arm].prim_path, body0=self.eef_links[arm].prim_path,
body1=ag_link.prim_path, body1=ag_link.prim_path,
enabled=True, enabled=True,
exclude_from_articulation=True,
joint_frame_in_parent_frame_pos=parent_frame_pos / self.scale, joint_frame_in_parent_frame_pos=parent_frame_pos / self.scale,
joint_frame_in_parent_frame_quat=parent_frame_orn, joint_frame_in_parent_frame_quat=parent_frame_orn,
joint_frame_in_child_frame_pos=child_frame_pos / ag_obj.scale, joint_frame_in_child_frame_pos=child_frame_pos / ag_obj.scale,
@ -1448,6 +1460,7 @@ class ManipulationRobot(BaseRobot):
body0=ag_link.prim_path, body0=ag_link.prim_path,
body1=None, body1=None,
enabled=False, enabled=False,
exclude_from_articulation=True,
joint_frame_in_child_frame_pos=attachment_point_pos, joint_frame_in_child_frame_pos=attachment_point_pos,
) )

View File

@ -262,10 +262,16 @@ def create_physx_particleset_pointinstancer(
def apply_force_at_pos(prim, force, pos): def apply_force_at_pos(prim, force, pos):
if isinstance(force, th.Tensor):
force = force.cpu().numpy()
if isinstance(pos, th.Tensor):
pos = pos.cpu().numpy()
prim_id = lazy.pxr.PhysicsSchemaTools.sdfPathToInt(prim.prim_path) prim_id = lazy.pxr.PhysicsSchemaTools.sdfPathToInt(prim.prim_path)
og.sim.psi.apply_force_at_pos(og.sim.stage_id, prim_id, force, pos) og.sim.psi.apply_force_at_pos(og.sim.stage_id, prim_id, force, pos)
def apply_torque(prim, foward_vect, roll_torque_scalar): def apply_torque(prim, foward_vect, roll_torque_scalar):
if isinstance(foward_vect, th.Tensor):
foward_vect = foward_vect.cpu().numpy()
prim_id = lazy.pxr.PhysicsSchemaTools.sdfPathToInt(prim.prim_path) prim_id = lazy.pxr.PhysicsSchemaTools.sdfPathToInt(prim.prim_path)
og.sim.psi.apply_torque(og.sim.stage_id, prim_id, foward_vect * roll_torque_scalar) og.sim.psi.apply_torque(og.sim.stage_id, prim_id, foward_vect * roll_torque_scalar)

View File

@ -89,6 +89,7 @@ def create_joint(
body0=None, body0=None,
body1=None, body1=None,
enabled=True, enabled=True,
exclude_from_articulation=False,
joint_frame_in_parent_frame_pos=None, joint_frame_in_parent_frame_pos=None,
joint_frame_in_parent_frame_quat=None, joint_frame_in_parent_frame_quat=None,
joint_frame_in_child_frame_pos=None, joint_frame_in_child_frame_pos=None,
@ -107,6 +108,7 @@ def create_joint(
body0 (str or None): absolute path to the first body's prim. At least @body0 or @body1 must be specified. body0 (str or None): absolute path to the first body's prim. At least @body0 or @body1 must be specified.
body1 (str or None): absolute path to the second body's prim. At least @body0 or @body1 must be specified. body1 (str or None): absolute path to the second body's prim. At least @body0 or @body1 must be specified.
enabled (bool): whether to enable this joint or not. enabled (bool): whether to enable this joint or not.
exclude_from_articulation (bool): whether to exclude this joint from the articulation or not.
joint_frame_in_parent_frame_pos (th.tensor or None): relative position of the joint frame to the parent frame (body0). joint_frame_in_parent_frame_pos (th.tensor or None): relative position of the joint frame to the parent frame (body0).
joint_frame_in_parent_frame_quat (th.tensor or None): relative orientation of the joint frame to the parent frame (body0). joint_frame_in_parent_frame_quat (th.tensor or None): relative orientation of the joint frame to the parent frame (body0).
joint_frame_in_child_frame_pos (th.tensor or None): relative position of the joint frame to the child frame (body1). joint_frame_in_child_frame_pos (th.tensor or None): relative position of the joint frame to the child frame (body1).
@ -168,6 +170,9 @@ def create_joint(
# Possibly (un-/)enable this joint # Possibly (un-/)enable this joint
joint_prim.GetAttribute("physics:jointEnabled").Set(enabled) joint_prim.GetAttribute("physics:jointEnabled").Set(enabled)
# Possibly exclude this joint from the articulation
joint_prim.GetAttribute("physics:excludeFromArticulation").Set(exclude_from_articulation)
# We update the simulation now without stepping physics if sim is playing so we can bypass the snapping warning from PhysicsUSD # We update the simulation now without stepping physics if sim is playing so we can bypass the snapping warning from PhysicsUSD
if og.sim.is_playing(): if og.sim.is_playing():
with suppress_omni_log(channels=["omni.physx.plugin"]): with suppress_omni_log(channels=["omni.physx.plugin"]):
@ -389,12 +394,14 @@ class RigidContactAPIImpl:
# Get all of the (row, col) pairs where the impulse is greater than 0 # Get all of the (row, col) pairs where the impulse is greater than 0
return { return {
(interesting_row_paths[row], interesting_col_paths[col]) (interesting_row_paths[row], interesting_col_paths[col])
for row, col in th.nonzero(interesting_impulses > 0, as_tuple=True) for row, col in zip(*th.nonzero(interesting_impulses > 0, as_tuple=True))
} }
def get_contact_data(self, scene_idx, row_prim_paths=None, column_prim_paths=None): def get_contact_data(self, scene_idx, row_prim_paths=None, column_prim_paths=None):
# First check if the object has any contacts # First check if the object has any contacts
impulses = self.get_all_impulses(scene_idx) impulses = th.norm(self.get_all_impulses(scene_idx), dim=-1)
assert impulses.ndim == 2, f"Impulse matrix should be 2D, found shape {impulses.shape}"
row_idx = ( row_idx = (
list(range(impulses.shape[0])) list(range(impulses.shape[0]))
if row_prim_paths is None if row_prim_paths is None
@ -406,6 +413,8 @@ class RigidContactAPIImpl:
else [self.get_body_col_idx(path)[1] for path in column_prim_paths] else [self.get_body_col_idx(path)[1] for path in column_prim_paths]
) )
relevant_impulses = impulses[row_idx][:, col_idx] relevant_impulses = impulses[row_idx][:, col_idx]
# Early return if not in contact.
if not th.any(relevant_impulses > 0): if not th.any(relevant_impulses > 0):
return [] return []

View File

@ -716,19 +716,24 @@ def test_toggled_on(env):
assert not stove.states[ToggledOn].get_value() assert not stove.states[ToggledOn].get_value()
@pytest.mark.skip(reason="skipping attachment for now")
@og_test @og_test
def test_attached_to(env): def test_attached_to(env):
shelf_back_panel = env.scene.object_registry("name", "shelf_back_panel") shelf_back_panel = env.scene.object_registry("name", "shelf_back_panel")
shelf_shelf = env.scene.object_registry("name", "shelf_shelf") shelf_shelf = env.scene.object_registry("name", "shelf_shelf")
shelf_baseboard = env.scene.object_registry("name", "shelf_baseboard") shelf_baseboard = env.scene.object_registry("name", "shelf_baseboard")
# Lower the mass of the shelf - otherwise, the gravity will create enough torque to break the joint
shelf_shelf.root_link.mass = 0.1
shelf_back_panel.set_position_orientation(position=[0, 0, 0.01], orientation=[0, 0, 0, 1]) shelf_back_panel.set_position_orientation(position=[0, 0, 0.01], orientation=[0, 0, 0, 1])
shelf_back_panel.keep_still() shelf_back_panel.keep_still()
shelf_shelf.set_position_orientation(position=[0, 0.03, 0.17], orientation=[0, 0, 0, 1]) shelf_shelf.set_position_orientation(position=[0, 0.03, 0.17], orientation=[0, 0, 0, 1])
shelf_shelf.keep_still() shelf_shelf.keep_still()
og.sim.step()
# The shelf should not be attached to the back panel (no contact yet) # The shelf should not be attached to the back panel (no contact yet)
assert not shelf_shelf.states[Touching].get_value(shelf_back_panel)
assert not shelf_shelf.states[AttachedTo].get_value(shelf_back_panel) assert not shelf_shelf.states[AttachedTo].get_value(shelf_back_panel)
# Let the shelf fall # Let the shelf fall
@ -736,16 +741,20 @@ def test_attached_to(env):
og.sim.step() og.sim.step()
# The shelf should be attached to the back panel # The shelf should be attached to the back panel
assert shelf_shelf.states[Touching].get_value(shelf_back_panel)
assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel) assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel)
# Try to attach again (should be no-op)
assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, True) assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, True)
# The shelf should still be attached to the back panel # The shelf should still be attached to the back panel
assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel) assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel)
# Detach
assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, False) assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, False)
# The shelf should not be attached to the back panel # The shelf should not be attached to the back panel
assert not shelf_shelf.states[AttachedTo].get_value(shelf_back_panel) assert not shelf_shelf.states[AttachedTo].get_value(shelf_back_panel)
# Attach again
assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, True) assert shelf_shelf.states[AttachedTo].set_value(shelf_back_panel, True)
# shelf should be attached to the back panel # shelf should be attached to the back panel
assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel) assert shelf_shelf.states[AttachedTo].get_value(shelf_back_panel)