Set nativeccd as default.

PiperOrigin-RevId: 731343200
Change-Id: I315779017b676f3d118e0d38ee53a515272f50b8
This commit is contained in:
Kyle Bayes
2025-02-26 09:14:19 -08:00
committed by Copybara-Service
parent 4f691eeae4
commit ed16f2daf2
21 changed files with 232 additions and 157 deletions
+4 -9
View File
@@ -499,16 +499,11 @@ Returns the smallest signed distance between two geoms and optionally the segmen
Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is
found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0). found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0).
.. admonition:: Positive ``distmax`` values .. admonition:: different (correct) behavior under `nativeccd`
:class: note :class: note
.. TODO: b/339596989 - Improve mjc_Convex. As explained in :ref:`Collision Detection<coDistance>`, distances are inaccurate when using the
:ref:`legacy CCD pipeline<coCCD>`, and its use is discouraged.
For some colliders, a large, positive ``distmax`` will result in an accurate measurement. However, for collision
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely inaccurate.
This is considered a bug to be fixed in a future release.
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
.. _mj_contactForce: .. _mj_contactForce:
+4 -9
View File
@@ -222,16 +222,11 @@ Returns the smallest signed distance between two geoms and optionally the segmen
Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is
found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0). found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0).
.. admonition:: Positive ``distmax`` values .. admonition:: different (correct) behavior under `nativeccd`
:class: note :class: note
.. TODO: b/339596989 - Improve mjc_Convex. As explained in :ref:`Collision Detection<coDistance>`, distances are inaccurate when using the
:ref:`legacy CCD pipeline<coCCD>`, and its use is discouraged.
For some colliders, a large, positive ``distmax`` will result in an accurate measurement. However, for collision
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely inaccurate.
This is considered a bug to be fixed in a future release.
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
.. _mj_fullM: .. _mj_fullM:
+13 -20
View File
@@ -582,6 +582,12 @@ from its default.
This flag disables the mid-phase collision filtering using a static AABB bounding volume hierarchy (a BVH binary This flag disables the mid-phase collision filtering using a static AABB bounding volume hierarchy (a BVH binary
tree). If disabled, all geoms pairs that are allowed to collide are checked for collisions. tree). If disabled, all geoms pairs that are allowed to collide are checked for collisions.
.. _option-flag-nativeccd:
:at:`nativeccd`: :at-val:`[disable, enable], "enable"`
This flag enables the native convex collision detection pipeline instead of using the
`libccd library <https://github.com/danfis/libccd>`__, see :ref:`convex collisions<coCCD>` for more details.
.. _option-flag-eulerdamp: .. _option-flag-eulerdamp:
:at:`eulerdamp`: :at-val:`[disable, enable], "enable"` :at:`eulerdamp`: :at-val:`[disable, enable], "enable"`
@@ -635,14 +641,12 @@ from its default.
.. _option-flag-multiccd: .. _option-flag-multiccd:
:at:`multiccd`: :at-val:`[disable, enable], "disable"` |nbsp| |nbsp| |nbsp| (experimental feature) :at:`multiccd`: :at-val:`[disable, enable], "disable"`
This flag enables multiple-contact collision detection for geom pairs that use a general-purpose convex-convex This flag enables multiple-contact collision detection for geom pairs that use a general-purpose convex-convex
collider e.g., mesh-mesh collisions. This can be useful when the contacting geoms have a flat surface, and the collider e.g., mesh-mesh collisions. This can be useful when the contacting geoms have a flat surface and the
single contact point generated by the convex-convex collider cannot accurately capture the surface contact, leading single contact point generated by the convex-convex collider cannot accurately capture the surface contact, leading
to instabilities that typically manifest as sliding or wobbling. Multiple contact points are found by rotating the to instabilities that typically manifest as sliding or wobbling. The implementation of this feature depends on the
two geoms by ±1e-3 radians around the tangential axes and re-running the collision function. If a new contact is selected convex collision pipeline, see :ref:`convex collisions<coCCD>` for more details.
detected it is added, allowing for up to 4 additional contact points. This feature is currently considered
experimental, and both the behavior and the way it is activated may change in the future.
.. _option-flag-island: .. _option-flag-island:
@@ -652,12 +656,6 @@ from its default.
allows for `island visualization <https://youtu.be/Vc1tq0fFvQA>`__. allows for `island visualization <https://youtu.be/Vc1tq0fFvQA>`__.
In a future release, the constraint solver will exploit the disjoint nature of constraint islands. In a future release, the constraint solver will exploit the disjoint nature of constraint islands.
.. _option-flag-nativeccd:
:at:`nativeccd`: :at-val:`[disable, enable], "disable"`
This flag enables the native convex collision detection pipeline instead of using the
`libccd library <https://github.com/danfis/libccd>`__.
.. _compiler: .. _compiler:
**compiler** (*) **compiler** (*)
@@ -6977,16 +6975,11 @@ pipeline. These 3 sensors share some common properties:
to geom-geom penetration) will be reported by :ref:`sensor/distance<sensor-distance>`. to geom-geom penetration) will be reported by :ref:`sensor/distance<sensor-distance>`.
In order to determine collision properties of non-penetrating geom pairs, a positive :at:`cutoff` is required. In order to determine collision properties of non-penetrating geom pairs, a positive :at:`cutoff` is required.
.. admonition:: Positive cutoff values .. admonition:: different (correct) behavior under `nativeccd`
:class: note :class: note
.. TODO: b/339596989 - Improve mjc_Convex. As explained in :ref:`Collision Detection<coDistance>`, distances are inaccurate when using the
:ref:`legacy CCD pipeline<coCCD>`, and its use is discouraged.
For some colliders, a positive :at:`cutoff` will result in an accurate measurement. However, for collision
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely inaccurate.
This is considered a bug to be fixed in a future release.
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
:at:`geom1`, :at:`geom2`, :at:`body1`, :at:`body2` :at:`geom1`, :at:`geom2`, :at:`body1`, :at:`body2`
For all 3 collision sensor types, the two colliding geoms can be specified explicitly using the :at:`geom1` and For all 3 collision sensor types, the two colliding geoms can be specified explicitly using the :at:`geom1` and
+21 -2
View File
@@ -23,6 +23,23 @@ Feature promotion
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper_trilinear.xml>`__ flexes for modeling <https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper_trilinear.xml>`__ flexes for modeling
deformable gripper pads. deformable gripper pads.
- .. image:: images/computation/ccd_light.gif
:width: 20%
:align: right
:class: only-light
.. image:: images/computation/ccd_dark.gif
:width: 20%
:align: right
:class: only-dark
The native convex collision detection pipeline introduced in 3.2.3 and enabled by the
:ref:`nativeccd<option-flag-nativeccd>` flag, is now the default. See the section on
:ref:`Convex Collision Detection<coCCD>` for more details.
**Migration:** If the new pipeline breaks your workflow, set :ref:`nativeccd<option-flag-nativeccd>` to "disable".
General General
^^^^^^^ ^^^^^^^
- Add support for custom plots in the MuJoCo viewer by exposing a ``viewport`` property, a ``set_figures`` method, - Add support for custom plots in the MuJoCo viewer by exposing a ``viewport`` property, a ``set_figures`` method,
@@ -36,6 +53,8 @@ General
.. admonition:: Breaking API changes .. admonition:: Breaking API changes
:class: attention :class: attention
- As mentioned above, the native convex collision detection pipeline is now the default, which may break some
workflows. In this case, set :ref:`nativeccd<option-flag-nativeccd>` to "disable" to restore the old behavior.
- Added :ref:`mjs_setDeepCopy` API function. When the deep copy flag is 0, attaching a model will not copy it to the - Added :ref:`mjs_setDeepCopy` API function. When the deep copy flag is 0, attaching a model will not copy it to the
parent, so the original references to the child can be used to modify the parent after attachment. The default parent, so the original references to the child can be used to modify the parent after attachment. The default
behavior is to perform such a shallow copy. The old behavior of creating a deep copy of the child model while behavior is to perform such a shallow copy. The old behavior of creating a deep copy of the child model while
@@ -184,8 +203,8 @@ General
1. The Newton solver no longer requires ``nv*nv`` memory allocation, allowing for much larger models. See e.g., 1. The Newton solver no longer requires ``nv*nv`` memory allocation, allowing for much larger models. See e.g.,
`100_humanoids.xml <https://github.com/google-deepmind/mujoco/blob/main/model/humanoid/100_humanoids.xml>`__. `100_humanoids.xml <https://github.com/google-deepmind/mujoco/blob/main/model/humanoid/100_humanoids.xml>`__.
Two quadratic-memory allocations still remain to be fully sparsified: ``mjData.actuator_moment`` and the matrices used Two quadratic-memory allocations still remain to be fully sparsified: ``mjData.actuator_moment`` and the matrices
by the PGS solver. used by the PGS solver.
2. Removed the :at:`solid` and :at:`membrane` plugins and moved the associated computations into the engine. See `3D 2. Removed the :at:`solid` and :at:`membrane` plugins and moved the associated computations into the engine. See `3D
example model <https://github.com/google-deepmind/mujoco/blob/main/model/flex/floppy.xml>`__ and `2D example model example model <https://github.com/google-deepmind/mujoco/blob/main/model/flex/floppy.xml>`__ and `2D example model
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/trampoline.xml>`__ for examples of flex objects <https://github.com/google-deepmind/mujoco/blob/main/model/flex/trampoline.xml>`__ for examples of flex objects
+96 -17
View File
@@ -1539,27 +1539,106 @@ Filtering
Checking Checking
~~~~~~~~ ~~~~~~~~
Detailed collision checking, also known as *near-phase* or narrow-phase_ collision detection, is performed by functions
that depend on the geom types in the pair. The table of narrow-phase collision functions can be inspected at the top of
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__
and exposed to users who wish to install their own colliders as :ref:`mjCOLLISIONFUNC`. MuJoCo supports several
primitive geometric shapes: plane, sphere, capsule, cylinder, ellipsoid, and box. It also supports triangulated meshes and
height-fields.
Detailed collision checking is performed by functions that depend on the geom types in the pair. MuJoCo supports several .. _narrow-phase: https://en.wikipedia.org/wiki/Collision_detection#Narrow_phase
primitive geometric shapes: plane, sphere, capsule, cylinder, ellipsoid, box. It also supports triangulated meshes and
height fields.
We have chosen to limit collision detection to *convex* geoms. All primitive types are convex. Height fields are not With the notable exception of :ref:`SDF plugins<exSDF>` (see documentation therein), collision detection is limited to
convex but internally they are treated as unions of triangular prisms (using custom collision pruning beyond the filters *convex* geoms. All primitive types are convex. Height-fields are not convex but internally they are treated as a
described above). Meshes specified by the user can be non-convex, and are rendered as such. For collision purposes collection of triangular prisms (using custom collision pruning beyond the filters described above). Meshes specified by
however they are replaced with their convex hulls. Mesh collisions are based on the Minkowski Portal Refinement (MPR) the user can be non-convex, and are rendered as such. For collision purposes however they are replaced with their convex
algorithm as implemented in `libccd <https://github.com/danfis/libccd>`__. It has tolerance and maximum iteration hulls (visualized with the 'H' key in :ref:`simulate <saSimulate>`), computed by the `qhull <http://www.qhull.org/>`__
parameters exposed as ``mjModel.opt.ccd_tolerance`` and ``mjModel.opt.ccd_iterations`` respectively. MPR operates on the library.
convex hull implicitly, however pre-computing that hull can substantially improve performance for large meshes. The
model compiler does that by default, using the `qhull <http://www.qhull.org/>`__ library. .. _coCCD:
Convex collisions
^^^^^^^^^^^^^^^^^
All collisions involving pairs of geoms that do not have an analytic collider (e.g., meshes), are handled by one of two
general-purpose convex collision detection (CCD) pipelines:
native pipeline (default)
The native CCD pipeline ("nativeccd") is implemented natively in MuJoCo, based on the Gilbert-Johnson-Keerthi and
Expanding Polytope algorithms (GJK_ / EPA_). The native pipeline is both faster and more robust than the MPR-based
pipeline.
libccd pipeline (legacy)
This legacy pipeline is based on the libccd_ library, and uses Minkowski Portal Refinement (MPR_). It is activated by
disabling the :ref:`nativeccd<option-flag-nativeccd>` flag.
.. _libccd: https://github.com/danfis/libccd
.. _MPR: https://en.wikipedia.org/wiki/Minkowski_Portal_Refinement
.. _GJK: https://en.wikipedia.org/wiki/Gilbert%E2%80%93Johnson%E2%80%93Keerthi_distance_algorithm
.. _EPA: http://scroll.stanford.edu/courses/cs468-01-fall/Papers/van-den-bergen.pdf
Both pipelines are controlled by a tolerance (in units of distance) and maximum iteration parameters exposed as
``mjOption.ccd_tolerance`` (:ref:`ccd_tolerance<option-ccd_tolerance>`) and ``mjOption.ccd_iterations``
(:ref:`ccd_iterations<option-ccd_iterations>`), respectively.
.. _coMultiCCD:
Multiple contacts
^^^^^^^^^^^^^^^^^
Some colliders can return more than one contact per colliding pair to model line or surface contacts, as when two flat
objects touch. For example the capsule-plane and box-plane colliders can return up to two or four contacts,
respectively. Standard general-purpose convex collision algorithms like MPR and GJK always return a single contact
point, which is problematic for surface contact scenarios (e.g., box-stacking). Both of MuJoCo's CCD pipelines can
return multiple points per contacting pair ("multiccd"). This behavior is controlled by the
:ref:`multiccd<option-flag-multiccd>` flag, but is implemented in different ways with different trade-offs:
libccd pipeline (legacy)
Multiple contact points are found by rotating the two geoms by ±1e-3 radians around the tangential axes and
re-running the collision routine. If a new contact is detected it is added, allowing for up to 4 additional contact
points. This method is effective, but increases the cost of each collision call by a factor of 5.
native pipeline
Native multiccd discovers multiple contacts using a novel analysis of the contacting surfaces at the solution,
avoiding full re-runs of the collision routine, and is thus effectively "free". Note that native multiccd currently
does not support positive contact margins. If one of the two geoms has a positive margin, native multiccd will fall
back to legacy algorithm.
.. _coDistance:
Geom distance
^^^^^^^^^^^^^
.. image:: ../images/computation/ccd_light.gif
:width: 25%
:align: right
:class: only-light
.. image:: ../images/computation/ccd_dark.gif
:width: 25%
:align: right
:class: only-dark
The narrow-phase collision functions described :ref:`above<coChecking>` drive the :ref:`mj_geomDistance` function and
associated :ref:`collision-sensors`. Due to the limitations of MPR, the legacy pipeline will return incorrect values
(top) except at very small distances relative to the geom sizes, and is discouraged for this use case. In
contrast, the GJK-based native pipeline (bottom), computes the correct values at all distances.
Convex decomposition
^^^^^^^^^^^^^^^^^^^^
In order to model a non-convex object other than a height field, the user must decompose it into a union of convex geoms In order to model a non-convex object other than a height field, the user must decompose it into a union of convex geoms
(which can be primitive shapes or meshes) and attach them to the same body. Open tools like the `CoACD library (which can be primitive shapes or meshes) and attach them to the same body. A height-field is essentially a shape that
<https://github.com/SarahWeiii/CoACD>`__ can be used outside MuJoCo to automate this process. Finally, all built-in is automatically-decomposed into prisms
collision functions can be replaced with custom callbacks. This can be used to incorporate a general-purpose "triangle
soup" collision detector for example. However we do not recommend such an approach. Pre-processing the geometry and Open mesh-decomposition tools like the
representing it as a union of convex geoms takes some work, but it pays off at runtime and yields both faster and more `CoACD library <https://github.com/SarahWeiii/CoACD>`__ can be used outside MuJoCo to automate this process. Finally,
stable simulation. all built-in collision functions can be replaced with custom callbacks. This can be used to incorporate a
general-purpose "triangle soup" collision detector for example. However we do not recommend such an approach.
Pre-processing the geometry and representing it as a union of convex geoms takes some work, but it pays off at runtime
and yields both faster and more stable simulation.
The exception to this rule are :ref:`SDF plugins<exSDF>` (see documentation therein), which in
`certain cases <https://github.com/google-deepmind/mujoco/blob/main/plugin/sdf/README.md#gear>`__ can be efficient,
but other requirements and limitations.
.. _Pipeline: .. _Pipeline:
Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

+3 -3
View File
@@ -429,8 +429,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering
mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator
mjDSBL_AUTORESET = 1<<15, // automatic reset when numerical issues are detected mjDSBL_AUTORESET = 1<<15, // automatic reset when numerical issues are detected
mjDSBL_NATIVECCD = 1<<16, // native convex collision detection
mjNDISABLE = 16 // number of disable flags mjNDISABLE = 17 // number of disable flags
} mjtDisableBit; } mjtDisableBit;
typedef enum mjtEnableBit_ { // enable optional feature bitflags typedef enum mjtEnableBit_ { // enable optional feature bitflags
mjENBL_OVERRIDE = 1<<0, // override contact parameters mjENBL_OVERRIDE = 1<<0, // override contact parameters
@@ -440,9 +441,8 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
// experimental features: // experimental features:
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
mjENBL_ISLAND = 1<<5, // constraint island discovery mjENBL_ISLAND = 1<<5, // constraint island discovery
mjENBL_NATIVECCD = 1<<6, // native convex collision detection
mjNENABLE = 7 // number of enable flags mjNENABLE = 6 // number of enable flags
} mjtEnableBit; } mjtEnableBit;
typedef enum mjtJoint_ { // type of degree of freedom typedef enum mjtJoint_ { // type of degree of freedom
mjJNT_FREE = 0, // global position and orientation (quat) (7) mjJNT_FREE = 0, // global position and orientation (quat) (7)
+30 -35
View File
@@ -66,9 +66,9 @@ directly from the top-level ``mjx`` module.
Structs Structs
------- -------
Before running MJX functions on an accelerator device, structs must be copied onto the device via the ``mjx.put_model`` and ``mjx.put_data`` Before running MJX functions on an accelerator device, structs must be copied onto the device via the ``mjx.put_model``
functions. Placing an :ref:`mjModel` on device yields an ``mjx.Model``. Placing an :ref:`mjData` on device yields and ``mjx.put_data`` functions. Placing an :ref:`mjModel` on device yields an ``mjx.Model``. Placing an :ref:`mjData` on
an ``mjx.Data``: device yields an ``mjx.Data``:
.. code-block:: python .. code-block:: python
@@ -86,12 +86,10 @@ These MJX variants mirror their MuJoCo counterparts but have a few key differenc
express domain randomization (in the case of ``mjx.Model``) or high-throughput simulation for reinforcement learning express domain randomization (in the case of ``mjx.Model``) or high-throughput simulation for reinforcement learning
(in the case of ``mjx.Data``). (in the case of ``mjx.Data``).
#. Numpy arrays in ``mjx.Model`` and ``mjx.Data`` are structural fields that control the output of JIT compilation. #. Numpy arrays in ``mjx.Model`` and ``mjx.Data`` are structural fields that control the output of JIT compilation.
Modifying these arrays will force JAX to recompile MJX functions. As an example, Modifying these arrays will force JAX to recompile MJX functions. As an example, ``jnt_limited`` is a numpy array
``jnt_limited`` is a numpy array passed by reference from :ref:`mjModel`, which determines if joint limit passed by reference from :ref:`mjModel`, which determines if joint limit constraints should be applied. If
constraints should be applied. If ``jnt_limited`` is modified, JAX will ``jnt_limited`` is modified, JAX will re-compile MJX functions. On the other hand, ``jnt_range`` is a JAX array that
re-compile MJX functions. can be modified at runtime, and will only apply to joints with limits as specified by the ``jnt_limited`` field.
On the other hand, ``jnt_range`` is a JAX array that can be modified at runtime, and will only apply to joints with limits
as specified by the ``jnt_limited`` field.
Neither ``mjx.Model`` nor ``mjx.Data`` are meant to be constructed manually. An ``mjx.Data`` may be created by calling Neither ``mjx.Model`` nor ``mjx.Data`` are meant to be constructed manually. An ``mjx.Data`` may be created by calling
@@ -110,9 +108,9 @@ Using ``mjx.make_data`` may be preferable when constructing batched ``mjx.Data``
Functions Functions
--------- ---------
MuJoCo functions are exposed as MJX functions of the same name, but following MuJoCo functions are exposed as MJX functions of the same name, but following `PEP 8
`PEP 8 <https://peps.python.org/pep-0008/>`__-compliant names. Most of the :ref:`main simulation <Mainsimulation>` and <https://peps.python.org/pep-0008/>`__-compliant names. Most of the :ref:`main simulation <Mainsimulation>` and some of
some of the :ref:`sub-components <Subcomponents>` for forward simulation are available from the top-level ``mjx`` module. the :ref:`sub-components <Subcomponents>` for forward simulation are available from the top-level ``mjx`` module.
MJX functions are not `JIT compiled <https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html>`__ by default -- we MJX functions are not `JIT compiled <https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html>`__ by default -- we
leave it to the user to JIT MJX functions, or JIT their own functions that reference MJX functions. See the leave it to the user to JIT MJX functions, or JIT their own functions that reference MJX functions. See the
@@ -225,7 +223,8 @@ The following features are **fully supported** in MJX:
- ``PLANE``, ``HFIELD``, ``SPHERE``, ``CAPSULE``, ``BOX``, ``MESH`` are fully implemented. ``ELLIPSOID`` and - ``PLANE``, ``HFIELD``, ``SPHERE``, ``CAPSULE``, ``BOX``, ``MESH`` are fully implemented. ``ELLIPSOID`` and
``CYLINDER`` are implemented but only collide with other primitives, note that ``BOX`` is implemented as a mesh. ``CYLINDER`` are implemented but only collide with other primitives, note that ``BOX`` is implemented as a mesh.
* - :ref:`Constraint <mjtConstraint>` * - :ref:`Constraint <mjtConstraint>`
- ``EQUALITY``, ``LIMIT_JOINT``, ``CONTACT_FRICTIONLESS``, ``CONTACT_PYRAMIDAL``, ``CONTACT_ELLIPTIC``, ``FRICTION_DOF``, ``FRICTION_TENDON`` - ``EQUALITY``, ``LIMIT_JOINT``, ``CONTACT_FRICTIONLESS``, ``CONTACT_PYRAMIDAL``, ``CONTACT_ELLIPTIC``,
``FRICTION_DOF``, ``FRICTION_TENDON``
* - :ref:`Equality <mjtEq>` * - :ref:`Equality <mjtEq>`
- ``CONNECT``, ``WELD``, ``JOINT``, ``TENDON`` - ``CONNECT``, ``WELD``, ``JOINT``, ``TENDON``
* - :ref:`Integrator <mjtIntegrator>` * - :ref:`Integrator <mjtIntegrator>`
@@ -315,22 +314,19 @@ Single scene simulation
carefully optimized for CPU. MJX works best when simulating thousands or tens of thousands of scenes in parallel. carefully optimized for CPU. MJX works best when simulating thousands or tens of thousands of scenes in parallel.
Collisions between large meshes Collisions between large meshes
MJX supports collisions between convex mesh geometries. However the convex collision algorithms MJX supports collisions between convex mesh geometries. However the convex collision algorithms in MJX are implemented
in MJX are implemented differently than in MuJoCo. MJX uses a branchless version of the differently than in MuJoCo. MJX uses a branchless version of the `Separating Axis Test
`Separating Axis Test <https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2013/slides/822403Gregorius_Dirk_TheSeparatingAxisTest.pdf>`__ <https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2013/slides/822403Gregorius_Dirk_TheSeparatingAxisTest.pdf>`__
(SAT) to determine if geometries are colliding with convex meshes, while MuJoCo uses the Minkowski Portal Refinement (MPR) (SAT) to determine if geometries are colliding with convex meshes, while MuJoCo uses either MPR or GJK/EPA, see
algorithm as implemented in `libccd <https://github.com/danfis/libccd>`__. :ref:`Collision Detection<coChecking>` for more details. SAT works well for smaller meshes but suffers in both runtime
SAT works well for smaller meshes but suffers in both runtime and memory for larger meshes. and memory for larger meshes.
For For collisions with convex meshes and primitives, the convex decompositon of the mesh should have roughly **200
collisions with convex meshes and primitives, the convex decompositon of the mesh should have vertices or less** for reasonable performance. For convex-convex collisions, the convex mesh should have roughly
roughly **200 vertices or less** for reasonable performance. For convex-convex collisions, **fewer than 32 vertices**. We recommend using :ref:`maxhullvert<asset-mesh-maxhullvert>` in the MuJoCo compiler to
the convex mesh should have roughly **fewer than 32 vertices**. We recommend using achieve desired convex mesh properties. With careful tuning, MJX can simulate scenes with mesh collisions -- see the
:ref:`maxhullvert<asset-mesh-maxhullvert>` in the MuJoCo compiler to achieve desired convex mesh properties. MJX `shadow hand <https://github.com/google-deepmind/mujoco/tree/main/mjx/mujoco/mjx/test_data/shadow_hand>`__ config
With careful for an example. Speeding up mesh collision detection is an active area of development for MJX.
tuning, MJX can simulate scenes with mesh collisions -- see the MJX
`shadow hand <https://github.com/google-deepmind/mujoco/tree/main/mjx/mujoco/mjx/test_data/shadow_hand>`__
config for an example. Speeding up mesh collision detection is an active area of development for MJX.
Large, complex scenes with many contacts Large, complex scenes with many contacts
Accelerators exhibit poor performance for Accelerators exhibit poor performance for
@@ -392,13 +388,12 @@ For MJX to perform well, some configuration parameters should be adjusted from t
of 10% to 20%, as long as the dense matrices can fit on the device. of 10% to 20%, as long as the dense matrices can fit on the device.
Broadphase Broadphase
While MuJoCo handles broadphase culling out of the box, MJX requires additional parameters. For an approximate version of While MuJoCo handles broadphase culling out of the box, MJX requires additional parameters. For an approximate version
broadphase, use the experimental custom numeric parameters of broadphase, use the experimental custom numeric parameters ``max_contact_points`` and ``max_geom_pairs``.
``max_contact_points`` and ``max_geom_pairs``. ``max_contact_points`` caps the number of contact points ``max_contact_points`` caps the number of contact points sent to the solver for each condim type. ``max_geom_pairs``
sent to the solver for each condim type. ``max_geom_pairs`` caps the total number of geom-pairs sent to caps the total number of geom-pairs sent to respective collision functions for each geom-type pair. As an example, the
respective collision functions for each geom-type pair. As an example, the `shadow hand <https://github.com/google-deepmind/mujoco/tree/main/mjx/mujoco/mjx/test_data/shadow_hand>`__ environment
`shadow hand <https://github.com/google-deepmind/mujoco/tree/main/mjx/mujoco/mjx/test_data/shadow_hand>`__ makes use of these parameters.
environment makes use of these parameters.
GPU performance GPU performance
--------------- ---------------
+1
View File
@@ -293,6 +293,7 @@ The plugins in the `sensor/ <https://github.com/google-deepmind/mujoco/tree/main
custom sensors. Currently the sole sensor plugin is the touch grid sensor, see the `README custom sensors. Currently the sole sensor plugin is the touch grid sensor, see the `README
<https://github.com/google-deepmind/mujoco/blob/main/plugin/sensor/README.md>`__ for details. <https://github.com/google-deepmind/mujoco/blob/main/plugin/sensor/README.md>`__ for details.
.. _exSDF:
sdf sdf
""" """
+3 -3
View File
@@ -64,8 +64,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering
mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator
mjDSBL_AUTORESET = 1<<15, // automatic reset when numerical issues are detected mjDSBL_AUTORESET = 1<<15, // automatic reset when numerical issues are detected
mjDSBL_NATIVECCD = 1<<16, // native convex collision detection
mjNDISABLE = 16 // number of disable flags mjNDISABLE = 17 // number of disable flags
} mjtDisableBit; } mjtDisableBit;
@@ -77,9 +78,8 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
// experimental features: // experimental features:
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
mjENBL_ISLAND = 1<<5, // constraint island discovery mjENBL_ISLAND = 1<<5, // constraint island discovery
mjENBL_NATIVECCD = 1<<6, // native convex collision detection
mjNENABLE = 7 // number of enable flags mjNENABLE = 6 // number of enable flags
} mjtEnableBit; } mjtEnableBit;
@@ -60,7 +60,6 @@ def _collide(
mujoco.mj_resetDataKeyframe(m, d, keyframe) mujoco.mj_resetDataKeyframe(m, d, keyframe)
dx = mjx.put_data(m, d) dx = mjx.put_data(m, d)
m.opt.enableflags |= mujoco.mjtEnableBit.mjENBL_NATIVECCD
mujoco.mj_step(m, d) mujoco.mj_step(m, d)
collision_jit_fn = jax.jit(mjx.collision) collision_jit_fn = jax.jit(mjx.collision)
kinematics_jit_fn = jax.jit(mjx.kinematics) kinematics_jit_fn = jax.jit(mjx.kinematics)
+1 -1
View File
@@ -938,7 +938,7 @@ Euler integrator, semi-implicit in velocity.
self.assertEqual(mujoco.mjtEnableBit.mjENBL_OVERRIDE, 1 << 0) self.assertEqual(mujoco.mjtEnableBit.mjENBL_OVERRIDE, 1 << 0)
self.assertEqual(mujoco.mjtEnableBit.mjENBL_ENERGY, 1 << 1) self.assertEqual(mujoco.mjtEnableBit.mjENBL_ENERGY, 1 << 1)
self.assertEqual(mujoco.mjtEnableBit.mjENBL_FWDINV, 1 << 2) self.assertEqual(mujoco.mjtEnableBit.mjENBL_FWDINV, 1 << 2)
self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 7) self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 6)
self.assertEqual(mujoco.mjtGeom.mjGEOM_PLANE, 0) self.assertEqual(mujoco.mjtGeom.mjGEOM_PLANE, 0)
self.assertEqual(mujoco.mjtGeom.mjGEOM_HFIELD, 1) self.assertEqual(mujoco.mjtGeom.mjGEOM_HFIELD, 1)
self.assertEqual(mujoco.mjtGeom.mjGEOM_SPHERE, 2) self.assertEqual(mujoco.mjtGeom.mjGEOM_SPHERE, 2)
+3 -3
View File
@@ -43,7 +43,8 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjDSBL_MIDPHASE', 8192), ('mjDSBL_MIDPHASE', 8192),
('mjDSBL_EULERDAMP', 16384), ('mjDSBL_EULERDAMP', 16384),
('mjDSBL_AUTORESET', 32768), ('mjDSBL_AUTORESET', 32768),
('mjNDISABLE', 16), ('mjDSBL_NATIVECCD', 65536),
('mjNDISABLE', 17),
]), ]),
)), )),
('mjtEnableBit', ('mjtEnableBit',
@@ -57,8 +58,7 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjENBL_INVDISCRETE', 8), ('mjENBL_INVDISCRETE', 8),
('mjENBL_MULTICCD', 16), ('mjENBL_MULTICCD', 16),
('mjENBL_ISLAND', 32), ('mjENBL_ISLAND', 32),
('mjENBL_NATIVECCD', 64), ('mjNENABLE', 6),
('mjNENABLE', 7),
]), ]),
)), )),
('mjtJoint', ('mjtJoint',
+1 -2
View File
@@ -44,8 +44,7 @@ class EnumsTest(absltest.TestCase):
('mjENBL_INVDISCRETE', 1<<3), ('mjENBL_INVDISCRETE', 1<<3),
('mjENBL_MULTICCD', 1<<4), ('mjENBL_MULTICCD', 1<<4),
('mjENBL_ISLAND', 1<<5), ('mjENBL_ISLAND', 1<<5),
('mjENBL_NATIVECCD', 1<<6), ('mjNENABLE', 6)))
('mjNENABLE', 7)))
# values mostly increment by one with occasional overrides # values mostly increment by one with occasional overrides
def test_mjtGeom(self): # pylint: disable=invalid-name def test_mjtGeom(self): # pylint: disable=invalid-name
+32 -32
View File
@@ -33,38 +33,38 @@
// call libccd or nativeccd to recover penetration info // call libccd or nativeccd to recover penetration info
static int mjc_penetration(const mjModel* m, mjCCDObj* obj1, mjCCDObj* obj2, static int mjc_penetration(const mjModel* m, mjCCDObj* obj1, mjCCDObj* obj2,
const ccd_t* ccd, ccd_real_t* depth, ccd_vec3_t* dir, ccd_vec3_t* pos) { const ccd_t* ccd, ccd_real_t* depth, ccd_vec3_t* dir, ccd_vec3_t* pos) {
if (mjENABLED(mjENBL_NATIVECCD)) { // fallback to MPR
mjCCDConfig config; if (mjDISABLED(mjDSBL_NATIVECCD)) {
mjCCDStatus status; return ccdMPRPenetration(obj1, obj2, ccd, depth, dir, pos);
// set config
config.max_iterations = ccd->max_iterations,
config.tolerance = ccd->mpr_tolerance,
config.max_contacts = 1;
config.dist_cutoff = 0; // no geom distances needed
mjtNum dist = mjc_ccd(&config, &status, obj1, obj2);
if (dist < 0) {
if (depth) *depth = -dist;
if (dir) {
mju_sub3(dir->v, status.x1, status.x2);
mju_normalize3(dir->v);
}
if (pos) {
pos->v[0] = 0.5 * (status.x1[0] + status.x2[0]);
pos->v[1] = 0.5 * (status.x1[1] + status.x2[1]);
pos->v[2] = 0.5 * (status.x1[2] + status.x2[2]);
}
return 0;
}
if (depth) *depth = 0;
if (dir) mju_zero3(dir->v);
if (pos) mju_zero3(dir->v);
return 1;
} }
// fallback to MPR mjCCDConfig config;
return ccdMPRPenetration(obj1, obj2, ccd, depth, dir, pos); mjCCDStatus status;
// set config
config.max_iterations = ccd->max_iterations,
config.tolerance = ccd->mpr_tolerance,
config.max_contacts = 1;
config.dist_cutoff = 0; // no geom distances needed
mjtNum dist = mjc_ccd(&config, &status, obj1, obj2);
if (dist < 0) {
if (depth) *depth = -dist;
if (dir) {
mju_sub3(dir->v, status.x1, status.x2);
mju_normalize3(dir->v);
}
if (pos) {
pos->v[0] = 0.5 * (status.x1[0] + status.x2[0]);
pos->v[1] = 0.5 * (status.x1[1] + status.x2[1]);
pos->v[2] = 0.5 * (status.x1[2] + status.x2[2]);
}
return 0;
}
if (depth) *depth = 0;
if (dir) mju_zero3(dir->v);
if (pos) mju_zero3(dir->v);
return 1;
} }
@@ -791,7 +791,7 @@ static void mjc_initCCD(ccd_t* ccd, const mjModel* m) {
// find convex-convex collision // find convex-convex collision
static int mjc_CCDIteration(const mjModel* m, const mjData* d, mjCCDObj* obj1, mjCCDObj* obj2, static int mjc_CCDIteration(const mjModel* m, const mjData* d, mjCCDObj* obj1, mjCCDObj* obj2,
mjContact* con, int max_contacts, mjtNum margin) { mjContact* con, int max_contacts, mjtNum margin) {
if (mjENABLED(mjENBL_NATIVECCD)) { if (!mjDISABLED(mjDSBL_NATIVECCD)) {
mjCCDConfig config; mjCCDConfig config;
mjCCDStatus status; mjCCDStatus status;
@@ -928,7 +928,7 @@ int mjc_Convex(const mjModel* m, const mjData* d,
int ncon = mjc_CCDIteration(m, d, &obj1, &obj2, con, max_contacts, margin); int ncon = mjc_CCDIteration(m, d, &obj1, &obj2, con, max_contacts, margin);
// nativeccd supports multi Box-Box collision directly // nativeccd supports multi Box-Box collision directly
if (mjENABLED(mjENBL_NATIVECCD) && singlePass(&obj1, &obj2)) { if (!mjDISABLED(mjDSBL_NATIVECCD) && singlePass(&obj1, &obj2)) {
return ncon; return ncon;
} }
+4 -4
View File
@@ -61,7 +61,8 @@ const char* mjDISABLESTRING[mjNDISABLE] = {
"Sensor", "Sensor",
"Midphase", "Midphase",
"Eulerdamp", "Eulerdamp",
"AutoReset" "AutoReset",
"NativeCCD"
}; };
@@ -72,8 +73,7 @@ const char* mjENABLESTRING[mjNENABLE] = {
"Fwdinv", "Fwdinv",
"InvDiscrete", "InvDiscrete",
"MultiCCD", "MultiCCD",
"Island", "Island"
"NativeCCD"
}; };
@@ -1425,7 +1425,7 @@ mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
} }
// use nativecdd if flag is enabled // use nativecdd if flag is enabled
if (mjENABLED(mjENBL_NATIVECCD)) { if (!mjDISABLED(mjDSBL_NATIVECCD)) {
if (func == mjc_Convex || func == mjc_BoxBox) { if (func == mjc_Convex || func == mjc_BoxBox) {
return mj_geomDistanceCCD(m, d, g1, g2, distmax, fromto); return mj_geomDistanceCCD(m, d, g1, g2, distmax, fromto);
} }
+1 -1
View File
@@ -1118,6 +1118,7 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
READDSBL("midphase", mjDSBL_MIDPHASE) READDSBL("midphase", mjDSBL_MIDPHASE)
READDSBL("eulerdamp", mjDSBL_EULERDAMP) READDSBL("eulerdamp", mjDSBL_EULERDAMP)
READDSBL("autoreset", mjDSBL_AUTORESET) READDSBL("autoreset", mjDSBL_AUTORESET)
READDSBL("nativeccd", mjDSBL_NATIVECCD)
#undef READDSBL #undef READDSBL
#define READENBL(NAME, MASK) \ #define READENBL(NAME, MASK) \
@@ -1131,7 +1132,6 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
READENBL("invdiscrete", mjENBL_INVDISCRETE) READENBL("invdiscrete", mjENBL_INVDISCRETE)
READENBL("multiccd", mjENBL_MULTICCD) READENBL("multiccd", mjENBL_MULTICCD)
READENBL("island", mjENBL_ISLAND) READENBL("island", mjENBL_ISLAND)
READENBL("nativeccd", mjENBL_NATIVECCD)
#undef READENBL #undef READENBL
} }
} }
+1 -1
View File
@@ -1028,6 +1028,7 @@ void mjXWriter::Option(XMLElement* root) {
WRITEDSBL("midphase", mjDSBL_MIDPHASE) WRITEDSBL("midphase", mjDSBL_MIDPHASE)
WRITEDSBL("eulerdamp", mjDSBL_EULERDAMP) WRITEDSBL("eulerdamp", mjDSBL_EULERDAMP)
WRITEDSBL("autoreset", mjDSBL_AUTORESET) WRITEDSBL("autoreset", mjDSBL_AUTORESET)
WRITEDSBL("nativeccd", mjDSBL_NATIVECCD)
#undef WRITEDSBL #undef WRITEDSBL
#define WRITEENBL(NAME, MASK) \ #define WRITEENBL(NAME, MASK) \
@@ -1039,7 +1040,6 @@ void mjXWriter::Option(XMLElement* root) {
WRITEENBL("invdiscrete", mjENBL_INVDISCRETE) WRITEENBL("invdiscrete", mjENBL_INVDISCRETE)
WRITEENBL("multiccd", mjENBL_MULTICCD) WRITEENBL("multiccd", mjENBL_MULTICCD)
WRITEENBL("island", mjENBL_ISLAND) WRITEENBL("island", mjENBL_ISLAND)
WRITEENBL("nativeccd", mjENBL_NATIVECCD)
#undef WRITEENBL #undef WRITEENBL
} }
+11 -11
View File
@@ -42,11 +42,11 @@ static const char kMixedPath[] =
class TestHarness { class TestHarness {
public: public:
TestHarness(const char* xml_path, std::string label, int enable_flags = 0) { TestHarness(const char* xml_path, std::string label, int disable_flags = 0) {
// Fail test if there are any mujoco errors // Fail test if there are any mujoco errors
MujocoErrorTestGuard guard; MujocoErrorTestGuard guard;
model_ = LoadModelFromPath(xml_path); model_ = LoadModelFromPath(xml_path);
model_->opt.enableflags |= enable_flags; model_->opt.disableflags |= disable_flags;
data_ = mj_makeData(model_); data_ = mj_makeData(model_);
for (int i=0; i < kNumWarmupSteps; i++) { for (int i=0; i < kNumWarmupSteps; i++) {
mj_step(model_, data_); mj_step(model_, data_);
@@ -100,43 +100,43 @@ class TestHarness {
void ABSL_ATTRIBUTE_NO_TAIL_CALL void ABSL_ATTRIBUTE_NO_TAIL_CALL
BM_BoxMesh_NativeCCD(benchmark::State& state) { BM_BoxMesh_NativeCCD(benchmark::State& state) {
static TestHarness harness(kBoxMeshPath, "boxmesh.xml (nativeccd)", static TestHarness harness(kBoxMeshPath, "boxmesh.xml (nativeccd)");
mjENBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_BoxMesh_NativeCCD); BENCHMARK(BM_BoxMesh_NativeCCD);
void ABSL_ATTRIBUTE_NO_TAIL_CALL void ABSL_ATTRIBUTE_NO_TAIL_CALL
BM_BoxMesh_LibCCD(benchmark::State& state) { BM_BoxMesh_LibCCD(benchmark::State& state) {
static TestHarness harness(kBoxMeshPath, "boxmesh.xml (libccd)"); static TestHarness harness(kBoxMeshPath, "boxmesh.xml (libccd)",
mjDSBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_BoxMesh_LibCCD); BENCHMARK(BM_BoxMesh_LibCCD);
void ABSL_ATTRIBUTE_NO_TAIL_CALL void ABSL_ATTRIBUTE_NO_TAIL_CALL
BM_Ellipsoid_NativeCCD(benchmark::State& state) { BM_Ellipsoid_NativeCCD(benchmark::State& state) {
static TestHarness harness(kEllipsoidPath, "ellipsoid.xml (nativeccd)", static TestHarness harness(kEllipsoidPath, "ellipsoid.xml (nativeccd)");
mjENBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_Ellipsoid_NativeCCD); BENCHMARK(BM_Ellipsoid_NativeCCD);
void ABSL_ATTRIBUTE_NO_TAIL_CALL void ABSL_ATTRIBUTE_NO_TAIL_CALL
BM_Ellipsoid_LibCCD(benchmark::State& state) { BM_Ellipsoid_LibCCD(benchmark::State& state) {
static TestHarness harness(kEllipsoidPath, "ellipsoid.xml (libccd)"); static TestHarness harness(kEllipsoidPath, "ellipsoid.xml (libccd)",
mjDSBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_Ellipsoid_LibCCD); BENCHMARK(BM_Ellipsoid_LibCCD);
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_Mixed_NativeCCD(benchmark::State& state) { void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_Mixed_NativeCCD(benchmark::State& state) {
static TestHarness harness(kMixedPath, "mixed.xml (nativeccd)", static TestHarness harness(kMixedPath, "mixed.xml (nativeccd)");
mjENBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_Mixed_NativeCCD); BENCHMARK(BM_Mixed_NativeCCD);
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_Mixed_LibCCD(benchmark::State& state) { void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_Mixed_LibCCD(benchmark::State& state) {
static TestHarness harness(kMixedPath, "mixed.xml (libccd)"); static TestHarness harness(kMixedPath, "mixed.xml (libccd)",
mjDSBL_NATIVECCD);
harness.RunBenchmark(state); harness.RunBenchmark(state);
} }
BENCHMARK(BM_Mixed_LibCCD); BENCHMARK(BM_Mixed_LibCCD);
+3 -3
View File
@@ -160,7 +160,8 @@ public enum mjtDisableBit : int{
mjDSBL_MIDPHASE = 8192, mjDSBL_MIDPHASE = 8192,
mjDSBL_EULERDAMP = 16384, mjDSBL_EULERDAMP = 16384,
mjDSBL_AUTORESET = 32768, mjDSBL_AUTORESET = 32768,
mjNDISABLE = 16, mjDSBL_NATIVECCD = 65536,
mjNDISABLE = 17,
} }
public enum mjtEnableBit : int{ public enum mjtEnableBit : int{
mjENBL_OVERRIDE = 1, mjENBL_OVERRIDE = 1,
@@ -169,8 +170,7 @@ public enum mjtEnableBit : int{
mjENBL_INVDISCRETE = 8, mjENBL_INVDISCRETE = 8,
mjENBL_MULTICCD = 16, mjENBL_MULTICCD = 16,
mjENBL_ISLAND = 32, mjENBL_ISLAND = 32,
mjENBL_NATIVECCD = 64, mjNENABLE = 6,
mjNENABLE = 7,
} }
public enum mjtJoint : int{ public enum mjtJoint : int{
mjJNT_FREE = 0, mjJNT_FREE = 0,