Skip to content

Commit e0e8af6

Browse files
matafelachenjianyuecideng
authored
support collision body visualization for rigid object and articulation (#27)
Co-authored-by: chenjian <[email protected]> Co-authored-by: Yueci Deng <[email protected]>
1 parent 120280f commit e0e8af6

File tree

12 files changed

+283
-34
lines changed

12 files changed

+283
-34
lines changed

embodichain/lab/sim/objects/articulation.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,11 @@ def __init__(
579579
# set default collision filter
580580
self._set_default_collision_filter()
581581

582+
# flag for collision visible node existence
583+
self._has_collision_visible_node_dict = dict()
584+
for link_name in self.link_names:
585+
self._has_collision_visible_node_dict[link_name] = False
586+
582587
def __str__(self) -> str:
583588
parent_str = super().__str__()
584589
return parent_str + f" | dof: {self.dof} | num_links: {self.num_links}"
@@ -1535,6 +1540,47 @@ def get_visual_material_inst(
15351540
result.append(mat_dict)
15361541
return result
15371542

1543+
def set_physical_visible(
1544+
self,
1545+
visible: bool = True,
1546+
link_names: List[str] | None = None,
1547+
rgba: Sequence[float] | None = None,
1548+
):
1549+
"""set collision
1550+
1551+
Args:
1552+
visible (bool, optional): is collision body visible. Defaults to True.
1553+
link_names (List[str] | None, optional): links to set visibility. Defaults to None.
1554+
rgba (Sequence[float] | None, optional): collision body visible rgba. It will be defined at the first time the function is called. Defaults to None.
1555+
"""
1556+
rgba = rgba if rgba is not None else (0.8, 0.2, 0.2, 0.7)
1557+
if len(rgba) != 4:
1558+
logger.log_error(f"Invalid rgba {rgba}, should be a sequence of 4 floats.")
1559+
rgba = np.array(
1560+
[
1561+
rgba[0],
1562+
rgba[1],
1563+
rgba[2],
1564+
rgba[3],
1565+
]
1566+
)
1567+
link_names = self.link_names if link_names is None else link_names
1568+
1569+
# create collision visible node if not exist
1570+
if visible:
1571+
for i, env_idx in enumerate(self._all_indices):
1572+
for link_name in link_names:
1573+
if self._has_collision_visible_node_dict[link_name] is False:
1574+
self._entities[env_idx].create_physical_visible_node(
1575+
rgba, link_name
1576+
)
1577+
self._has_collision_visible_node_dict[link_name] = True
1578+
1579+
# set visibility
1580+
for i, env_idx in enumerate(self._all_indices):
1581+
for link_name in link_names:
1582+
self._entities[env_idx].set_physical_visible(visible, link_name)
1583+
15381584
def destroy(self) -> None:
15391585
env = self._world.get_env()
15401586
arenas = env.get_all_arenas()

embodichain/lab/sim/objects/gizmo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ def toggle_visibility(self) -> bool:
424424

425425
return self._is_visible
426426

427-
def set_visibility(self, visible: bool):
427+
def set_visible(self, visible: bool):
428428
"""
429429
Set the visibility of the gizmo.
430430

embodichain/lab/sim/objects/rigid_object.py

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import numpy as np
2020

2121
from dataclasses import dataclass
22-
from typing import List, Sequence, Optional, Union
22+
from typing import List, Sequence, Union
2323

2424
from dexsim.models import MeshObject
2525
from dexsim.types import RigidBodyGPUAPIReadType, RigidBodyGPUAPIWriteType
@@ -177,7 +177,7 @@ def __init__(
177177
)
178178

179179
# data for managing body data (only for dynamic and kinematic bodies) on GPU.
180-
self._data: Optional[RigidBodyData] = None
180+
self._data: RigidBodyData | None = None
181181
if self.is_static is False:
182182
self._data = RigidBodyData(entities=entities, ps=self._ps, device=device)
183183

@@ -196,6 +196,9 @@ def __init__(
196196
# set default collision filter
197197
self._set_default_collision_filter()
198198

199+
# reserve flag for collision visible node existence
200+
self._has_collision_visible_node = False
201+
199202
def __str__(self) -> str:
200203
parent_str = super().__str__()
201204
return (
@@ -204,7 +207,7 @@ def __str__(self) -> str:
204207
)
205208

206209
@property
207-
def body_data(self) -> Optional[RigidBodyData]:
210+
def body_data(self) -> RigidBodyData | None:
208211
"""Get the rigid body data manager for this rigid object.
209212
210213
Returns:
@@ -266,7 +269,7 @@ def _set_default_collision_filter(self) -> None:
266269
self.set_collision_filter(collision_filter_data)
267270

268271
def set_collision_filter(
269-
self, filter_data: torch.Tensor, env_ids: Optional[Sequence[int]] = None
272+
self, filter_data: torch.Tensor, env_ids: Sequence[int] | None = None
270273
) -> None:
271274
"""set collision filter data for the rigid object.
272275
@@ -276,7 +279,7 @@ def set_collision_filter(
276279
If 2nd element is 0, the object will collision with all other objects in world.
277280
3rd and 4th elements are not used currently.
278281
279-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used. Defaults to None.
282+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used. Defaults to None.
280283
"""
281284
local_env_ids = self._all_indices if env_ids is None else env_ids
282285

@@ -292,13 +295,13 @@ def set_collision_filter(
292295
)
293296

294297
def set_local_pose(
295-
self, pose: torch.Tensor, env_ids: Optional[Sequence[int]] = None
298+
self, pose: torch.Tensor, env_ids: Sequence[int] | None = None
296299
) -> None:
297300
"""Set local pose of the rigid object.
298301
299302
Args:
300303
pose (torch.Tensor): The local pose of the rigid object with shape (N, 7) or (N, 4, 4).
301-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
304+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
302305
"""
303306
local_env_ids = self._all_indices if env_ids is None else env_ids
304307

@@ -395,10 +398,10 @@ def get_local_pose_cpu(
395398

396399
def add_force_torque(
397400
self,
398-
force: Optional[torch.Tensor] = None,
399-
torque: Optional[torch.Tensor] = None,
400-
pos: Optional[torch.Tensor] = None,
401-
env_ids: Optional[Sequence[int]] = None,
401+
force: torch.Tensor | None = None,
402+
torque: torch.Tensor | None = None,
403+
pos: torch.Tensor | None = None,
404+
env_ids: Sequence[int] | None = None,
402405
) -> None:
403406
"""Add force and/or torque to the rigid object.
404407
@@ -409,10 +412,10 @@ def add_force_torque(
409412
- if not `pos` is specified, the force and torque are applied at the center of mass of the rigid body.
410413
411414
Args:
412-
force (Optional[torch.Tensor] = None): The force to add with shape (N, 3). Defaults to None.
413-
torque (Optional[torch.Tensor], optional): The torque to add with shape (N, 3). Defaults to None.
414-
pos (Optional[torch.Tensor], optional): The position to apply the force at with shape (N, 3). Defaults to None.
415-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
415+
force (torch.Tensor | None = None): The force to add with shape (N, 3). Defaults to None.
416+
torque (torch.Tensor | None, optional): The torque to add with shape (N, 3). Defaults to None.
417+
pos (torch.Tensor | None, optional): The position to apply the force at with shape (N, 3). Defaults to None.
418+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
416419
"""
417420
if force is None and torque is None:
418421
logger.log_warning(
@@ -463,13 +466,13 @@ def add_force_torque(
463466
def set_attrs(
464467
self,
465468
attrs: Union[RigidBodyAttributesCfg, List[RigidBodyAttributesCfg]],
466-
env_ids: Optional[Sequence[int]] = None,
469+
env_ids: Sequence[int] | None = None,
467470
) -> None:
468471
"""Set physical attributes for the rigid object.
469472
470473
Args:
471474
attrs (Union[RigidBodyAttributesCfg, List[RigidBodyAttributesCfg]]): The physical attributes to set.
472-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
475+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
473476
"""
474477
local_env_ids = self._all_indices if env_ids is None else env_ids
475478

@@ -487,13 +490,13 @@ def set_attrs(
487490
self._entities[env_idx].set_physical_attr(attrs[i].attr())
488491

489492
def set_visual_material(
490-
self, mat: VisualMaterial, env_ids: Optional[Sequence[int]] = None
493+
self, mat: VisualMaterial, env_ids: Sequence[int] | None = None
491494
) -> None:
492495
"""Set visual material for the rigid object.
493496
494497
Args:
495498
mat (VisualMaterial): The material to set.
496-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
499+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
497500
"""
498501
local_env_ids = self._all_indices if env_ids is None else env_ids
499502

@@ -503,12 +506,12 @@ def set_visual_material(
503506
self._visual_material[env_idx] = mat_inst
504507

505508
def get_visual_material_inst(
506-
self, env_ids: Optional[Sequence[int]] = None
509+
self, env_ids: Sequence[int] | None = None
507510
) -> List[VisualMaterialInst]:
508511
"""Get material instances for the rigid object.
509512
510513
Args:
511-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
514+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
512515
513516
Returns:
514517
List[MaterialInst]: List of material instances.
@@ -533,12 +536,12 @@ def share_visual_material_inst(self, mat_insts: List[VisualMaterialInst]) -> Non
533536
entity.set_material(mat_insts[i].mat)
534537
self._visual_material[i] = mat_insts[i]
535538

536-
def get_body_scale(self, env_ids: Optional[Sequence[int]] = None) -> torch.Tensor:
539+
def get_body_scale(self, env_ids: Sequence[int] | None = None) -> torch.Tensor:
537540
"""
538541
Retrieve the body scale for specified environment instances.
539542
540543
Args:
541-
env_ids (Optional[Sequence[int]]): A sequence of environment instance IDs.
544+
env_ids (Sequence[int] | None): A sequence of environment instance IDs.
542545
If None, retrieves the body scale for all instances.
543546
544547
Returns:
@@ -553,13 +556,13 @@ def get_body_scale(self, env_ids: Optional[Sequence[int]] = None) -> torch.Tenso
553556
)
554557

555558
def set_body_scale(
556-
self, scale: torch.Tensor, env_ids: Optional[Sequence[int]] = None
559+
self, scale: torch.Tensor, env_ids: Sequence[int] | None = None
557560
) -> None:
558561
"""Set the scale of the rigid body.
559562
560563
Args:
561564
scale (torch.Tensor): The scale to set with shape (N, 3).
562-
env_ids (Optional[Sequence[int]], optional): Environment indices. If None, then all indices are used.
565+
env_ids (Sequence[int] | None, optional): Environment indices. If None, then all indices are used.
563566
"""
564567
local_env_ids = self._all_indices if env_ids is None else env_ids
565568

@@ -575,12 +578,12 @@ def set_body_scale(
575578
else:
576579
logger.log_error(f"Setting body scale on GPU is not supported yet.")
577580

578-
def get_vertices(self, env_ids: Optional[Sequence[int]] = None) -> torch.Tensor:
581+
def get_vertices(self, env_ids: Sequence[int] | None = None) -> torch.Tensor:
579582
"""
580583
Retrieve the vertices of the rigid objects.
581584
582585
Args:
583-
env_ids (Optional[Sequence[int]]): A sequence of environment IDs for which to retrieve vertices.
586+
env_ids (Sequence[int] | None): A sequence of environment IDs for which to retrieve vertices.
584587
If None, retrieves vertices for all instances.
585588
586589
Returns:
@@ -607,11 +610,11 @@ def get_user_ids(self) -> torch.Tensor:
607610
device=self.device,
608611
)
609612

610-
def clear_dynamics(self, env_ids: Optional[Sequence[int]] = None) -> None:
613+
def clear_dynamics(self, env_ids: Sequence[int] | None = None) -> None:
611614
"""Clear the dynamics of the rigid bodies by resetting velocities and applying zero forces and torques.
612615
613616
Args:
614-
env_ids (Optional[Sequence[int]]): Environment indices. If None, then all indices are used.
617+
env_ids (Sequence[int] | None): Environment indices. If None, then all indices are used.
615618
"""
616619
if self.is_non_dynamic:
617620
return
@@ -648,7 +651,51 @@ def clear_dynamics(self, env_ids: Optional[Sequence[int]] = None) -> None:
648651
data_type=RigidBodyGPUAPIWriteType.TORQUE,
649652
)
650653

651-
def reset(self, env_ids: Optional[Sequence[int]] = None) -> None:
654+
def set_physical_visible(
655+
self,
656+
visible: bool = True,
657+
rgba: Sequence[float] | None = None,
658+
):
659+
"""set collion render visibility
660+
661+
Args:
662+
visible (bool, optional): is collision body visible. Defaults to True.
663+
rgba (Sequence[float] | None, optional): collision body visible rgba. It will be defined at the first time the function is called. Defaults to None.
664+
"""
665+
rgba = rgba if rgba is not None else (0.8, 0.2, 0.2, 0.7)
666+
if len(rgba) != 4:
667+
logger.log_error(f"Invalid rgba {rgba}, should be a sequence of 4 floats.")
668+
669+
# create collision visible node if not exist
670+
if visible:
671+
if not self._has_collision_visible_node:
672+
for i, env_idx in enumerate(self._all_indices):
673+
self._entities[env_idx].create_physical_visible_node(
674+
np.array(
675+
[
676+
rgba[0],
677+
rgba[1],
678+
rgba[2],
679+
rgba[3],
680+
]
681+
)
682+
)
683+
self._has_collision_visible_node = True
684+
685+
# create collision visible node if not exist
686+
for i, env_idx in enumerate(self._all_indices):
687+
self._entities[env_idx].set_physical_visible(visible)
688+
689+
def set_visible(self, visible: bool = True) -> None:
690+
"""Set the visibility of the rigid object.
691+
692+
Args:
693+
visible (bool, optional): Whether the rigid object is visible. Defaults to True.
694+
"""
695+
for i, env_idx in enumerate(self._all_indices):
696+
self._entities[env_idx].set_visible(visible)
697+
698+
def reset(self, env_ids: Sequence[int] | None = None) -> None:
652699
local_env_ids = self._all_indices if env_ids is None else env_ids
653700
num_instances = len(local_env_ids)
654701
self.set_attrs(self.cfg.attrs, env_ids=local_env_ids)

embodichain/lab/sim/objects/rigid_object_group.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ def __init__(
204204
# set default collision filter
205205
self._set_default_collision_filter()
206206

207+
# reserve flag for collision visible node existence
208+
n_instances = len(self._entities[0])
209+
self._has_collision_visible_node_list = [False] * n_instances
210+
207211
def __str__(self) -> str:
208212
parent_str = super().__str__()
209213
return (
@@ -503,6 +507,53 @@ def reset(self, env_ids: Sequence[int] | None = None) -> None:
503507

504508
self.clear_dynamics(env_ids=local_env_ids)
505509

510+
def set_physical_visible(
511+
self,
512+
visible: bool = True,
513+
rgba: Sequence[float] | None = None,
514+
):
515+
"""set collion render visibility
516+
517+
Args:
518+
visible (bool, optional): is collision body visible. Defaults to True.
519+
rgba (Sequence[float] | None, optional): collision body visible rgba. It will be defined at the first time the function is called. Defaults to None.
520+
"""
521+
rgba = rgba if rgba is not None else (0.8, 0.2, 0.2, 0.7)
522+
if len(rgba) != 4:
523+
logger.log_error(f"Invalid rgba {rgba}, should be a sequence of 4 floats.")
524+
525+
# create collision visible node if not exist
526+
if visible:
527+
for i, env_idx in enumerate(self._all_indices):
528+
for intance_id, entity in enumerate(self._entities[env_idx]):
529+
if not self._has_collision_visible_node_list[intance_id]:
530+
entity.create_physical_visible_node(
531+
np.array(
532+
[
533+
rgba[0],
534+
rgba[1],
535+
rgba[2],
536+
rgba[3],
537+
]
538+
)
539+
)
540+
self._has_collision_visible_node_list[intance_id] = True
541+
542+
# create collision visible node if not exist
543+
for i, env_idx in enumerate(self._all_indices):
544+
for entity in self._entities[env_idx]:
545+
entity.set_physical_visible(visible)
546+
547+
def set_visible(self, visible: bool = True) -> None:
548+
"""Set the visibility of the rigid object group.
549+
550+
Args:
551+
visible (bool, optional): Whether the rigid object group is visible. Defaults to True.
552+
"""
553+
for i, env_idx in enumerate(self._all_indices):
554+
for entity in self._entities[env_idx]:
555+
entity.set_visible(visible)
556+
506557
def destroy(self) -> None:
507558
env = self._world.get_env()
508559
arenas = env.get_all_arenas()

0 commit comments

Comments
 (0)