Unverified Commit b124a55d authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Adds methods to set physics-based schemas (#110)

# Description

This MR introduces wrappers around different [USD
Physics](https://openusd.org/dev/api/usd_physics_page_front.html) and
[PhysX
solver](https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/104.2/index.html)
schemas. The functions allow modifying the properties on an asset prim
using configuration objects.

The schemas supersede the current `omni.isaac.orbit.utils.kit.py` which
did the same job but had duplication in the implementations.

## Type of change

- New feature (non-breaking change which adds functionality)
- This change requires a documentation update

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./orbit.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file

---------
Signed-off-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
parent c438fe97
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.8.7" version = "0.8.8"
# Description # Description
title = "ORBIT framework for Robot Learning" title = "ORBIT framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.8.8 (2023-08-09)
~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added configuration classes and functions for setting different physics-based schemas in the
:mod:`omni.isaac.orbit.sim.schemas` module. These allow modifying properties of the physics solver
on the asset using configuration objects.
0.8.7 (2023-08-03) 0.8.7 (2023-08-03)
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
......
...@@ -3,13 +3,17 @@ ...@@ -3,13 +3,17 @@
# #
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
"""This sub-module provides the ``SimulationContext`` class. """This sub-module contains utilities for simulation.
To make it convenient to use the module, we recommend importing the module as follows:
.. code-block:: python
import omni.isaac.orbit.sim as sim_utils
The :class:`SimulationContext` inherits from the :class:`omni.isaac.core.simulation_context.SimulationContext` class
to provide additional functionality for the Orbit extension. This includes configuring the simulation through
the configuration class :class:`SimulationCfg` and providing a context manager for the simulation.
""" """
from .schemas import * # noqa: F401, F403
from .simulation_cfg import PhysicsMaterialCfg, PhysxCfg, SimulationCfg from .simulation_cfg import PhysicsMaterialCfg, PhysxCfg, SimulationCfg
from .simulation_context import SimulationContext from .simulation_context import SimulationContext
......
# Copyright [2023] Boston Dynamics AI Institute, Inc.
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Sub-module containing utilities for schemas used in Omniverse.
We wrap the USD schemas for PhysX and USD Physics in a more convenient API for setting the parameters from
Python. This is done so that configuration objects can define the schema properties to set and make it easier
to tune the physics parameters without requiring to open Omniverse Kit and manually set the parameters into
the respective USD attributes.
.. caution::
Schema properties cannot be applied on prims that are prototypes as they are read-only prims. This
particularly affects instanced assets where some of the prims (usually the visual and collision meshes)
are prototypes so that the instancing can be done efficiently.
In such cases, it is assumed that the prototypes have sim-ready properties on them that don't need to be modified.
Trying to set properties into prototypes will throw a warning saying that the prim is a prototype and the
properties cannot be set.
The schemas are defined in the following links:
* `UsdPhysics schema <https://openusd.org/dev/api/usd_physics_page_front.html>`_
* `PhysxSchema schema <https://docs.omniverse.nvidia.com/kit/docs/omni_usd_schema_physics/104.2/index.html>`_
Locally, the schemas are defined in the following files:
* ``_isaac_sim/kit/extsPhysics/omni.usd.schema.physics/plugins/UsdPhysics/resources/UsdPhysics/schema.usda``
* ``_isaac_sim/kit/extsPhysics/omni.usd.schema.physx/plugins/PhysxSchema/resources/PhysxSchema/schema.usda``
"""
from .schemas import (
set_articulation_root_properties,
set_collision_properties,
set_mass_properties,
set_rigid_body_properties,
)
from .schemas_cfg import (
ArticulationRootPropertiesCfg,
CollisionPropertiesCfg,
MassPropertiesCfg,
RigidBodyPropertiesCfg,
)
__all__ = [
# articulation root
"ArticulationRootPropertiesCfg",
"set_articulation_root_properties",
# rigid bodies
"RigidBodyPropertiesCfg",
"set_rigid_body_properties",
# colliders
"CollisionPropertiesCfg",
"set_collision_properties",
# mass
"MassPropertiesCfg",
"set_mass_properties",
]
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
from omni.isaac.orbit.utils import configclass
@configclass
class ArticulationRootPropertiesCfg:
"""Properties to apply to the root of an articulation.
See :meth:`set_articulation_root_properties` for more information.
.. note::
If the values are :obj:`None`, they are not modified. This is useful when you want to set only a subset of
the properties and leave the rest as-is.
"""
articulation_enabled: bool | None = None
"""Whether to enable or disable articulation."""
enabled_self_collisions: bool | None = None
"""Whether to enable or disable self-collisions."""
solver_position_iteration_count: int | None = None
"""Solver position iteration counts for the body."""
solver_velocity_iteration_count: int | None = None
"""Solver position iteration counts for the body."""
sleep_threshold: float | None = None
"""Mass-normalized kinetic energy threshold below which an actor may go to sleep."""
stabilization_threshold: float | None = None
"""The mass-normalized kinetic energy threshold below which an articulation may participate in stabilization."""
@configclass
class RigidBodyPropertiesCfg:
"""Properties to apply to a rigid body.
See :meth:`set_rigid_body_properties` for more information.
.. note::
If the values are :obj:`None`, they are not modified. This is useful when you want to set only a subset of
the properties and leave the rest as-is.
"""
rigid_body_enabled: bool | None = None
"""Whether to enable or disable the rigid body."""
kinematic_enabled: bool | None = None
"""Determines whether the body is kinematic or not.
A kinematic body is a body that is moved through animated poses or through user defined poses. The simulation
still derives velocities for the kinematic body based on the external motion.
For more information on kinematic bodies, please refer to the `documentation <https://openusd.org/release/wp_rigid_body_physics.html#kinematic-bodies>`_.
"""
disable_gravity: bool | None = False
"""Disable gravity for the actor. Defaults to False."""
linear_damping: float | None = None
"""Linear damping for the body."""
angular_damping: float | None = None
"""Angular damping for the body."""
max_linear_velocity: float | None = None
"""Maximum linear velocity for rigid bodies (in m/s)."""
max_angular_velocity: float | None = None
"""Maximum angular velocity for rigid bodies (in rad/s)."""
max_depenetration_velocity: float | None = None
"""Maximum depenetration velocity permitted to be introduced by the solver (in m/s)."""
max_contact_impulse: float | None = None
"""The limit on the impulse that may be applied at a contact."""
enable_gyroscopic_forces: bool | None = None
"""Enables computation of gyroscopic forces on the rigid body."""
retain_accelerations: bool | None = None
"""Carries over forces/accelerations over sub-steps."""
solver_position_iteration_count: int | None = None
"""Solver position iteration counts for the body."""
solver_velocity_iteration_count: int | None = None
"""Solver position iteration counts for the body."""
sleep_threshold: float | None = None
"""Mass-normalized kinetic energy threshold below which an actor may go to sleep."""
stabilization_threshold: float | None = None
"""The mass-normalized kinetic energy threshold below which an actor may participate in stabilization."""
@configclass
class CollisionPropertiesCfg:
"""Properties to apply to colliders in a rigid body.
See :meth:`set_collision_properties` for more information.
.. note::
If the values are :obj:`None`, they are not modified. This is useful when you want to set only a subset of
the properties and leave the rest as-is.
"""
collision_enabled: bool | None = None
"""Whether to enable or disable collisions."""
contact_offset: float | None = None
"""Contact offset for the collision shape (in m).
The collision detector generates contact points as soon as two shapes get closer than the sum of their
contact offsets. This quantity should be non-negative which means that contact generation can potentially start
before the shapes actually penetrate.
"""
rest_offset: float | None = None
"""Rest offset for the collision shape (in m).
The rest offset quantifies how close a shape gets to others at rest, At rest, the distance between two
vertically stacked objects is the sum of their rest offsets. If a pair of shapes have a positive rest
offset, the shapes will be separated at rest by an air gap.
"""
torsional_patch_radius: float | None = None
"""Radius of the contact patch for applying torsional friction (in m).
It is used to approximate rotational friction introduced by the compression of contacting surfaces.
If the radius is zero, no torsional friction is applied.
"""
min_torsional_patch_radius: float | None = None
"""Minimum radius of the contact patch for applying torsional friction (in m)."""
@configclass
class MassPropertiesCfg:
"""Properties to define explicit mass properties of a rigid body.
See :meth:`set_mass_properties` for more information.
.. note::
If the values are :obj:`None`, they are not modified. This is useful when you want to set only a subset of
the properties and leave the rest as-is.
"""
mass: float | None = None
"""The mass of the rigid body (in kg).
Note:
If non-zero, the mass is ignored and the density is used to compute the mass.
"""
density: float | None = None
"""The density of the rigid body (in kg/m^3).
The density indirectly defines the mass of the rigid body. It is generally computed using the collision
approximation of the body.
"""
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
import functools
from typing import Any, Callable
import carb
import omni.isaac.core.utils.prims as prim_utils
import omni.kit.commands
from pxr import Sdf, Usd
from omni.isaac.orbit.utils.string import to_camel_case
def safe_set_attribute_on_usd_schema(schema_api: Usd.APISchemaBase, name: str, value: Any, camel_case: bool = True):
"""Set the value of an attribute on its USD schema if it exists.
A USD API schema serves as an interface or API for authoring and extracting a set of attributes.
They typically derive from the :class:`pxr.Usd.SchemaBase` class. This function checks if the
attribute exists on the schema and sets the value of the attribute if it exists.
Args:
schema_api (Usd.APISchemaBase): The USD schema to set the attribute on.
name (str): The name of the attribute.
value (Any): The value to set the attribute to.
camel_case (bool, optional): Whether to convert the attribute name to camel case. Defaults to True.
Raises:
TypeError: When the input attribute name does not exist on the provided schema API.
"""
# if value is None, do nothing
if value is None:
return
# convert attribute name to camel case
if camel_case:
attr_name = to_camel_case(name, to="CC")
else:
attr_name = name
# retrieve the attribute
# reference: https://openusd.org/dev/api/_usd__page__common_idioms.html#Usd_Create_Or_Get_Property
attr = getattr(schema_api, f"Create{attr_name}Attr", None)
# check if attribute exists
if attr is not None:
attr().Set(value)
else:
# think: do we ever need to create the attribute if it doesn't exist?
# currently, we are not doing this since the schemas are already created with some defaults.
carb.log_error(f"Attribute '{attr_name}' does not exist on prim '{schema_api.GetPath()}'.")
raise TypeError(f"Attribute '{attr_name}' does not exist on prim '{schema_api.GetPath()}'.")
def safe_set_attribute_on_usd_prim(prim: Usd.Prim, attr_name: str, value: Any, camel_case: bool = True):
"""Set the value of a attribute on its USD prim.
The function creates a new attribute if it does not exist on the prim. This is because in some cases (such
as with shaders), their attributes are not exposed as USD prim properties that can be altered. This function
allows us to set the value of the attributes in these cases.
Args:
prim (Usd.Prim): The USD prim to set the attribute on.
attr_name (str): The name of the attribute.
value (Any): The value to set the attribute to.
camel_case (bool, optional): Whether to convert the attribute name to camel case. Defaults to True.
"""
# if value is None, do nothing
if value is None:
return
# convert attribute name to camel case
if camel_case:
attr_name = to_camel_case(attr_name, to="cC")
# resolve sdf type based on value
if isinstance(value, bool):
sdf_type = Sdf.ValueTypeNames.Bool
elif isinstance(value, int):
sdf_type = Sdf.ValueTypeNames.Int
elif isinstance(value, float):
sdf_type = Sdf.ValueTypeNames.Float
elif isinstance(value, (tuple, list)) and len(value) == 3 and any(isinstance(v, float) for v in value):
sdf_type = Sdf.ValueTypeNames.Float3
elif isinstance(value, (tuple, list)) and len(value) == 2 and any(isinstance(v, float) for v in value):
sdf_type = Sdf.ValueTypeNames.Float2
else:
raise NotImplementedError(
f"Cannot set attribute '{attr_name}' with value '{value}'. Please modify the code to support this type."
)
# change property
omni.kit.commands.execute(
"ChangePropertyCommand",
prop_path=Sdf.Path(f"{prim.GetPath()}.{attr_name}"),
value=value,
prev=None,
type_to_create_if_not_exist=sdf_type,
usd_context_name=prim.GetStage(),
)
def apply_nested(func: Callable) -> Callable:
"""Decorator to apply a function to all prims under a specified prim-path.
The function iterates over the provided prim path and all its children to apply input function
to all prims under the specified prim path.
Note:
If the function succeeds to apply to a prim, it will not look at the children of that prim.
This is based on the physics behavior that nested schemas are not allowed. For example, a parent prim
and its child prim cannot both have a rigid-body schema applied on them, or it is not possible to
have nested articulations.
Args:
func (Callable): The function to apply to all prims under a specified prim-path. The function
must take the prim-path as the first argument and the configuration as the second argument.
Returns:
Callable: The wrapped function that applies the function to all prims under a specified prim-path.
"""
@functools.wraps(func)
def wrapper(prim_path: str, cfg: object):
# get USD prim
prim = prim_utils.get_prim_at_path(prim_path)
# check if prim is valid
if not prim.IsValid():
raise ValueError(f"Prim at path '{prim_path}' is not valid.")
# iterate over all prims under prim-path
all_prims = [prim]
while len(all_prims) > 0:
# get current prim
child_prim = all_prims.pop(0)
child_prim_path = prim_utils.get_prim_path(child_prim)
# check if prim is a prototype
# note: we prefer throwing a warning instead of ignoring the prim since the user may
# have intended to set properties on the prototype prim.
if child_prim.IsInstance():
carb.log_warn(f"Cannot perform '{func.__name__}' on instanced prim: '{child_prim_path}'")
continue
# set properties
success = func(child_prim_path, cfg)
# if successful, do not look at children
# this is based on the physics behavior that nested schemas are not allowed
if not success:
all_prims += child_prim.GetChildren()
return wrapper
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment