-
Notifications
You must be signed in to change notification settings - Fork 8
feat: import usd #127
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
feat: import usd #127
Changes from all commits
9b0a5ee
82f9990
ed8c68e
f7792f9
6ab4676
feba885
fac439b
b172eb9
6aca2a4
b755cda
74cb0b4
387aacf
69e44e4
b65eb0e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,11 @@ The {class}`~objects.Articulation` class represents the fundamental physics enti | |
| Articulations are configured using the {class}`~cfg.ArticulationCfg` dataclass. | ||
| | Parameter | Type | Default | Description | | ||
| | :--- | :--- | :--- | :--- | | ||
| | `fpath` | `str` | `None` | Path to the asset file (URDF/MJCF). | | ||
| | `fpath` | `str` | `None` | Path to the asset file (URDF/USD). | | ||
| | `init_pos` | `tuple` | `(0,0,0)` | Initial root position `(x, y, z)`. | | ||
| | `init_rot` | `tuple` | `(0,0,0)` | Initial root rotation `(r, p, y)` in degrees. | | ||
| | `fix_base` | `bool` | `True` | Whether to fix the base of the articulation. | | ||
| | `use_usd_properties` | `bool` | `False` | If True, use physical properties from USD file; if False, override with config values. Only effective for usd files. | | ||
| | `init_qpos` | `List[float]` | `None` | Initial joint positions. | | ||
| | `body_scale` | `List[float]` | `[1.0, 1.0, 1.0]` | Scaling factors for the articulation links. | | ||
| | `disable_self_collisions` | `bool` | `True` | Whether to disable self-collisions. | | ||
|
|
@@ -61,6 +62,32 @@ articulation: Articulation = sim.add_articulation(cfg=art_cfg) | |
| # This performs a global reset of the simulation state | ||
| sim.reset_objects_state() | ||
| ``` | ||
|
|
||
| ### USD Import | ||
|
|
||
| You can import USD files (`.usd`, `.usda`, `.usdc`) as articulations: | ||
|
|
||
| ```python | ||
| from embodichain.data import get_data_path | ||
|
|
||
| # Import USD with properties from file | ||
| usd_art_cfg = ArticulationCfg( | ||
| fpath=get_data_path("path/to/robot.usd"), | ||
| init_pos=(0, 0, 0.5), | ||
| use_usd_properties=True # Keep USD drive/physics properties | ||
| ) | ||
| usd_robot = sim.add_articulation(cfg=usd_art_cfg) | ||
|
|
||
| # Or override USD properties with config (URDF behavior) | ||
| usd_art_cfg_override = ArticulationCfg( | ||
| fpath=get_data_path("path/to/robot.usd"), | ||
| init_pos=(0, 0, 0.5), | ||
| use_usd_properties=False, # Use config instead | ||
| drive_props=JointDrivePropertiesCfg(stiffness=5000, damping=500) | ||
| ) | ||
| robot = sim.add_articulation(cfg=usd_art_cfg_override) | ||
|
Comment on lines
+70
to
+88
|
||
| ``` | ||
|
|
||
| ## Articulation Class | ||
|
|
||
| State data is accessed via getter methods that return batched tensors. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ Configured via the {class}`~cfg.RigidObjectCfg` class. | |
| | `attrs` | {class}`~cfg.RigidBodyAttributesCfg` | defaults in code | Physical attributes (mass, damping, friction, restitution, collision offsets, CCD, etc.). | | ||
| | `init_pos` | `Sequence[float]` | `(0,0,0)` | Initial root position (x, y, z). | | ||
| | `init_rot` | `Sequence[float]` | `(0,0,0)` (Euler degrees) | Initial root orientation (Euler angles in degrees) or provide `init_local_pose`. | | ||
| | `use_usd_properties` | `bool` | `False` | If True, use physical properties from USD file; if False, override with config values. Only effective for usd files. | | ||
| | `uid` | `str` | `None` | Optional unique identifier for the object; manager will assign one if omitted. | | ||
|
|
||
| ### Rigid Body Attributes ({class}`~cfg.RigidBodyAttributesCfg`) | ||
|
|
@@ -73,6 +74,31 @@ sim.update() | |
|
|
||
| > Note: `scripts/tutorials/sim/create_scene.py` provides a minimal working example of adding a rigid cube and running the simulation loop. | ||
|
|
||
| ### USD Import | ||
|
|
||
| You can import USD files (`.usd`, `.usda`, `.usdc`) as rigid objects: | ||
|
|
||
| ```python | ||
| from embodichain.data import get_data_path | ||
|
|
||
| # Import USD with properties from file | ||
| usd_cfg = RigidObjectCfg( | ||
| shape=MeshCfg(fpath=get_data_path("path/to/object.usd")), | ||
| body_type="dynamic", | ||
| use_usd_properties=True # Keep USD properties | ||
| ) | ||
| obj = sim.add_rigid_object(cfg=usd_cfg) | ||
|
|
||
| # Or override USD properties with config | ||
| usd_cfg_override = RigidObjectCfg( | ||
| shape=MeshCfg(fpath=get_data_path("path/to/object.usd")), | ||
| body_type="dynamic", | ||
| use_usd_properties=False, # Use config instead | ||
| attrs=RigidBodyAttributesCfg(mass=2.0) | ||
| ) | ||
| obj2 = sim.add_rigid_object(cfg=usd_cfg_override) | ||
| ``` | ||
|
Comment on lines
+81
to
+100
|
||
|
|
||
| ## Rigid Object Class — Common Methods & Attributes | ||
|
|
||
| Rigid objects are observed and controlled via single poses and linear/angular velocities. Key APIs include: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -582,41 +582,55 @@ def __init__( | |
| if self.cfg.init_qpos is None: | ||
| self.cfg.init_qpos = torch.zeros(self.dof, dtype=torch.float32) | ||
|
|
||
| # Set articulation configuration in DexSim | ||
| set_dexsim_articulation_cfg(entities, self.cfg) | ||
|
|
||
| # Init joint drive parameters. | ||
| num_entities = len(entities) | ||
| dof = self._data.dof | ||
| default_cfg = JointDrivePropertiesCfg() | ||
| self.default_joint_damping = torch.full( | ||
| (num_entities, dof), default_cfg.damping, dtype=torch.float32, device=device | ||
| ) | ||
| self.default_joint_stiffness = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.stiffness, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_max_effort = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.max_effort, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_max_velocity = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.max_velocity, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_friction = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.friction, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self._set_default_joint_drive() | ||
| # Determine if we should use USD properties or cfg properties. | ||
| is_usd_file = cfg.fpath.endswith((".usd", ".usda", ".usdc")) | ||
| use_cfg_properties = not (cfg.use_usd_properties and is_usd_file) | ||
|
|
||
| if use_cfg_properties: | ||
| # Set articulation configuration in DexSim | ||
| set_dexsim_articulation_cfg(entities, self.cfg) | ||
|
|
||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| num_entities = len(entities) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These parts must be set and should not be placed inside the if condition.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adjusted |
||
| dof = self._data.dof | ||
| default_cfg = JointDrivePropertiesCfg() | ||
| self.default_joint_damping = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.damping, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_stiffness = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.stiffness, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_max_effort = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.max_effort, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_max_velocity = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.max_velocity, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self.default_joint_friction = torch.full( | ||
| (num_entities, dof), | ||
| default_cfg.friction, | ||
| dtype=torch.float32, | ||
| device=device, | ||
| ) | ||
| self._set_default_joint_drive() | ||
| else: | ||
| # Read current properties from USD-loaded entities | ||
| self.default_joint_stiffness = self._data.joint_stiffness.clone() | ||
| self.default_joint_damping = self._data.joint_damping.clone() | ||
| self.default_joint_friction = self._data.joint_friction.clone() | ||
| self.default_joint_max_effort = self._data.qf_limits.clone() | ||
| self.default_joint_max_velocity = self._data.qvel_limits.clone() | ||
|
|
||
| self.pk_chain = None | ||
| if self.cfg.build_pk_chain: | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -211,9 +211,18 @@ def __init__( | |||||||||
| self._visual_material: List[VisualMaterialInst] = [None] * len(entities) | ||||||||||
| self.is_shared_visual_material = False | ||||||||||
|
|
||||||||||
| for entity in entities: | ||||||||||
| entity.set_body_scale(*cfg.body_scale) | ||||||||||
| entity.set_physical_attr(cfg.attrs.attr()) | ||||||||||
| # Determine if we should use USD properties or cfg properties. | ||||||||||
| from embodichain.lab.sim.shapes import MeshCfg | ||||||||||
|
|
||||||||||
| is_usd_file = isinstance(cfg.shape, MeshCfg) and cfg.shape.fpath.endswith( | ||||||||||
| (".usd", ".usda", ".usdc") | ||||||||||
| ) | ||||||||||
| use_cfg_properties = not (cfg.use_usd_properties and is_usd_file) | ||||||||||
|
|
||||||||||
| if use_cfg_properties: | ||||||||||
| for entity in entities: | ||||||||||
| entity.set_body_scale(*cfg.body_scale) | ||||||||||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| entity.set_physical_attr(cfg.attrs.attr()) | ||||||||||
|
|
||||||||||
| if device.type == "cuda": | ||||||||||
| self._world.update(0.001) | ||||||||||
|
|
@@ -869,7 +878,7 @@ def set_visible(self, visible: bool = True) -> None: | |||||||||
| def reset(self, env_ids: Sequence[int] | None = None) -> None: | ||||||||||
| local_env_ids = self._all_indices if env_ids is None else env_ids | ||||||||||
| num_instances = len(local_env_ids) | ||||||||||
| self.set_attrs(self.cfg.attrs, env_ids=local_env_ids) | ||||||||||
| # self.set_attrs(self.cfg.attrs, env_ids=local_env_ids) | ||||||||||
|
||||||||||
| # self.set_attrs(self.cfg.attrs, env_ids=local_env_ids) | |
| # Reset physical attributes unless we explicitly want to preserve USD-authored properties. | |
| if not getattr(self.cfg, "use_usd_properties", False): | |
| self.set_attrs(self.cfg.attrs, env_ids=local_env_ids) |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -1041,9 +1041,32 @@ def add_articulation( | |||||||
| env_list = [self._env] if len(self._arenas) == 0 else self._arenas | ||||||||
| obj_list = [] | ||||||||
|
|
||||||||
| for env in env_list: | ||||||||
| art = env.load_urdf(cfg.fpath) | ||||||||
| obj_list.append(art) | ||||||||
| is_usd = cfg.fpath.endswith((".usd", ".usda", ".usdc")) | ||||||||
| if is_usd: | ||||||||
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| # TODO: Currently add checking for num_envs when file is USD. After we support spawn via cloning, we can remove this. | ||||||||
| if len(env_list) > 1: | ||||||||
| logger.log_error(f"Currently not supporting multiple arenas for USD.") | ||||||||
| env = self._env | ||||||||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| results = env.import_from_usd_file(cfg.fpath, return_object=True) | ||||||||
| # print("USD import results:", results) | ||||||||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| articulations_found = [] | ||||||||
| for key, value in results.items(): | ||||||||
| if isinstance(value, dexsim.engine.Articulation): | ||||||||
| articulations_found.append(value) | ||||||||
|
|
||||||||
| if len(articulations_found) == 0: | ||||||||
| logger.log_error(f"No articulation found in USD file {cfg.fpath}.") | ||||||||
| elif len(articulations_found) > 1: | ||||||||
| logger.log_error( | ||||||||
| f"Multiple articulations found in USD file {cfg.fpath}. " | ||||||||
| ) | ||||||||
| elif len(articulations_found) == 1: | ||||||||
| obj_list.append(articulations_found[0]) | ||||||||
| else: | ||||||||
| for env in env_list: | ||||||||
| art = env.load_urdf(cfg.fpath) | ||||||||
| obj_list.append(art) | ||||||||
|
|
||||||||
| articulation = Articulation(cfg=cfg, entities=obj_list, device=self.device) | ||||||||
|
|
||||||||
|
|
@@ -1109,9 +1132,32 @@ def add_robot(self, cfg: RobotCfg) -> Robot | None: | |||||||
| env_list = [self._env] if len(self._arenas) == 0 else self._arenas | ||||||||
| obj_list = [] | ||||||||
|
|
||||||||
| for env in env_list: | ||||||||
| art = env.load_urdf(cfg.fpath) | ||||||||
| obj_list.append(art) | ||||||||
| is_usd = cfg.fpath.endswith((".usd", ".usda", ".usdc")) | ||||||||
| if is_usd: | ||||||||
| # TODO: Currently add checking for num_envs when file is USD. After we support spawn via cloning, we can remove this. | ||||||||
| if len(env_list) > 1: | ||||||||
| logger.log_error(f"Currently not supporting multiple arenas for USD.") | ||||||||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| env = self._env | ||||||||
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
MahooX marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| env = self._env | |
| # Use the same arena selection logic as for URDF loading | |
| env = env_list[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The USD import doc example uses
drive_props=..., but the actual config field isdrive_pros(seeArticulationCfg.drive_prosinembodichain/lab/sim/cfg.py). As written, the example will raise aTypeErrorfor an unexpected keyword argument. Update the docs to use the correct parameter name.