Merge branch 'google-deepmind:main' into vidur/3.3.0-2
This commit is contained in:
@@ -96,7 +96,7 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER:STRING=clang++-10
|
||||
-DMUJOCO_HARDEN:BOOL=ON
|
||||
tmpdir: "/tmp"
|
||||
- os: macos-12
|
||||
- os: macos-13
|
||||
cmake_args: >-
|
||||
-G Ninja
|
||||
-DMUJOCO_HARDEN:BOOL=ON
|
||||
@@ -239,7 +239,6 @@ jobs:
|
||||
- name: Test Python bindings
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
shell: bash
|
||||
working-directory: python/dist
|
||||
env:
|
||||
MUJOCO_GL: disable
|
||||
run: >
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON)
|
||||
|
||||
project(
|
||||
mujoco
|
||||
VERSION 3.2.5
|
||||
VERSION 3.3.1
|
||||
DESCRIPTION "MuJoCo Physics Simulator"
|
||||
HOMEPAGE_URL "https://mujoco.org"
|
||||
)
|
||||
|
||||
@@ -33,8 +33,9 @@ We also provide [Python bindings] and a plug-in for the [Unity] game engine.
|
||||
|
||||
## Documentation
|
||||
|
||||
MuJoCo's documentation can be found at [mujoco.readthedocs.io]. Upcoming features due for the next
|
||||
release can be found in the [changelog] in the latest branch.
|
||||
MuJoCo's documentation can be found at [mujoco.readthedocs.io]. Upcoming
|
||||
features due for the next release can be found in the [changelog] in the
|
||||
"latest" branch.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -52,14 +53,19 @@ running on Google Colab:
|
||||
|
||||
- The **introductory** tutorial teaches MuJoCo basics:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/tutorial.ipynb)
|
||||
- The **LQR** tutorial synthesizes a linear-quadratic controller, balancing a humanoid on one leg:
|
||||
- The **rollout** tutorial shows how to use the multithreaded `rollout` module:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/rollout.ipynb)
|
||||
- The **LQR** tutorial synthesizes a linear-quadratic controller, balancing a
|
||||
humanoid on one leg:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/LQR.ipynb)
|
||||
- The **least-squares** tutorial explains how to use the Python-based nonlinear least-squares solver:
|
||||
- The **least-squares** tutorial explains how to use the Python-based nonlinear
|
||||
least-squares solver:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/least_squares.ipynb)
|
||||
- The **MJX** tutorial provides usage examples of
|
||||
[MuJoCo XLA](https://mujoco.readthedocs.io/en/stable/mjx.html), a branch of MuJoCo written in JAX:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/mjx/tutorial.ipynb)
|
||||
- The **differentiable physics** tutorial trains locomotion policies with analytical gradients automatically derived from MuJoCo's physics step:
|
||||
- The **differentiable physics** tutorial trains locomotion policies with
|
||||
analytical gradients automatically derived from MuJoCo's physics step:
|
||||
[](https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/mjx/training_apg.ipynb)
|
||||
|
||||
## Installation
|
||||
@@ -73,7 +79,7 @@ and macOS (universal). This is the recommended way to use the software.
|
||||
### Building from source
|
||||
|
||||
Users who wish to build MuJoCo from source should consult the [build from
|
||||
source] section of the documentation. However, please note that the commit at
|
||||
source] section of the documentation. However, note that the commit at
|
||||
the tip of the `main` branch may be unstable.
|
||||
|
||||
### Python (>= 3.9)
|
||||
@@ -109,7 +115,8 @@ GitHub [Issues](https://github.com/google-deepmind/mujoco/issues) are reserved
|
||||
for bug reports, feature requests and other development-related subjects.
|
||||
|
||||
## Related software
|
||||
MuJoCo is the backbone for numerous environment packages. Below we list several bindings and converters.
|
||||
MuJoCo is the backbone for numerous environment packages. Below we list several
|
||||
bindings and converters.
|
||||
|
||||
### Bindings
|
||||
|
||||
@@ -137,7 +144,6 @@ These packages give users of various languages access to MuJoCo functionality:
|
||||
- **Java**: [mujoco-java](https://github.com/CommonWealthRobotics/mujoco-java)
|
||||
- **Julia**: [MuJoCo.jl](https://github.com/JamieMair/MuJoCo.jl)
|
||||
|
||||
|
||||
### Converters
|
||||
|
||||
- **OpenSim**: [MyoConverter](https://github.com/MyoHub/myoconverter) converts
|
||||
@@ -146,6 +152,8 @@ These packages give users of various languages access to MuJoCo functionality:
|
||||
two-way SDFormat <-> MJCF conversion tool.
|
||||
- **OBJ**: [obj2mjcf](https://github.com/kevinzakka/obj2mjcf)
|
||||
a script for converting composite OBJ files into a loadable MJCF model.
|
||||
- **onshape**: [Onshape to Robot](https://github.com/rhoban/onshape-to-robot)
|
||||
Converts [onshape](https://www.onshape.com/en/) CAD assemblies to MJCF.
|
||||
|
||||
## Citation
|
||||
|
||||
@@ -184,7 +192,6 @@ This is not an officially supported Google product.
|
||||
[Getting Started]: https://mujoco.readthedocs.io/en/latest/programming#getting-started
|
||||
[Unity]: https://unity.com/
|
||||
[releases page]: https://github.com/google-deepmind/mujoco/releases
|
||||
[GitHub Issues]: https://github.com/google-deepmind/mujoco/issues
|
||||
[mujoco.readthedocs.io]: https://mujoco.readthedocs.io
|
||||
[changelog]: https://mujoco.readthedocs.io/en/latest/changelog.html
|
||||
[Python bindings]: https://mujoco.readthedocs.io/en/stable/python.html#python-bindings
|
||||
|
||||
+5
-1
@@ -6,7 +6,7 @@ possible in your code contributions.
|
||||
|
||||
### Scope of this guide
|
||||
|
||||
MuJoCo has three main code categories:
|
||||
Most of this guide involves C/C++ code. For Python, jump to the section [below](#python-code). For MuJoCo C/C++, code has three main categories:
|
||||
|
||||
1. **C code:** MuJoCo's core codebase. It consists of public headers under
|
||||
`include/` and C source files and internal headers under `src/`. This style
|
||||
@@ -158,3 +158,7 @@ example above.
|
||||
New code should use the C99 convention. When editing an existing function,
|
||||
please move existing variable declarations into local scope. Pull requests
|
||||
helping us to complete the migration are very welcome.
|
||||
|
||||
### [Python code](#python-code)
|
||||
|
||||
For Python code, run `pyink foo.py` to adhere to Google's [Python style guide](https://google.github.io/styleguide/pyguide.html). For sorting and cleaning imports, run `isort foo.py`. Both `pyink` and `isort` can be pip installed via `pip install pyink isort`.
|
||||
|
||||
@@ -39,22 +39,22 @@ set(MUJOCO_DEP_VERSION_qhull
|
||||
CACHE STRING "Version of `qhull` to be fetched."
|
||||
)
|
||||
set(MUJOCO_DEP_VERSION_Eigen3
|
||||
b396a6fbb2e173f52edb3360485dedf3389ef830
|
||||
66f7f51b7e069d0a03a21157fa60b24aece69aeb
|
||||
CACHE STRING "Version of `Eigen3` to be fetched."
|
||||
)
|
||||
|
||||
set(MUJOCO_DEP_VERSION_abseil
|
||||
4447c7562e3bc702ade25105912dce503f0c4010 # LTS 20240722.0
|
||||
9ac7062b1860d895fb5a8cbf58c3e9ef8f674b5f # LTS 20250127.0
|
||||
CACHE STRING "Version of `abseil` to be fetched."
|
||||
)
|
||||
|
||||
set(MUJOCO_DEP_VERSION_gtest
|
||||
b514bdc898e2951020cbdca1304b75f5950d1f59 # v1.15.2
|
||||
6910c9d9165801d8827d628cb72eb7ea9dd538c5 # v1.16.0
|
||||
CACHE STRING "Version of `gtest` to be fetched."
|
||||
)
|
||||
|
||||
set(MUJOCO_DEP_VERSION_benchmark
|
||||
7c8ed6b082aa3c7a3402f18e50da4480421d08fd # v1.8.4
|
||||
049f6e79cc3e8636cec21bbd94ed185b4a5f2653
|
||||
CACHE STRING "Version of `benchmark` to be fetched."
|
||||
)
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -1,6 +1,6 @@
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 3,2,5,0
|
||||
PRODUCTVERSION 3,2,5,0
|
||||
FILEVERSION 3,3,1,0
|
||||
PRODUCTVERSION 3,3,1,0
|
||||
FILEOS 0x4
|
||||
FILETYPE 0x1
|
||||
{
|
||||
@@ -9,9 +9,9 @@ FILETYPE 0x1
|
||||
BLOCK "040904b0"
|
||||
{
|
||||
VALUE "ProductName", "MuJoCo"
|
||||
VALUE "ProductVersion", "3.2.5"
|
||||
VALUE "ProductVersion", "3.3.1"
|
||||
VALUE "FileDescription", "MuJoCo"
|
||||
VALUE "FileVersion", "3.2.5"
|
||||
VALUE "FileVersion", "3.3.1"
|
||||
VALUE "InternalName", "mujoco.dll"
|
||||
VALUE "OriginalFilename", "mujoco.dll"
|
||||
VALUE "CompanyName", "Google DeepMind"
|
||||
|
||||
Vendored
+4
-4
@@ -1,8 +1,8 @@
|
||||
MUJOCO ICON "mujoco.ico"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 3,2,5,0
|
||||
PRODUCTVERSION 3,2,5,0
|
||||
FILEVERSION 3,3,1,0
|
||||
PRODUCTVERSION 3,3,1,0
|
||||
FILEOS 0x4
|
||||
FILETYPE 0x1
|
||||
{
|
||||
@@ -11,9 +11,9 @@ FILETYPE 0x1
|
||||
BLOCK "040904b0"
|
||||
{
|
||||
VALUE "ProductName", "MuJoCo"
|
||||
VALUE "ProductVersion", "3.2.5"
|
||||
VALUE "ProductVersion", "3.3.1"
|
||||
VALUE "FileDescription", "MuJoCo"
|
||||
VALUE "FileVersion", "3.2.5"
|
||||
VALUE "FileVersion", "3.3.1"
|
||||
VALUE "InternalName", "simulate.exe"
|
||||
VALUE "OriginalFilename", "simulate.exe"
|
||||
VALUE "CompanyName", "Google DeepMind"
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.. raw:: html
|
||||
|
||||
<div id="fetchlines"/>
|
||||
|
||||
|
||||
.. _API:
|
||||
|
||||
=========
|
||||
|
||||
@@ -517,7 +517,7 @@ shown in the table below. Their names are in the format ``mjKEY_XXX``. They corr
|
||||
- Maximum number of UI rectangles.
|
||||
Defined in `mjui.h <https://github.com/google-deepmind/mujoco/blob/main/include/mujoco/mjui.h>`_.
|
||||
* - ``mjVERSION_HEADER``
|
||||
- 325
|
||||
- 331
|
||||
- The version of the MuJoCo headers; changes with every release. This is an integer equal to 100x the software
|
||||
version, so 210 corresponds to version 2.1. Defined in mujoco.h. The API function :ref:`mj_version` returns a
|
||||
number with the same meaning but for the compiled library.
|
||||
|
||||
@@ -28,7 +28,7 @@ MuJoCo defines a large number of types:
|
||||
- :ref:`mjOption` (embedded in :ref:`mjModel`).
|
||||
- :ref:`mjData`.
|
||||
|
||||
- :ref:`Auxillary struct types<tyAuxStructure>`, also used by the engine.
|
||||
- :ref:`Auxiliary struct types<tyAuxStructure>`, also used by the engine.
|
||||
- Structs for collecting :ref:`simulation statistics<tyStatStructure>`.
|
||||
- Structs for :ref:`abstract visualization<tyVisStructure>`.
|
||||
- Structs used by the :ref:`openGL renderer<tyRenderStructure>`.
|
||||
@@ -752,7 +752,7 @@ modifiable inputs and write their outputs.
|
||||
|
||||
.. _tyAuxStructure:
|
||||
|
||||
Auxillary
|
||||
Auxiliary
|
||||
^^^^^^^^^
|
||||
|
||||
These struct types are used in the engine and their names are prefixed with ``mj``. :ref:`mjVisual`
|
||||
@@ -1140,6 +1140,16 @@ behavior.
|
||||
.. mujoco-include:: mjsElement
|
||||
|
||||
|
||||
.. _mjsCompiler:
|
||||
|
||||
mjsCompiler
|
||||
~~~~~~~~~~~
|
||||
|
||||
Compiler options.
|
||||
|
||||
.. mujoco-include:: mjsCompiler
|
||||
|
||||
|
||||
.. _mjsBody:
|
||||
|
||||
mjsBody
|
||||
|
||||
+1047
-974
File diff suppressed because it is too large
Load Diff
@@ -47,13 +47,23 @@ struct pointers while editing the model programmatically.
|
||||
:ref:`mj_recompile` returns 0 if compilation succeed. In the case of failure, the given :ref:`mjModel` and :ref:`mjData`
|
||||
instances will be deleted; as in :ref:`mj_compile`, the compilation error can be read with :ref:`mjs_getError`.
|
||||
|
||||
.. _mj_saveLastXML:
|
||||
|
||||
Update XML data structures with info from low-level model created with :ref:`mj_loadXML`, save as MJCF.
|
||||
If error is not NULL, it must have size error_sz.
|
||||
|
||||
Note that this function only saves models that have been loaded with :ref:`mj_loadXML`, the legacy loading mechanism.
|
||||
See the :ref:`model editing<meOverview>` chapter to understand the difference between the old and new model loading and
|
||||
saving mechanisms.
|
||||
|
||||
.. _mj_saveXMLString:
|
||||
|
||||
Save spec to XML string, return 1 on success, 0 otherwise. XML saving requires that the spec first be compiled.
|
||||
Save spec to XML string, return 0 on success, -1 on failure. If the length of the output buffer is too small, returns
|
||||
the required size. XML saving automatically compiles the spec before saving.
|
||||
|
||||
.. _mj_saveXML:
|
||||
|
||||
Save spec to XML file, return 1 on success, 0 otherwise. XML saving requires that the spec first be compiled.
|
||||
Save spec to XML file, return 0 on success, -1 otherwise. XML saving requires that the spec first be compiled.
|
||||
|
||||
.. _Mainsimulation:
|
||||
|
||||
@@ -221,16 +231,16 @@ 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
|
||||
found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0).
|
||||
|
||||
.. admonition:: Positive ``distmax`` values
|
||||
:class: note
|
||||
.. admonition:: different (correct) behavior under `nativeccd`
|
||||
: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 innacurate.
|
||||
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:
|
||||
|
||||
Convert sparse inertia matrix ``M`` into full (i.e. dense) matrix.
|
||||
|br| ``dst`` must be of size ``nv x nv``, ``M`` must be of the same size as ``mjData.qM``.
|
||||
|
||||
.. _mj_mulM:
|
||||
|
||||
@@ -694,3 +704,8 @@ to the inputs. Below, :math:`\bar q` denotes the pre-modified quaternion:
|
||||
|
||||
Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math:`s = h v`).
|
||||
All outputs are optional.
|
||||
|
||||
.. _mjs_delete:
|
||||
|
||||
Delete object corresponding to the given element, return 0 on success. This function should only be used for element
|
||||
types that cannot have children, i.e. excluding bodies and default classes.
|
||||
|
||||
+264
-386
@@ -264,6 +264,11 @@ how to use includes and how to modularize large files if desired.
|
||||
The name of the XML file to be included. The file location is relative to the directory of the main MJCF file. If the
|
||||
file is not in the same directory, it should be prefixed with a relative path.
|
||||
|
||||
.. admonition:: Prefer attach to include
|
||||
:class: note
|
||||
|
||||
While some use cases for :ref:`include<include>` remain valid, it is recommended to use the
|
||||
:ref:`attach<body-attach>` element instead, where applicable.
|
||||
|
||||
|
||||
.. _mujoco:
|
||||
@@ -577,6 +582,12 @@ from its default.
|
||||
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.
|
||||
|
||||
.. _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:
|
||||
|
||||
:at:`eulerdamp`: :at-val:`[disable, enable], "enable"`
|
||||
@@ -586,7 +597,7 @@ from its default.
|
||||
.. _option-flag-autoreset:
|
||||
|
||||
:at:`autoreset`: :at-val:`[disable, enable], "enable"`
|
||||
This flag disables the automatic reseting of the simulation state when numerical issues are detected.
|
||||
This flag disables the automatic resetting of the simulation state when numerical issues are detected.
|
||||
|
||||
.. _option-flag-override:
|
||||
|
||||
@@ -596,9 +607,17 @@ from its default.
|
||||
.. _option-flag-energy:
|
||||
|
||||
:at:`energy`: :at-val:`[disable, enable], "disable"`
|
||||
This flag enables the computation of kinetic and potential energy, stored in mjData.energy and displayed in the GUI.
|
||||
This feature adds some CPU time but it is usually negligible. Monitoring energy for a system that is supposed to be
|
||||
energy-conserving is one of the best ways to assess the accuracy of a complex simulation.
|
||||
This flag enables the computation of potential and kinetic energy in ``mjData.energy[0, 1]`` respectively,
|
||||
and displayed in the simulate GUI info overlay. Potential energy includes the gravitational component summed over
|
||||
all bodies :math:`\sum_b m_b g h` and energy stored in passive springs in joints, tendons and flexes
|
||||
:math:`\tfrac{1}{2} k x^2`, where :math:`x` is the displacement and :math:`k` is the spring constant. Kinetic
|
||||
energy is given by :math:`\tfrac{1}{2} v^T M v`, where :math:`v` is the velocity and :math:`M` is the
|
||||
mass matrix. Note that potential and kinetic energy in constraints is not accounted for.
|
||||
|
||||
The extra computation (also triggered by :ref:`potential<sensor-e_potential>` and
|
||||
:ref:`kinetic<sensor-e_kinetic>` energy sensors) adds some CPU time but it is usually negligible. Monitoring energy
|
||||
for a system that is supposed to be energy-conserving is one of the best ways to assess the accuracy of a complex
|
||||
simulation.
|
||||
|
||||
.. _option-flag-fwdinv:
|
||||
|
||||
@@ -622,14 +641,12 @@ from its default.
|
||||
|
||||
.. _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
|
||||
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
|
||||
to instabilities that typically manifest as sliding or wobbling. Multiple contact points are found by rotating the
|
||||
two geoms by ±1e-3 radians around the tangential axes and re-running the collision function. If a new contact is
|
||||
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.
|
||||
to instabilities that typically manifest as sliding or wobbling. The implementation of this feature depends on the
|
||||
selected convex collision pipeline, see :ref:`convex collisions<coCCD>` for more details.
|
||||
|
||||
.. _option-flag-island:
|
||||
|
||||
@@ -639,12 +656,6 @@ from its default.
|
||||
allows for `island visualization <https://youtu.be/Vc1tq0fFvQA>`__.
|
||||
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** (*)
|
||||
@@ -772,23 +783,11 @@ has any effect. The settings here are global and apply to the entire model.
|
||||
models compiled with this flag, it is important to remember that collision geoms are often placed in a
|
||||
:ref:`group<body-geom-group>` which is invisible by default.
|
||||
|
||||
.. _compiler-convexhull:
|
||||
|
||||
:at:`convexhull`: :at-val:`[false, true], "true"`
|
||||
If this attribute is "true", the compiler will automatically generate a convex hull for every mesh that is used in at
|
||||
least one non-visual geom (in the sense of the discardvisual attribute above). This is done to speed up collision
|
||||
detection; recall :ref:`Collision` section in the Computation chapter. Even if the mesh is already convex, the hull
|
||||
contains edge information that is not present in the mesh file, so it needs to be constructed. The only reason to
|
||||
disable this feature is to speed up re-loading of a model with large meshes during model editing (since the convex
|
||||
hull computation is the slowest operation performed by the compiler). However once model design is finished, this
|
||||
feature should be enabled, because the availability of convex hulls substantially speeds up collision detection with
|
||||
large meshes.
|
||||
|
||||
.. _compiler-usethread:
|
||||
|
||||
:at:`usethread`: :at-val:`[false, true], "true"`
|
||||
If this attribute is "true", the model compiler will run in multi-threaded mode. Currently multi-threading is used
|
||||
for computing the length ranges of actuators and for loading meshes.
|
||||
for computing the length ranges of actuators and for parallel loading of meshes.
|
||||
|
||||
.. _compiler-fusestatic:
|
||||
|
||||
@@ -1245,21 +1244,21 @@ The full list of processing steps applied by the compiler to each mesh is as fol
|
||||
|
||||
.. _asset-mesh-inertia:
|
||||
|
||||
:at:`inertia`: :at-val:`[convex, exact, legacy], "legacy"`
|
||||
:at:`inertia`: :at-val:`[convex, exact, legacy, shell], "legacy"`
|
||||
This attribute controls how the mesh is used when mass and inertia are
|
||||
:ref:`inferred from geometry<_compiler-inertiafromgeom>`. The current default value :at-val:`legacy` will be changed
|
||||
:ref:`inferred from geometry<compiler-inertiafromgeom>`. The current default value :at-val:`legacy` will be changed
|
||||
to :at-val:`convex` in a future release.
|
||||
|
||||
:at-val:`convex`
|
||||
Use the mesh's convex hull to compute volume and inertia.
|
||||
:at-val:`convex`: Use the mesh's convex hull to compute volume and inertia, assuming uniform density.
|
||||
|
||||
:at-val:`exact`
|
||||
Use an exact algorithm to compute volume and inertia. This algorithm requires a well-oriented, watertight mesh and
|
||||
will error otherwise.
|
||||
:at-val:`exact`: Compute volume and inertia exactly, even for non-convex meshes. This algorithm requires a
|
||||
well-oriented, watertight mesh and will error otherwise.
|
||||
|
||||
:at-val:`legacy`
|
||||
Use the legacy algorithm, which is similar to :at-val:`convex`, but leads to volume overcounting for non-convex
|
||||
meshes.
|
||||
:at-val:`legacy`: Use the legacy algorithm, leads to volume overcounting for non-convex meshes. Though currently the
|
||||
default to avoid breakages, it is not recommended.
|
||||
|
||||
:at-val:`shell`: Assume mass is concentrated on the surface of the mesh. Use the mesh's surface to compute
|
||||
the inertia, assuming uniform surface density.
|
||||
|
||||
.. _asset-mesh-smoothnormal:
|
||||
|
||||
@@ -1273,7 +1272,7 @@ The full list of processing steps applied by the compiler to each mesh is as fol
|
||||
|
||||
:at:`maxhullvert`: :at-val:`int, "-1"`
|
||||
Maximum number of vertices in a mesh's convex hull. Currently this is implemented by asking qhull
|
||||
`to teminate <http://www.qhull.org/html/qh-optt.htm#TAn>`__ after :at:`maxhullvert` vertices. The default
|
||||
`to terminate <http://www.qhull.org/html/qh-optt.htm#TAn>`__ after :at:`maxhullvert` vertices. The default
|
||||
value of -1 means "unlimited". Positive values must be larger than 3.
|
||||
|
||||
.. _asset-mesh-vertex:
|
||||
@@ -1710,9 +1709,9 @@ properties are grouped together.
|
||||
loaded explicitly via the :ref:`texture <asset-texture>` element and then referenced here. The texture referenced
|
||||
here is used for specifying the RGB values. For advanced rendering (e.g., Physics-Based Rendering), more texture
|
||||
types need to be specified (e.g., roughness, metallic). In this case, this texture attribute should be omitted, and
|
||||
the texture types should be specified explicitly via the specific role child elements, e.g.,
|
||||
:ref:`texture <material-orm>`. Note however that the built-in renderer does not support PBR properties, so these
|
||||
advanced rendering features are only available when using an external renderer.
|
||||
the texture types should be specified using :ref:`layer <material-layer>` child elements. Note however that the
|
||||
built-in renderer does not support PBR properties, so these advanced rendering features are only available when using
|
||||
an external renderer.
|
||||
|
||||
.. _asset-material-texrepeat:
|
||||
|
||||
@@ -1784,116 +1783,59 @@ properties are grouped together.
|
||||
model element which defines its own local rgba attribute, the local definition has precedence. Note that this "local"
|
||||
definition could in fact come from a defaults class. The remaining material properties always apply.
|
||||
|
||||
.. _material-rgb:
|
||||
.. _material-layer:
|
||||
|
||||
:el-prefix:`material/` |-| **rgb** (?)
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
:el-prefix:`material/` |-| **layer** (?)
|
||||
''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify base color / albedo values.
|
||||
If multiple textures are needed to specify the appearance of a material, the :ref:`texture <asset-material-texture>`
|
||||
attribute cannot be used, and :el:`layer` child elements must be used instead. Specifying both the :at:`texture`
|
||||
attribute and :el:`layer` child elements is an error.
|
||||
|
||||
.. _material-rgb-texture:
|
||||
.. _material-layer-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly 3 channels.
|
||||
Name of the texture, like the :ref:`texture <asset-material-texture>` attribute.
|
||||
|
||||
.. _material-normal:
|
||||
.. _material-layer-role:
|
||||
|
||||
:el-prefix:`material/` |-| **normal** (?)
|
||||
'''''''''''''''''''''''''''''''''''''''''
|
||||
:at:`role`: :at-val:`string, required`
|
||||
Role of the texture. The valid values, expected number of channels, and the role semantics are:
|
||||
|
||||
This element references a texture asset used to specify the bump map (surface normals).
|
||||
|
||||
.. _material-normal-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly 3 channels.
|
||||
|
||||
.. _material-occlusion:
|
||||
|
||||
:el-prefix:`material/` |-| **occlusion** (?)
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify ambient occlusion.
|
||||
|
||||
.. _material-occlusion-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly one channel.
|
||||
|
||||
.. _material-roughness:
|
||||
|
||||
:el-prefix:`material/` |-| **roughness** (?)
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify the roughness map.
|
||||
|
||||
.. _material-roughness-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly one channel.
|
||||
|
||||
.. _material-metallic:
|
||||
|
||||
:el-prefix:`material/` |-| **metallic** (?)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify the metallic map.
|
||||
|
||||
.. _material-metallic-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly one channel.
|
||||
|
||||
.. _material-opacity:
|
||||
|
||||
:el-prefix:`material/` |-| **opacity** (?)
|
||||
''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify the opacity map (alpha channel, transparency).
|
||||
|
||||
.. _material-opacity-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly one channel.
|
||||
|
||||
.. _material-emissive:
|
||||
|
||||
:el-prefix:`material/` |-| **emissive** (?)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify light emission.
|
||||
|
||||
.. _material-emissive-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly 4 channels.
|
||||
|
||||
.. _material-orm:
|
||||
|
||||
:el-prefix:`material/` |-| **orm** (?)
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify a packed ORM map, where occlusion, roughness, and metallic
|
||||
are joined into the corresponding RGB values of a single texture.
|
||||
|
||||
.. _material-orm-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly 3 channels.
|
||||
|
||||
.. _material-rgba:
|
||||
|
||||
:el-prefix:`material/` |-| **rgba** (?)
|
||||
'''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This element references a texture asset used to specify a packed map where albedo and opacity are joined into the same
|
||||
4-channel texture.
|
||||
|
||||
.. _material-rgba-texture:
|
||||
|
||||
:at:`texture`: :at-val:`string, required`
|
||||
Name of the texture, expected to have exactly 4 channels.
|
||||
.. list-table::
|
||||
:widths: 1 1 8
|
||||
:header-rows: 1
|
||||
|
||||
* - value
|
||||
- channels
|
||||
- description
|
||||
* - :at:`rgb`
|
||||
- 3
|
||||
- base color / albedo [red, green, blue]
|
||||
* - :at:`normal`
|
||||
- 3
|
||||
- bump map (surface normals)
|
||||
* - :at:`occlusion`
|
||||
- 1
|
||||
- ambient occlusion
|
||||
* - :at:`roughness`
|
||||
- 1
|
||||
- roughness
|
||||
* - :at:`metallic`
|
||||
- 1
|
||||
- metallicity
|
||||
* - :at:`opacity`
|
||||
- 1
|
||||
- opacity (alpha channel)
|
||||
* - :at:`emissive`
|
||||
- 4
|
||||
- RGB light emmision intensity, exposure weight in 4th channel
|
||||
* - :at:`orm`
|
||||
- 3
|
||||
- packed 3 channel [occlusion, roughness, metallic]
|
||||
* - :at:`rgba`
|
||||
- 4
|
||||
- packed 4 channel [red, green, blue, alpha]
|
||||
|
||||
.. _asset-model:
|
||||
|
||||
@@ -1946,11 +1888,11 @@ defined. Its body name is automatically defined as "world".
|
||||
:at:`mocap`: :at-val:`[false, true], "false"`
|
||||
If this attribute is "true", the body is labeled as a mocap body. This is allowed only for bodies that are children
|
||||
of the world body and have no joints. Such bodies are fixed from the viewpoint of the dynamics, but nevertheless the
|
||||
forward kinematics set their position and orientation from the fields mjData.mocap_pos and mjData.mocap_quat at each
|
||||
forward kinematics set their position and orientation from the fields ``mjData.mocap_{pos,quat}`` at each
|
||||
time step. The size of these arrays is adjusted by the compiler so as to match the number of mocap bodies in the
|
||||
model. This mechanism can be used to stream motion capture data into the simulation. Mocap bodies can also be moved
|
||||
via mouse perturbations in the interactive visualizer, even in dynamic simulation mode. This can be useful for
|
||||
creating props with adjustable position and orientation. See also the mocap attribute of :ref:`flag <option-flag>`.
|
||||
creating props with adjustable position and orientation.
|
||||
|
||||
.. _body-pos:
|
||||
|
||||
@@ -2181,7 +2123,7 @@ rotations as unit quaternions.
|
||||
(``mjData.qfrc_actuator``) rather than passive forces (``mjData.qfrc_passive``). Notionally, this means that gravity
|
||||
compensation is the result of a control system rather than natural buoyancy. In practice, enabling this flag is
|
||||
useful when joint-level actuator force clamping is used. In this case, the total actuation force applied on a joint,
|
||||
including gravity compensation, is guaranteed to not exceeed the specified limits. See :ref:`CForceRange` and
|
||||
including gravity compensation, is guaranteed to not exceed the specified limits. See :ref:`CForceRange` and
|
||||
:ref:`actuatorfrcrange<body-joint-actuatorfrcrange>` for more details on this type of force limit.
|
||||
|
||||
.. _body-joint-margin:
|
||||
@@ -2426,7 +2368,8 @@ helps clarify the role of bodies and geoms in MuJoCo.
|
||||
This attribute specifies an integer group to which the geom belongs. The only effect on the physics is at compile
|
||||
time, when body masses and inertias are inferred from geoms selected based on their group; see inertiagrouprange
|
||||
attribute of :ref:`compiler <compiler>`. At runtime this attribute is used by the visualizer to enable and disable
|
||||
the rendering of entire geom groups. It can also be used as a tag for custom computations.
|
||||
the rendering of entire geom groups. By default, groups 0, 1 and 2 are visible, while all other groups are invisible.
|
||||
The group attribute can also be used as a tag for custom computations.
|
||||
|
||||
.. _body-geom-priority:
|
||||
|
||||
@@ -2515,8 +2458,10 @@ helps clarify the role of bodies and geoms in MuJoCo.
|
||||
.. _body-geom-shellinertia:
|
||||
|
||||
:at:`shellinertia` :at-val:`[false, true], "false"`
|
||||
If true, the geom's inertia is computed assuming that all the mass is concentrated on the boundary. In this case
|
||||
:at:`density` is interpreted as surface density rather than volumetric density.
|
||||
If true, the geom's inertia is computed assuming that all the mass is concentrated on the surface. In this case
|
||||
:at:`density` is interpreted as surface rather than volumetric density. This attribute only applies to primitive
|
||||
geoms and is ignored for meshes. Surface inertia for meshes can be specified by setting the
|
||||
:ref:`asset/mesh/inertia<asset-mesh-inertia>` attribute to :at-val:`"shell"`.
|
||||
|
||||
.. _body-geom-solmix:
|
||||
|
||||
@@ -2563,7 +2508,7 @@ helps clarify the role of bodies and geoms in MuJoCo.
|
||||
<COrientation>`. The frame position is in the middle between the end points. If this attribute is specified, the
|
||||
remaining position and orientation-related attributes are ignored. The image on the right demonstrates use of
|
||||
:at:`fromto` with the four supported geoms, using identical Z values. The model is `here <_static/fromto.xml>`__.
|
||||
Note that the :at:`fromto` semantics of *capsule* are unique: the two end points specify the segement around which
|
||||
Note that the :at:`fromto` semantics of *capsule* are unique: the two end points specify the segment around which
|
||||
the radius defines the capsule surface.
|
||||
|
||||
.. _body-geom-pos:
|
||||
@@ -2794,7 +2739,7 @@ and the +Y axis points up. Thus the frame position and orientation are the key a
|
||||
:at:`mode`: :at-val:`[fixed, track, trackcom, targetbody, targetbodycom], "fixed"`
|
||||
This attribute specifies how the camera position and orientation in world coordinates are computed in forward
|
||||
kinematics (which in turn determine what the camera sees). "fixed" means that the position and orientation specified
|
||||
below are fixed relative to the the body where the camera is defined. "track" means that the camera position is at a
|
||||
below are fixed relative to the body where the camera is defined. "track" means that the camera position is at a
|
||||
constant offset from the body in world coordinates, while the camera orientation is constant in world coordinates.
|
||||
These constants are determined by applying forward kinematics in qpos0 and treating the camera as fixed. Tracking can
|
||||
be used for example to position a camera above a body, point it down so it sees the body, and have it always remain
|
||||
@@ -3017,14 +2962,14 @@ the direction specified by the dir attribute. It does not have a full spatial fr
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is not a model element, but rather a macro which expands into multiple model elements representing a composite
|
||||
object. These elements are bodies (with their own joints, geoms and sites) that become children of the parent body
|
||||
containing the macro, as well as tendons and equality constraints added to the corresponding model sections. The
|
||||
automatically-generated bodies are laid out in a regular grid in 1D, 2D or 3D depending on the object type and count
|
||||
attributes. The macro expansion is done by the model compiler. If the resulting model is then saved, the macro will be
|
||||
object. These elements are bodies (with their own joints and geoms) that become children of the parent body containing
|
||||
the macro. The macro expansion is done by the model compiler. If the resulting model is then saved, the macro will be
|
||||
replaced with the actual model elements. The defaults mechanism used in the rest of MJCF does not apply here, even if
|
||||
the parent body has a childclass attribute defined. Instead there are internal defaults adjusted automatically for each
|
||||
composite object type. Composite objects can only be defined if the model is in local coordinates. Using them in global
|
||||
coordinates results in compiler error. See :ref:`CComposite` in the modeling guide for more detailed explanation.
|
||||
composite object type. See :ref:`CComposite` in the modeling guide for more detailed explanation. Note that there used
|
||||
to be several composite types, but they have incrementally replaced by :ref:`replicate<replicate>` (for repeated
|
||||
objects) and :ref:`flexcomp<body-flexcomp>` (for soft objects). Therefore, the only supported composite type is now
|
||||
cable, which produces an inextensible chain of bodies connected with ball joints.
|
||||
|
||||
.. _body-composite-prefix:
|
||||
|
||||
@@ -3035,37 +2980,8 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
|
||||
|
||||
.. _body-composite-type:
|
||||
|
||||
:at:`type`: :at-val:`[particle, grid, cable, rope, loop, cloth, box, cylinder, ellipsoid], required`
|
||||
This attribute determines the type of composite object. The remaining attributes and sub-elements are then
|
||||
interpreted according to the type. Default settings are also adjusted depending on the type.
|
||||
|
||||
The **particle** type creates a 1D, 2D or 3D grid of equally-spaced bodies. By default, each body has a single sphere
|
||||
geom and 3 orthogonal sliding joints, allowing translation but not rotation. The geom condim and priority attributes
|
||||
are set to 1 by default. This makes the spheres have frictionless contacts with all other geoms (unless the priority
|
||||
of some frictional geom is higher). The user can replace the default sliders with multiple joints of kind="particle"
|
||||
and replace the default sphere with a custom geom. Note that the particle composite type is deprecated and might be
|
||||
removed in a future version. Instead of particle, it is recommended to use :ref:`replicate`.
|
||||
|
||||
The **grid** type creates a 1D or 2D grid of bodies, each having a sphere geom, a sphere site, and 3 orthogonal
|
||||
sliding joints by default. The :el:`pin` sub-element can be used to specify that some bodies should not have joints,
|
||||
and instead should be pinned to the parent body. Unlike the particle type, here each two neighboring bodies are
|
||||
connected with a spatial tendon whose length is equality-constrained to its initial value (the sites are needed to
|
||||
define the tendons). The "main" tendons are parallel to the axes of the grid. In addition one can create diagonal
|
||||
"shear" tendons, using the :el:`tendon` sub-element. This type is suitable for simulating strings as well as cloth.
|
||||
|
||||
The **rope** type creates a 1D grid of bodies, each having a geom with user-defined type (sphere, capsule or
|
||||
ellipsoid) and 2 hinge joints with axes orthogonal to the grid, creating a universal joint with the previous body.
|
||||
This corresponds to a kinematic chain which can bend but cannot stretch or twist. In addition, one can specify
|
||||
stretch and twist joints (slide and hinge respectively) with the :el:`joint` sub-element. When specified, these extra
|
||||
joints are equality-constrained, but the constraint is soft by default so that some stretch and twist are possible.
|
||||
The rope can extend in one or both directions from the parent body. To specify the origin of the rope, the parent
|
||||
body *must* be named so that it fits the automatic naming convention. For example, to make the parent be the first
|
||||
body in the chain, and assuming we have prefix="C", the parent body should be named "CB0". When the parent is not at
|
||||
the end, the rope consists of two kinematic chains starting at the parent and extending in opposite directions.
|
||||
|
||||
The **loop** type is the same as the rope type except the elements are arranged in a circle, and the first and last
|
||||
elements are equality-constrained to remain connected (using the "connect" constraint type). The softness of this
|
||||
equality constraint is adjusted with the attributes solrefsmooth and solimpsmooth.
|
||||
:at:`type`: :at-val:`[cable], required`
|
||||
This attribute determines the type of composite object. The only supported type is cable.
|
||||
|
||||
The **cable** type creates a 1D chain of bodies connected with ball joints, each having a geom with user-defined type
|
||||
(cylinder, capsule or box). The geometry can either be defined with an array of 3D vertex coordinates :at:`vertex`
|
||||
@@ -3074,34 +2990,6 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
|
||||
:at:`size`, resulting in :math:`f(s)=\{\text{size}[1]\cdot\cos(2\pi\cdot\text{size}[2]),\;
|
||||
\text{size}[1]\cdot\sin(2\pi\cdot\text{size}[2]),\; \text{size}[0]\cdot s\}`.
|
||||
|
||||
The **cloth** type is a different way to model cloth, beyond type="grid". Here the elements are connected with
|
||||
universal joints and form a kinematic spanning tree. The root of the tree is the parent body, and its coordinates in
|
||||
the grid are inferred from its name - similar to rope but here the naming format is "CB2_0". Neighboring bodies that
|
||||
are not connected with joints are then connected with equality-constrained spatial tendons. The resulting cloth is
|
||||
non-homogeneous, because the kinematic constraints cannot be violated while the tendon equality constraints are soft.
|
||||
One can make it more homogeneous by adding stretch and twist joints (similar to rope) and adjusting the strength of
|
||||
their equality constraints. Shear tendons can also be added. In addition to the different physics, cloth can do
|
||||
things that a 2D grid cannot do. This is because the elements of cloth have both position and orientation, while the
|
||||
elements of grid can only translate. The geoms used in cloth can be ellipsoids and capsules in addition to spheres.
|
||||
When elongated geoms are used, they are rotated and interleaved in a pattern that fills the holes, preventing objects
|
||||
from penetrating the cloth. Furthermore the inertia of the cloth elements can be modified with the flatinertia
|
||||
attribute, and can then be used with lift and drag forces to simulate ripple effects.
|
||||
|
||||
The **box** type creates a 3D arrangement of bodies forming the outer shell of a (soft) box. The parent body is at
|
||||
the center of the box. Each element body has a geom (sphere, ellipsoid or capsule) and a single sliding joint
|
||||
pointing away from the center of the box. The sliding joints are equality-constrained to their initial value.
|
||||
Furthermore, to achieve smooth deformations of the sides of the box, each joint is equality-constrained to remain
|
||||
equal to its neighbor joints. To preserve the volume of the soft box approximately, a fixed tendon is used to
|
||||
constrain the sum of all joints to remain constant. When the user specifies elongated geoms (capsules or ellipsoids)
|
||||
their long axis is aligned with the sliding joint axis. This makes the shell thicker for collision detection
|
||||
purposes, preventing objects from penetrating the box. It is important to disable contacts between the elements of
|
||||
the box. This is done by setting the default geom contype to 0. The user can change it of course, but if the geoms
|
||||
comprising the soft box are allowed to contact each other the model will not work as intended.
|
||||
|
||||
The **cylinder** and **ellipsoid** types are the same as box, except the elements are projected on the surface of an
|
||||
ellipsoid or a cylinder respectively. Thus the composite soft body shape is different, while everything else is the
|
||||
same as in the box type.
|
||||
|
||||
.. _body-composite-count:
|
||||
|
||||
:at:`count`: :at-val:`int(3), required`
|
||||
@@ -3112,61 +3000,33 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
|
||||
parent body. Note that some types imply a grid of certain dimensionality, so the requirements for this attribute
|
||||
depend on the specified type.
|
||||
|
||||
.. _body-composite-spacing:
|
||||
|
||||
:at:`spacing`: :at-val:`real, required`
|
||||
The spacing between the centers of the grid elements. This spacing is the same in all dimensions. It should normally
|
||||
be set to a value larger than the geom size, otherwise there will be a lot of contacts in the reference model
|
||||
configuration (which is allowed but rarely desirable).
|
||||
|
||||
.. _body-composite-offset:
|
||||
|
||||
:at:`offset`: :at-val:`real(3), "0 0 0"`
|
||||
This attribute affects particle and grid types, and is ignored for all other types. It specifies a 3D offset from the
|
||||
center of the parent body to the center of the grid of elements. The offset is expressed in the local coordinate
|
||||
frame of the parent body.
|
||||
|
||||
.. _body-composite-flatinertia:
|
||||
|
||||
:at:`flatinertia`: :at-val:`real, "0"`
|
||||
This attribute affects the cloth type and is ignored for all other types. The default value 0 disables this
|
||||
mechanism. When the value is positive, it specifies the ratio of the small-to-large axes of the modified diagonal
|
||||
inertia. The idea is to set it to a small value, say 0.01, in which case the inertias of the body elements will
|
||||
corresponds to flat boxes aligned with the cloth (which can then be used for lift forces). This will not change the
|
||||
geom shapes, but instead will set the body inertias directly and disable the automatic computation of inertia from
|
||||
geom shape for the composite body only.
|
||||
|
||||
.. _body-composite-solrefsmooth:
|
||||
|
||||
.. _body-composite-solimpsmooth:
|
||||
|
||||
:at:`solrefsmooth`, :at:`solimpsmooth`
|
||||
These are the solref and solimp attributes of the loop-closure equality constraint for loop types, and the
|
||||
smoothness-preserving equality constraint for box, cylinder and ellipsoid types. For all other types they have no
|
||||
effect. They obey the same rules as all other solref and solimp attributes in MJCF, except their defaults here are
|
||||
adjusted depending on the composite type. See :ref:`CSolver`.
|
||||
It specifies a 3D offset from the center of the parent body to the center of the grid of elements. The offset is
|
||||
expressed in the local coordinate frame of the parent body.
|
||||
|
||||
.. _body-composite-vertex:
|
||||
|
||||
:at:`vertex`: :at-val:`real(3*nvert), optional`
|
||||
Vertex 3D positions in global coordinates (cable only).
|
||||
Vertex 3D positions in global coordinates.
|
||||
|
||||
.. _body-composite-initial:
|
||||
|
||||
:at:`initial`: :at-val:`[free, ball, none], "0"`
|
||||
Behavior of the first point (cable only). Free: free joint. Ball: ball joint. None: no dof.
|
||||
Behavior of the first point. Free: free joint. Ball: ball joint. None: no dof.
|
||||
|
||||
.. _body-composite-curve:
|
||||
|
||||
:at:`curve`: :at-val:`string(3), optional`
|
||||
Functions specifying the vertex positions (cable only). Available functions are `s`, `cos(s)`, and `sin(s)`, where
|
||||
`s` is the arc length parameter.
|
||||
Functions specifying the vertex positions. Available functions are `s`, `cos(s)`, and `sin(s)`, where `s` is the arc
|
||||
length parameter.
|
||||
|
||||
.. _body-composite-size:
|
||||
|
||||
:at:`size`: :at-val:`int(3), optional`
|
||||
Scaling of the curve functions (cable only). `size[0]` is the scaling of `s`, `size[1]` is the radius of `\cos(s)`
|
||||
and `\sin(s)`, and `size[2]` is the speed of the argument (i.e. `\cos(2*\pi*size[2]*s)`).
|
||||
Scaling of the curve functions. `size[0]` is the scaling of `s`, `size[1]` is the radius of `\cos(s)` and `\sin(s)`,
|
||||
and `size[2]` is the speed of the argument (i.e. `\cos(2*\pi*size[2]*s)`).
|
||||
|
||||
|
||||
.. _composite-joint:
|
||||
@@ -3180,7 +3040,7 @@ joints should be created, as well as to adjust the attributes of both automatic
|
||||
|
||||
.. _composite-joint-kind:
|
||||
|
||||
:at:`kind`: :at-val:`[main, twist, stretch, particle], required`
|
||||
:at:`kind`: :at-val:`[main], required`
|
||||
The joint kind here is orthogonal to the joint type in the rest of MJCF. The joint kind refers to the function of the
|
||||
joint within the mechanism comprising the composite body, while the joint type (hinge or slide) is implied by the
|
||||
joint kind and composite body type.
|
||||
@@ -3190,18 +3050,6 @@ joints should be created, as well as to adjust the attributes of both automatic
|
||||
sliders for box, cylinder and rope; universal joints for cloth, rope and loop. Even though the main joints are
|
||||
included automatically, this sub-element is still useful for adjusting their attributes.
|
||||
|
||||
The **twist** kind corresponds to hinge joints enabling rope, loop and cloth objects to twist. These are optional
|
||||
joints and are only created if this sub-element is present. This sub-element is also used to adjust the attributes of
|
||||
the optional twist joints. For other composite object types this sub-element has no effect.
|
||||
|
||||
The **stretch** kind corresponds to slide joints enabling rope, loop and cloth objects to stretch. These are optional
|
||||
joints and are only created if this sub-element is present. This sub-element is also used to adjust the attributes of
|
||||
the optional stretch joints. For other composite object types this sub-element has no effect.
|
||||
|
||||
The **particle** kind can only be used with the particle composite type. As opposed to all previous kinds, this kind
|
||||
*replaces* the default 3 sliders with user-defined joints. User-defined joints can be repeated, for example
|
||||
to create planar particles with two sliders and a hinge.
|
||||
|
||||
.. _composite-joint-solreffix:
|
||||
|
||||
.. _composite-joint-solimpfix:
|
||||
@@ -3249,73 +3097,6 @@ joints should be created, as well as to adjust the attributes of both automatic
|
||||
Same meaning as regular :ref:`joint <body-joint>` attributes.
|
||||
|
||||
|
||||
.. _composite-tendon:
|
||||
|
||||
:el-prefix:`composite/` |-| **tendon** (*)
|
||||
''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Tendons are treated similarly to joints in composite objects. The tendon kind specified here together with the composite
|
||||
body type imply the tendon type as used in the rest of MJCF. This sub-element is used to both create optional tendons,
|
||||
and adjust the attributes of automatic and optional tendons. One difference from joints is that all tendons used in
|
||||
composite objects are equality-constrained.
|
||||
|
||||
.. _composite-tendon-kind:
|
||||
|
||||
:at:`kind`: :at-val:`[main, shear], required`
|
||||
The **main** kind corresponds to tendons holding the composite body together. These are the spatial tendons that
|
||||
connect neighboring bodies in grid and cloth, and the fixed tendon used to preserve the volume of box, cylinder and
|
||||
ellipsoid. For other composite types this sub-element has no effect.
|
||||
|
||||
The **shear** kind corresponds to diagonal tendons that prevent shear (as opposed to enabling - which is the function
|
||||
of optional joints). Such tendons can be created in 2D grid objects and cloth objects. For all other composite object
|
||||
types this sub-element has no effect.
|
||||
|
||||
.. _composite-tendon-solreffix:
|
||||
|
||||
.. _composite-tendon-solimpfix:
|
||||
|
||||
:at:`solreffix`, :at:`solimpfix`
|
||||
These are the solref and solimp attributes used to equality-constrain the tendon. The defaults are adjusted depending
|
||||
on the composite type. Otherwise these attributes obey the same rules as all other solref and solimp attributes in
|
||||
MJCF. See :ref:`CSolver`.
|
||||
|
||||
.. _composite-tendon-group:
|
||||
|
||||
.. _composite-tendon-stiffness:
|
||||
|
||||
.. _composite-tendon-damping:
|
||||
|
||||
.. _composite-tendon-limited:
|
||||
|
||||
.. _composite-tendon-range:
|
||||
|
||||
.. _composite-tendon-margin:
|
||||
|
||||
.. _composite-tendon-solreflimit:
|
||||
|
||||
.. _composite-tendon-solimplimit:
|
||||
|
||||
.. _composite-tendon-frictionloss:
|
||||
|
||||
.. _composite-tendon-solreffriction:
|
||||
|
||||
.. _composite-tendon-solimpfriction:
|
||||
|
||||
.. _composite-tendon-material:
|
||||
|
||||
.. _composite-tendon-rgba:
|
||||
|
||||
.. _composite-tendon-width:
|
||||
|
||||
.. |body/composite/tendon attrib list| replace::
|
||||
:at:`group`, :at:`stiffness`, :at:`damping`, :at:`limited`, :at:`range`, :at:`margin`, :at:`solreflimit`,
|
||||
:at:`solimplimit`, :at:`frictionloss`, :at:`solreffriction`, :at:`solimpfriction`, :at:`material`, :at:`rgba`,
|
||||
:at:`width`
|
||||
|
||||
|body/composite/tendon attrib list|
|
||||
Same meaning as regular :ref:`tendon <tendon>` attributes.
|
||||
|
||||
|
||||
.. _composite-geom:
|
||||
|
||||
:el-prefix:`composite/` |-| **geom** (?)
|
||||
@@ -3437,22 +3218,6 @@ automatically-generated skin.
|
||||
bi-cubic interpolation. This increases the quality of the rendering (especially in the absence of textures) but also
|
||||
slows down the renderer, so use it with caution. Values above 3 are unlikely to be needed.
|
||||
|
||||
.. _composite-pin:
|
||||
|
||||
:el-prefix:`composite/` |-| **pin** (*)
|
||||
'''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This sub-element can be used to pin some of the element bodies in grid objects (both 1D and 2D). Pinning means that the
|
||||
corresponding body has no joints, and therefore it is rigidly fixed to the parent body. When the parent is the world,
|
||||
this has the effect of hanging a string or a cloth in space. If the parent body is moving, this can be used to model a
|
||||
handle where the composite object is attached. For other composite types this sub-element has no effect.
|
||||
|
||||
.. _composite-pin-coord:
|
||||
|
||||
:at:`coord`: :at-val:`int(2), required`
|
||||
The grid coordinates of the element body which should be pinned. The coordinates are zero-based. For 1D grids this
|
||||
attribute can have only one number, in which case the second number is automatically set to 0.
|
||||
|
||||
|
||||
.. _composite-plugin:
|
||||
|
||||
@@ -3559,9 +3324,48 @@ saving the XML:
|
||||
radius in 2D, and tetrahedra with radius in 3D. Certain flexcomp types imply a dimensionality, in which case the
|
||||
value specified here is ignored.
|
||||
|
||||
.. youtube:: uNt3i8hrJu4
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
.. _body-flexcomp-dof:
|
||||
|
||||
:at:`dof`: :at-val:`[full, radial, trilinear], "full"`
|
||||
The parametrization of the flex's degrees of freedom (dofs). See the video on the right illustrating the
|
||||
different parametrizations with deformable spheres. The three models in the video are respectively
|
||||
`sphere_full <https://github.com/google-deepmind/mujoco/blob/main/model/flex/sphere_full.xml>`__,
|
||||
`sphere_radial <https://github.com/google-deepmind/mujoco/blob/main/model/flex/sphere_radial.xml>`__
|
||||
and `sphere_trilinear <https://github.com/google-deepmind/mujoco/blob/main/model/flex/sphere_trilinear.xml>`__.
|
||||
|
||||
**full**
|
||||
Three translational dofs per vertex. This is the most expressive but also the most expensive option.
|
||||
|
||||
**radial**
|
||||
A single radial translational dof per vertex. Note that unlike in the "full" case, the radial parametrization
|
||||
requires a free joint at the flex's parent in order for free body motion to be possible. This type of
|
||||
parametrization is appropriate for shapes that are relatively spherical.
|
||||
|
||||
**trilinear**
|
||||
Three translational dofs at each corner of the bounding box of the flex, for a total of 24 dofs for the entire
|
||||
flex, independent of the number of vertices. The positions of the vertices are updated using trilinear
|
||||
interpolation over the bounding box.
|
||||
|
||||
.. youtube:: qJFbx-FR7Bc
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
Trilinear flexes are much faster than the previous two options, and are the preferred choice if the expected
|
||||
deformations can be captured by the reduced parametriation. For example, see the video on the right comparing `full
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper.xml>`__ and `trilinear
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper_trilinear.xml>`__ flexes for modeling
|
||||
deformable gripper pads.
|
||||
|
||||
Note that the choice of dof parametrization affects the deformation modes of the flex but has no effect on the
|
||||
accuracy of the collision geometry, which always takes into account the high-resolution mesh of the flex.
|
||||
|
||||
.. _body-flexcomp-type:
|
||||
|
||||
:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, mesh, gmsh, direct], "grid"`
|
||||
:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, disc, circle, mesh, gmsh, direct], "grid"`
|
||||
This attribute determines the type of :el:`flexcomp` object. The remaining attributes and sub-elements are then
|
||||
interpreted according to the type. Default settings are also adjusted depending on the type. Different types
|
||||
correspond to different methods for specifying the flexcomp points and the stretchable elements that connect them.
|
||||
@@ -3585,11 +3389,19 @@ saving the XML:
|
||||
|
||||
**ellipsoid** is the same as **box**, except the points are projected on the surface of an ellipsoid.
|
||||
|
||||
**disc** is the same as **box**, except the points are projected on the surface of a disc. It is only compatible
|
||||
with :at:`dim=2`.
|
||||
|
||||
**circle** is the same as **grid**, except the points are sampled along a circle so that the first and last points
|
||||
are the same. The radius of the circle is computed such that each segment has the requested spacing. It is only
|
||||
compatible with :at:`dim=1`.
|
||||
|
||||
**mesh** loads the flexcomp points and elements (i.e. triangles) from a mesh file, in the same file formats as mesh
|
||||
assets. A mesh asset is not actually added to the model. Instead the vertex and face data from the mesh file are used
|
||||
to populate the point and element data of the flexcomp. :at:`dim` is automatically set to 2. Recall that a mesh asset
|
||||
in MuJoCo can be used as a rigid geom attached to a single body. In contrast, the flex generated here corresponds to
|
||||
a soft mesh with the same initial shape, where each vertex is a separate moving body (unless pinned).
|
||||
assets, excluding the legacy .msh format. A mesh asset is not actually added to the model. Instead the vertex and
|
||||
face data from the mesh file are used to populate the point and element data of the flexcomp. :at:`dim` is
|
||||
automatically set to 2. Recall that a mesh asset in MuJoCo can be used as a rigid geom attached to a single body. In
|
||||
contrast, the flex generated here corresponds to a soft mesh with the same initial shape, where each vertex is a
|
||||
separate moving body (unless pinned).
|
||||
|
||||
.. _gmsh-file-docs:
|
||||
|
||||
@@ -3638,9 +3450,10 @@ saving the XML:
|
||||
|
||||
:at:`texcoord`: :at-val:`real(2*npoint), optional`
|
||||
Texture coordinates of each point, passed through to the automatically-generated flex. Note that flexcomp does not
|
||||
generate texture coordinates automatically, except for 2D grids. For all other types, the user can specify explicit
|
||||
texture coordinates here, even if the points themselves were generated automatically. This requires understanding of
|
||||
the layout of the automatically-generated points and how they correspond to the texture referenced by the material.
|
||||
generate texture coordinates automatically, except for 2D grids, box, cylinder and ellipsoid. For all other types,
|
||||
the user can specify explicit texture coordinates here, even if the points themselves were generated automatically.
|
||||
This requires understanding of the layout of the automatically-generated points and how they correspond to the
|
||||
texture referenced by the material.
|
||||
|
||||
.. _body-flexcomp-mass:
|
||||
|
||||
@@ -3659,9 +3472,9 @@ saving the XML:
|
||||
|
||||
:at:`file`: :at-val:`string, optional`
|
||||
The name of the file from which a **surface** (triangular) or **volumetric** (tetrahedral) mesh is loaded. For
|
||||
surface meshes, the file extension is used to determine the file format. Supported formats are the same as in
|
||||
:ref:`mesh assets<asset-mesh>` and also including GMSH. Volumetric meshes are supported only in GMSH format.
|
||||
See :ref:`here<gmsh-file-docs>` for more information on GMSH files.
|
||||
surface meshes, the file extension is used to determine the file format. Supported formats are GMSH and the formats
|
||||
specified in :ref:`mesh assets<asset-mesh>`, excluding the legacy .msh format. Volumetric meshes are supported only
|
||||
in GMSH format. See :ref:`here<gmsh-file-docs>` for more information on GMSH files.
|
||||
|
||||
.. _body-flexcomp-rigid:
|
||||
|
||||
@@ -3705,6 +3518,13 @@ saving the XML:
|
||||
These attributes are directly passed through to the automatically-generated :ref:`flex<deformable-flex>` object and
|
||||
have the same meaning.
|
||||
|
||||
.. _body-flexcomp-origin:
|
||||
|
||||
:at:`origin`: :at-val:`real(3), "0 0 0"`
|
||||
The origin of the flexcomp. Used for generating a volumetric mesh from an OBJ surface mesh. Each surface triangle is
|
||||
connected to the origin to create a tetrahedron, so the resulting volumetric mesh is guaranteed to be well-formed
|
||||
only for convex shapes.
|
||||
|
||||
.. _flexcomp-contact:
|
||||
|
||||
:el-prefix:`flexcomp/` |-| **contact** (*)
|
||||
@@ -4097,9 +3917,14 @@ cases, the user will specify a :el:`flexcomp` which will then automatically cons
|
||||
|
||||
.. _deformable-flex-texcoord:
|
||||
|
||||
:at:`texcoord`: :at-val:`real(2*nvert), optional`
|
||||
Texture coordinates for each vertex. If omitted, texture mapping for this flex is disabled, even if a texture is
|
||||
specified in the material.
|
||||
:at:`texcoord`: :at-val:`real(2*vert or ntexcoord), optional`
|
||||
Texture coordinates. If omitted, texture mapping for this flex is disabled, even if a texture is specified in the
|
||||
material.
|
||||
|
||||
.. _deformable-flex-elemtexcoord:
|
||||
|
||||
:at:`elemtexcoord`: :at-val:`int((dim+1)*nelem), optional`
|
||||
Texture indices for each face. If omitted, texture are assumed to be vertex-based.
|
||||
|
||||
.. _deformable-flex-element:
|
||||
|
||||
@@ -4109,7 +3934,7 @@ cases, the user will specify a :el:`flexcomp` which will then automatically cons
|
||||
which is why the number of indices equals (dim+1) times the number of elements. In 2D, the vertices should be listed
|
||||
in counter-clockwise order. In 1D and 3D the order is irrelevant; in 3D the model compiler will rearrange the
|
||||
vertices as needed. Repeated vertex indices within a flex element are not allowed. The topology of the flex is not
|
||||
enforced; it could corespond to a continuous soft body, or a collection of disconnected stretchable elements, or
|
||||
enforced; it could correspond to a continuous soft body, or a collection of disconnected stretchable elements, or
|
||||
anything in-between.
|
||||
|
||||
.. _deformable-flex-flatskin:
|
||||
@@ -4138,6 +3963,12 @@ cases, the user will specify a :el:`flexcomp` which will then automatically cons
|
||||
Integer group to which the flex belongs. This attribute can be used for custom tags. It is also used by the
|
||||
visualizer to enable and disable the rendering of entire groups of flexes.
|
||||
|
||||
.. _deformable-flex-node:
|
||||
|
||||
:at:`node`: :at-val:`string(nnode), optional`
|
||||
The degrees-of-freedom of the flex.
|
||||
An array of MuJoCo body names (separated by white space) to which each node belongs. The number of body names
|
||||
should equal the number of nodes (nnode). See the flexcomp :ref:`dof<body-flexcomp-dof>` attribute for more details.
|
||||
|
||||
.. _flex-edge:
|
||||
|
||||
@@ -4197,7 +4028,7 @@ stress-strain relationship.. See also :ref:`deformable <CDeformable>` objects.
|
||||
:at:`thickness`: :at-val:`real(1), "-1"`
|
||||
Shell thickness, units of length; only for used 2D flexes. Used to scale the stretching stiffness.
|
||||
This thickness can be set equal to 2 times the :ref:`radius <deformable-flex-radius>` in order to match the geometry,
|
||||
but is exposed seperately since the radius might be constrained by considerations related to collision detection.
|
||||
but is exposed separately since the radius might be constrained by considerations related to collision detection.
|
||||
|
||||
|
||||
.. _flex-contact:
|
||||
@@ -4210,7 +4041,7 @@ extensions specific to flexes.
|
||||
|
||||
.. _flex-contact-internal:
|
||||
|
||||
:at:`internal`: :at-val:`[true, false], "true"`
|
||||
:at:`internal`: :at-val:`[true, false], "false"`
|
||||
Enables or disables internal collisions which prevent flex self-penetration and element inversion. Note that flex
|
||||
elements that have shared vertices cannot collide (or else there will be permanent contacts). In 1D and 2D, internal
|
||||
collision checks rely on predefined vertex-element pairs, where the vertex is treated as a sphere with the same
|
||||
@@ -4218,7 +4049,9 @@ extensions specific to flexes.
|
||||
flex. The pre-defined vertex-element pairs are generated by the model compiler automatically. In 3D, internal
|
||||
collision checks are performed within each tetraheron: each vertex is collided with the plane corresponding to the
|
||||
opposing triangle face (again using the flex radius). The resulting contacts are always created with condim 1, gap 0,
|
||||
margin 0.
|
||||
margin 0. Note that internal contacts modify the behavior implied by the :ref:`elasticity
|
||||
parameters<flex-elasticity>` and is recommended only for flexes where element inversion cannot be prevented. The
|
||||
default value of this attribute was changed from "true" to "false" in version 3.3.1.
|
||||
|
||||
.. _flex-contact-selfcollide:
|
||||
|
||||
@@ -4438,7 +4271,7 @@ ball joint outside the kinematic tree. Connect constraints can be specified in o
|
||||
|
||||
- Using :ref:`body1<equality-connect-body1>` and :ref:`anchor<equality-connect-anchor>` (both required) and
|
||||
optionally :ref:`body2<equality-connect-body2>`. When using this specification, the constraint is assumed to be
|
||||
satisfied in the configuration in which the model is defined.
|
||||
satisfied at the configuration in which the model is defined (``mjData.qpos0``).
|
||||
- :ref:`site1<equality-connect-site1>` and :ref:`site2<equality-connect-site2>` (both required). When using this
|
||||
specification, the two sites will be pulled together by the constraint, regardless of their position in the default
|
||||
configuration. An example of this specification is shown in
|
||||
@@ -4484,8 +4317,8 @@ ball joint outside the kinematic tree. Connect constraints can be specified in o
|
||||
|
||||
:at:`anchor`: :at-val:`real(3), optional`
|
||||
Coordinates of the 3D anchor point where the two bodies are connected, in the local coordinate frame of :at:`body1`.
|
||||
The constraint is assumed to be satisfied in the configuration in which the model is defined, which lets the compiler
|
||||
compute the associated anchor point for :at:`body2`.
|
||||
The constraint is assumed to be satisfied in the configuration at which the model is defined (``mjData.qpos0``),
|
||||
which lets the compiler compute the associated anchor point for :at:`body2`.
|
||||
|
||||
.. _equality-connect-site1:
|
||||
|
||||
@@ -4739,6 +4572,18 @@ A second form of wrapping is where the tendon is constrained to pass *through* a
|
||||
wrap around it. This is enabled automatically when a sidesite is specified and its position is inside the volume of
|
||||
the obstacle geom.
|
||||
|
||||
.. youtube:: I2q7D0Vda-A
|
||||
:width: 300px
|
||||
:align: right
|
||||
|
||||
**Visualization:** Tendon paths are visualized as in the image above, respecting the :ref:`width<tendon-spatial-width>`,
|
||||
:ref:`material<tendon-spatial-material>` and :ref:`rgba<tendon-spatial-rgba>` attributes below. A special kind of
|
||||
visualization is used for unactuated 2-point tendons with :ref:`range<tendon-spatial-range>` or
|
||||
:ref:`springlength<tendon-spatial-springlength>` of the form :at-val:`[0 X]`, with positive X. Such tendons act like a
|
||||
cable, applying force only when stretched. Therefore when not stretched, they are drawn as a catenary of
|
||||
length X, as in the clip on the right of `this example model
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/test/engine/testdata/catenary.xml>`__.
|
||||
|
||||
.. _tendon-spatial-name:
|
||||
|
||||
:at:`name`: :at-val:`string, optional`
|
||||
@@ -6255,13 +6100,12 @@ excluded; this is because sensor calculations are independent of the visualizer.
|
||||
:el-prefix:`sensor/` |-| **camprojection** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This element creates a camprojection sensor, which returns the location of a target site, projected onto a camera image
|
||||
in pixel coordinates. The origin of this system is located at the top-left corner of the first pixel, so a target
|
||||
which projects exactly onto the corner of the image, will have value (0, 0). Values are not clipped, so targets which
|
||||
fall outside the camera image will take values above or below the pixel limits. Moreover, points behind the camera
|
||||
are also projected onto the image, so it is up to the user to filter out such points, if desired. This can be done using
|
||||
a `framepos<sensor-framepos>` sensor with the camera as reference frame, then a negative/positive value in the
|
||||
z-coordinate indicates (respectively) a location in the front/back of the camera.
|
||||
This element creates a camera projection sensor: the location of a target site, projected onto a camera image in pixel
|
||||
coordinates. The pixel origin (0, 0) is located at the top-left corner. Values are not clipped, so targets which fall
|
||||
outside the camera image will take values above or below the pixel range limits. Moreover, points behind the camera are
|
||||
also projected onto the image, so it is up to the user to filter out such points, if desired. This can be done using a
|
||||
:ref:`framepos<sensor-framepos>` sensor with the camera as a reference frame: a negative/positive value in the
|
||||
z-coordinate indicates a location in front of/behind the camera plane, respectively.
|
||||
|
||||
.. _sensor-camprojection-site:
|
||||
|
||||
@@ -6471,7 +6315,7 @@ contributed by all actuators to a single scalar joint (hinge or slider). If the
|
||||
:ref:`actuatorgravcomp<body-joint-actuatorgravcomp>` attribute is "true", this sensor will also measure contributions by
|
||||
gravity compensation forces (which are added directly to the joint and would *not* register in the
|
||||
:ref:`actuatorfrc<sensor-actuatorfrc>`) sensor. This type of sensor is important when multiple actuators act on a single
|
||||
joint or when a single actuator act on multiple joints. See :ref:`CForceRange` for details.
|
||||
joint or when a single actuator acts on multiple joints. See :ref:`CForceRange` for details.
|
||||
|
||||
|
||||
.. _sensor-jointactuatorfrc-name:
|
||||
@@ -7139,16 +6983,11 @@ pipeline. These 3 sensors share some common properties:
|
||||
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.
|
||||
|
||||
.. admonition:: Positive cutoff values
|
||||
.. admonition:: different (correct) behavior under `nativeccd`
|
||||
:class: note
|
||||
|
||||
.. TODO: b/339596989 - Improve mjc_Convex.
|
||||
|
||||
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 innacurate.
|
||||
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>`__.
|
||||
As explained in :ref:`Collision Detection<coDistance>`, distances are inaccurate when using the
|
||||
:ref:`legacy CCD pipeline<coCCD>`, and its use is discouraged.
|
||||
|
||||
: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
|
||||
@@ -7300,6 +7139,45 @@ See :ref:`collision-sensors` for more details about sensors of this type.
|
||||
:at:`name`, :at:`noise`, :at:`user`
|
||||
See :ref:`CSensor`.
|
||||
|
||||
|
||||
.. _sensor-e_potential:
|
||||
|
||||
:el-prefix:`sensor/` |-| **e_potential** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This element creates sensor that returns the potential energy.
|
||||
|
||||
.. _sensor-e_potential-name:
|
||||
|
||||
.. _sensor-e_potential-noise:
|
||||
|
||||
.. _sensor-e_potential-cutoff:
|
||||
|
||||
.. _sensor-e_potential-user:
|
||||
|
||||
:at:`name`, :at:`noise`, :at:`cutoff`, :at:`user`
|
||||
See :ref:`CSensor`.
|
||||
|
||||
|
||||
.. _sensor-e_kinetic:
|
||||
|
||||
:el-prefix:`sensor/` |-| **e_kinetic** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This element creates sensor that returns the kinetic energy.
|
||||
|
||||
.. _sensor-e_kinetic-name:
|
||||
|
||||
.. _sensor-e_kinetic-noise:
|
||||
|
||||
.. _sensor-e_kinetic-cutoff:
|
||||
|
||||
.. _sensor-e_kinetic-user:
|
||||
|
||||
:at:`name`, :at:`noise`, :at:`cutoff`, :at:`user`
|
||||
See :ref:`CSensor`.
|
||||
|
||||
|
||||
.. _sensor-clock:
|
||||
|
||||
:el-prefix:`sensor/` |-| **clock** (*)
|
||||
|
||||
+30
-92
@@ -52,9 +52,9 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`fitaabb<compiler-fitaabb>` | :ref:`eulerseq<compiler-eulerseq>` | :ref:`meshdir<compiler-meshdir>` | :ref:`texturedir<compiler-texturedir>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`discardvisual<compiler-discardvisual>` | :ref:`convexhull<compiler-convexhull>` | :ref:`usethread<compiler-usethread>` | :ref:`fusestatic<compiler-fusestatic>` | |
|
||||
| | | | :ref:`discardvisual<compiler-discardvisual>` | :ref:`usethread<compiler-usethread>` | :ref:`fusestatic<compiler-fusestatic>` | :ref:`inertiafromgeom<compiler-inertiafromgeom>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`inertiafromgeom<compiler-inertiafromgeom>` | :ref:`inertiagrouprange<compiler-inertiagrouprange>` | :ref:`assetdir<compiler-assetdir>` | :ref:`alignfree<compiler-alignfree>` | |
|
||||
| | | | :ref:`inertiagrouprange<compiler-inertiagrouprange>` | :ref:`assetdir<compiler-assetdir>` | :ref:`alignfree<compiler-alignfree>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| compiler |br| |_| |L| | | .. table:: |
|
||||
@@ -178,66 +178,10 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`rgb | ? | :class: mjcf-attributes |
|
||||
| <material-rgb>` | | |
|
||||
| :ref:`layer | \* | :class: mjcf-attributes |
|
||||
| <material-layer>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-rgb-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`occlusion | ? | :class: mjcf-attributes |
|
||||
| <material-occlusion>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-occlusion-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`roughness | ? | :class: mjcf-attributes |
|
||||
| <material-roughness>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-roughness-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`metallic | ? | :class: mjcf-attributes |
|
||||
| <material-metallic>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-metallic-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`normal | ? | :class: mjcf-attributes |
|
||||
| <material-normal>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-normal-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`opacity | ? | :class: mjcf-attributes |
|
||||
| <material-opacity>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-opacity-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`emissive | ? | :class: mjcf-attributes |
|
||||
| <material-emissive>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-emissive-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`rgba | ? | :class: mjcf-attributes |
|
||||
| <material-rgba>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-rgba-texture>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| material |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`orm | ? | :class: mjcf-attributes |
|
||||
| <material-orm>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`texture<material-orm-texture>` | | | | |
|
||||
| | | | :ref:`texture<material-layer-texture>` | :ref:`role<material-layer-role>` | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| asset |br| |_| |L| | | .. table:: |
|
||||
@@ -396,9 +340,7 @@
|
||||
| :ref:`composite | \* | :class: mjcf-attributes |
|
||||
| <body-composite>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`prefix<body-composite-prefix>` | :ref:`type<body-composite-type>` | :ref:`count<body-composite-count>` | :ref:`spacing<body-composite-spacing>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`offset<body-composite-offset>` | :ref:`flatinertia<body-composite-flatinertia>` | :ref:`solrefsmooth<body-composite-solrefsmooth>` | :ref:`solimpsmooth<body-composite-solimpsmooth>` | |
|
||||
| | | | :ref:`prefix<body-composite-prefix>` | :ref:`type<body-composite-type>` | :ref:`count<body-composite-count>` | :ref:`offset<body-composite-offset>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`vertex<body-composite-vertex>` | :ref:`initial<body-composite-initial>` | :ref:`curve<body-composite-curve>` | :ref:`size<body-composite-size>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
@@ -419,21 +361,6 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| composite |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`tendon | \* | :class: mjcf-attributes |
|
||||
| <composite-tendon>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`kind<composite-tendon-kind>` | :ref:`group<composite-tendon-group>` | :ref:`stiffness<composite-tendon-stiffness>` | :ref:`damping<composite-tendon-damping>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`solreffix<composite-tendon-solreffix>` | :ref:`solimpfix<composite-tendon-solimpfix>` | :ref:`limited<composite-tendon-limited>` | :ref:`range<composite-tendon-range>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`margin<composite-tendon-margin>` | :ref:`solreflimit<composite-tendon-solreflimit>` | :ref:`solimplimit<composite-tendon-solimplimit>` | :ref:`frictionloss<composite-tendon-frictionloss>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`solreffriction<composite-tendon-solreffriction>` | :ref:`solimpfriction<composite-tendon-solimpfriction>` | :ref:`material<composite-tendon-material>` | :ref:`rgba<composite-tendon-rgba>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`width<composite-tendon-width>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| composite |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`skin | ? | :class: mjcf-attributes |
|
||||
| <composite-skin>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
@@ -465,13 +392,6 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| composite |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`pin | \* | :class: mjcf-attributes |
|
||||
| <composite-pin>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`coord<composite-pin-coord>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| composite |br| |_2| |L| | | .. table:: |
|
||||
| :ref:`plugin | \* | :class: mjcf-attributes |
|
||||
| <composite-plugin>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
@@ -491,15 +411,17 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`name<body-flexcomp-name>` | :ref:`type<body-flexcomp-type>` | :ref:`group<body-flexcomp-group>` | :ref:`dim<body-flexcomp-dim>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`count<body-flexcomp-count>` | :ref:`spacing<body-flexcomp-spacing>` | :ref:`radius<body-flexcomp-radius>` | :ref:`rigid<body-flexcomp-rigid>` | |
|
||||
| | | | :ref:`dof<body-flexcomp-dof>` | :ref:`count<body-flexcomp-count>` | :ref:`spacing<body-flexcomp-spacing>` | :ref:`radius<body-flexcomp-radius>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`mass<body-flexcomp-mass>` | :ref:`inertiabox<body-flexcomp-inertiabox>` | :ref:`scale<body-flexcomp-scale>` | :ref:`file<body-flexcomp-file>` | |
|
||||
| | | | :ref:`rigid<body-flexcomp-rigid>` | :ref:`mass<body-flexcomp-mass>` | :ref:`inertiabox<body-flexcomp-inertiabox>` | :ref:`scale<body-flexcomp-scale>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`point<body-flexcomp-point>` | :ref:`element<body-flexcomp-element>` | :ref:`texcoord<body-flexcomp-texcoord>` | :ref:`material<body-flexcomp-material>` | |
|
||||
| | | | :ref:`file<body-flexcomp-file>` | :ref:`point<body-flexcomp-point>` | :ref:`element<body-flexcomp-element>` | :ref:`texcoord<body-flexcomp-texcoord>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`rgba<body-flexcomp-rgba>` | :ref:`flatskin<body-flexcomp-flatskin>` | :ref:`pos<body-flexcomp-pos>` | :ref:`quat<body-flexcomp-quat>` | |
|
||||
| | | | :ref:`material<body-flexcomp-material>` | :ref:`rgba<body-flexcomp-rgba>` | :ref:`flatskin<body-flexcomp-flatskin>` | :ref:`pos<body-flexcomp-pos>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`axisangle<body-flexcomp-axisangle>` | :ref:`xyaxes<body-flexcomp-xyaxes>` | :ref:`zaxis<body-flexcomp-zaxis>` | :ref:`euler<body-flexcomp-euler>` | |
|
||||
| | | | :ref:`quat<body-flexcomp-quat>` | :ref:`axisangle<body-flexcomp-axisangle>` | :ref:`xyaxes<body-flexcomp-xyaxes>` | :ref:`zaxis<body-flexcomp-zaxis>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`euler<body-flexcomp-euler>` | :ref:`origin<body-flexcomp-origin>` | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| flexcomp |br| |_2| |L| | | .. table:: |
|
||||
@@ -563,7 +485,9 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`material<deformable-flex-material>` | :ref:`rgba<deformable-flex-rgba>` | :ref:`flatskin<deformable-flex-flatskin>` | :ref:`body<deformable-flex-body>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`vertex<deformable-flex-vertex>` | :ref:`element<deformable-flex-element>` | :ref:`texcoord<deformable-flex-texcoord>` | | |
|
||||
| | | | :ref:`vertex<deformable-flex-vertex>` | :ref:`element<deformable-flex-element>` | :ref:`texcoord<deformable-flex-texcoord>` | :ref:`elemtexcoord<deformable-flex-elemtexcoord>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`node<deformable-flex-node>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_2| flex |br| |_2| |L| | | .. table:: |
|
||||
@@ -1285,6 +1209,20 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||
| :ref:`e_potential | \* | :class: mjcf-attributes |
|
||||
| <sensor-e_potential>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`name<sensor-e_potential-name>` | :ref:`cutoff<sensor-e_potential-cutoff>` | :ref:`noise<sensor-e_potential-noise>` | :ref:`user<sensor-e_potential-user>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||
| :ref:`e_kinetic | \* | :class: mjcf-attributes |
|
||||
| <sensor-e_kinetic>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`name<sensor-e_kinetic-name>` | :ref:`cutoff<sensor-e_kinetic-cutoff>` | :ref:`noise<sensor-e_kinetic-noise>` | :ref:`user<sensor-e_kinetic-user>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||
| :ref:`clock | \* | :class: mjcf-attributes |
|
||||
| <sensor-clock>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
|
||||
+223
-20
@@ -5,19 +5,206 @@ Changelog
|
||||
Upcoming version (not yet released)
|
||||
-----------------------------------
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
- The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
|
||||
:ref:`inertia<asset-mesh-inertia>` attribute.
|
||||
.. admonition:: Breaking API changes
|
||||
:class: attention
|
||||
|
||||
MJX
|
||||
^^^
|
||||
- Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
|
||||
- Added ``TOUCH`` sensor.
|
||||
- The default value of the flag for toggling :ref:`internal flex contacts<flex-contact-internal>` was changed from
|
||||
"true" to "false". This feature has proven to be counterintuitive for users.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
- Fixed a bug in slider-crank ref:`transmission<geTransmission>`. The bug was introduced in 3.0.0.
|
||||
- :ref:`mj_jacDot` was missing a term that accounts for the motion of the point with respect to
|
||||
which the Jacobian is computed, now fixed.
|
||||
- Fixed a bug that caused the parent frame of elements in the child worldbody to be incorrectly set when attaching an
|
||||
mjSpec to a frame or a site.
|
||||
|
||||
Version 3.3.0 (Feb 26, 2025)
|
||||
----------------------------
|
||||
|
||||
|
||||
Feature promotion
|
||||
^^^^^^^^^^^^^^^^^
|
||||
.. youtube:: qJFbx-FR7Bc
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
1. Introduced a new kind of **fast deformable body**, activated by setting :ref:`flexcomp/dof<body-flexcomp-dof>` to
|
||||
"trilinear". This type of :ref:`deformable<CDeformable>` flex object has the same collision geometry as a regular
|
||||
flex, but has far fewer degrees of freedom. Instead of 3 dofs per vertex, only the corners of the bounding box are
|
||||
free to move, with the positions of the interior vertices computed with trilinear interpolation of the 8 corners, for
|
||||
a total of 24 dofs for the entire flex object (or less, if some of the corners are pinned). This limits the types of
|
||||
deformation achievable by the flex, but allows for much faster simulation. For example, see the video on the right
|
||||
comparing `full <https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper.xml>`__ and `trilinear
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/gripper_trilinear.xml>`__ flexes for modeling
|
||||
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
|
||||
|
||||
2. 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
|
||||
^^^^^^^
|
||||
3. Add support for custom plots in the MuJoCo viewer by exposing a ``viewport`` property, a ``set_figures`` method,
|
||||
and a ``clear_figures`` method.
|
||||
4. Separate collision and deformation meshes for :ref:`flex<deformable-flex>`. This enables a fixed cost for the soft
|
||||
body computations, while preserving the fidelity of high-resolution collisions.
|
||||
5. Added :ref:`potential<sensor-e_potential>` and :ref:`kinetic<sensor-e_kinetic>` energy sensors.
|
||||
6. Improved shadow rendering in the native renderer.
|
||||
7. Moved ``introspect`` to ``python/introspect``.
|
||||
|
||||
.. admonition:: Breaking API changes
|
||||
:class: attention
|
||||
|
||||
8. 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.
|
||||
9. 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
|
||||
behavior is to perform such a shallow copy. The old behavior of creating a deep copy of the child model while
|
||||
attaching can be restored by setting the deep copy flag to 1.
|
||||
10. Changes to inertia inference from meshes:
|
||||
|
||||
Previously, in order to specify that the mass lies on the surface, :ref:`geom/shellinertia<body-geom-shellinertia>`
|
||||
could be used for any geom type. Now this attribute is ignored if the geom is a mesh; instead, inertia inference
|
||||
for meshes is specified in the asset, using the :ref:`asset/mesh/inertia<asset-mesh-inertia>` attribute.
|
||||
|
||||
Previously, if the volumetric inertia computation failed (for example due to a very flat mesh), the compiler
|
||||
would silently fall back to surface inertia computation. Now, the compiler will throw an informative error.
|
||||
11. Removed the composite type ``grid``. Users should instead use :ref:`flexcomp<body-flexcomp>`.
|
||||
12. Removed the ``particle`` composite type. It is recommended to use the more generic :ref:`replicate<replicate>`
|
||||
instead, see for example `this model
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/replicate/particle.xml>`__.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
13. Added support for spatial tendons with internal sphere and cylinder wrapping.
|
||||
14. Fix a bug with box-box collisions :github:issue:`2356`.
|
||||
|
||||
Python bindings
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
15. Added a pedagogical colab notebook for ``mujoco.rollout``, a Python module for multithreaded simulation rollouts.
|
||||
It is available here |rollout_colab|.
|
||||
|br| Contribution by :github:user:`aftersomemath`.
|
||||
|
||||
.. |rollout_colab| image:: https://colab.research.google.com/assets/colab-badge.svg
|
||||
:target: https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/rollout.ipynb
|
||||
|
||||
Version 3.2.7 (Jan 14, 2025)
|
||||
----------------------------
|
||||
|
||||
Python bindings
|
||||
^^^^^^^^^^^^^^^
|
||||
1. :ref:`rollout<PyRollout>` now features native multi-threading. If a sequence of ``MjData`` instances
|
||||
of length ``nthread`` is passed in, ``rollout`` will automatically create a thread pool and parallelize
|
||||
the computation. The thread pool can be reused across calls, but then the function cannot be called simultaneously
|
||||
from multiple threads. To run multiple threaded rollouts simultaneously, use the new class ``Rollout`` which
|
||||
encapsulates the thread pool. Contribution by :github:user:`aftersomemath`.
|
||||
2. Fix global namespace pollution when using ``mjpython`` (:github:issue:`2265`).
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
.. admonition:: Breaking API changes (minor)
|
||||
:class: attention
|
||||
|
||||
3. The field ``mjData.qLDiagSqrtInv`` has been removed. This field is only required for the dual solvers. It is now
|
||||
computed as-needed rather than unconditionally. Relatedly, added the corresponding argument to :ref:`mj_solveM2`.
|
||||
|
||||
4. Reduced the memory footprint of the PGS solver's :ref:`A matrix<soDual>`. This was the last remaining dense-memory
|
||||
allocation in MuJoCo, allowing for a significant reduction of the :ref:`dynamic memory allocation heuristic<CSize>`.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
5. Fixed a bug in the box-sphere collider, depth was incorrect for deep penetrations (:github:issue:`2206`).
|
||||
6. Fixed a bug in :ref:`mj_mulM2` and added a test.
|
||||
|
||||
Version 3.2.6 (Dec 2, 2024)
|
||||
---------------------------
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
1. Removed rope and loop from :ref:`composite<body-composite>`. The user is encouraged to instead use the :at:`cable`
|
||||
plugin or :ref:`flexcomp<body-flexcomp>`, respectively.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
2. Added muscle actuators.
|
||||
|
||||
Python bindings
|
||||
^^^^^^^^^^^^^^^
|
||||
3. Provide prebuilt wheels for Python 3.13.
|
||||
4. Added ``bind`` method and removed id attribute from :ref:`mjSpec` objects. Using ids is error prone in scenarios of
|
||||
repeated attachment and detachment. Python users are encouraged to use names for unique identification of model
|
||||
elements.
|
||||
5. :ref:`rollout<PyRollout>` can now accept sequences of MjModel of length ``nroll``. Also removed the ``nroll``
|
||||
argument because its value can always be inferred.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
6. Fixed :github:issue:`2212`, type error in ``mjx.get_data``.
|
||||
7. Fixed bug introduced in 3.2.0 in handling of :ref:`texrepeat<asset-material-texrepeat>` attribute, was mistakenly
|
||||
cast from ``float`` to ``int``, (fixed :github:issue:`2223`).
|
||||
|
||||
Version 3.2.5 (Nov 4, 2024)
|
||||
---------------------------
|
||||
|
||||
Feature promotion
|
||||
^^^^^^^^^^^^^^^^^
|
||||
1. The :doc:`Model Editing<programming/modeledit>` framework afforded by :ref:`mjSpec`, introduced in 3.2.0 as an
|
||||
in-development feature, is now stable and recommended for general use.
|
||||
2. The native convex collision detection pipeline introduced in 3.2.3 and enabled by the
|
||||
:ref:`nativeccd<option-flag-nativeccd>` flag, is not yet the default but is already recommended for general use.
|
||||
Please try it when encountering collision-related problems and report any issues you encounter.
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
3. The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
|
||||
:ref:`inertia<asset-mesh-inertia>` attribute.
|
||||
4. The not-useful ``convexhull`` compiler option (to disable computation of mesh convex hulls) has been removed.
|
||||
5. Removed the deprecated ``mju_rotVecMat``, ``mju_rotVecMatT`` and ``mjv_makeConnector`` functions.
|
||||
6. Sorting now uses a faster, native sort function (fixes :github:issue:`1638`).
|
||||
7. The PBR texture layers introduced in 3.2.1 were refactored from separate sub-elements to a single
|
||||
:ref:`layer<material-layer>` sub-element.
|
||||
8. The composite types box, cylinder, and sphere have been removed. Users should instead use the equivalent types
|
||||
available in :ref:`flexcomp<body-flexcomp>`.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
9. Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
|
||||
10. Added ``TOUCH`` sensor.
|
||||
11. Added support for ``eq_active``. Fixes :github:issue:`2173`.
|
||||
12. Added ray intersection with ellipsoid.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
13. Fixed several bugs related to connect and weld constraints with site semantics (fixes :github:issue:`2179`, reported
|
||||
by :github:user:`yinfanyi`). The introduction of site specification to connects and welds in 3.2.3 conditionally
|
||||
changed the semantics of `mjData.eq_obj1id` and `mjData.eq_obj2id`, but these changes were not properly propagated
|
||||
in several places leading to incorrect computations of constraint inertia, readings of affected force/torque sensors
|
||||
and runtime enabling/disabling of such constraints.
|
||||
14. Fixed a bug in slider-crank :ref:`transmission<geTransmission>`. The bug was introduced in 3.0.0.
|
||||
15. Fixed a bug in flex texture coordinates that prevented the correct allocation of textures in mjModel.
|
||||
|
||||
|
||||
Documentation
|
||||
^^^^^^^^^^^^^
|
||||
16. Function headers in the :doc:`API reference <../APIreference/APIfunctions>` now link to their source definitions
|
||||
in GitHub.
|
||||
|
||||
Version 3.2.4 (Oct 15, 2024)
|
||||
----------------------------
|
||||
@@ -26,13 +213,14 @@ General
|
||||
^^^^^^^
|
||||
|
||||
.. youtube:: e8lUuykQPGs
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
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>`__.
|
||||
Two quadratic-memory allocations still remain to be fully sparsified: ``mjData.actuator_moment`` and the matrices used
|
||||
by the PGS solver.
|
||||
Two quadratic-memory allocations still remain to be fully sparsified: ``mjData.actuator_moment`` and the matrices
|
||||
used by the PGS solver.
|
||||
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
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/flex/trampoline.xml>`__ for examples of flex objects
|
||||
@@ -81,10 +269,11 @@ General
|
||||
|
||||
4. Added the :ref:`nativeccd<option-flag-nativeccd>` flag. When this flag is enabled, general convex collision
|
||||
detection is handled with a new native code path, rather than `libccd <https://github.com/danfis/libccd>`__.
|
||||
This feature is in early stages of testing, but users who've experienced issues related to collsion detection are
|
||||
This feature is in early stages of testing, but users who've experienced issues related to collision detection are
|
||||
welcome to experiment with it and report any issues.
|
||||
|
||||
.. youtube:: kcM_oauk3ZA
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -167,7 +356,7 @@ General
|
||||
2. Added a new :ref:`autoreset<option-flag-autoreset>` flag to disable automatic reset when NaNs or infinities are
|
||||
detected.
|
||||
3. Added sub-elements to the MJCF :ref:`material<asset-material>` element, to allow specification of multiple textures
|
||||
for rendering (e.g., :ref:`occlusion-roughness-metallic<material-orm>`). Note that the MuJoCo renderer doesn't
|
||||
for rendering (e.g., ``occlusion, roughness, metallic``). Note that the MuJoCo renderer doesn't
|
||||
support these new features, and they are made available for use with external renderers.
|
||||
4. Sorting (``mjQUICKSORT``) now calls ``std::sort`` when building with C++ (:github:issue:`1638`).
|
||||
|
||||
@@ -214,12 +403,12 @@ General
|
||||
3. Calls to :ref:`mj_defaultVFS` may allocate memory inside VFS, and the corresponding
|
||||
:ref:`mj_deleteVFS` must be called to deallocate any internal allocated memory.
|
||||
|
||||
4. Deprecated :ref:`mju_rotVecMat` and :ref:`mju_rotVecMatT` in favor of :ref:`mju_mulMatVec3` and
|
||||
4. Deprecated ``mju_rotVecMat`` and ``mju_rotVecMatT`` in favor of :ref:`mju_mulMatVec3` and
|
||||
:ref:`mju_mulMatTVec3`. These function names and argument order are more consistent with the rest of the API.
|
||||
The older functions have been removed from the Python bindings and will be removed from the C API in the next
|
||||
release.
|
||||
5. Removed the ``actuator_actdim`` callback from actuator plugins. They now have the ``actdim`` attribute, which
|
||||
must be used with actuators that write state to the ``act`` array. This fixed a crash which happend when
|
||||
must be used with actuators that write state to the ``act`` array. This fixed a crash which happened when
|
||||
keyframes were used in a model with stateful actuator plugins. The PID plugin will give an error when the wrong
|
||||
value of actdim is provided.
|
||||
|
||||
@@ -229,6 +418,7 @@ General
|
||||
speed and memory footprint.
|
||||
|
||||
.. youtube:: ZXBTEIDWHhs
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -317,6 +507,7 @@ General
|
||||
^^^^^^^
|
||||
|
||||
.. youtube:: 5k0_wsIRAFc
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -421,6 +612,7 @@ Python bindings
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. youtube:: xHDS0n5DpqM
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -480,7 +672,7 @@ General
|
||||
1. Improved the :ref:`discardvisual<compiler-discardvisual>` compiler flag, which now discards all visual-only assets.
|
||||
See :ref:`discardvisual<compiler-discardvisual>` for details.
|
||||
2. Removed the :ref:`timer<mjtTimer>` for midphase colllision detection, it is now folded in with the narrowphase
|
||||
timer. This is because timing the two phases seperately required fine-grained timers inside the collision
|
||||
timer. This is because timing the two phases separately required fine-grained timers inside the collision
|
||||
functions; these functions are so small and fast that the timer itself was incurring a measurable cost.
|
||||
3. Added the flag :ref:`bvactive<visual-global-bvactive>` to ``visual/global``, allowing users to turn off
|
||||
visualisation of active bounding volumes (the red/green boxes in this :ref:`this changelog item<midphase>`). For
|
||||
@@ -585,6 +777,7 @@ General
|
||||
``qfrc_{spring, damper, gravcomp, fluid}``. The sum of these vectors equals ``qfrc_passive``.
|
||||
|
||||
.. youtube:: H9qG9Zf2W44
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -648,6 +841,7 @@ Documentation
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. youtube:: cE3s_IfO4g4
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -679,6 +873,7 @@ New features
|
||||
:target: https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/mjx/tutorial.ipynb
|
||||
|
||||
.. youtube:: QewlEqIZi1o
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -689,6 +884,7 @@ New features
|
||||
gradient at query points. See the :ref:`documentation<exWriting>` for more details.
|
||||
|
||||
.. youtube:: ra2bTiZHGlw
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -709,6 +905,7 @@ New features
|
||||
this functionality to be unified in the future.
|
||||
|
||||
.. youtube:: Vc1tq0fFvQA
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -837,6 +1034,7 @@ Simulate
|
||||
^^^^^^^^
|
||||
|
||||
.. youtube:: YSvWn_poqWs
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -851,6 +1049,7 @@ Documentation
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. youtube:: nljr0X79vI0
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
@@ -884,8 +1083,8 @@ General
|
||||
used to determine the type of the asset file without resorting to pulling the type from the file extension.
|
||||
#. Added analytic derivatives for quaternion :ref:`subtraction<mjd_subQuat>` and :ref:`integration<mjd_quatIntegrate>`
|
||||
(rotation with an angular velocity). Derivatives are in the 3D tangent space.
|
||||
#. Added :ref:`mjv_connector` which has identical functionality to :ref:`mjv_makeConnector`, but with more convenient
|
||||
"from-to" argument parametrization. :ref:`mjv_makeConnector` is now deprecated.
|
||||
#. Added :ref:`mjv_connector` which has identical functionality to ``mjv_makeConnector``, but with more convenient
|
||||
"from-to" argument parametrization. ``mjv_makeConnector`` is now deprecated.
|
||||
#. Bumped oldest supported MacOS from version 10.12 to 11. MacOS 11 is the oldest version still maintained by Apple.
|
||||
|
||||
Python bindings
|
||||
@@ -949,11 +1148,12 @@ Simulate
|
||||
^^^^^^^^
|
||||
|
||||
.. youtube:: mXVPbppGk5I
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
6. Added Visualization tab to simulate UI, corresponding to elements of the :ref:`visual<visual>` MJCF element. After
|
||||
modifying values in the GUI, a saved XML will contain the new values. The modifyable members of
|
||||
modifying values in the GUI, a saved XML will contain the new values. The modifiable members of
|
||||
:ref:`mjStatistic` (:ref:`extent<statistic-extent>`, :ref:`meansize<statistic-meansize>` and
|
||||
:ref:`center<statistic-center>`) are computed by the compiler and therefore do not have defaults. In order for these
|
||||
attributes to appear in the saved XML, a value must be specified in the loaded XML.
|
||||
@@ -1072,7 +1272,7 @@ Python bindings
|
||||
passive viewer now also requires an explicit call to ``sync`` on its handle to pick up any update to the physics
|
||||
state. This is to avoid race conditions that can result in visual artifacts. See
|
||||
:ref:`documentation<PyViewerPassive>` for details.
|
||||
#. The ``viewer.launch_repl`` function has been removed since its functionality is superceded by ``launch_passive``.
|
||||
#. The ``viewer.launch_repl`` function has been removed since its functionality is superseded by ``launch_passive``.
|
||||
#. Added a small number of missing struct fields discovered through the new ``introspect`` metadata.
|
||||
|
||||
Bug fixes
|
||||
@@ -1322,6 +1522,7 @@ General
|
||||
See the :ref:`Memory allocation <CSize>` section for details.
|
||||
|
||||
.. youtube:: RHnXD6uO3Mg
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:height: 150px
|
||||
|
||||
@@ -1409,6 +1610,7 @@ General
|
||||
^^^^^^^
|
||||
|
||||
.. youtube:: BcHZ5BFeTmU
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:height: 150px
|
||||
|
||||
@@ -1425,6 +1627,7 @@ General
|
||||
different uses of new weld attributes.
|
||||
|
||||
.. youtube:: s-0JHanqV1A
|
||||
:aspect: 16:7
|
||||
:align: right
|
||||
:height: 150px
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ also disables the inertia-based model for the parent body. The
|
||||
Elements of the model are a generalization of :cite:t:`andersen2005b` to 3 dimensions.
|
||||
The force :math:`\mathbf{f}_{\text{ellipsoid}}` and torque
|
||||
:math:`\mathbf{g}_{\text{ellipsoid}}` exerted by the fluid onto the solid are
|
||||
the sum of of the terms
|
||||
the sum of the terms
|
||||
|
||||
.. math::
|
||||
\begin{align*}
|
||||
@@ -308,7 +308,7 @@ We present the following result.
|
||||
:math:`\mathcal{E}` with :math:`\Pi_{\mathbf{u}}` (denoted :math:`\mathcal{E}^{\cap}_{\mathbf{u}}`).
|
||||
|
||||
An important property of :math:`\mathcal{E}^{\mathrm{proj}}_{\mathbf{u}}` is that :math:`\mathbf{u}` is tangent
|
||||
tangent to the ellipsoid :math:`\mathcal{E}` at every point on :math:`\mathcal{E}^{\mathrm{proj}}_{\mathbf{u}}`.
|
||||
to the ellipsoid :math:`\mathcal{E}` at every point on :math:`\mathcal{E}^{\mathrm{proj}}_{\mathbf{u}}`.
|
||||
|
||||
We can regard :math:`\mathcal{E}` as the image of the unit sphere :math:`\mathcal{S}` under a stretching
|
||||
transformation :math:`T = \mathrm{diag}(r_x, r_y, r_z)`. Furthermore, if :math:`\mathbf{\tilde{u}}` is a vector
|
||||
|
||||
+135
-42
@@ -241,7 +241,7 @@ The computation of the constraint force is the hard part and will be described l
|
||||
description of the general framework by summarizing how the above quantities up to the constraint Jacobian are computed.
|
||||
|
||||
- The applied force :math:`\tau` includes :ref:`passive <gePassive>` forces from spring-dampers and fluid dynamics,
|
||||
:ref:`actuation <geActuation>` forces, and additonal forces specified by the user.
|
||||
:ref:`actuation <geActuation>` forces, and additional forces specified by the user.
|
||||
- The bias force :math:`c` includes Coriolis, centrifugal and gravitational forces. Their sum is computed using the
|
||||
Recursive Newton-Euler (RNE) algorithm with acceleration set to 0.
|
||||
- The joint-space inertia matrix :math:`M` is computed using the Composite Rigid-Body (CRB) algorithm. This matrix is
|
||||
@@ -414,7 +414,7 @@ with MuJoCo's operation as long as such user forces depend only on position and
|
||||
|
||||
MuJoCo can compute three types of passive forces:
|
||||
|
||||
- Spring-dampers in joints and tendons. See the following attribues for details.
|
||||
- Spring-dampers in joints and tendons. See the following attributes for details.
|
||||
|br| **Joints:**
|
||||
:ref:`stiffness<body-joint-stiffness>`, :ref:`springref<body-joint-springref>`,
|
||||
:ref:`damping<body-joint-damping>`, :ref:`springdamper<body-joint-springdamper>`.
|
||||
@@ -484,24 +484,27 @@ acceleration as a function of velocity: :math:`a_t = a(v_t)`, the velocity updat
|
||||
This is a non-linear equation in the unknown vector :math:`v_{t+h}` and can be solved numerically at each time step
|
||||
using a first-order expansion of :math:`a(v_{t+h})` around :math:`v_t`. Recall that the forward dynamics are
|
||||
|
||||
.. math:: a(v) = M^{-1} \big(\tau(v) - c(v) + J^T f(v)\big)
|
||||
.. math::
|
||||
:label: eq_forward
|
||||
|
||||
a(v) = M^{-1} \big(\tau(v) - c(v) + J^T f(v)\big)
|
||||
|
||||
Thus we define the derivative
|
||||
|
||||
.. math::
|
||||
\begin{aligned}
|
||||
{\partial a(v) \over \partial v} &= M^{-1} D \\
|
||||
D &\equiv {\partial \over \partial v} \Big(\tau(v) - c (v) + J^T f(v)\Big)
|
||||
\end{aligned}
|
||||
\begin{aligned}
|
||||
{\partial a(v) \over \partial v} &= M^{-1} D \\
|
||||
D &\equiv {\partial \over \partial v} \Big(\tau(v) - c (v) + J^T f(v)\Big)
|
||||
\end{aligned}
|
||||
|
||||
The velocity update corresponding to Newton's method is as follows. First, we expand the right hand side to first order
|
||||
|
||||
.. math::
|
||||
\begin{aligned}
|
||||
v_{t+h} &= v_t + h a(v_{t+h}) \\
|
||||
&\approx v_t + h \big( a(v_t) + {\partial a(v) \over \partial v} \cdot (v_{t+h}-v_t) \big) \\
|
||||
&= v_t + h a(v_t) + h M^{-1} D \cdot (v_{t+h}-v_t)
|
||||
\end{aligned}
|
||||
\begin{aligned}
|
||||
v_{t+h} &= v_t + h a(v_{t+h}) \\
|
||||
&\approx v_t + h \big( a(v_t) + {\partial a(v) \over \partial v} \cdot (v_{t+h}-v_t) \big) \\
|
||||
&= v_t + h a(v_t) + h M^{-1} D \cdot (v_{t+h}-v_t)
|
||||
\end{aligned}
|
||||
|
||||
Premultiplying by :math:`M` and rearranging yields
|
||||
|
||||
@@ -512,16 +515,27 @@ Solving for :math:`v_{t+h}`, we obtain the implicit-in-velocity update
|
||||
.. math::
|
||||
:label: eq_implicit_update
|
||||
|
||||
v_{t+h} = v_t + h (M-h D)^{-1} M a(v_t)
|
||||
\begin{aligned}
|
||||
v_{t+h} &= v_t + h \widehat{M}^{-1} M a(v_t) \\
|
||||
\widehat{M} &\equiv M-h D
|
||||
\end{aligned}
|
||||
|
||||
.. _geIntegrators:
|
||||
|
||||
Integrators
|
||||
^^^^^^^^^^^
|
||||
MuJoCo supports four integrators: three single-step integrators and the multi-step 4th order Runge-Kutta integrator.
|
||||
All three single-step integrators in MuJoCo use the update :eq:`eq_implicit_update`, with different definitions of the
|
||||
:math:`D` matrix, which is always computed analytically.
|
||||
|
||||
Semi-implicit with implicit joint damping (``Euler``)
|
||||
For this method, :math:`D` only includes derivatives of joint damping. Note that in this case :math:`D` is diagonal
|
||||
and :math:`M-h D` is symmetric, so Cholesky decomposition can be used. If the model has no joint damping or the
|
||||
and :math:`\widehat{M}` is symmetric, so :math:`L^TL` decomposition (a variant of Cholesky) can be used. This
|
||||
factorization is stored ``mjData.qLD``. If the model has no joint damping or the
|
||||
:ref:`eulerdamp<option-flag-eulerdamp>` disable-flag is set, implicit damping is disabled and the semi-implicit
|
||||
update :eq:`eq_semimplicit` is used, rather than :eq:`eq_implicit_update`.
|
||||
update :eq:`eq_semimplicit` is used, rather than :eq:`eq_implicit_update`, avoiding the additional factorization of
|
||||
:math:`\widehat{M}` (*additional* because :math:`M` is already factorized for the acceleration update
|
||||
:eq:`eq_forward`).
|
||||
|
||||
Implicit-in-velocity (``implicit``)
|
||||
For this method, :math:`D` includes derivatives of all forces except the constraint forces :math:`J^T f(v)`. These
|
||||
@@ -530,8 +544,8 @@ Implicit-in-velocity (``implicit``)
|
||||
future version. Additionally, we restrict :math:`D` to have the same sparsity pattern as :math:`M`, for computational
|
||||
efficiency. This restriction will exclude damping in tendons which connect bodies that are on different branches of
|
||||
the kinematic tree. Since :math:`D` is not symmetric, we cannot use Cholesky factorization, but because :math:`D` and
|
||||
:math:`M` have the same sparsity pattern corresponding to the topology of the kinematic tree, reverse-order LU
|
||||
factorization of :math:`M-h D` is `guaranteed to have no fill-in
|
||||
:math:`M` have the same sparsity pattern corresponding to the topology of the kinematic tree, reverse-order
|
||||
:math:`LU` factorization of :math:`\widehat{M}` is guaranteed to have `no fill-in
|
||||
<https://link.springer.com/book/10.1007/978-1-4899-7560-7>`_. This factorization is stored ``mjData.qLU``.
|
||||
|
||||
Fast implicit-in-velocity (``implicitfast``)
|
||||
@@ -541,7 +555,7 @@ Fast implicit-in-velocity (``implicitfast``)
|
||||
Second, these forces change rapidly only at high rotational velocities of complex pendula and spinning bodies,
|
||||
scenarios which are not common and already well-handled by the Runge-Kutta integrator (see below). Because the RNE
|
||||
derivatives are also the main source of asymmetry of :math:`D`, by dropping them and symmetrizing, we can use the
|
||||
faster Cholesky rather than LU decomposition.
|
||||
faster :math:`L^TL` rather than :math:`LU` decomposition.
|
||||
|
||||
4th-order Runge-Kutta (``RK4``)
|
||||
One advantage of our continuous-time formulation is that we can use higher order integrators such as Runge-Kutta or
|
||||
@@ -576,8 +590,8 @@ Fast implicit-in-velocity (``implicitfast``)
|
||||
increased stability, and is therefore a strict improvement. It is the recommended integrator for most models.
|
||||
**implicit**:
|
||||
The benefit over ``implicitfast`` is the implicit integration of Coriolis and centripetal forces, including
|
||||
gyroscopic forces. The most common case where integrating such forces implicitly leads to noticable improvement is
|
||||
when free objects with assymetric inertia are spinning quickly. `gyroscopic.xml <../_static/gyroscopic.xml>`__
|
||||
gyroscopic forces. The most common case where integrating such forces implicitly leads to noticeable improvement is
|
||||
when free objects with asymmetric inertia are spinning quickly. `gyroscopic.xml <../_static/gyroscopic.xml>`__
|
||||
shows an ellipsoid rolling on an inclined plane which quickly diverges with ``implicitfast`` but is stable with
|
||||
``implicit``.
|
||||
**RK4**:
|
||||
@@ -646,7 +660,7 @@ Control: ``ctrl``
|
||||
generalized forces directly (stateless actuators), or affect the actuator activations in ``mjData.act``, which then
|
||||
produce forces.
|
||||
|
||||
Auxillary Controls: ``qfrc_applied`` and ``xfrc_applied``
|
||||
Auxiliary Controls: ``qfrc_applied`` and ``xfrc_applied``
|
||||
| ``mjData.qfrc_applied`` are directly applied generalized forces.
|
||||
| ``mjData.xfrc_applied`` are Cartesian wrenches applied to the CoM of individual bodies. This field is used for
|
||||
example, by the :ref:`native viewer<saSimulate>` to apply mouse perturbations.
|
||||
@@ -1525,27 +1539,106 @@ Filtering
|
||||
|
||||
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
|
||||
primitive geometric shapes: plane, sphere, capsule, cylinder, ellipsoid, box. It also supports triangulated meshes and
|
||||
height fields.
|
||||
.. _narrow-phase: https://en.wikipedia.org/wiki/Collision_detection#Narrow_phase
|
||||
|
||||
We have chosen to limit collision detection to *convex* geoms. All primitive types are convex. Height fields are not
|
||||
convex but internally they are treated as unions of triangular prisms (using custom collision pruning beyond the filters
|
||||
described above). Meshes specified by the user can be non-convex, and are rendered as such. For collision purposes
|
||||
however they are replaced with their convex hulls. Mesh collisions are based on the Minkowski Portal Refinement (MPR)
|
||||
algorithm as implemented in `libccd <https://github.com/danfis/libccd>`__. It has tolerance and maximum iteration
|
||||
parameters exposed as ``mjModel.opt.ccd_tolerance`` and ``mjModel.opt.ccd_iterations`` respectively. MPR operates on the
|
||||
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.
|
||||
With the notable exception of :ref:`SDF plugins<exSDF>` (see documentation therein), collision detection is limited to
|
||||
*convex* geoms. All primitive types are convex. Height-fields are not convex but internally they are treated as a
|
||||
collection of triangular prisms (using custom collision pruning beyond the filters described above). Meshes specified by
|
||||
the user can be non-convex, and are rendered as such. For collision purposes however they are replaced with their convex
|
||||
hulls (visualized with the 'H' key in :ref:`simulate <saSimulate>`), computed by 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
|
||||
(which can be primitive shapes or meshes) and attach them to the same body. Open tools like the `CoACD library
|
||||
<https://github.com/SarahWeiii/CoACD>`__ can be used outside MuJoCo to automate this process. Finally, 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.
|
||||
(which can be primitive shapes or meshes) and attach them to the same body. A height-field is essentially a shape that
|
||||
is automatically-decomposed into prisms
|
||||
|
||||
Open mesh-decomposition tools like the
|
||||
`CoACD library <https://github.com/SarahWeiii/CoACD>`__ can be used outside MuJoCo to automate this process. Finally,
|
||||
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 have other requirements and limitations.
|
||||
|
||||
.. _Pipeline:
|
||||
|
||||
@@ -1597,14 +1690,14 @@ The stages below compute quantities that depend on the generalized positions ``m
|
||||
3. Compute the body inertias and joint axes, in global frames centered at the centers of mass of the corresponding
|
||||
kinematic subtrees: :ref:`mj_comPos`
|
||||
4. Compute quantities related to :ref:`flex<deformable-flex>` objects: :ref:`mj_flex`
|
||||
5. Compute the actuator lengths and moment arms: :ref:`mj_tendon`
|
||||
5. Compute the tendon lengths and moment arms. This includes the computation of minimal-length paths for spatial
|
||||
tendons: :ref:`mj_tendon`
|
||||
6. Compute the composite rigid body inertias and joint-space inertia matrix: :ref:`mj_crb`
|
||||
7. Compute the sparse factorization of the joint-space inertia matrix: :ref:`mj_factorM`
|
||||
8. Construct the list of active contacts. This includes both broad-phase and near-phase collision detection:
|
||||
:ref:`mj_collision`
|
||||
9. Construct the constraint Jacobian and compute the constraint residuals: :ref:`mj_makeConstraint`
|
||||
10. Compute the tendon lengths and moment arms. This includes the computation of minimal-length paths for spatial
|
||||
tendons: :ref:`mj_transmission`
|
||||
10. Compute the actuator lengths and moment arms: :ref:`mj_transmission`
|
||||
11. Compute the matrices and vectors needed by the constraint solvers: :ref:`mj_projectConstraint`
|
||||
12. Compute sensor data that only depends on position, and the potential energy if enabled: :ref:`mj_sensorPos`,
|
||||
:ref:`mj_energyPos`
|
||||
@@ -1722,7 +1815,7 @@ The top-level function :ref:`mj_inverse` invokes the following sequence of compu
|
||||
Derivatives
|
||||
-----------
|
||||
|
||||
MuJoCo's entire computational pipline including its constraint solver are analytically differentiable in principle.
|
||||
MuJoCo's entire computational pipeline including its constraint solver are analytically differentiable in principle.
|
||||
Writing efficient implementations of these derivatives is a long term goal of the development team. Analytic derivatives
|
||||
of the smooth dynamics (excluding constraints) with respect to velocity are already computed and enable the two
|
||||
:ref:`implicit integrators<geIntegration>`.
|
||||
|
||||
@@ -159,10 +159,14 @@ pygments_dark_style = 'monokai'
|
||||
html_static_path = [
|
||||
'_static',
|
||||
'css',
|
||||
'js',
|
||||
]
|
||||
html_css_files = [
|
||||
'theme_overrides.css',
|
||||
]
|
||||
html_js_files = [
|
||||
'linenumbers.js',
|
||||
]
|
||||
|
||||
favicons = [
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ h4 {
|
||||
|
||||
/* Paragraph margins don't apply to table cell contents. */
|
||||
.rst-content table.docutils td>p {
|
||||
margin-top: 0px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Set padding of in-line highlighted text. */
|
||||
@@ -200,8 +200,8 @@ table.docutils:not(.mjcf-attributes) > tbody > tr.row-odd {
|
||||
/* MJCF attributes table. */
|
||||
table.mjcf-attributes {
|
||||
border-style: none;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -209,7 +209,7 @@ table.mjcf-attributes {
|
||||
table.mjcf-attributes tbody tr td,
|
||||
table.mjcf-attributes tbody tr:nth-child(2n-1) td {
|
||||
border-style: none;
|
||||
padding: 0px 0px 0px 0px;
|
||||
padding: 0 0 0 0;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@@ -217,13 +217,13 @@ table.mjcf-attributes tbody tr td p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.table-wrapper.mjcf-attributes {
|
||||
.table-wrapper.mjcf-attributes {
|
||||
margin: 0.5em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table td > div.wy-table-responsive {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Remove vertical spacing before/after code blocks. */
|
||||
|
||||
@@ -98,7 +98,7 @@ def read(lines: List[str]) -> Dict[str, ApiDefinition]:
|
||||
if section is not None:
|
||||
if 'MJAPI FUNCTIONS' in section:
|
||||
# Stripped functions do not begin with MJAPI, and must be under the
|
||||
# predefiend section 'MJAPI FUNCTIONS'. This is because the docs don't
|
||||
# predefined section 'MJAPI FUNCTIONS'. This is because the docs don't
|
||||
# include this prefix, and so we need to read such functions from the
|
||||
# reference header.
|
||||
stripped_functions = True
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 300 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 267 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
+118
-66
@@ -169,7 +169,8 @@ struct mjData_ {
|
||||
int nf; // number of friction constraints
|
||||
int nl; // number of limit constraints
|
||||
int nefc; // number of constraints
|
||||
int nnzJ; // number of non-zeros in constraint Jacobian
|
||||
int nJ; // number of non-zeros in constraint Jacobian
|
||||
int nA; // number of non-zeros in constraint inverse inertia matrix
|
||||
int nisland; // number of detected constraint islands
|
||||
|
||||
// global properties
|
||||
@@ -263,8 +264,8 @@ struct mjData_ {
|
||||
mjtNum* actuator_length; // actuator lengths (nu x 1)
|
||||
int* moment_rownnz; // number of non-zeros in actuator_moment row (nu x 1)
|
||||
int* moment_rowadr; // row start address in colind array (nu x 1)
|
||||
int* moment_colind; // column indices in sparse Jacobian (nu x nv)
|
||||
mjtNum* actuator_moment; // actuator moments (nu x nv)
|
||||
int* moment_colind; // column indices in sparse Jacobian (nJmom x 1)
|
||||
mjtNum* actuator_moment; // actuator moments (nJmom x 1)
|
||||
|
||||
// computed by mj_fwdPosition/mj_crb
|
||||
mjtNum* crb; // com-based composite inertia and mass (nbody x 10)
|
||||
@@ -273,7 +274,6 @@ struct mjData_ {
|
||||
// computed by mj_fwdPosition/mj_factorM
|
||||
mjtNum* qLD; // L'*D*L factorization of M (sparse) (nM x 1)
|
||||
mjtNum* qLDiagInv; // 1/diag(D) (nv x 1)
|
||||
mjtNum* qLDiagSqrtInv; // 1/sqrt(diag(D)) (nv x 1)
|
||||
|
||||
// computed by mj_collisionTree
|
||||
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
|
||||
@@ -312,12 +312,17 @@ struct mjData_ {
|
||||
int* B_rownnz; // body-dof: non-zeros in each row (nbody x 1)
|
||||
int* B_rowadr; // body-dof: address of each row in B_colind (nbody x 1)
|
||||
int* B_colind; // body-dof: column indices of non-zeros (nB x 1)
|
||||
int* M_rownnz; // inertia: non-zeros in each row (nv x 1)
|
||||
int* M_rowadr; // inertia: address of each row in M_colind (nv x 1)
|
||||
int* M_colind; // inertia: column indices of non-zeros (nM x 1)
|
||||
int* mapM2M; // index mapping from M (legacy) to M (CSR) (nM x 1)
|
||||
int* C_rownnz; // reduced dof-dof: non-zeros in each row (nv x 1)
|
||||
int* C_rowadr; // reduced dof-dof: address of each row in C_colind (nv x 1)
|
||||
int* C_colind; // reduced dof-dof: column indices of non-zeros (nC x 1)
|
||||
int* mapM2C; // index mapping from M to C (nC x 1)
|
||||
int* D_rownnz; // dof-dof: non-zeros in each row (nv x 1)
|
||||
int* D_rowadr; // dof-dof: address of each row in D_colind (nv x 1)
|
||||
int* D_diag; // dof-dof: index of diagonal element (nv x 1)
|
||||
int* D_colind; // dof-dof: column indices of non-zeros (nD x 1)
|
||||
int* mapM2D; // index mapping from M to D (nD x 1)
|
||||
int* mapD2M; // index mapping from D to M (nM x 1)
|
||||
@@ -361,13 +366,13 @@ struct mjData_ {
|
||||
int* efc_J_rownnz; // number of non-zeros in constraint Jacobian row (nefc x 1)
|
||||
int* efc_J_rowadr; // row start address in colind array (nefc x 1)
|
||||
int* efc_J_rowsuper; // number of subsequent rows in supernode (nefc x 1)
|
||||
int* efc_J_colind; // column indices in constraint Jacobian (nnzJ x 1)
|
||||
int* efc_J_colind; // column indices in constraint Jacobian (nJ x 1)
|
||||
int* efc_JT_rownnz; // number of non-zeros in constraint Jacobian row T (nv x 1)
|
||||
int* efc_JT_rowadr; // row start address in colind array T (nv x 1)
|
||||
int* efc_JT_rowsuper; // number of subsequent rows in supernode T (nv x 1)
|
||||
int* efc_JT_colind; // column indices in constraint Jacobian T (nnzJ x 1)
|
||||
mjtNum* efc_J; // constraint Jacobian (nnzJ x 1)
|
||||
mjtNum* efc_JT; // constraint Jacobian transposed (nnzJ x 1)
|
||||
int* efc_JT_colind; // column indices in constraint Jacobian T (nJ x 1)
|
||||
mjtNum* efc_J; // constraint Jacobian (nJ x 1)
|
||||
mjtNum* efc_JT; // constraint Jacobian transposed (nJ x 1)
|
||||
mjtNum* efc_pos; // constraint position (equality, contact) (nefc x 1)
|
||||
mjtNum* efc_margin; // inclusion margin (contact) (nefc x 1)
|
||||
mjtNum* efc_frictionloss; // frictionloss (friction) (nefc x 1)
|
||||
@@ -391,8 +396,8 @@ struct mjData_ {
|
||||
// computed by mj_projectConstraint (PGS solver)
|
||||
int* efc_AR_rownnz; // number of non-zeros in AR (nefc x 1)
|
||||
int* efc_AR_rowadr; // row start address in colind array (nefc x 1)
|
||||
int* efc_AR_colind; // column indices in sparse AR (nefc x nefc)
|
||||
mjtNum* efc_AR; // J*inv(M)*J' + R (nefc x nefc)
|
||||
int* efc_AR_colind; // column indices in sparse AR (nA x 1)
|
||||
mjtNum* efc_AR; // J*inv(M)*J' + R (nA x 1)
|
||||
|
||||
//-------------------- arena-allocated: POSITION, VELOCITY dependent
|
||||
|
||||
@@ -428,8 +433,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
|
||||
mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering
|
||||
mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator
|
||||
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;
|
||||
typedef enum mjtEnableBit_ { // enable optional feature bitflags
|
||||
mjENBL_OVERRIDE = 1<<0, // override contact parameters
|
||||
@@ -439,9 +445,8 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
|
||||
// experimental features:
|
||||
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
|
||||
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;
|
||||
typedef enum mjtJoint_ { // type of degree of freedom
|
||||
mjJNT_FREE = 0, // global position and orientation (quat) (7)
|
||||
@@ -598,7 +603,9 @@ typedef enum mjtObj_ { // type of MujoCo object
|
||||
mjNOBJECT, // number of object types
|
||||
|
||||
// meta elements, do not appear in mjModel
|
||||
mjOBJ_FRAME = 100 // frame
|
||||
mjOBJ_FRAME = 100, // frame
|
||||
mjOBJ_DEFAULT // default
|
||||
|
||||
} mjtObj;
|
||||
typedef enum mjtConstraint_ { // type of constraint
|
||||
mjCNSTR_EQUALITY = 0, // equality constraint
|
||||
@@ -673,6 +680,8 @@ typedef enum mjtSensor_ { // type of sensor
|
||||
mjSENS_GEOMFROMTO, // segment between two geoms
|
||||
|
||||
// global sensors
|
||||
mjSENS_E_POTENTIAL, // potential energy
|
||||
mjSENS_E_KINETIC, // kinetic energy
|
||||
mjSENS_CLOCK, // simulation time
|
||||
|
||||
// plugin-controlled sensors
|
||||
@@ -898,6 +907,7 @@ struct mjModel_ {
|
||||
int ncam; // number of cameras
|
||||
int nlight; // number of lights
|
||||
int nflex; // number of flexes
|
||||
int nflexnode; // number of dofs in all flexes
|
||||
int nflexvert; // number of vertices in all flexes
|
||||
int nflexedge; // number of edges in all flexes
|
||||
int nflexelem; // number of elements in all flexes
|
||||
@@ -912,6 +922,9 @@ struct mjModel_ {
|
||||
int nmeshtexcoord; // number of texcoords in all meshes
|
||||
int nmeshface; // number of triangular faces in all meshes
|
||||
int nmeshgraph; // number of ints in mesh auxiliary data
|
||||
int nmeshpoly; // number of polygons in all meshes
|
||||
int nmeshpolyvert; // number of vertices in all polygons
|
||||
int nmeshpolymap; // number of polygons in vertex map
|
||||
int nskin; // number of skins
|
||||
int nskinvert; // number of vertices in all skins
|
||||
int nskintexvert; // number of vertiex with texcoords in all skins
|
||||
@@ -948,19 +961,20 @@ struct mjModel_ {
|
||||
int nuser_actuator; // number of mjtNums in actuator_user
|
||||
int nuser_sensor; // number of mjtNums in sensor_user
|
||||
int nnames; // number of chars in all names
|
||||
int nnames_map; // number of slots in the names hash map
|
||||
int npaths; // number of chars in all paths
|
||||
|
||||
// sizes set after mjModel construction (only affect mjData)
|
||||
// sizes set after mjModel construction
|
||||
int nnames_map; // number of slots in the names hash map
|
||||
int nM; // number of non-zeros in sparse inertia matrix
|
||||
int nB; // number of non-zeros in sparse body-dof matrix
|
||||
int nC; // number of non-zeros in sparse reduced dof-dof matrix
|
||||
int nD; // number of non-zeros in sparse dof-dof matrix
|
||||
int nJmom; // number of non-zeros in sparse actuator_moment matrix
|
||||
int ntree; // number of kinematic trees under world body
|
||||
int ngravcomp; // number of bodies with nonzero gravcomp
|
||||
int nemax; // number of potential equality-constraint rows
|
||||
int njmax; // number of available rows in constraint Jacobian
|
||||
int nconmax; // number of potential contacts in contact list
|
||||
int njmax; // number of available rows in constraint Jacobian (legacy)
|
||||
int nconmax; // number of potential contacts in contact list (legacy)
|
||||
int nuserdata; // number of mjtNums reserved for the user
|
||||
int nsensordata; // number of mjtNums in sensor data vector
|
||||
int npluginstate; // number of mjtNums in plugin state vector
|
||||
@@ -1149,6 +1163,9 @@ struct mjModel_ {
|
||||
int* flex_dim; // 1: lines, 2: triangles, 3: tetrahedra (nflex x 1)
|
||||
int* flex_matid; // material id for rendering (nflex x 1)
|
||||
int* flex_group; // group for visibility (nflex x 1)
|
||||
int* flex_interp; // interpolation (0: vertex, 1: nodes) (nflex x 1)
|
||||
int* flex_nodeadr; // first node address (nflex x 1)
|
||||
int* flex_nodenum; // number of nodes (nflex x 1)
|
||||
int* flex_vertadr; // first vertex address (nflex x 1)
|
||||
int* flex_vertnum; // number of vertices (nflex x 1)
|
||||
int* flex_edgeadr; // first edge address (nflex x 1)
|
||||
@@ -1162,15 +1179,19 @@ struct mjModel_ {
|
||||
int* flex_evpairadr; // first evpair address (nflex x 1)
|
||||
int* flex_evpairnum; // number of evpairs (nflex x 1)
|
||||
int* flex_texcoordadr; // address in flex_texcoord; -1: none (nflex x 1)
|
||||
int* flex_nodebodyid; // node body ids (nflexnode x 1)
|
||||
int* flex_vertbodyid; // vertex body ids (nflexvert x 1)
|
||||
int* flex_edge; // edge vertex ids (2 per edge) (nflexedge x 2)
|
||||
int* flex_elem; // element vertex ids (dim+1 per elem) (nflexelemdata x 1)
|
||||
int* flex_elemtexcoord; // element texture coordinates (dim+1) (nflexelemdata x 1)
|
||||
int* flex_elemedge; // element edge ids (nflexelemedge x 1)
|
||||
int* flex_elemlayer; // element distance from surface, 3D only (nflexelem x 1)
|
||||
int* flex_shell; // shell fragment vertex ids (dim per frag) (nflexshelldata x 1)
|
||||
int* flex_evpair; // (element, vertex) collision pairs (nflexevpair x 2)
|
||||
mjtNum* flex_vert; // vertex positions in local body frames (nflexvert x 3)
|
||||
mjtNum* flex_xvert0; // Cartesian vertex positions in qpos0 (nflexvert x 3)
|
||||
mjtNum* flex_vert0; // vertex positions in qpos0 on [0, 1]^d (nflexvert x 3)
|
||||
mjtNum* flex_node; // node positions in local body frames (nflexnode x 3)
|
||||
mjtNum* flex_node0; // Cartesian node positions in qpos0 (nflexnode x 3)
|
||||
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
|
||||
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
|
||||
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
|
||||
@@ -1211,6 +1232,15 @@ struct mjModel_ {
|
||||
mjtNum* mesh_pos; // translation applied to asset vertices (nmesh x 3)
|
||||
mjtNum* mesh_quat; // rotation applied to asset vertices (nmesh x 4)
|
||||
int* mesh_pathadr; // address of asset path for mesh; -1: none (nmesh x 1)
|
||||
int* mesh_polynum; // number of polygons per mesh (nmesh x 1)
|
||||
int* mesh_polyadr; // first polygon address per mesh (nmesh x 1)
|
||||
mjtNum* mesh_polynormal; // all polygon normals (nmeshpoly x 3)
|
||||
int* mesh_polyvertadr; // polygon vertex start address (nmeshpoly x 1)
|
||||
int* mesh_polyvertnum; // number of vertices per polygon (nmeshpoly x 1)
|
||||
int* mesh_polyvert; // all polygon vertices (nmeshpolyvert x 1)
|
||||
int* mesh_polymapadr; // first polygon address per vertex (nmeshvert x 1)
|
||||
int* mesh_polymapnum; // number of polygons per vertex (nmeshvert x 1)
|
||||
int* mesh_polymap; // vertex to polygon map (nmeshpolymap x 1)
|
||||
|
||||
// skins
|
||||
int* skin_matid; // skin material id; -1: none (nskin x 1)
|
||||
@@ -1582,14 +1612,14 @@ struct mjrContext_ { // custom OpenGL context
|
||||
unsigned int auxColor_r[mjNAUX]; // auxiliary color buffer for resolving
|
||||
|
||||
// materials with textures
|
||||
int mat_texid[mjMAXMATERIAL*mjNTEXROLE]; // material texture ids (-1: no texture)
|
||||
int mat_texuniform[mjMAXMATERIAL]; // texture repetition for 2d mapping
|
||||
int mat_texrepeat[mjMAXMATERIAL*2]; // texture repetition for 2d mapping
|
||||
int mat_texid[mjMAXMATERIAL*mjNTEXROLE]; // material texture ids (-1: no texture)
|
||||
int mat_texuniform[mjMAXMATERIAL]; // uniform cube mapping
|
||||
float mat_texrepeat[mjMAXMATERIAL*2]; // texture repetition for 2d mapping
|
||||
|
||||
// texture objects and info
|
||||
int ntexture; // number of allocated textures
|
||||
int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture)
|
||||
unsigned int texture[mjMAXTEXTURE]; // texture names
|
||||
int ntexture; // number of allocated textures
|
||||
int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture)
|
||||
unsigned int texture[mjMAXTEXTURE]; // texture names
|
||||
|
||||
// displaylist starting positions
|
||||
unsigned int basePlane; // all planes from model
|
||||
@@ -1628,23 +1658,24 @@ struct mjrContext_ { // custom OpenGL context
|
||||
int windowDoublebuffer; // is default/window framebuffer double buffered
|
||||
|
||||
// framebuffer
|
||||
int currentBuffer; // currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN
|
||||
int currentBuffer; // currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN
|
||||
|
||||
// pixel output format
|
||||
int readPixelFormat; // default color pixel format for mjr_readPixels
|
||||
int readPixelFormat; // default color pixel format for mjr_readPixels
|
||||
|
||||
// depth output format
|
||||
int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR
|
||||
int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR
|
||||
};
|
||||
typedef struct mjrContext_ mjrContext;
|
||||
typedef enum mjtGeomInertia_ { // type of inertia inference
|
||||
mjINERTIA_VOLUME = 0, // mass distributed in the volume
|
||||
mjINERTIA_SHELL, // mass distributed on the surface
|
||||
} mjtGeomInertia;
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjINERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjINERTIA_EXACT, // exact mesh inertia
|
||||
mjINERTIA_LEGACY, // legacy mesh inertia
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjMESH_INERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjMESH_INERTIA_EXACT, // exact mesh inertia
|
||||
mjMESH_INERTIA_LEGACY, // legacy mesh inertia
|
||||
mjMESH_INERTIA_SHELL // shell mesh inertia
|
||||
} mjtMeshInertia;
|
||||
typedef enum mjtBuiltin_ { // type of built-in procedural texture
|
||||
mjBUILTIN_NONE = 0, // no built-in texture
|
||||
@@ -1683,30 +1714,32 @@ typedef enum mjtOrientation_ { // type of orientation specifier
|
||||
typedef struct mjsElement_ { // element type, do not modify
|
||||
mjtObj elemtype; // element type
|
||||
} mjsElement;
|
||||
typedef struct mjSpec_ { // model specification
|
||||
mjsElement* element; // element type
|
||||
mjString* modelname; // model name
|
||||
|
||||
// compiler settings
|
||||
typedef struct mjsCompiler_ { // compiler options
|
||||
mjtByte autolimits; // infer "limited" attribute based on range
|
||||
double boundmass; // enforce minimum body mass
|
||||
double boundinertia; // enforce minimum body diagonal inertia
|
||||
double settotalmass; // rescale masses and inertias; <=0: ignore
|
||||
mjtByte balanceinertia; // automatically impose A + B >= C rule
|
||||
mjtByte strippath; // automatically strip paths from mesh files
|
||||
mjtByte fitaabb; // meshfit to aabb instead of inertia box
|
||||
mjtByte degree; // angles in radians or degrees
|
||||
char eulerseq[3]; // sequence for euler rotations
|
||||
mjString* meshdir; // mesh and hfield directory
|
||||
mjString* texturedir; // texture directory
|
||||
mjtByte discardvisual; // discard visual geoms in parser
|
||||
mjtByte convexhull; // compute mesh convex hulls
|
||||
mjtByte usethread; // use multiple threads to speed up compiler
|
||||
mjtByte fusestatic; // fuse static bodies with parent
|
||||
int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom)
|
||||
int inertiagrouprange[2]; // range of geom groups used to compute inertia
|
||||
int alignfree; // align free joints with inertial frame
|
||||
mjLROpt LRopt; // options for lengthrange computation
|
||||
} mjsCompiler;
|
||||
typedef struct mjSpec_ { // model specification
|
||||
mjsElement* element; // element type
|
||||
mjString* modelname; // model name
|
||||
|
||||
// compiler data
|
||||
mjsCompiler compiler; // compiler options
|
||||
mjtByte strippath; // automatically strip paths from mesh files
|
||||
mjString* meshdir; // mesh and hfield directory
|
||||
mjString* texturedir; // texture directory
|
||||
|
||||
// engine data
|
||||
mjOption option; // physics options
|
||||
@@ -1977,10 +2010,13 @@ typedef struct mjsFlex_ { // flex specification
|
||||
double thickness; // thickness (2D only)
|
||||
|
||||
// mesh properties
|
||||
mjStringVec* nodebody; // node body names
|
||||
mjStringVec* vertbody; // vertex body names
|
||||
mjDoubleVec* node; // node positions
|
||||
mjDoubleVec* vert; // vertex positions
|
||||
mjIntVec* elem; // element vertex ids
|
||||
mjFloatVec* texcoord; // vertex texture coordinates
|
||||
mjIntVec* elemtexcoord; // element texture coordinates
|
||||
|
||||
// other
|
||||
mjString* info; // message appended to compiler errors
|
||||
@@ -1993,14 +2029,13 @@ typedef struct mjsMesh_ { // mesh specification
|
||||
double refpos[3]; // reference position
|
||||
double refquat[4]; // reference orientation
|
||||
double scale[3]; // rescale mesh
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact)
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact, shell)
|
||||
mjtByte smoothnormal; // do not exclude large-angle faces from normals
|
||||
int maxhullvert; // maximum vertex count for the convex hull
|
||||
mjFloatVec* uservert; // user vertex data
|
||||
mjFloatVec* usernormal; // user normal data
|
||||
mjFloatVec* usertexcoord; // user texcoord data
|
||||
mjIntVec* userface; // user vertex indices
|
||||
mjIntVec* userfacenormal; // user normal indices
|
||||
mjIntVec* userfacetexcoord; // user texcoord indices
|
||||
mjsPlugin plugin; // sdf plugin
|
||||
mjString* info; // message appended to compiler errors
|
||||
@@ -2496,7 +2531,7 @@ struct mjUI_ { // entire UI
|
||||
|
||||
// UI sizes (framebuffer units)
|
||||
int width; // width
|
||||
int height; // current heigth
|
||||
int height; // current height
|
||||
int maxheight; // height when all sections open
|
||||
int scroll; // scroll from top of UI
|
||||
|
||||
@@ -2963,9 +2998,14 @@ struct mjvSceneState_ {
|
||||
int* flex_dim;
|
||||
int* flex_matid;
|
||||
int* flex_group;
|
||||
int* flex_interp;
|
||||
int* flex_nodeadr;
|
||||
int* flex_nodenum;
|
||||
int* flex_nodebodyid;
|
||||
int* flex_vertadr;
|
||||
int* flex_vertnum;
|
||||
int* flex_elem;
|
||||
int* flex_elemtexcoord;
|
||||
int* flex_elemlayer;
|
||||
int* flex_elemadr;
|
||||
int* flex_elemnum;
|
||||
@@ -2976,8 +3016,11 @@ struct mjvSceneState_ {
|
||||
int* flex_texcoordadr;
|
||||
int* flex_bvhadr;
|
||||
int* flex_bvhnum;
|
||||
mjtByte* flex_centered;
|
||||
mjtNum* flex_node;
|
||||
mjtNum* flex_radius;
|
||||
float* flex_rgba;
|
||||
float* flex_texcoord;
|
||||
|
||||
int* hfield_pathadr;
|
||||
|
||||
@@ -3178,11 +3221,12 @@ mjSpec* mj_makeSpec(void);
|
||||
mjSpec* mj_copySpec(const mjSpec* s);
|
||||
void mj_deleteSpec(mjSpec* s);
|
||||
int mjs_activatePlugin(mjSpec* s, const char* name);
|
||||
int mjs_setDeepCopy(mjSpec* s, int deepcopy);
|
||||
void mj_printFormattedModel(const mjModel* m, const char* filename, const char* float_format);
|
||||
void mj_printModel(const mjModel* m, const char* filename);
|
||||
void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
|
||||
void mj_printFormattedData(const mjModel* m, const mjData* d, const char* filename,
|
||||
const char* float_format);
|
||||
void mj_printData(const mjModel* m, mjData* d, const char* filename);
|
||||
void mj_printData(const mjModel* m, const mjData* d, const char* filename);
|
||||
void mju_printMat(const mjtNum* mat, int nr, int nc);
|
||||
void mju_printMatSparse(const mjtNum* mat, int nr,
|
||||
const int* rownnz, const int* rowadr, const int* colind);
|
||||
@@ -3217,7 +3261,8 @@ void mj_transmission(const mjModel* m, mjData* d);
|
||||
void mj_crb(const mjModel* m, mjData* d);
|
||||
void mj_factorM(const mjModel* m, mjData* d);
|
||||
void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
|
||||
void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
|
||||
void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y,
|
||||
const mjtNum* sqrtInvD, int n);
|
||||
void mj_comVel(const mjModel* m, mjData* d);
|
||||
void mj_passive(const mjModel* m, mjData* d);
|
||||
void mj_subtreeVel(const mjModel* m, mjData* d);
|
||||
@@ -3335,9 +3380,6 @@ void mjv_defaultOption(mjvOption* opt);
|
||||
void mjv_defaultFigure(mjvFigure* fig);
|
||||
void mjv_initGeom(mjvGeom* geom, int type, const mjtNum size[3],
|
||||
const mjtNum pos[3], const mjtNum mat[9], const float rgba[4]);
|
||||
void mjv_makeConnector(mjvGeom* geom, int type, mjtNum width,
|
||||
mjtNum a0, mjtNum a1, mjtNum a2,
|
||||
mjtNum b0, mjtNum b1, mjtNum b2);
|
||||
void mjv_connector(mjvGeom* geom, int type, mjtNum width,
|
||||
const mjtNum from[3], const mjtNum to[3]);
|
||||
void mjv_defaultScene(mjvScene* scn);
|
||||
@@ -3348,6 +3390,7 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
|
||||
int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt,
|
||||
const mjvPerturb* pert, mjvCamera* cam, int catmask,
|
||||
mjvScene* scn);
|
||||
void mjv_copyModel(mjModel* dest, const mjModel* src);
|
||||
void mjv_defaultSceneState(mjvSceneState* scnstate);
|
||||
void mjv_makeSceneState(const mjModel* m, const mjData* d,
|
||||
mjvSceneState* scnstate, int maxgeom);
|
||||
@@ -3429,8 +3472,6 @@ mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]);
|
||||
mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]);
|
||||
void mju_mulMatVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
|
||||
void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
|
||||
void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
|
||||
void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
|
||||
void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
|
||||
void mju_zero4(mjtNum res[4]);
|
||||
void mju_unit4(mjtNum res[4]);
|
||||
@@ -3467,6 +3508,8 @@ void mju_sqrMatTD(mjtNum* res, const mjtNum* mat, const mjtNum* diag, int nr, in
|
||||
void mju_transformSpatial(mjtNum res[6], const mjtNum vec[6], int flg_force,
|
||||
const mjtNum newpos[3], const mjtNum oldpos[3],
|
||||
const mjtNum rotnew2old[9]);
|
||||
int mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
|
||||
int* rownnz, int* rowadr, int* colind, int nnz);
|
||||
void mju_sparse2dense(mjtNum* res, const mjtNum* mat, int nr, int nc,
|
||||
const int* rownnz, const int* rowadr, const int* colind);
|
||||
void mju_rotVecQuat(mjtNum res[3], const mjtNum vec[3], const mjtNum quat[4]);
|
||||
@@ -3481,6 +3524,7 @@ void mju_mat2Quat(mjtNum quat[4], const mjtNum mat[9]);
|
||||
void mju_derivQuat(mjtNum res[4], const mjtNum quat[4], const mjtNum vel[3]);
|
||||
void mju_quatIntegrate(mjtNum quat[4], const mjtNum vel[3], mjtNum scale);
|
||||
void mju_quatZ2Vec(mjtNum quat[4], const mjtNum vec[3]);
|
||||
int mju_mat2Rot(mjtNum quat[4], const mjtNum mat[9]);
|
||||
void mju_euler2Quat(mjtNum quat[4], const mjtNum euler[3], const char* seq);
|
||||
void mju_mulPose(mjtNum posres[3], mjtNum quatres[4],
|
||||
const mjtNum pos1[3], const mjtNum quat1[4],
|
||||
@@ -3567,23 +3611,26 @@ mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
|
||||
const char* prefix, const char* suffix);
|
||||
mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
|
||||
const char* prefix, const char* suffix);
|
||||
mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
|
||||
const char* prefix, const char* suffix);
|
||||
int mjs_detachBody(mjSpec* s, mjsBody* b);
|
||||
mjsBody* mjs_addBody(mjsBody* body, mjsDefault* def);
|
||||
mjsSite* mjs_addSite(mjsBody* body, mjsDefault* def);
|
||||
mjsJoint* mjs_addJoint(mjsBody* body, mjsDefault* def);
|
||||
int mjs_detachDefault(mjSpec* s, mjsDefault* d);
|
||||
mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def);
|
||||
mjsSite* mjs_addSite(mjsBody* body, const mjsDefault* def);
|
||||
mjsJoint* mjs_addJoint(mjsBody* body, const mjsDefault* def);
|
||||
mjsJoint* mjs_addFreeJoint(mjsBody* body);
|
||||
mjsGeom* mjs_addGeom(mjsBody* body, mjsDefault* def);
|
||||
mjsCamera* mjs_addCamera(mjsBody* body, mjsDefault* def);
|
||||
mjsLight* mjs_addLight(mjsBody* body, mjsDefault* def);
|
||||
mjsGeom* mjs_addGeom(mjsBody* body, const mjsDefault* def);
|
||||
mjsCamera* mjs_addCamera(mjsBody* body, const mjsDefault* def);
|
||||
mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def);
|
||||
mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
|
||||
void mjs_delete(mjsElement* element);
|
||||
mjsActuator* mjs_addActuator(mjSpec* s, mjsDefault* def);
|
||||
int mjs_delete(mjsElement* element);
|
||||
mjsActuator* mjs_addActuator(mjSpec* s, const mjsDefault* def);
|
||||
mjsSensor* mjs_addSensor(mjSpec* s);
|
||||
mjsFlex* mjs_addFlex(mjSpec* s);
|
||||
mjsPair* mjs_addPair(mjSpec* s, mjsDefault* def);
|
||||
mjsPair* mjs_addPair(mjSpec* s, const mjsDefault* def);
|
||||
mjsExclude* mjs_addExclude(mjSpec* s);
|
||||
mjsEquality* mjs_addEquality(mjSpec* s, mjsDefault* def);
|
||||
mjsTendon* mjs_addTendon(mjSpec* s, mjsDefault* def);
|
||||
mjsEquality* mjs_addEquality(mjSpec* s, const mjsDefault* def);
|
||||
mjsTendon* mjs_addTendon(mjSpec* s, const mjsDefault* def);
|
||||
mjsWrap* mjs_wrapSite(mjsTendon* tendon, const char* name);
|
||||
mjsWrap* mjs_wrapGeom(mjsTendon* tendon, const char* name, const char* sidesite);
|
||||
mjsWrap* mjs_wrapJoint(mjsTendon* tendon, const char* name, double coef);
|
||||
@@ -3594,16 +3641,18 @@ mjsTuple* mjs_addTuple(mjSpec* s);
|
||||
mjsKey* mjs_addKey(mjSpec* s);
|
||||
mjsPlugin* mjs_addPlugin(mjSpec* s);
|
||||
mjsDefault* mjs_addDefault(mjSpec* s, const char* classname, const mjsDefault* parent);
|
||||
mjsMesh* mjs_addMesh(mjSpec* s, mjsDefault* def);
|
||||
mjsMesh* mjs_addMesh(mjSpec* s, const mjsDefault* def);
|
||||
mjsHField* mjs_addHField(mjSpec* s);
|
||||
mjsSkin* mjs_addSkin(mjSpec* s);
|
||||
mjsTexture* mjs_addTexture(mjSpec* s);
|
||||
mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def);
|
||||
mjsMaterial* mjs_addMaterial(mjSpec* s, const mjsDefault* def);
|
||||
mjSpec* mjs_getSpec(mjsElement* element);
|
||||
mjSpec* mjs_findSpec(mjSpec* spec, const char* name);
|
||||
mjsBody* mjs_findBody(mjSpec* s, const char* name);
|
||||
mjsElement* mjs_findElement(mjSpec* s, mjtObj type, const char* name);
|
||||
mjsBody* mjs_findChild(mjsBody* body, const char* name);
|
||||
mjsBody* mjs_getParent(mjsElement* element);
|
||||
mjsFrame* mjs_getFrame(mjsElement* element);
|
||||
mjsFrame* mjs_findFrame(mjSpec* s, const char* name);
|
||||
mjsDefault* mjs_getDefault(mjsElement* element);
|
||||
mjsDefault* mjs_findDefault(mjSpec* s, const char* classname);
|
||||
@@ -3626,11 +3675,14 @@ void mjs_setDouble(mjDoubleVec* dest, const double* array, int size);
|
||||
void mjs_setPluginAttributes(mjsPlugin* plugin, void* attributes);
|
||||
const char* mjs_getString(const mjString* source);
|
||||
const double* mjs_getDouble(const mjDoubleVec* source, int* size);
|
||||
void mjs_setDefault(mjsElement* element, mjsDefault* def);
|
||||
void mjs_setDefault(mjsElement* element, const mjsDefault* def);
|
||||
void mjs_setFrame(mjsElement* dest, mjsFrame* frame);
|
||||
const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const char* sequence,
|
||||
const mjsOrientation* orientation);
|
||||
mjsFrame* mjs_bodyToFrame(mjsBody** body);
|
||||
void mjs_setUserValue(mjsElement* element, const char* key, const void* data);
|
||||
const void* mjs_getUserValue(mjsElement* element, const char* key);
|
||||
void mjs_deleteUserValue(mjsElement* element, const char* key);
|
||||
void mjs_defaultSpec(mjSpec* spec);
|
||||
void mjs_defaultOrientation(mjsOrientation* orient);
|
||||
void mjs_defaultBody(mjsBody* body);
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
const SRCS = [
|
||||
'xml/xml_api.cc',
|
||||
'user/user_api.cc',
|
||||
'user/user_init.c',
|
||||
'user/user_vfs.cc',
|
||||
'thread/thread_task.cc',
|
||||
'thread/thread_pool.cc',
|
||||
'render/render_context.c',
|
||||
'render/render_gl2.c',
|
||||
'render/render_gl3.c',
|
||||
'render/render_util.c',
|
||||
'engine/engine_derivative.c',
|
||||
'engine/engine_io.c',
|
||||
'engine/engine_sensor.c',
|
||||
'engine/engine_callback.c',
|
||||
'engine/engine_collision_driver.c',
|
||||
'engine/engine_core_constraint.c',
|
||||
'engine/engine_core_smooth.c',
|
||||
'engine/engine_derivative_fd.c',
|
||||
'engine/engine_forward.c',
|
||||
'engine/engine_inverse.c',
|
||||
'engine/engine_island.c',
|
||||
'engine/engine_name.c',
|
||||
'engine/engine_passive.c',
|
||||
'engine/engine_plugin.cc',
|
||||
'engine/engine_print.c',
|
||||
'engine/engine_ray.c',
|
||||
'engine/engine_setconst.c',
|
||||
'engine/engine_solver.c',
|
||||
'engine/engine_support.c',
|
||||
'engine/engine_util_blas.c',
|
||||
'engine/engine_util_container.c',
|
||||
'engine/engine_util_errmem.c',
|
||||
'engine/engine_util_misc.c',
|
||||
'engine/engine_util_solve.c',
|
||||
'engine/engine_util_spatial.c',
|
||||
'engine/engine_util_sparse.c',
|
||||
'engine/engine_vis_init.c',
|
||||
'engine/engine_vis_interact.c',
|
||||
'engine/engine_vis_state.c',
|
||||
'engine/engine_vis_visualize.c',
|
||||
'ui/ui_main.c',
|
||||
];
|
||||
|
||||
class LineNumbers {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
static fetch(src) {
|
||||
const url = `https://raw.githubusercontent.com/google-deepmind/mujoco/refs/heads/main/src/${src}`;
|
||||
const request = new XMLHttpRequest();
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onreadystatechange = () => {
|
||||
if (request.readyState === 4) {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText);
|
||||
} else {
|
||||
reject(request.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
request.open('GET', url, true);
|
||||
request.send();
|
||||
});
|
||||
}
|
||||
|
||||
fetchAll() {
|
||||
let requests = [];
|
||||
for (const src of SRCS) {
|
||||
requests.push(LineNumbers.fetch(src).then(contents => {
|
||||
this.processSrc(src, contents);
|
||||
}, reason => {/* swallow error */}));
|
||||
}
|
||||
Promise.all(requests).then(() => {
|
||||
const anchors = document.querySelectorAll('h3 a.reference.external');
|
||||
for (const anchor of anchors) {
|
||||
const url = anchor.getAttribute('href');
|
||||
if (url.startsWith('#')) {
|
||||
const key = url.substring(1);
|
||||
if (this.map.has(key)) {
|
||||
anchor.href = this.map.get(key);
|
||||
} else {
|
||||
console.log(`No line number found for ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
processSrc(src, contents) {
|
||||
const lines = contents.split('\n');
|
||||
const re = /^(const )?[a-zA-Z0-9_*]+\s(.+)\(.+[{,]$/;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].match(re)) {
|
||||
const key = lines[i].match(re)[2];
|
||||
this.map.set(key, `https://github.com/google-deepmind/mujoco/blob/main/src/${src}#L${i+1}`);
|
||||
}
|
||||
}
|
||||
|
||||
// edge cases
|
||||
if (src == 'user/user_api.cc') {
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('[[nodiscard]] int mj_recompile(')) {
|
||||
const key = 'mj_recompile';
|
||||
this.map.set(key, `https://github.com/google-deepmind/mujoco/blob/main/src/${src}#L${i+1}`);
|
||||
}
|
||||
}
|
||||
} else if (src == 'engine/engine_io.c') {
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('void mj_freeStack(')) {
|
||||
const key = 'mj_freeStack';
|
||||
this.map.set(key, `https://github.com/google-deepmind/mujoco/blob/main/src/${src}#L${i+1}`);
|
||||
} else if (lines[i].startsWith('void mj_markStack(')) {
|
||||
const key = 'mj_markStack';
|
||||
this.map.set(key, `https://github.com/google-deepmind/mujoco/blob/main/src/${src}#L${i+1}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
if (document.getElementById('fetchlines')) {
|
||||
let lines = new LineNumbers();
|
||||
lines.fetchAll();
|
||||
}
|
||||
};
|
||||
+65
-55
@@ -7,7 +7,7 @@ MuJoCo XLA (MJX)
|
||||
Starting with version 3.0.0, MuJoCo includes MuJoCo XLA (MJX) under the
|
||||
`mjx <https://github.com/google-deepmind/mujoco/tree/main/mjx>`__ directory. MJX allows MuJoCo to run on compute
|
||||
hardware supported by the `XLA <https://www.tensorflow.org/xla>`__ compiler via the
|
||||
`JAX <https://github.com/google/jax#readme>`__ framework. MJX runs on a
|
||||
`JAX <https://github.com/jax-ml/jax#readme>`__ framework. MJX runs on a
|
||||
`all platforms supported by JAX <https://jax.readthedocs.io/en/latest/installation.html#supported-platforms>`__: Nvidia
|
||||
and AMD GPUs, Apple Silicon, and `Google Cloud TPUs <https://cloud.google.com/tpu>`__.
|
||||
|
||||
@@ -50,7 +50,7 @@ The recommended way to install this package is via `PyPI <https://pypi.org/proje
|
||||
|
||||
pip install mujoco-mjx
|
||||
|
||||
A copy of the MuJoCo library is provided as part of this package's depdendencies and does **not** need to be downloaded
|
||||
A copy of the MuJoCo library is provided as part of this package's dependencies and does **not** need to be downloaded
|
||||
or installed separately.
|
||||
|
||||
.. _MjxUsage:
|
||||
@@ -66,9 +66,9 @@ directly from the top-level ``mjx`` module.
|
||||
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``
|
||||
functions. Placing an :ref:`mjModel` on device yields an ``mjx.Model``. Placing an :ref:`mjData` on device yields
|
||||
an ``mjx.Data``:
|
||||
Before running MJX functions on an accelerator device, structs must be copied onto the device via the ``mjx.put_model``
|
||||
and ``mjx.put_data`` functions. Placing an :ref:`mjModel` on device yields an ``mjx.Model``. Placing an :ref:`mjData` on
|
||||
device yields an ``mjx.Data``:
|
||||
|
||||
.. 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
|
||||
(in the case of ``mjx.Data``).
|
||||
#. 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,
|
||||
``jnt_limited`` is a numpy array passed by reference from :ref:`mjModel`, which determines if joint limit
|
||||
constraints should be applied. If ``jnt_limited`` is modified, JAX will
|
||||
re-compile MJX functions.
|
||||
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.
|
||||
Modifying these arrays will force JAX to recompile MJX functions. As an example, ``jnt_limited`` is a numpy array
|
||||
passed by reference from :ref:`mjModel`, which determines if joint limit constraints should be applied. If
|
||||
``jnt_limited`` is modified, JAX will re-compile MJX functions. 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
|
||||
@@ -110,9 +108,9 @@ Using ``mjx.make_data`` may be preferable when constructing batched ``mjx.Data``
|
||||
Functions
|
||||
---------
|
||||
|
||||
MuJoCo functions are exposed as MJX functions of the same name, but following
|
||||
`PEP 8 <https://peps.python.org/pep-0008/>`__-compliant names. Most of the :ref:`main simulation <Mainsimulation>` and
|
||||
some of the :ref:`sub-components <Subcomponents>` for forward simulation are available from the top-level ``mjx`` module.
|
||||
MuJoCo functions are exposed as MJX functions of the same name, but following `PEP 8
|
||||
<https://peps.python.org/pep-0008/>`__-compliant names. Most of the :ref:`main simulation <Mainsimulation>` and some of
|
||||
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
|
||||
leave it to the user to JIT MJX functions, or JIT their own functions that reference MJX functions. See the
|
||||
@@ -165,6 +163,30 @@ Minimal example
|
||||
pos = jax.jit(batched_step)(vel)
|
||||
print(pos)
|
||||
|
||||
.. _MjxCli:
|
||||
|
||||
Helpful Command Line Scripts
|
||||
----------------------------
|
||||
|
||||
We provide two command line scripts with the ``mujoco-mjx`` package:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mjx-testspeed --mjcf=/PATH/TO/MJCF/ --base_path=.
|
||||
|
||||
This command takes in a path to an MJCF file along with optional arguments (use ``--help`` for more information)
|
||||
and computes helpful metrics for performance tuning. The command will output, among other things, the total
|
||||
simulation time, the total steps per second and the total realtime factor (here total is across all available
|
||||
devices).
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mjx-viewer --help
|
||||
|
||||
This command launches the MJX model in the simulate viewer, allowing you to visualize and interact with the model.
|
||||
Note this steps the simulation using MJX physics (not C MuJoCo) so it can be helpful for example for debugging
|
||||
solver parameters.
|
||||
|
||||
.. _MjxFeatureParity:
|
||||
|
||||
Feature Parity
|
||||
@@ -190,18 +212,19 @@ The following features are **fully supported** in MJX:
|
||||
* - :ref:`Transmission <mjtTrn>`
|
||||
- ``JOINT``, ``JOINTINPARENT``, ``SITE``, ``TENDON``
|
||||
* - :ref:`Actuator Dynamics <mjtDyn>`
|
||||
- ``NONE``, ``INTEGRATOR``, ``FILTER``, ``FILTEREXACT``
|
||||
- ``NONE``, ``INTEGRATOR``, ``FILTER``, ``FILTEREXACT``, ``MUSCLE``
|
||||
* - :ref:`Actuator Gain <mjtGain>`
|
||||
- ``FIXED``, ``AFFINE``
|
||||
- ``FIXED``, ``AFFINE``, ``MUSCLE``
|
||||
* - :ref:`Actuator Bias <mjtBias>`
|
||||
- ``NONE``, ``AFFINE``
|
||||
- ``NONE``, ``AFFINE``, ``MUSCLE``
|
||||
* - :ref:`Tendon Wrapping <mjtWrap>`
|
||||
- ``JOINT``, ``SITE``, ``PULLEY``
|
||||
- ``JOINT``, ``SITE``, ``PULLEY``, ``SPHERE``, ``CYLINDER``
|
||||
* - :ref:`Geom <mjtGeom>`
|
||||
- ``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.
|
||||
* - :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>`
|
||||
- ``CONNECT``, ``WELD``, ``JOINT``, ``TENDON``
|
||||
* - :ref:`Integrator <mjtIntegrator>`
|
||||
@@ -209,19 +232,20 @@ The following features are **fully supported** in MJX:
|
||||
* - :ref:`Cone <mjtCone>`
|
||||
- ``PYRAMIDAL``, ``ELLIPTIC``
|
||||
* - :ref:`Condim <coContact>`
|
||||
- 1, 3, 4, 6
|
||||
- 1, 3, 4, 6 (1 is not supported with ``ELLIPTIC``)
|
||||
* - :ref:`Solver <mjtSolver>`
|
||||
- ``CG``, ``NEWTON``
|
||||
* - Fluid Model
|
||||
- :ref:`flInertia`
|
||||
* - :ref:`Tendons <tendon>`
|
||||
- :ref:`Fixed <tendon-fixed>`
|
||||
- :ref:`Fixed <tendon-fixed>`, :ref:`Spatial <tendon-spatial>`
|
||||
* - :ref:`Sensors <mjtSensor>`
|
||||
- ``MAGNETOMETER``, ``CAMPROJECTION``, ``RANGEFINDER``, ``JOINTPOS``, ``TENDONPOS``, ``ACTUATORPOS``, ``BALLQUAT``,
|
||||
``FRAMEPOS``, ``FRAMEXAXIS``, ``FRAMEYAXIS``, ``FRAMEZAXIS``, ``FRAMEQUAT``, ``SUBTREECOM``, ``CLOCK``,
|
||||
``VELOCIMETER``, ``GYRO``, ``JOINTVEL``, ``TENDONVEL``, ``ACTUATORVEL``, ``BALLANGVEL``, ``FRAMELINVEL``,
|
||||
``FRAMEANGVEL``, ``SUBTREELINVEL``, ``SUBTREEANGMOM``, ``TOUCH``, ``ACCELEROMETER``, ``FORCE``, ``TORQUE``,
|
||||
``ACTUATORFRC``, ``JOINTACTFRC``, ``FRAMELINACC``, ``FRAMEANGACC``.
|
||||
``ACTUATORFRC``, ``JOINTACTFRC``, ``FRAMELINACC``, ``FRAMEANGACC``
|
||||
(``ACCELEROMETER``, ``FORCE``, ``TORQUE`` not supported with connect or weld equality constraints)
|
||||
|
||||
The following features are **in development** and coming soon:
|
||||
|
||||
@@ -240,18 +264,8 @@ The following features are **in development** and coming soon:
|
||||
- ``IMPLICIT``
|
||||
* - Dynamics
|
||||
- :ref:`Inverse <mj_inverse>`
|
||||
* - :ref:`Actuator Dynamics <mjtDyn>`
|
||||
- ``MUSCLE``
|
||||
* - :ref:`Actuator Gain <mjtGain>`
|
||||
- ``MUSCLE``
|
||||
* - :ref:`Actuator Bias <mjtBias>`
|
||||
- ``MUSCLE``
|
||||
* - :ref:`Tendon Wrapping <mjtWrap>`
|
||||
- ``SPHERE``, ``CYLINDER``
|
||||
* - Fluid Model
|
||||
- :ref:`flEllipsoid`
|
||||
* - :ref:`Tendons <tendon>`
|
||||
- :ref:`Spatial <tendon-spatial>`
|
||||
* - :ref:`Sensors <mjtSensor>`
|
||||
- All except ``PLUGIN``, ``USER``
|
||||
* - Lights
|
||||
@@ -300,22 +314,19 @@ Single scene simulation
|
||||
carefully optimized for CPU. MJX works best when simulating thousands or tens of thousands of scenes in parallel.
|
||||
|
||||
Collisions between large meshes
|
||||
MJX supports collisions between convex mesh geometries. However the convex collision algorithms
|
||||
in MJX are implemented differently than in MuJoCo. MJX uses a branchless version of the
|
||||
`Separating Axis Test <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)
|
||||
algorithm as implemented in `libccd <https://github.com/danfis/libccd>`__.
|
||||
SAT works well for smaller meshes but suffers in both runtime and memory for larger meshes.
|
||||
MJX supports collisions between convex mesh geometries. However the convex collision algorithms in MJX are implemented
|
||||
differently than in MuJoCo. MJX uses a branchless version of the `Separating Axis Test
|
||||
<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 either MPR or GJK/EPA, see
|
||||
:ref:`Collision Detection<coChecking>` for more details. SAT works well for smaller meshes but suffers in both runtime
|
||||
and memory for larger meshes.
|
||||
|
||||
For
|
||||
collisions with convex meshes and primitives, the convex decompositon of the mesh should have
|
||||
roughly **200 vertices or less** for reasonable performance. For convex-convex collisions,
|
||||
the convex mesh should have roughly **fewer than 32 vertices**. We recommend using
|
||||
:ref:`maxhullvert<asset-mesh-maxhullvert>` in the MuJoCo compiler to achieve desired convex mesh properties.
|
||||
With careful
|
||||
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.
|
||||
For collisions with convex meshes and primitives, the convex decompositon of the mesh should have roughly **200
|
||||
vertices or less** for reasonable performance. For convex-convex collisions, the convex mesh should have roughly
|
||||
**fewer than 32 vertices**. We recommend using :ref:`maxhullvert<asset-mesh-maxhullvert>` in the MuJoCo compiler to
|
||||
achieve desired convex mesh properties. With careful 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
|
||||
Accelerators exhibit poor performance for
|
||||
@@ -377,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.
|
||||
|
||||
Broadphase
|
||||
While MuJoCo handles broadphase culling out of the box, MJX requires additional parameters. For an approximate version of
|
||||
broadphase, use the experimental custom numeric parameters
|
||||
``max_contact_points`` and ``max_geom_pairs``. ``max_contact_points`` caps the number of contact points
|
||||
sent to the solver for each condim type. ``max_geom_pairs`` caps the total number of geom-pairs sent to
|
||||
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 makes use of these parameters.
|
||||
While MuJoCo handles broadphase culling out of the box, MJX requires additional parameters. For an approximate version
|
||||
of broadphase, use the experimental custom numeric parameters ``max_contact_points`` and ``max_geom_pairs``.
|
||||
``max_contact_points`` caps the number of contact points sent to the solver for each condim type. ``max_geom_pairs``
|
||||
caps the total number of geom-pairs sent to 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
|
||||
makes use of these parameters.
|
||||
|
||||
GPU performance
|
||||
---------------
|
||||
@@ -393,4 +403,4 @@ The following environment variables should be set:
|
||||
``XLA_FLAGS=--xla_gpu_triton_gemm_any=true``
|
||||
This enables the Triton-based GEMM (matmul) emitter for any GEMM that it supports. This can yield a 30% speedup on
|
||||
NVIDIA GPUs. If you have multiple GPUs, you may also benefit from enabling flags related to
|
||||
`communciation between GPUs <https://jax.readthedocs.io/en/latest/gpu_performance_tips.html>`__.
|
||||
`communication between GPUs <https://jax.readthedocs.io/en/latest/gpu_performance_tips.html>`__.
|
||||
|
||||
+75
-205
@@ -136,21 +136,21 @@ the model. We start with an example.
|
||||
.. code-block:: xml
|
||||
|
||||
<mujoco>
|
||||
<default class="main">
|
||||
<geom rgba="1 0 0 1"/>
|
||||
<default class="sub">
|
||||
<geom rgba="0 1 0 1"/>
|
||||
</default>
|
||||
<default class="main">
|
||||
<geom rgba="1 0 0 1"/>
|
||||
<default class="sub">
|
||||
<geom rgba="0 1 0 1"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom type="box"/>
|
||||
<body childclass="sub">
|
||||
<geom type="ellipsoid"/>
|
||||
<geom type="sphere" rgba="0 0 1 1"/>
|
||||
<geom type="cylinder" class="main"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<worldbody>
|
||||
<geom type="box"/>
|
||||
<body childclass="sub">
|
||||
<geom type="ellipsoid"/>
|
||||
<geom type="sphere" rgba="0 0 1 1"/>
|
||||
<geom type="cylinder" class="main"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
This example will not actually compile because some required information is missing, but here we are only interested
|
||||
@@ -629,11 +629,12 @@ Group disable
|
||||
The :ref:`actuatorgroupdisable<option-actuatorgroupdisable>` attribute, which can be changed at runtime by setting the
|
||||
:ref:`mjOption.disableactuator<mjOption>` integer bitfield, allows the user to disable sets of actuators according to
|
||||
their :ref:`group<actuator-general-group>`. This feature is convenient when one would like to use multiple types of
|
||||
actuators for the same kinematic tree. For example consider a robot with firmware that supports mutiple control modes
|
||||
actuators for the same kinematic tree. For example consider a robot with firmware that supports multiple control modes
|
||||
e.g., torque-control and position-control. In this case, one can define both types of actuators in the same MJCF
|
||||
model, assigning one type of actuator to group 0 and the other to group 1.
|
||||
|
||||
.. youtube:: H9qG9Zf2W44
|
||||
:aspect: 2:1
|
||||
:align: right
|
||||
:width: 40%
|
||||
|
||||
@@ -1120,122 +1121,40 @@ Intrinsics
|
||||
Composite objects
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Composite objects are not new model elements. Instead, they are (large) collections of existing elements designed to
|
||||
simulate particle systems, ropes, cloth, and soft bodies. These collections are generated by the model compiler
|
||||
automatically. The user configures the automatic generator on a high level, using the new XML element
|
||||
:ref:`composite <body-composite>` and its attributes and sub-elements, as described in the XML reference
|
||||
chapter. If the compiled model is then saved, :el:`composite` is no longer present and is replaced with the collection
|
||||
of regular model elements that were automatically generated. So think of it as a macro that gets expanded by the model
|
||||
compiler.
|
||||
Composite objects are not new model elements. Instead, they are collections of existing element originally designed to
|
||||
simulate particle systems, ropes, cloth, and soft bodies. Over time, most of these types have been replaced by
|
||||
:ref:`replicate<replicate>` (for repeated objects) and :ref:`flexcomp<body-flexcomp>` (for soft objects). Therefore, the
|
||||
only supported composite type is now ``cable``, which produces an inextensible chain of bodies connected with ball
|
||||
joints.
|
||||
|
||||
Composite objects are made up of regular MuJoCo bodies, which we call "element bodies" in this context. The element
|
||||
bodies are created as children of the body within which :el:`composite` appears; thus a composite object appears in the
|
||||
same place in the XML where a regular child body may have been defined. Each automatically-generated element body has a
|
||||
single geom attached to it, usually a sphere but could also be a capsule or an ellipsoid. Thus the composite object is
|
||||
essentially a particle system, however the particles can be constrained to move together in ways that simulate various
|
||||
flexible objects. The initial positions of the element bodies form a regular grid in 1D, 2D or 3D. They could all be
|
||||
children of the parent body (which can be the world or another regular body; composite objects cannot be nested) and
|
||||
have joints allowing motion relative to the parent, or they could form a kinematic tree with joints between the element
|
||||
bodies. They can also be connected with tendons with soft equality constraints on the tendon length, creating the
|
||||
necessary coupling. Joint equality constraints are also used in some cases. The :at:`solref` and :at:`solimp` attributes
|
||||
of these equality constraints can be adjusted by the user, thereby adjusting the softness and flexibility of the
|
||||
composite objects.
|
||||
Composite objects are made up of regular MuJoCo bodies, which we call "element bodies" in this context. The collection
|
||||
of element bodies is generated by the model compiler automatically. The user configures the automatic generator on a
|
||||
high level, using the new XML element :ref:`composite <body-composite>` and its attributes and sub-elements, as
|
||||
described in the XML reference chapter. If the compiled model is then saved, :el:`composite` is no longer present and is
|
||||
replaced with the collection of regular model elements that were automatically generated. So think of it as a macro that
|
||||
gets expanded by the model compiler. The element bodies are created as children of the body within which :el:`composite`
|
||||
appears; thus a composite object appears in the same place in the XML where a regular child body may have been defined.
|
||||
Each automatically-generated element body has a single geom attached to it. We have designed the composite object
|
||||
generator to have intuitive high-level controls as much as possible, but at the same time it exposes a large number of
|
||||
options that interact with each other and can profoundly affect the resulting physics. So at some point users should
|
||||
read the :ref:`reference documentation <body-composite>` carefully.
|
||||
|
||||
In addition to setting up the physics, the composite object generator creates suitable rendering. 2D and 3D objects
|
||||
can be rendered as :ref:`skins <asset-skin>`. The skin is generated
|
||||
automatically, and can be textured as well as subdivided using bi-cubic interpolation. The actual physics and in
|
||||
particular the collision detection are based on the element bodies and their geoms, while the skin is purely a
|
||||
visualization object. Yet in most situations we prefer to look at the skin representation. To facilitate this, the
|
||||
generator places all geoms, sites and tendons in group 3 whose visualization is disabled by default. So when you load
|
||||
a 2D grid for example, you will see a continuous flexible surface and not a collection of spheres connected with
|
||||
tendons. However when fine-tuning the model and trying to understand the physics behind it, it is useful to be able to
|
||||
render the spheres and tendons. To switch the rendering style, disable the rendering of skins and enable group 3 for
|
||||
geoms and tendons.
|
||||
|
||||
We have designed the composite object generator to have intuitive high-level controls as much as possible, but at the
|
||||
same time it exposes a large number of options that interact with each other and can profoundly affect the resulting
|
||||
physics. So at some point users should read the :ref:`reference documentation <body-composite>` carefully.
|
||||
As a quick start though, MuJoCo comes with an example of each composite object type. Below we go over these
|
||||
examples and explain the less obvious aspects. In all examples we have a static scene which is included in the model,
|
||||
followed by a single composite object. The static scene has a mocap body (large capsule) that can be moved around with
|
||||
the mouse to probe the behavior of the system. The XML snippets below are just the definition of the composite object;
|
||||
see the XML model files in the distribution for the complete examples.
|
||||
|
||||
**Particle**.
|
||||
|
||||
|image4| |image5|
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<worldbody>
|
||||
<composite type="particle" count="10 10 10" spacing="0.07" offset="0 0 1">
|
||||
<geom size=".02" rgba=".8 .2 .1 1"/>
|
||||
</composite>
|
||||
</worldbody>
|
||||
|
||||
The above XML is all it takes to create a system with 1000 particles with initial positions on a 10-10-10 grid, and
|
||||
set the size, color, spacing and offset of the particles. The resulting element bodies become children of the world
|
||||
body. One could adjust many other properties including the softness of the contacts and the joint attributes. The plot
|
||||
on the right shows the joints. Each element body has 3 orthogonal slider joints, allowing it to translate but not
|
||||
rotate. The idea is that particles should have position but no orientation. MuJoCo bodies always have orientation,
|
||||
however by using only slider joints we do not allow the orientation to change. The geom defaults are adjusted
|
||||
automatically so that they make frictionless contacts with each other and with the rest of the model. So this system
|
||||
has 1000 bodies (each with a geom), 3000 degrees of freedom and around 1000 active contacts. Evaluating the dynamics
|
||||
takes around 1 ms on a single core of a modern processor. As with most other MuJoCo models, the soft constraints allow
|
||||
simulation at much larger timesteps (this model is stable at 30 ms timestep and even higher).
|
||||
|
||||
Particles are also compatible with the passive forces 2D and 3D plugins, discussed in the :ref:`deformable
|
||||
<CDeformable>` section. However, collisions are limited to the particle themselves and not to the whole boundary of the
|
||||
skin that encloses them. This makes contacts very fast but does not guarantee that all penetrations can be avoided. For
|
||||
a more complete treatment, see again the :ref:`deformable <CDeformable>` section, which outlines how to use
|
||||
:ref:`flexcomp<body-flexcomp>` to create such an object. It is easy to port models create with composite particles to
|
||||
flex, see the folder `elasticity/ <https://github.com/google-deepmind/mujoco/tree/main/model/plugin/elasticity>`__ for
|
||||
several examples.
|
||||
|
||||
**1D grid**.
|
||||
|
||||
|image6| |image7|
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<composite type="grid" count="20 1 1" spacing="0.045" offset="0 0 1">
|
||||
<joint kind="main" damping="0.001"/>
|
||||
<tendon kind="main" width="0.01"/>
|
||||
<geom size=".02" rgba=".8 .2 .1 1"/>
|
||||
<pin coord="1"/>
|
||||
<pin coord="13"/>
|
||||
</composite>
|
||||
|
||||
The grid type can create 1D or 2D grids, depending on the :at:`count` attribute. Here we illustrate 1D grids. These
|
||||
are strings of spheres connected with tendons whose length is soft-equality-constrained. The softness can be adjusted.
|
||||
Similar to particles, the element bodies here have slider joints but no rotational joints. The plot on the right
|
||||
illustrates pinning. The :el:`pin` sub-element is used to specify the grid coordinates of the pinned bodies, and the
|
||||
model compiler does not generate joints for these bodies, thereby fixing them rigidly to the parent body (in this case
|
||||
the world). This makes the string in the right plot hang in space. The same mechanism can be used to model a whip for
|
||||
example; in that case the parent body would be moving, and the first element body would be pinned to the parent.
|
||||
|
||||
**2D grid**.
|
||||
|
||||
|image8| |image9|
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<composite type="grid" count="9 9 1" spacing="0.05" offset="0 0 1">
|
||||
<skin material="matcarpet" inflate="0.001" subgrid="3" texcoord="true"/>
|
||||
<geom size=".02"/>
|
||||
<pin coord="0 0"/>
|
||||
<pin coord="8 0"/>
|
||||
</composite>
|
||||
|
||||
A 2D grid can be used to simulate cloth. What it really simulates is a 2D grid of spheres connected with
|
||||
equality-constrained tendons (not shown). The model compiler can also generate skin, enabled with the :el:`skin`
|
||||
sub-element in the above XML. Some of the element bodies can also be pinned, similar to 1D grids but using two grid
|
||||
coordinates. The plot on the right shows a cloth pinned to the world body at the two corners, and draping over our
|
||||
capsule probe. The skin on the right is subdivided using bi-cubic interpolation, which increases visual quality in the
|
||||
absence of textures. When textures are present (left) the benefits of subdivision are less visible.
|
||||
In addition to setting up the physics, the composite object generator creates suitable rendering. Objects can be
|
||||
rendered as :ref:`skins <asset-skin>`. The skin is generated automatically, and can be textured as well as subdivided
|
||||
using bi-cubic interpolation. The actual physics and in particular the collision detection are based on the element
|
||||
bodies and their geoms, while the skin is purely a visualization object. Yet in some situations we prefer to look at the
|
||||
skin representation, as in `this model
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/model/plugin/elasticity/belt.xml>`__, whose skin is a continuous
|
||||
flexible surface and not a collection of discontinuous thin boxes. However when fine-tuning the model and trying to
|
||||
understand the physics behind it, it is useful to be able to render the geoms. To switch the rendering style, disable
|
||||
the rendering of skins and enable group 3 for geoms and tendons.
|
||||
|
||||
**Cable**.
|
||||
|
||||
As a quick start, MuJoCo comes with an example of composite cables. In all examples we have a static scene which is
|
||||
included in the model, followed by a single composite object. The XML snippets below are just the definition of the
|
||||
composite object; see the XML model files in the distribution for the complete examples.
|
||||
|
||||
|coil|
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -1269,6 +1188,16 @@ stiffnesses can be set independently. Moreover, it is possible to specify if the
|
||||
curve, such as in the case of coil springs. The cable requires using a first-party :ref:`engine plugin<exPlugin>`, which
|
||||
may be integrated directly into the engine in the future.
|
||||
|
||||
**Particle**.
|
||||
|
||||
The particle type is deprecated. It is recommended to use the more generic :ref:`replicate<replicate>` instead, for
|
||||
example `this model <https://github.com/google-deepmind/mujoco/blob/main/model/replicate/particle.xml>`__.
|
||||
|
||||
**Grid**.
|
||||
|
||||
The grid composite type has been removed. It is recommended to use 2D flex :ref:`deformable objects <CDeformable>` for
|
||||
simulating thin elastic structures.
|
||||
|
||||
**Rope and loop**.
|
||||
|
||||
The rope and loop are deprecated. It is recommended to use the cable for simulating inextensible elastic rods that are
|
||||
@@ -1280,62 +1209,11 @@ scenario (e.g. a stretched rubber band).
|
||||
The cloth is deprecated. It is recommended to use 2D flex :ref:`deformable objects <CDeformable>` for simulating thin
|
||||
elastic structures.
|
||||
|
||||
**Box**.
|
||||
**Box, cylinder and ellipsoid**.
|
||||
|
||||
|image14| |image15|
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<body pos="0 0 1">
|
||||
<freejoint/>
|
||||
<composite type="box" count="7 7 7" spacing="0.04">
|
||||
<skin texcoord="true" material="matsponge" rgba=".7 .7 .7 1"/>
|
||||
<geom type="capsule" size=".015 0.05" rgba=".8 .2 .1 1"/>
|
||||
</composite>
|
||||
</body>
|
||||
|
||||
The box type, as well as the cylinder and ellipsoid types below, are used to model soft 3D objects. The element bodies
|
||||
form a grid along the outer shell, thus the number of element bodies scales with the square of the linear dimension.
|
||||
This is much more efficient than simulating a 3D grid. The parent body within which :el:`composite` appears is at the
|
||||
center of the soft object. All element bodies are children of the parent. Each element body has a single sliding joint
|
||||
pointing away from the parent. These joints allow the surface of the soft object to compress and expand at any point.
|
||||
The joints are equality-constrained to their initial position, so as to maintain the shape. In addition each joint is
|
||||
equality-constrained to its neighbor joints, so that when the soft objects deforms, the deformation is smooth.
|
||||
Finally, there is a tendon equality constraint specifying that the sum of all joints should remain constant. This
|
||||
attempts to preserve the volume of the soft object approximately. If the object is squeezed from all sides it will
|
||||
compress and the volume will decrease, but otherwise some element bodies will stick out to compensate for squeezing
|
||||
elsewhere. The plot on the left shows this effect; we are using the capsule probe to compress one corner, and the
|
||||
opposite sides of the cube expand a bit, while the deformations remain smooth. The :at:`count` attribute determines
|
||||
the number of element bodies in each dimension, so if the counts are different the resulting object will be a
|
||||
rectangular box and not a cube. The geoms attached to the element bodies can be spheres, capsules or ellipsoids.
|
||||
Spheres are faster for collision detection, but they result in a thin shell, allowing other bodies to "get under the
|
||||
skin" of the soft object. When capsules or ellipsoids are used, they are automatically oriented so that the long axis
|
||||
points to the outside, thus creating a thicker shell which is harder to penetrate.
|
||||
|
||||
**Cylinder and ellipsoid**.
|
||||
|
||||
|image16| |image17|
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<body pos="0 0 1">
|
||||
<freejoint/>
|
||||
<composite type="ellipsoid" count="5 7 9" spacing="0.05">
|
||||
<skin texcoord="true" material="matsponge" rgba=".7 .7 .7 1"/>
|
||||
<geom type="capsule" size=".015 0.05" rgba=".8 .2 .1 1"/>
|
||||
</composite>
|
||||
</body>
|
||||
|
||||
Cylinders and ellipsoids are created in the same way as boxes. The only difference is that the reference positions of
|
||||
the element bodies (relative to the parent) are projected on a cylinder or ellipsoid, with size implied by the
|
||||
:at:`count` attribute. The automatic skin generator is aware of the smooth surfaces, and adjusts the skin normals
|
||||
accordingly. In the plots we have used the capsule probe to press on each body, then paused the simulation and moved the
|
||||
probe away (which is possible because the probe is a mocap body which can move independent of the physics). In this way
|
||||
we can see the indentation made by the probe, and the resulting deformation in the rest of the body. By changing the
|
||||
solref and solimp attributes of the equality constraints that hold the soft object together, one can adjust the behavior
|
||||
of the system making it softer or harder, damped or springy, etc. Note that box, cylinder and ellipsoid objects do not
|
||||
involve long kinematic chains, and can be simulated at large timesteps -- similar to particle and grid, and unlike rope
|
||||
and cloth.
|
||||
The box type, as well as the cylinder and ellipsoid types, are now deprecated in favor of 3D flex :ref:`deformable
|
||||
objects <CDeformable>`. element.
|
||||
|
||||
.. _CDeformable:
|
||||
|
||||
@@ -1386,9 +1264,7 @@ improved realism and accuracy. The edge-based model could be seen as a "lumped"
|
||||
coupling of deformation modes (e.g. shear and volumetric) is averaged in a single quantity. The continuum model enables
|
||||
instead to specify shear and volumetic stiffnesses separately using the `Poisson's ratio
|
||||
<https://en.wikipedia.org/wiki/Poisson%27s_ratio>`__ of the material. For more details, see the `Saint Venant-Kirchhoff
|
||||
<https://en.wikipedia.org/wiki/Hyperelastic_material#Saint_Venant%E2%80%93Kirchhoff_model>`__ hyperelastic model. This
|
||||
functionality is currently based on first-party :ref:`engine plugins<exPlugin>` as of MuJoCo 3.0 but may be integrated
|
||||
into the engine in future releases.
|
||||
<https://en.wikipedia.org/wiki/Hyperelastic_material#Saint_Venant%E2%80%93Kirchhoff_model>`__ hyperelastic model.
|
||||
|
||||
**Creation and visualization**.
|
||||
|
||||
@@ -1396,20 +1272,12 @@ into the engine in future releases.
|
||||
|
||||
<option timestep=".001"/>
|
||||
|
||||
<extension>
|
||||
<plugin plugin="mujoco.elasticity.solid"/>
|
||||
</extension>
|
||||
|
||||
<worldbody>
|
||||
<flexcomp type="grid" count="24 4 4" spacing=".1 .1 .1" pos=".1 0 1.5"
|
||||
radius=".0" rgba="0 .7 .7 1" name="softbody" dim="3" mass="7">
|
||||
<contact condim="3" solref="0.01 1" solimp=".95 .99 .0001" selfcollide="none"/>
|
||||
<edge damping="1"/>
|
||||
<plugin plugin="mujoco.elasticity.solid">
|
||||
<config key="poisson" value="0.2"/>
|
||||
<!--Units are in Pa (SI)-->
|
||||
<config key="young" value="5e4"/>
|
||||
</plugin>
|
||||
<elasticity poisson="0.2" young="5e4">
|
||||
</flexcomp>
|
||||
</worldbody>
|
||||
|
||||
@@ -1503,7 +1371,7 @@ elements available in MuJoCo. In addition to standard URDF files, MuJoCo can loa
|
||||
viewpoint of URDF) :el:`mujoco` element as a child of the top-level element :el:`robot`. This custom element can have
|
||||
sub-elements :ref:`compiler <compiler>`, :ref:`option <option>`,
|
||||
:ref:`size <size>` with the same functionality as in MJCF, except that the default compiler settings
|
||||
are modified so as to accomodate the URDF modeling convention. The :ref:`compiler <compiler>` extension
|
||||
are modified so as to accommodate the URDF modeling convention. The :ref:`compiler <compiler>` extension
|
||||
in particular has proven very useful, and indeed several of its attributes were introduced because a number of
|
||||
existing URDF models have non-physical dynamics parameters which MuJoCo's built-in compiler will reject if left
|
||||
unmodified. This extension is also needed to specify mesh directories. Also note that the compiler attributes
|
||||
@@ -1554,15 +1422,15 @@ mocap bodies around:
|
||||
|
||||
|particle|
|
||||
|
||||
The key thing to understand about mocap bodies is that the simulator treats them as being fixed. We are causing them
|
||||
to move from one simulation time step to the next by updating their position and orientation directly, but as far as
|
||||
the physics model is concerned their position and orientation are constant. So what happens if we make contact with a
|
||||
regular dynamic body, as in the composite object examples provided with the MuJoCo distribution (recall that in
|
||||
those example we have a capsule probe which is a mocap body that we move with the mouse). A contact between two
|
||||
regular bodies will experience penetration as well as relative velocity, while contact with a mocap body is missing
|
||||
the relative velocity component because the simulator does not know that the mocap body itself is moving. So the
|
||||
resulting contact force is smaller and it takes longer for the contact to push the dynamic object away. Also, in more
|
||||
complex simulations the fact that we are doing something inconsistent with the physics can cause instabilities.
|
||||
The key thing to understand about mocap bodies is that the simulator treats them as being fixed. We are causing them to
|
||||
move from one simulation time step to the next by updating their position and orientation directly, but as far as the
|
||||
physics model is concerned their position and orientation are constant. So what happens if we make contact with a
|
||||
regular dynamic body, as in the particle examples provided with the MuJoCo distribution (recall that in those example we
|
||||
have a capsule probe which is a mocap body that we move with the mouse). A contact between two regular bodies will
|
||||
experience penetration as well as relative velocity, while contact with a mocap body is missing the relative velocity
|
||||
component because the simulator does not know that the mocap body itself is moving. So the resulting contact force is
|
||||
smaller and it takes longer for the contact to push the dynamic object away. Also, in more complex simulations the fact
|
||||
that we are doing something inconsistent with the physics can cause instabilities.
|
||||
|
||||
There is however a better-behaved alternative. In addition to the mocap body, we include a second regular body and
|
||||
connect it to the mocap body with a weld equality constraint. In the plots below, the pink box is the mocap body and
|
||||
@@ -1658,8 +1526,8 @@ dedicated section :ref:`therein<MjxPerformance>`.
|
||||
:ref:`Numerical Integration<geIntegration>` section. The default recommended choice is the ``implicitfast``
|
||||
integrator.
|
||||
3. :ref:`Constraint Jacobians<option-jacobian>`: Try switching the Jacobian setting between "dense" and "sparse". These
|
||||
two options use seperate code paths using dense or sparse algebra, but are otherwise compationally identical, so the
|
||||
faster one is always preferred. The default "auto" heuristic does not always make the right choice.
|
||||
two options use separate code paths using dense or sparse algebra, but are otherwise computationally identical, so
|
||||
the faster one is always preferred. The default "auto" heuristic does not always make the right choice.
|
||||
4. **Constraint solver:** If the profiler reports that a large chunk of time is spent in the solver, consider the
|
||||
following:
|
||||
|
||||
@@ -1668,8 +1536,8 @@ dedicated section :ref:`therein<MjxPerformance>`.
|
||||
freedom than constraints, the PGS solver will be fastest, though this situation is not common.
|
||||
- :ref:`iterations<option-iterations>` and :ref:`tolerance<option-tolerance>`: Try reducing the number of iterations
|
||||
or, equivalently, increasing the solver's termination tolerance. In particular for the Newton solver, which
|
||||
typically acheives numerical convergence in 2-3 (expensive) iterations, the last iteration increases the precision
|
||||
to a level that has no noticable effect, and can be skipped.
|
||||
typically achieves numerical convergence in 2-3 (expensive) iterations, the last iteration increases the precision
|
||||
to a level that has no noticeable effect, and can be skipped.
|
||||
5. **Collisions:** If the profiler reports that collision detection takes up a large chunk of the computation
|
||||
time, consider the following steps:
|
||||
|
||||
@@ -1723,6 +1591,8 @@ better visualize and understand the contact configuration and resulting forces.
|
||||
geometry (e.g., bumps), so slippage is prevented by the normal force and not only frictional components.
|
||||
b. If contacts are between flat surfaces, try enabling the :ref:`multiccd<option-flag-multiccd>` flag, which allows
|
||||
the detector to find more contacts than the single contact returned by the convex-convex collider.
|
||||
c. Try enabling the native collision detection pipeline by setting the :ref:`nativeccd<option-flag-nativeccd>` flag,
|
||||
which uses a more accurate and efficient convex collision detection algorithm.
|
||||
|
||||
**High-frequency vibration**
|
||||
High-frequency, low-amplitude vibrations are also a real-world problem in many industrial settings, but unlike in
|
||||
|
||||
+2
-2
@@ -591,7 +591,7 @@ Flex
|
||||
Flexes were added in MuJoCo 3.0. They represent deformable meshes that can be 1, 2 or 3 dimensional (thus their elements
|
||||
are capsules, triangles or tetrahedra). Unlike geoms which are static shapes attached rigidly to a single body, the
|
||||
elements of a flex are deformable: they are constructed by connecting multiple bodies, thus the body positions and
|
||||
orientations determine the shape of the flex elements at runtime. These deformable elements suport collisions and
|
||||
orientations determine the shape of the flex elements at runtime. These deformable elements support collisions and
|
||||
contact forces, as well as generate passive and constraint forces which softly preserve the shape of the deformable
|
||||
entity. Automation is provided to load a mesh from a file, construct bodies corresponding to the mesh vertices,
|
||||
construct flex elements corresponding to the mesh faces (or lines or tetrahedra, depending on dimensionality), and
|
||||
@@ -939,7 +939,7 @@ positions and orientations as well as the linear and angular velocities of such
|
||||
|
||||
The semantics of free joints are as follows. The position data is 7 numbers (3D position followed
|
||||
by unit quaternion) while the velocity data is 6 numbers (3D linear velocity followed by 3D angular velocity).
|
||||
The linear postions of free joints are in the global frame, as are
|
||||
The linear positions of free joints are in the global frame, as are
|
||||
linear velocities. The orientation of a free joint (the quaternion) is also in the global frame. However, the rotational
|
||||
velocities of a free joint are in the local body frame. This is not so much a design decision but rather correct
|
||||
use of the topology of quaternions. Angular velocities live in the quaternion tangent space, which is defined locally
|
||||
|
||||
@@ -201,22 +201,22 @@ the ``copy`` callback from :ref:`mjpPlugin` for each plugin instance present.
|
||||
|
||||
.. _exActuatorAct:
|
||||
|
||||
Actuator activations
|
||||
""""""""""""""""""""
|
||||
Actuator states
|
||||
"""""""""""""""
|
||||
|
||||
When writing stateful actuator plugins, there are two choices for where to save the actuator state. One option is using
|
||||
``plugin_state`` as described above, and the other is to use ``mjData.act`` by implementing the ``actuator_actdim`` and
|
||||
``actuator_act_dot`` callbacks on :ref:`mjpPlugin`.
|
||||
``plugin_state`` as described above, and the other is to use ``mjData.act`` by implementing the callback on
|
||||
:ref:`mjpPlugin`.
|
||||
|
||||
When using the latter option, the actuator plugin's state will be added to ``mjData.act``, and MuJoCo will
|
||||
automatically integrate ``mjData.act_dot`` values between timesteps. One advantage of this approach is that
|
||||
finite-differencing functions like :ref:`mjd_transitionFD` will work as they do for native actuators. The
|
||||
``mjpPlugin.advance`` callback will be called after ``act_dot`` is integrated, and actuator plugins may overwrite
|
||||
the ``act`` values at that point, if Euler integration isn't appropriate.
|
||||
the ``act`` values at that point, if the built-in integrator is not appropriate.
|
||||
|
||||
Users may specify the :ref:`dyntype<actuator-plugin-dyntype>` attribute on actuator plugins, to introduce a filter or
|
||||
an integrator between user inputs and actuator activations. When they do, the activation variable introduced by
|
||||
``dyntype`` will be placed *after* the plugin's activation variables in the ``act`` array.
|
||||
an integrator between user inputs and actuator states. When they do, the state variable introduced by
|
||||
``dyntype`` will be placed *after* the plugin's state variables in the ``act`` array.
|
||||
|
||||
.. _exRegistration:
|
||||
|
||||
@@ -269,43 +269,58 @@ A future version of this section will include:
|
||||
|
||||
There are several first-party plugin directories:
|
||||
|
||||
* **actuator:** The plugins in the `actuator/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/actuator>`__
|
||||
directory implement custom actuators, so far only a PID controller. See the
|
||||
`README <https://github.com/google-deepmind/mujoco/blob/main/plugin/actuator/README.md>`__ for details.
|
||||
* **elasticity:** The plugins in the `elasticity/
|
||||
<https://github.com/google-deepmind/mujoco/tree/main/plugin/elasticity>`__ directory are passive forces based on
|
||||
continuum mechanics for 1-dimensional and 2-dimensional bodies. The 1D model is invariant under rotations and captures
|
||||
the large deformation of elastic cables, decoupling twisting and bending strains. The 2D model is a suitable for
|
||||
computing the bending stiffness of thin elastic plates (i.e. shells having a flat stress-free configuration). In this
|
||||
case, the elastic energy is quadratic and therefore the stiffness matrix is constant. For more information, please see
|
||||
the `README <https://github.com/google-deepmind/mujoco/blob/main/plugin/elasticity/README.md>`__.
|
||||
* **sensor:** The plugins in the `sensor/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/sensor>`__
|
||||
directory implement 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.
|
||||
* **sdf:** The plugins in the `sdf/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/sdf>`__ directory
|
||||
specify custom shapes in a mesh-free manner, by defining methods computing a signed distance field and its gradient at
|
||||
query points. This shape then acts as a new geom type in the collision table at the top of `engine_collision_driver.c
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__. For more information
|
||||
concerning the available SDFs and how to write your own implicit geometry, please see the `README
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/plugin/sdf/README.md>`__. The rest of this section will give more
|
||||
detail concerning the collision algorithm and the plugin engine interface.
|
||||
actuator
|
||||
""""""""
|
||||
The plugins in the `actuator/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/actuator>`__ directory
|
||||
implement custom actuators, so far only a PID controller. See the `README
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/plugin/actuator/README.md>`__ for details.
|
||||
|
||||
Collision points are found by minimizing the function A + B + abs(max(A, B)), where A and B are the two colliding
|
||||
SDFs, via gradient descent. Because SDFs are non-convex, multiple starting points are required in order to converge to
|
||||
multiple local minima. The number of starting points is set using :ref:`sdf_initpoints<option-sdf_initpoints>`, and
|
||||
are initialized using the Halton sequence inside the intersection of the axis-aligned bounding boxes. The number of
|
||||
gradient descent iterations is set using :ref:`sdf_iterations<option-sdf_iterations>`.
|
||||
|
||||
While *exact* SDFs---encoding the precise signed distance to the surface---are preferred, collisions are possible with
|
||||
any function whose value vanishes at the surface and grows monotonically away from it, with a negative sign in the
|
||||
interior. For such functions, it is still possible to find collisons, albeit with a possibly
|
||||
increased number of starting points.
|
||||
elasticity
|
||||
""""""""""
|
||||
The plugins in the `elasticity/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/elasticity>`__ directory are
|
||||
passive forces based on continuum mechanics for 1-dimensional and 2-dimensional bodies. The 1D model is invariant under
|
||||
rotations and captures the large deformation of elastic cables, decoupling twisting and bending strains. The 2D model is
|
||||
a suitable for computing the bending stiffness of thin elastic plates (i.e. shells having a flat stress-free
|
||||
configuration). In this case, the elastic energy is quadratic and therefore the stiffness matrix is constant. For more
|
||||
information, please see the `README
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/plugin/elasticity/README.md>`__.
|
||||
|
||||
The ``sdf_distance`` method is called by the compiler to produce a visual mesh for rendering using the marching cubes
|
||||
algorithm implemented by `MarchingCubeCpp <https://github.com/aparis69/MarchingCubeCpp>`__.
|
||||
|
||||
Future improvement to the gradient descent algorithm, such as a line search which takes advantage of the properties of
|
||||
SDFs, might reduce the number of iterations and/or starting points.
|
||||
sensor
|
||||
""""""
|
||||
The plugins in the `sensor/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/sensor>`__ directory implement
|
||||
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.
|
||||
|
||||
.. _exSDF:
|
||||
|
||||
sdf
|
||||
"""
|
||||
The plugins in the `sdf/ <https://github.com/google-deepmind/mujoco/tree/main/plugin/sdf>`__ directory
|
||||
specify custom shapes in a mesh-free manner, by defining methods computing a signed distance field and its gradient at
|
||||
query points. This shape then acts as a new geom type in the collision table at the top of `engine_collision_driver.c
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__. For more information
|
||||
concerning the available SDFs and how to write your own implicit geometry, please see the `README
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/plugin/sdf/README.md>`__. The rest of this section will give more
|
||||
detail concerning the collision algorithm and the plugin engine interface.
|
||||
|
||||
Collision points are found by minimizing the function A + B + abs(max(A, B)), where A and B are the two colliding
|
||||
SDFs, via gradient descent. Because SDFs are non-convex, multiple starting points are required in order to converge to
|
||||
multiple local minima. The number of starting points is set using :ref:`sdf_initpoints<option-sdf_initpoints>`, and
|
||||
are initialized using the Halton sequence inside the intersection of the axis-aligned bounding boxes. The number of
|
||||
gradient descent iterations is set using :ref:`sdf_iterations<option-sdf_iterations>`.
|
||||
|
||||
While *exact* SDFs---encoding the precise signed distance to the surface---are preferred, collisions are possible with
|
||||
any function whose value vanishes at the surface and grows monotonically away from it, with a negative sign in the
|
||||
interior. For such functions, it is still possible to find collisions, albeit with a possibly
|
||||
increased number of starting points.
|
||||
|
||||
The ``sdf_distance`` method is called by the compiler to produce a visual mesh for rendering using the marching cubes
|
||||
algorithm implemented by `MarchingCubeCpp <https://github.com/aparis69/MarchingCubeCpp>`__.
|
||||
|
||||
Future improvement to the gradient descent algorithm, such as a line search which takes advantage of the properties of
|
||||
SDFs, might reduce the number of iterations and/or starting points.
|
||||
|
||||
For the sdf plugin, the following methods need to be specified
|
||||
|
||||
@@ -317,7 +332,7 @@ For the sdf plugin, the following methods need to be specified
|
||||
required because mesh creation occurs during model compilation before the plugin object has been instantiated.
|
||||
|
||||
``sdf_gradient``:
|
||||
Computes the gradient in local coodinates of the SDF at the query point.
|
||||
Computes the gradient in local coordinates of the SDF at the query point.
|
||||
|
||||
``sdf_aabb``:
|
||||
Computes the axis-aligned bounding box in local coordinates. This volume is voxelized uniformly before the call to
|
||||
@@ -378,7 +393,7 @@ Resource providers work via callbacks:
|
||||
resource name. For example, the resource name ``http://www.example.com/myasset.obj`` would have
|
||||
``http://www.example.com/`` as its directory.
|
||||
- :ref:`mjfResourceModified<mjfResourceModified>`: This callback is optional and is used to check if an existing
|
||||
opened resource has been modifed from its orginal source.
|
||||
opened resource has been modified from its original source.
|
||||
|
||||
.. _exProviderUsage:
|
||||
|
||||
|
||||
@@ -94,8 +94,8 @@ Building from source
|
||||
|
||||
To build MuJoCo from source, you will need CMake and a working C++17 compiler installed. The steps are:
|
||||
|
||||
#. Clone the ``mujoco`` repository from GitHub.
|
||||
#. Create a new build directory somewhere, and ``cd`` into it.
|
||||
#. Clone the ``mujoco`` repository: ``git clone https://github.com/deepmind/mujoco.git``
|
||||
#. Create a new build directory and ``cd`` into it.
|
||||
#. Run :shell:`cmake $PATH_TO_CLONED_REPO` to configure the build.
|
||||
#. Run ``cmake --build .`` to build.
|
||||
|
||||
@@ -120,6 +120,19 @@ installed (see :github:issue:`862` for more details).
|
||||
`continuous integration setup <https://github.com/google-deepmind/mujoco/blob/main/.github/workflows/build.yml>`_ on
|
||||
GitHub.
|
||||
|
||||
.. _inBuildDocs:
|
||||
|
||||
Building the docs
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you wish to build the documentation locally, for example to test pull-requests that improve it, do:
|
||||
|
||||
1. Clone the ``mujoco`` repository: ``git clone https://github.com/deepmind/mujoco.git``
|
||||
2. Go to the ``doc/`` directory: ``cd mujoco/doc``
|
||||
3. Install the dependencies: ``pip install -r requirements.txt``
|
||||
4. Build the HTML: ``make html``
|
||||
5. Open ``_build/html/index.html`` in your browser of choice.
|
||||
|
||||
.. _inHeader:
|
||||
|
||||
Header files
|
||||
@@ -130,7 +143,7 @@ links below, to make this documentation self-contained.
|
||||
|
||||
`mujoco.h <https://github.com/google-deepmind/mujoco/blob/main/include/mujoco/mujoco.h>`__
|
||||
This is the main header file and must be included in all programs using MuJoCo. It defines all API functions and
|
||||
global variables, and includes the all other header files except mjxmacro.h.
|
||||
global variables, and includes all other header files except mjxmacro.h.
|
||||
`mjmodel.h <https://github.com/google-deepmind/mujoco/blob/main/include/mujoco/mjmodel.h>`__
|
||||
Defines the C structure :ref:`mjModel` which is the runtime representation of the
|
||||
model being simulated. It also defines a number of primitive types and other structures needed to define mjModel.
|
||||
|
||||
+130
-47
@@ -1,13 +1,13 @@
|
||||
Model Editing
|
||||
-------------
|
||||
|
||||
.. admonition:: Unstable API
|
||||
:class: attention
|
||||
.. admonition:: New API
|
||||
:class: note
|
||||
|
||||
The API described below is new and unstable. There may be latent bugs and function signatures may change. Early
|
||||
adopters are welcome (indeed, encouraged) to try it out and report any issues on GitHub.
|
||||
The API described below is new but feature complete. It is recommended for general use, but latent bugs are still
|
||||
possible. Please report any issues on GitHub.
|
||||
|
||||
As of MuJoCo 3.2, it is possible to create and modify models using the :ref:`mjSpec` struct and related API.
|
||||
As of MuJoCo 3.2.0, it is possible to create and modify models using the :ref:`mjSpec` struct and related API.
|
||||
This datastructure is in one-to-one correspondence with MJCF and indeed, MuJoCo's own XML parsers (both MJCF and URDF)
|
||||
use this API when loading a model.
|
||||
|
||||
@@ -18,35 +18,38 @@ Overview
|
||||
~~~~~~~~
|
||||
|
||||
The new API augments the traditional workflow of creating and editing models using XML files, breaking up the *parse* and
|
||||
*compile* steps. As summarized in the the :ref:`Overview chapter<Instance>`, the traditional workflow is:
|
||||
*compile* steps. As summarized in the :ref:`Overview chapter<Instance>`, the traditional workflow is:
|
||||
|
||||
1. Create an XML model description file (MJCF or URDF) and associated assets. |br|
|
||||
2. Call :ref:`mj_loadXML`, obtain an :ref:`mjModel` instance.
|
||||
2. Call :ref:`mj_loadXML`, obtain an mjModel instance.
|
||||
|
||||
The new workflow is:
|
||||
The new workflow using :ref:`mjSpec` is:
|
||||
|
||||
1. :ref:`Create<mj_makeSpec>` an empty :ref:`mjSpec` or :ref:`parse<mj_parseXML>` an existing XML file to an
|
||||
:ref:`mjSpec`.
|
||||
2. Edit the mutable :ref:`mjSpec` datastructure adding, changing and removing elements.
|
||||
3. Compile the :ref:`mjSpec` at any point, obtaining an updated :ref:`mjModel` instance. After compilation, the
|
||||
:ref:`mjSpec` remains editable, so steps 2 and 3 are interchangable.
|
||||
1. :ref:`Create<mj_makeSpec>` an empty mjSpec or :ref:`parse<mj_parseXML>` an existing XML file.
|
||||
2. Programmatically edit the mjSpec datastructure by adding, modifying and removing elements.
|
||||
3. :ref:`Compile<mj_compile>` the mjSpec to an mjModel instance.
|
||||
|
||||
After compilation, the mjSpec remains editable, so steps 2 and 3 are interchangeable.
|
||||
|
||||
|
||||
.. _meUsage:
|
||||
|
||||
Usage
|
||||
~~~~~
|
||||
Here we describe the C API for procedural model editing, but it is also exposed in the
|
||||
:ref:`Python bindings<PyModelEdit>`.
|
||||
After creating a new :ref:`mjSpec` or parsing an existing XML file to an :ref:`mjSpec`, procedural editing corresponds
|
||||
to setting attributes. For example, in order to change the timestep, one can do:
|
||||
|
||||
Here we describe the C API for procedural model editing, but it is also exposed in the :ref:`Python
|
||||
bindings<PyModelEdit>`. Advanced users can refer to `user_api_test.cc
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/test/user/user_api_test.cc>`__ and the MJCF parser in
|
||||
`xml_native_reader.cc <https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc>`__ for more
|
||||
usage examples. After creating a new :ref:`mjSpec` or parsing an existing XML file to an :ref:`mjSpec`, procedural
|
||||
editing corresponds to setting attributes. For example, in order to change the timestep, one can do:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
mjSpec* spec = mj_makeSpec();
|
||||
spec->opt.timestep = 0.01;
|
||||
...
|
||||
mjModel* model = mj_compile(spec);
|
||||
mjModel* model = mj_compile(spec, NULL);
|
||||
|
||||
Attributes which have variable length are C++ vectors and strings, :ref:`exposed to C as opaque types<ArrayHandles>`.
|
||||
In C one uses the provided :ref:`getters<AttributeGetters>` and :ref:`setters<AttributeSetters>`:
|
||||
@@ -55,61 +58,141 @@ In C one uses the provided :ref:`getters<AttributeGetters>` and :ref:`setters<At
|
||||
|
||||
mjs_setString(model->modelname, "my_model");
|
||||
|
||||
In C++ one can use these directly:
|
||||
In C++, one can use vectors and strings directly:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
std::string modelname = "my_model";
|
||||
*spec->modelname = modelname;
|
||||
|
||||
Loading a spec from XML can be done as follows:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
std::array<char, 1000> error;
|
||||
mjSpec* s = mj_parseXML(filename, vfs, error.data(), error.size());
|
||||
|
||||
.. _meMjsElements:
|
||||
|
||||
Model elements
|
||||
^^^^^^^^^^^^^^
|
||||
Model elements corresponding to MJCF are exposed to the user as C structs with the ``mjs`` prefix, the definitions are
|
||||
listed under the :ref:`Model Editing<tySpecStructure>` section of the struct reference. For example, an MJCF
|
||||
:ref:`geom<body-geom>` corresponds to an :ref:`mjsGeom`.
|
||||
|
||||
Model elements corresponding to MJCF are added to the spec using the corresponding functions. For example, to add a box
|
||||
geom to the world body, one would do
|
||||
Global defaults for all elements are set by :ref:`initializers<ElementInitialization>` like :ref:`mjs_defaultGeom`.
|
||||
These functions are defined in `user_init.c
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/src/user/user_init.c>`__ and are the source of truth for all
|
||||
default values.
|
||||
|
||||
Elements cannot be created directly; they are returned to the user by the corresponding constructor function, e.g.
|
||||
:ref:`mjs_addGeom`. For example, to add a box geom to the world body, one would do
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
mjSpec* spec = mj_makeSpec();
|
||||
mjsBody* world = mjs_findBody(spec, "world");
|
||||
mjsGeom* my_geom = mjs_addGeom(world, NULL);
|
||||
my_geom->type = mjGEOM_BOX;
|
||||
my_geom->size[0] = my_geom->size[1] = my_geom->size[2] = 0.5;
|
||||
mjModel* model = mj_compile(spec);
|
||||
mjSpec* spec = mj_makeSpec(); // make an empty spec
|
||||
mjsBody* world = mjs_findBody(spec, "world"); // find the world body
|
||||
mjsGeom* my_geom = mjs_addGeom(world, NULL); // add a geom to the world
|
||||
my_geom->type = mjGEOM_BOX; // set geom type
|
||||
my_geom->size[0] = my_geom->size[1] = my_geom->size[2] = 0.5; // set box size
|
||||
mjModel* model = mj_compile(spec, NULL); // compile to mjModel
|
||||
|
||||
The ``NULL`` second argument to :ref:`mjs_addGeom` is the optional default class pointer. When using defaults
|
||||
procedurally, default classes are passed in explicitly to element constructors. The global defaults of all elements
|
||||
(used when no default class is passed in) can be inspected in
|
||||
`user_init.c <https://github.com/google-deepmind/mujoco/blob/main/src/user/user_init.c>`__.
|
||||
|
||||
|
||||
.. _meAttachment:
|
||||
|
||||
Attachment
|
||||
^^^^^^^^^^
|
||||
The new framework introduces a powerful new feature: attaching and detaching model subtrees. Attachment allows the user
|
||||
copy a subtree from one model into another, while also copying related referenced assets and referencing elements from
|
||||
outside the kinematic tree (e.g., actuators and sensors). Similarly, detaching a subtree will remove all associated
|
||||
elements from the model.
|
||||
|
||||
This feature is incomplete and will be described in detail once it is fully implemented, but it is already used to power
|
||||
the :ref:`attach<body-attach>` and :ref:`replicate<replicate>` meta-elements in MJCF.
|
||||
This framework introduces a powerful new feature: attaching and detaching model subtrees. This feature is already used
|
||||
to power the :ref:`attach<body-attach>` an :ref:`replicate<replicate>` meta-elements in MJCF. Attachment allows the user
|
||||
to move or copy a subtree from one model into another, while also copying or moving related referenced assets and
|
||||
referencing elements from outside the kinematic tree (e.g., actuators and sensors). Similarly, detaching a subtree will
|
||||
remove all associated elements from the model. The default behavior is to move during attach. The user can select to
|
||||
instead copy by passing the corresponding flag to ``mjs_setDeepCopy``. This flag is temporary set to true while parsing
|
||||
XMLs. It is possible to :ref:`attach a body to a frame<mjs_attachBody>`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
.. _meKnownIssues:
|
||||
mjSpec* parent = mj_makeSpec();
|
||||
mjSpec* child = mj_makeSpec();
|
||||
parent->compiler.degree = 0;
|
||||
child->compiler.degree = 1;
|
||||
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), NULL);
|
||||
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL);
|
||||
mjsBody* attached_body_1 = mjs_attachBody(frame, body, "attached-", "-1");
|
||||
|
||||
Known issues
|
||||
~~~~~~~~~~~~
|
||||
or :ref:`attach a body to a site<mjs_attachToSite>`:
|
||||
|
||||
- Better documentation is still missing and will be added in the future. In the meantime, advanced users can refer
|
||||
to `user_api_test.cc <https://github.com/google-deepmind/mujoco/blob/main/test/user/user_api_test.cc>`__ and the MJCF
|
||||
parser in `xml_native_reader.cc <https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc>`__,
|
||||
which is already using this API.
|
||||
- One of the central design considerations of the new API is incremental compilation, meaning that after making small
|
||||
changes to a spec that has already been compiled, subsequent re-compilation will be very fast. While the code is
|
||||
written to support incremental compilation, this functionality is not fully implemented and will be added in the
|
||||
future, resulting in faster re-compilation times.
|
||||
- Since the main test for the new API is the MJCF parser, which always constructs a model from scratch, there
|
||||
might be latent bugs related to model editing. Please report such bugs if you encounter them.
|
||||
.. code-block:: C
|
||||
|
||||
mjSpec* parent = mj_makeSpec();
|
||||
mjSpec* child = mj_makeSpec();
|
||||
mjsSite* site = mjs_addSite(mjs_findBody(parent, "world"), NULL);
|
||||
mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), NULL);
|
||||
mjsBody* attached_body_2 = mjs_attachToSite(site, body, "attached-", "-2");
|
||||
|
||||
or :ref:`attach a frame to a body<mjs_attachFrame>`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
mjSpec* parent = mj_makeSpec();
|
||||
mjSpec* child = mj_makeSpec();
|
||||
mjsBody* body = mjs_addBody(mjs_findBody(parent, "world"), NULL);
|
||||
mjsFrame* frame = mjs_addFrame(mjs_findBody(child, "world"), NULL);
|
||||
mjsFrame* attached_frame = mjs_attachFrame(body, frame, "attached-", "-1");
|
||||
|
||||
Note that in the above examples, the parent and child models have different values for ``compiler.degree``,
|
||||
corresponding to the :ref:`compiler/angle<compiler-angle>` attribute, specifying the units in which angles are
|
||||
interperted. Compiler flags are carried over during attachment, so the child model will be compiled using the child
|
||||
flags, while the parent will be compiled using the parent flags.
|
||||
|
||||
Note also that once a child is attached by reference to a parent, the child cannot be compiled on its own.
|
||||
|
||||
.. _meDefault:
|
||||
|
||||
Default classes
|
||||
^^^^^^^^^^^^^^^
|
||||
Default classes are fully supported in the new API, however using them requires an understanding of how defaults
|
||||
are implemented. As explained in the :ref:`Default settings <CDefault>` section, default classes are first loaded as a
|
||||
tree of dummy elements, which are then used to initialize elements which reference them. When editing models with
|
||||
defaults, this initialization is explicit:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
mjSpec* spec = mj_makeSpec();
|
||||
mjsDefault* main = mjs_getSpecDefault(spec);
|
||||
main->geom.type = mjGEOM_BOX;
|
||||
mjsGeom* geom = mjs_addGeom(mjs_findBody(spec, "world"), main);
|
||||
|
||||
Importantly, changing a default class after it has been used to initialize elements will not change the properties of
|
||||
already initialized elements.
|
||||
|
||||
.. admonition:: Possible future change
|
||||
:class: note
|
||||
|
||||
The behaviour described above, where defaults are only applied at initialization, is a remnant of the old, XML-only
|
||||
loading pipeline. A future API change could allow defaults to be changed and applied after initialization. If you
|
||||
think this feature is important to you, please let us know on GitHub.
|
||||
|
||||
.. _meSaving:
|
||||
|
||||
XML saving
|
||||
^^^^^^^^^^
|
||||
Specs can be saved to an XML file or string using :ref:`mj_saveXML` or :ref:`mj_saveXMLString`, respectively.
|
||||
Saving requires that the spec first be compiled.
|
||||
Importantly, the saved XML will take into account any defined defaults. This is useful when a model has many repeated
|
||||
values, for example if loaded from URDF, which does not support defaults. In such a case one can add default classes,
|
||||
set the class of the relevant elements, and save; the resulting XML will use the defaults and be more human-readable.
|
||||
|
||||
.. _meRecompilation:
|
||||
|
||||
In-place recompilation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Compilation with :ref:`mj_compile` can be called at any point to obtain a new mjModel instance. In contrast,
|
||||
:ref:`mj_recompile` updates an existing mjModel and mjData pair in-place, while preserving the simulation state. This
|
||||
allows model editing to occur **during simulation**, for example adding or removing bodies.
|
||||
|
||||
@@ -61,8 +61,8 @@ Where the command line arguments are
|
||||
- The ``ctrlnoise`` argument prevents models from settling into a static state where, due to warmstarts, one can
|
||||
measure artificially faster simulation.
|
||||
- When ``npoolthread > 1`` is specified, an engine-internal :ref:`mjThreadPool` is created with the specified number of
|
||||
threads, to speed up simulation of large scenes. Note that while it is possible to to use both ``nthread`` and
|
||||
``npoolthread``, the scenarios for which one would want these different type of multithreading are usually mutually
|
||||
threads, to speed up simulation of large scenes. Note that while it is possible to use both ``nthread`` and
|
||||
``npoolthread``, the scenarios for which one would want these different types of multithreading are usually mutually
|
||||
exclusive.
|
||||
- For more repeatable performance statistics, run the tool with the ``performance``
|
||||
`governor <https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt>`__ on Linux, or the
|
||||
@@ -85,6 +85,7 @@ functionality. This code sample uses the native UI to render various controls, a
|
||||
illustration of how the new UI framework is intended to be used. Below is a screen-capture of ``simulate`` in action:
|
||||
|
||||
.. youtube:: P83tKA1iz2Y
|
||||
:aspect: 16:9
|
||||
:width: 95%
|
||||
:align: center
|
||||
|
||||
@@ -203,7 +204,7 @@ data file into a playable movie file:
|
||||
ffmpeg -f rawvideo -pixel_format rgb24 -video_size 2560x1440
|
||||
-framerate 60 -i rgb.out -vf "vflip,format=yuv420p" video.mp4
|
||||
|
||||
Note that the offscreen rendering resolution of the model and ffmpeg's video_size must be the identical.
|
||||
Note that the offscreen rendering resolution of the model and ffmpeg's video_size must be identical.
|
||||
|
||||
This sample can be compiled in three ways which differ in how the OpenGL context is created: using GLFW with an
|
||||
invisible window, using OSMesa, or using EGL. The latter two options are only available on Linux and are envoked by
|
||||
|
||||
@@ -267,7 +267,7 @@ control laws obtained from trajectory optimizers would normally be indexed by ``
|
||||
The reason for the "official" caveat above is because user callbacks may store additional state variables that change
|
||||
over time and affect the callback outputs; indeed the field ``mjData.userdata`` exists mostly for that purpose. Other
|
||||
state-like quantities that are part of mjData and are treated as inputs by forward dynamics are ``mjData.mocap_pos`` and
|
||||
mjData.mocap_quat. These quantities are unusual in that they are meant to change at each time step (normally driven by a
|
||||
``mjData.mocap_quat``. These quantities are unusual in that they are meant to change at each time step (normally driven by a
|
||||
motion capture device), however this change is implemented by the user, while the simulator treats them as constants. In
|
||||
that sense they are no different from all the constants in mjModel, or the function callback pointers set by the user:
|
||||
such constants affect the computation, but are not part of the state vector of a dynamical system.
|
||||
@@ -286,7 +286,7 @@ Next we turn to the controls and applied forces. The control vector in MuJoCo is
|
||||
u = (mjData.ctrl, mjData.qfrc_applied, mjData.xfrc_applied)
|
||||
|
||||
These quantities specify control signals (``mjData.ctrl``) for the actuators defined in the model, or directly apply
|
||||
forces and torques specified in joint space (``mjData.qfrc_applied``) or in Cartesian space (mjData.xfrc_applied).
|
||||
forces and torques specified in joint space (``mjData.qfrc_applied``) or in Cartesian space (``mjData.xfrc_applied``).
|
||||
|
||||
Finally, calling mj_forward which corresponds to the abstract dynamics function ``f(t,x,u)`` computes the
|
||||
time-derivative of the state vector. The corresponding fields of mjData are
|
||||
@@ -556,6 +556,13 @@ or termination of the iterative solver.
|
||||
Model changes
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. admonition:: Model editing framework
|
||||
:class: tip
|
||||
|
||||
The discussion below regarding mjModel changes at runtime was written before the 3.2.0 introduction of the
|
||||
:doc:`Model Editing<modeledit>` framework. It is still valid, but the new framework is the safe and recommended way
|
||||
to modify models.
|
||||
|
||||
The MuJoCo model contained in mjModel is supposed to represent constant physical properties of the system, and in
|
||||
theory should not change after compilation. Of course in practice things are not that simple. It is often desirable to
|
||||
change the physics options in ``mjModel.opt``, so as to experiment with different aspects of the physics or to create
|
||||
@@ -590,8 +597,8 @@ asking a "decompiler" to make corresponding changes to the C code -- it is just
|
||||
|
||||
.. _siLayout:
|
||||
|
||||
Data layout and buffer allocation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Data layout
|
||||
~~~~~~~~~~~
|
||||
|
||||
All matrices in MuJoCo are in **row-major** format. For example, the linear memory array (a0, a1, ... a5) represents the
|
||||
2-by-3 matrix
|
||||
@@ -607,12 +614,15 @@ essential to keep it in mind at all times. All MuJoCo utility functions that ope
|
||||
:ref:`mju_mulMatMat`, :ref:`mju_mulMatVec` etc. assume this matrix layout. For vectors there is of course no
|
||||
difference between row-major and column-major formats.
|
||||
|
||||
.. TODO(tassa): update this section when qM is migrated to CSR.
|
||||
|
||||
When possible, MuJoCo exploits sparsity. This can make all the difference between O(N) and O(N^3) scaling. The inertia
|
||||
matrix ``mjData.qM`` and its LTDL factorization ``mjData.qLD`` are always represented as sparse, using a custom
|
||||
indexing format designed for matrices that correspond to tree topology. The functions :ref:`mj_factorM`,
|
||||
:ref:`mj_solveM`, :ref:`mj_solveM2` and :ref:`mj_mulM` are used for sparse factorization, substitution and
|
||||
matrix-vector multiplication. The user can also convert these matrices to dense format with the function
|
||||
:ref:`mj_fullM` although MuJoCo never does that internally.
|
||||
matrix ``mjData.qM`` and its LTDL factorization ``mjData.qLD`` are always represented as sparse. ``qM`` uses a custom
|
||||
indexing format designed for matrices that correspond to tree topology, while ``qLD`` uses the standard CSR format.
|
||||
``qM`` will be migrated to CSR in and upcoming change. The functions :ref:`mj_factorM`, :ref:`mj_solveM`,
|
||||
:ref:`mj_solveM2` and :ref:`mj_mulM` are used for sparse factorization, substitution and matrix-vector multiplication.
|
||||
The user can also convert these matrices to dense format with the function :ref:`mj_fullM` although MuJoCo never does
|
||||
that internally.
|
||||
|
||||
The constraint Jacobian matrix ``mjData.efc_J`` is represented as sparse whenever the sparse Jacobian option is
|
||||
enabled. The function :ref:`mj_isSparse` can be used to determine if sparse format is currently in use. In that case
|
||||
@@ -712,8 +722,8 @@ and :ref:`mj_stackAllocByte` is provided for allocation of arbitrary number of b
|
||||
|
||||
.. _siError:
|
||||
|
||||
Errors, warnings, memory allocation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Errors and warnings
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When a terminal error occurs, MuJoCo calls the function :ref:`mju_error` internally. Here is what mju_error does:
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ The low-level mjvGLCamera is what determines the actual rendering. There are two
|
||||
for each eye. Each has position, forward and up directions. Forward corresponds to the negative Z axis of the camera
|
||||
frame, while up corresponds to the positive Y axis. There is also a frustum in the sense of OpenGL, except we store the
|
||||
average of the left and right frustum edges and then during rendering compute the actual edges from the viewport aspect
|
||||
ratio assuming 1:1 pixel aspect ratio. The distance between the two camera positions corresponds to the inter-pupilary
|
||||
ratio assuming 1:1 pixel aspect ratio. The distance between the two camera positions corresponds to the inter-pupillary
|
||||
distance (ipd). When the low-level camera parameters are computed automatically from an abstract camera, the ipd as well
|
||||
as vertical field of view (fovy) are taken from ``mjModel.vis.global.ipd``/``fovy`` for free and tracking cameras, and
|
||||
from the camera-specific ``mjModel.cam_ipd/fovy`` for cameras defined in the model. When stereoscopic mode is not
|
||||
|
||||
+222
-34
@@ -126,7 +126,7 @@ attributes:
|
||||
- ``user_scn``: an :ref:`mjvScene` object that allows users to add change rendering flags and add custom
|
||||
visualization geoms to the rendered scene. This is separate from the ``mjvScene`` that the viewer uses internally to
|
||||
render the final scene, and is entirely under the user's control. User scripts can call e.g. :ref:`mjv_initGeom` or
|
||||
:ref:`mjv_makeConnector` to add visualization geoms to ``user_scn``, and upon the next call to ``sync()``, the viewer
|
||||
:ref:`mjv_connector` to add visualization geoms to ``user_scn``, and upon the next call to ``sync()``, the viewer
|
||||
will incorporate these geoms to future rendered images. Similarly, user scripts can make changes to ``user_scn.flags``
|
||||
which would be picked up at the next call to ``sync()``. The ``sync()`` call also copies changes to rendering flags
|
||||
made via the GUI back into ``user_scn`` to preserve consistency. For example:
|
||||
@@ -243,7 +243,9 @@ access to the raw memory used by MuJoCo without copying or buffering. This means
|
||||
:ref:`mj_step`) change the content of fields *in place*. The user is therefore advised to create copies where required.
|
||||
For example, when logging the position of a body, one could write
|
||||
``positions.append(data.body('my_body').xpos.copy())``. Without the ``.copy()``, the list would contain identical
|
||||
elements, all pointing to the most recent value.
|
||||
elements, all pointing to the most recent value. The same applies to NumPy slices. For example if a local
|
||||
variable ``qpos_slice = data.qpos[3:8]`` is created and then :ref:`mj_step` is called, the values in ``qpos_slice``
|
||||
will have been changed.
|
||||
|
||||
In order to conform to `PEP 8 <https://peps.python.org/pep-0008/>`__
|
||||
naming guidelines, struct names begin with a capital letter, for example ``mjData`` becomes ``mujoco.MjData`` in Python.
|
||||
@@ -469,19 +471,17 @@ the raw callback pointer, and the GIL will **not** be acquired each time the cal
|
||||
|
||||
Model editing
|
||||
=============
|
||||
The :doc:`Model Editing<programming/modeledit>` framework which allows for procedural model manipulation is exposed
|
||||
via Python. In many ways this API is conceptually similar to ``dm_control``'s
|
||||
`PyMJCF module <https://github.com/google-deepmind/dm_control/tree/main/dm_control/mjcf#readme>`__, where ``MjSpec``
|
||||
plays the role of ``mjcf_model``. The largest difference between these two APIs is speed. Native model manipulation via
|
||||
``MjSpec`` is around ~100x faster than PyMJCF.
|
||||
The C API for model editing is documented in the :doc:`Programming<../programming/modeledit>` chapter.
|
||||
This functionality is mirrored in the Python API, with the addition of several convenience methods.
|
||||
Below is a minimal usage example, more examples can be found in the Model Editing
|
||||
`colab notebook <https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/mjspec.ipynb>`__.
|
||||
|
||||
Below is a simple example of how to use the model editing API. For more examples, please refer to
|
||||
`specs_test.py <https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/specs_test.py>`__.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import mujoco
|
||||
spec = mujoco.MjSpec()
|
||||
spec.modelname = "my model"
|
||||
body = spec.worldbody.add_body(
|
||||
pos=[1, 2, 3],
|
||||
quat=[0, 1, 0, 0],
|
||||
@@ -495,18 +495,163 @@ Below is a simple example of how to use the model editing API. For more examples
|
||||
...
|
||||
model = spec.compile()
|
||||
|
||||
.. admonition:: Missing features
|
||||
:class: attention
|
||||
Construction
|
||||
------------
|
||||
|
||||
We are aware of multiple missing features in the Python API, including:
|
||||
The ``MjSpec`` object wraps the :ref:`mjSpec` struct and can be constructed in three ways:
|
||||
|
||||
- Better tree traversal utilities like :python:`children = body.children()` etc.
|
||||
- PyMJCF's notion of "binding", allowing access to :ref:`mjModel` and :ref:`mjData` values via the associated ``mjs``
|
||||
elements.
|
||||
1. Create an empty spec: ``spec = mujoco.MjSpec()``
|
||||
2. Load the spec from XML string: ``spec = mujoco.MjSpec.from_string(xml_string)``
|
||||
3. Load the spec from XML file: ``spec = mujoco.MjSpec.from_file(file_path)``
|
||||
|
||||
There are certainly other missing features that we are not aware of. Please contact us on GitHub with feature
|
||||
requests or bug reports and we will prioritize accordingly.
|
||||
Note the ``from_string()`` and ``from_file()`` methods can only be called at construction time.
|
||||
|
||||
Assets
|
||||
^^^^^^
|
||||
|
||||
All three methods take in an optional argument called ``assets`` which is used to resolve asset references in the XML.
|
||||
This argument is a dictionary that maps asset name (string) to asset data (bytes), as demonstrated below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assets = {'image.png': b'image_data'}
|
||||
spec = mujoco.MjSpec.from_string(xml_referencing_image_png, assets=assets)
|
||||
model = spec.compile()
|
||||
|
||||
Save to XML
|
||||
-----------
|
||||
|
||||
Compiled ``MjSpec`` objects can be saved to XML string with the ``to_xml()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print(spec.to_xml())
|
||||
|
||||
.. code-block:: XML
|
||||
|
||||
<mujoco model="my model">
|
||||
<compiler angle="radian"/>
|
||||
|
||||
<worldbody>
|
||||
<body pos="1 2 3" quat="0 1 0 0">
|
||||
<geom name="my_geom" size="1" rgba="1 0 0 1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
Attachment
|
||||
----------
|
||||
|
||||
It is possible to combine multiple specs by using attachments. The following options are possible:
|
||||
|
||||
- Attach a body from the child spec to a frame in the parent spec: ``body.attach_body(body, prefix, suffix)``, returns
|
||||
the reference to the attached body, which should be identical to the body used as input.
|
||||
- Attach a frame from the child spec to a body in the parent spec: ``body.attach_frame(frame, prefix, suffix)``,
|
||||
returns the reference to the attached frame, which should be identical to the frame used as input.
|
||||
- Attach a child spec to a site in the parent spec: ``parent_spec.attach(child_spec, site=site_name_or_obj)``, returns
|
||||
the reference to a frame, which is the attached worldbody transformed into a frame. The site must belong to the
|
||||
child spec. Prefix and suffix can also be specified as keyword arguments.
|
||||
- Attach a child spec to a frame in the parent spec: ``parent_spec.attach(child_spec, frame=frame_name_or_obj)``,
|
||||
returns the reference to a frame, which is the attached worldbody transformed into a frame. The frame must belong to
|
||||
the child spec. Prefix and suffix can also be specified as keyword arguments.
|
||||
|
||||
The default behavior of attaching is to not copy, so all the child references (except for the worldbody) are still valid
|
||||
in the parent and therefore modifying the child will modify the parent. This is not true for the attach
|
||||
:ref:`attach<body-attach>` and :ref:`replicate<replicate>` meta-elements in MJCF, which create deep copies while
|
||||
attaching. However, it is possible to override the default behavior by setting ``spec.copy_during_attach`` to
|
||||
``True``. In this case, the child spec is copied and the references to the child will not point to the parent.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import mujoco
|
||||
|
||||
# Create the parent spec.
|
||||
parent = mujoco.MjSpec()
|
||||
body = parent.worldbody.add_body()
|
||||
frame = parent.worldbody.add_frame()
|
||||
site = parent.worldbody.add_site()
|
||||
|
||||
# Create the child spec.
|
||||
child = mujoco.MjSpec()
|
||||
child_body = child.worldbody.add_body()
|
||||
child_frame = child.worldbody.add_frame()
|
||||
|
||||
# Attach the child to the parent in different ways.
|
||||
body_in_frame = frame.attach_body(child_body, 'child-', '')
|
||||
frame_in_body = body.attach_frame(child_frame, 'child-', '')
|
||||
worldframe_in_site = parent.attach(child, site=site, prefix='child-')
|
||||
worldframe_in_frame = parent.attach(child, frame=frame, prefix='child-')
|
||||
|
||||
Convenience methods
|
||||
-------------------
|
||||
|
||||
The Python bindings provide a number of convenience methods and attributes not directly available in the C API in order
|
||||
to make model editing easier:
|
||||
|
||||
Named access
|
||||
^^^^^^^^^^^^
|
||||
The ``MjSpec`` object has methods like ``.body(), .joint(), .site(), ...`` for named access of elements.
|
||||
``spec.geom('my_geom')`` will return the :ref:`mjsGeom` called "my_geom", or ``None`` if it does not exist.
|
||||
|
||||
Element lists
|
||||
^^^^^^^^^^^^^
|
||||
Lists of all elements in a spec can be accessed using named properties, using the plural form. For example,
|
||||
``spec.meshes`` returns a list of all meshes in the spec. The following properties are implemented: ``sites``,
|
||||
``geoms``, ``joints``, ``lights``, ``cameras``, ``bodies``, ``frames``, ``materials``, ``meshes``, ``pairs``,
|
||||
``equalities``, ``tendons``, ``actuators``, ``skins``, ``textures``, ``texts``, ``tuples``, ``flexes``, ``hfields``,
|
||||
``keys``, ``numerics``, ``excludes``, ``sensors``, ``plugins``.
|
||||
|
||||
Tree traversal
|
||||
^^^^^^^^^^^^^^
|
||||
Traversal of the kinematic tree is aided by the following methods which return tree-related lists of elements:
|
||||
|
||||
Direct children:
|
||||
Like the spec-level element lists described above, bodies have properties which return lists of all direct children.
|
||||
For example, ``body.geoms`` returns a list of all geoms that are direct children of the body. This works for all
|
||||
in tree elements namely ``bodies``, ``joints``, ``geoms``, ``sites``, ``cameras``, ``lights`` and ``frames``.
|
||||
|
||||
Recursive search:
|
||||
``body.find_all()`` returns a list of all elements of the given type which are in the subtree of the given body.
|
||||
Element types can be specified with the :ref:`mjtObj` enum, or with the corresponding string. For example either
|
||||
``body.find_all(mujoco.mjtObj.mjOBJ_SITE)`` or ``body.find_all('site')`` will return a list of all sites under the
|
||||
body.
|
||||
|
||||
Parent:
|
||||
The parent body of a given element -- including bodies and frames -- can be accessed via the ``parent`` property.
|
||||
For example, the parent of a site can be accessed via ``site.parent``.
|
||||
|
||||
Relationship to ``PyMJCF``
|
||||
--------------------------
|
||||
|
||||
`dm_control <https://github.com/google-deepmind/dm_control/tree/main>`__'s
|
||||
`PyMJCF <https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/README.md>`__ module provides similar
|
||||
functionality to the native model editing API described here, but is roughly two orders of magnitude slower due to its
|
||||
reliance on Python manipulation of strings.
|
||||
|
||||
For users familiar with ``PyMJCF``, the ``MjSpec`` object is conceptually similar to ``dm_control``'s
|
||||
``mjcf_model``. A more detailed migration guide could be added here in the future; in the meantime, note that the
|
||||
Model Editing
|
||||
`colab notebook <https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/mjspec.ipynb>`__
|
||||
includes a reimplementation of the ``PyMJCF`` example in the ``dm_control``
|
||||
`tutorial notebook <https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/tutorial.ipynb>`__.
|
||||
|
||||
``PyMJCF`` provides a notion of "binding", giving access to :ref:`mjModel` and :ref:`mjData` values via a helper class.
|
||||
In the native API, the helper class is not needed, so it is possible to directly bind an ``mjs`` object to
|
||||
:ref:`mjModel` and :ref:`mjData`. This requires the objects to have a non-empty name. For example, say we have multiple
|
||||
geoms containing the string "torso" in their name. We want to get their Cartesian positions in the XY plane from
|
||||
``mjData``. This can be done as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
torsos = [data.bind(geom) for geom in spec.geoms if 'torso' in geom.name]
|
||||
pos_x = [torso.xpos[0] for torso in torsos]
|
||||
pos_y = [torso.xpos[1] for torso in torsos]
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- :ref:`mj_recompile` works differently than in the C API. In the C API, it modifies the model and the data in place,
|
||||
while in the Python API it returns new :ref:`MjModel` and :ref:`MjData` objects. This is to avoid dangling references.
|
||||
|
||||
.. _PyBuild:
|
||||
|
||||
@@ -593,34 +738,77 @@ The ``mujoco`` package contains two sub-modules: ``mujoco.rollout`` and ``mujoco
|
||||
|
||||
rollout
|
||||
-------
|
||||
``mujoco.rollout`` and ``mujoco.rollout.Rollout`` shows how to add additional C/C++ functionality, exposed as a Python
|
||||
module via pybind11. It is implemented in `rollout.cc
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/rollout.cc>`__ and wrapped in `rollout.py
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/rollout.py>`__. The module addresses a common
|
||||
use-case where tight loops implemented outside of Python are beneficial: rolling out a trajectory (i.e., calling
|
||||
:ref:`mj_step` in a loop), given an initial state and sequence of controls, and returning subsequent states and sensor
|
||||
values. The rollouts are run in parallel with an internally managed thread pool if multiple MjData instances (one per
|
||||
thread) are passed as an argument. This notebook shows how to use ``rollout`` |rollout_colab|, along with some
|
||||
benchmarks e.g., the figure below.
|
||||
|
||||
``mujoco.rollout`` shows how to add additional C/C++ functionality, exposed as a Python module via pybind11. It is
|
||||
implemented in `rollout.cc <https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/rollout.cc>`__
|
||||
and wrapped in `rollout.py <https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/rollout.py>`__. The module
|
||||
performs a common functionality where tight loops implemented outside of Python are beneficial: rolling out a trajectory
|
||||
(i.e., calling :ref:`mj_step` in a loop), given an intial state and sequence of controls, and returning subsequent
|
||||
states and sensor values. The basic usage form is
|
||||
.. |rollout_colab| image:: https://colab.research.google.com/assets/colab-badge.svg
|
||||
:target: https://colab.research.google.com/github/google-deepmind/mujoco/blob/main/python/rollout.ipynb
|
||||
|
||||
.. image:: images/python/rollout.png
|
||||
:align: right
|
||||
:width: 97%
|
||||
|
||||
The basic usage form is
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
state, sensordata = rollout.rollout(model, data, initial_state, control)
|
||||
|
||||
``initial_state`` is an ``nroll x nstate`` array, with ``nroll`` initial states of size ``nstate``, where
|
||||
``nstate = mj_stateSize(model, mjtState.mjSTATE_FULLPHYSICS)`` is the size of the
|
||||
:ref:`full physics state<geFullPhysics>`. ``control`` is a ``nroll x nstep x ncontrol`` array of controls. Controls are
|
||||
by default the ``mjModel.nu`` standard actuators, but any combination of :ref:`user input<geInput>` arrays can be
|
||||
specified by passing an optional ``control_spec`` bitflag.
|
||||
- ``model`` is either a single instance of MjModel or a sequence of homogeneous MjModels of length ``nbatch``.
|
||||
Homogeneous models have the same integer sizes, but floating point values can differ.
|
||||
- ``data`` is either a single instance of MjData or a sequence of compatible MjDatas of length ``nthread``.
|
||||
- ``initial_state`` is an ``nbatch x nstate`` array, with ``nbatch`` initial states of size ``nstate``, where
|
||||
``nstate = mj_stateSize(model, mjtState.mjSTATE_FULLPHYSICS)`` is the size of the
|
||||
:ref:`full physics state<geFullPhysics>`.
|
||||
- ``control`` is a ``nbatch x nstep x ncontrol`` array of controls. Controls are by default the ``mjModel.nu`` standard
|
||||
actuators, but any combination of :ref:`user input<geInput>` arrays can be specified by passing an optional
|
||||
``control_spec`` bitflag.
|
||||
|
||||
If a rollout diverges, the current state and sensor values are used to fill the remainder of the trajectory.
|
||||
Therefore, non-increasing time values can be used to detect diverged rollouts.
|
||||
|
||||
The ``rollout`` function is designed to be completely stateless, so all inputs of the stepping pipeline are set and any
|
||||
values already present in the given ``MjData`` instance will have no effect on the output.
|
||||
The ``rollout`` function is designed to be computationally stateless, so all inputs of the stepping pipeline are set and
|
||||
any values already present in the given ``MjData`` instance will have no effect on the output.
|
||||
|
||||
Since the Global Interpreter Lock can be released, this function can be efficiently threaded using Python threads. See
|
||||
the ``test_threading`` function in
|
||||
By default ``rollout.rollout`` creates a new thread pool every call if ``len(data) > 1``. To reuse the thread pool
|
||||
over multiple calls use the ``persistent_pool`` argument. ``rollout.rollout`` is not thread safe when using
|
||||
a persistent pool. The basic usage form is
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
state, sensordata = rollout.rollout(model, data, initial_state, persistent_pool=True)
|
||||
|
||||
The pool is shutdown on interpreter shutdown or by a call to ``rollout.shutdown_persistent_pool``.
|
||||
|
||||
To use multiple thread pools from multiple threads, use ``Rollout`` objects. The basic usage form is
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Pool shutdown upon exiting block.
|
||||
with rollout.Rollout(nthread=nthread) as rollout_:
|
||||
rollout_.rollout(model, data, initial_state)
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Pool shutdown on object deletion or call to rollout_.close().
|
||||
# To ensure clean shutdown of threads, call close() before interpreter exit.
|
||||
rollout_ = rollout.Rollout(nthread=nthread)
|
||||
rollout_.rollout(model, data, initial_state)
|
||||
rollout_.close()
|
||||
|
||||
Since the Global Interpreter Lock is released, this function can also be threaded using Python threads. However, this
|
||||
is less efficient than using native threads. See the ``test_threading`` function in
|
||||
`rollout_test.py <https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/rollout_test.py>`__ for an example
|
||||
of threaded operation (and more generally for usage examples).
|
||||
of threaded operation (and for more general usage examples).
|
||||
|
||||
.. _PyMinimize:
|
||||
|
||||
@@ -769,7 +957,7 @@ USD Export API
|
||||
|
||||
- ``add_camera(self, pos, rotation_xyz, obj_name)``: adds a camera to the USD scene with the given properties post hoc.
|
||||
|
||||
- ``save_scene(self, filetype)``: exports the USD scene using one of the usd filetype extensions ``.usd``, ``.usda``,
|
||||
- ``save_scene(self, filetype)``: exports the USD scene using one of the USD filetype extensions ``.usd``, ``.usda``,
|
||||
or ``.usdc``.
|
||||
|
||||
.. _PyUSDTodos:
|
||||
|
||||
@@ -6,7 +6,7 @@ sphinxcontrib-youtube==1.2.0
|
||||
sphinx-copybutton==0.5.2
|
||||
sphinx-favicon==1.0.1
|
||||
sphinx-reredirects==0.1.1
|
||||
sphinx-toolbox==3.4.0
|
||||
sphinx-toolbox==3.8.2
|
||||
nbsphinx==0.9.1
|
||||
pandoc==1.1.0
|
||||
pygments==2.15.0
|
||||
|
||||
Vendored
+3
-3
@@ -11,10 +11,10 @@
|
||||
#}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', (event) => {
|
||||
let home_link = document.querySelector('a.icon');
|
||||
let homeLink = document.querySelector('a.icon');
|
||||
{# Be extra safe and don't break the page if theme changes and querySelector can't match. #}
|
||||
if (home_link) {
|
||||
home_link.href="https://mujoco.org";
|
||||
if (homeLink) {
|
||||
homeLink.href="https://mujoco.org";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
+34
-16
@@ -5,20 +5,27 @@ Unity Plug-in
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The MuJoCo `Unity plug-in <https://github.com/google-deepmind/mujoco/tree/main/unity>`_ allows the Unity Editor and
|
||||
The MuJoCo `Unity plug-in <https://github.com/google-deepmind/mujoco/tree/main/unity>`__ allows the Unity Editor and
|
||||
runtime to use the MuJoCo physics engine. Users can import MJCF files and edit the models in the Editor. The plug-in
|
||||
relies on Unity for most aspects -- assets, game logic, simulation time -- but uses MuJoCo to determine how objects
|
||||
move, giving the designer access to MuJoCo's full API.
|
||||
|
||||
An example project using MuJoCo's Unity plugin in a set of introductory tutorials are also available as a `standalone
|
||||
repository <https://github.com/Balint-H/mj-unity-tutorial>`__.
|
||||
|
||||
.. _UInstallation:
|
||||
|
||||
Installation instructions
|
||||
-------------------------
|
||||
|
||||
The plug-in directory (available at https://github.com/google-deepmind/mujoco/tree/main/unity) includes a
|
||||
The `plug-in directory <https://github.com/google-deepmind/mujoco/tree/main/unity>`__ includes a
|
||||
``package.json`` file. Unity's package manager recognizes this file and will import the plug-in's C# codebase to your
|
||||
project. In addition, Unity also needs the native MuJoCo library, which can be found in the specific platform archive at
|
||||
https://github.com/google-deepmind/mujoco/releases.
|
||||
project. In addition, Unity also needs the native MuJoCo library, which can be found in the corresponding `platform
|
||||
archive <https://github.com/google-deepmind/mujoco/releases>`__. If you wish to simply use the plug-in and not
|
||||
develop it, you should use one of the version-specific stable commits of the repository, identified by git tags. Check
|
||||
out the relevant version of the cloned repository with git (``git checkout 3.X.Y`` where X and Y specify the engine
|
||||
version). Simply using the ``main`` branch of the repository may not be compatible with the most recent release binary
|
||||
of MuJoCo.
|
||||
|
||||
On Unity version 2020.2 and later, the Package Manager will look for the native library file and copy it to the package
|
||||
directory when the package is imported. Alternatively, you can manually copy the native library to the package directory
|
||||
@@ -30,14 +37,14 @@ _____
|
||||
|
||||
The MuJoCo app needs to be run at least once before the native library can be used, in order to register the library as
|
||||
a trusted binary. Then, copy the dynamic library file from
|
||||
``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.2.5.dylib`` (it can be
|
||||
``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.3.1.dylib`` (it can be
|
||||
found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``.
|
||||
|
||||
Linux
|
||||
_____
|
||||
|
||||
Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from
|
||||
``~/.mujoco/mujoco-3.2.5/lib/libmujoco.so.3.2.5`` and rename it as ``libmujoco.so``.
|
||||
``~/.mujoco/mujoco-3.3.1/lib/libmujoco.so.3.3.1`` and rename it as ``libmujoco.so``.
|
||||
|
||||
Windows
|
||||
_______
|
||||
@@ -115,7 +122,7 @@ This design principle has several implications:
|
||||
- The layout of MuJoCo components in the GameObject hierarchy determines the layout of the resulting MuJoCo model.
|
||||
Therefore, we adopt a design rule that **every game object must have at most one MuJoCo component**.
|
||||
- We rely on Unity for spatial configuration, which requires vector components to be `swizzled
|
||||
<https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)>`_ since Unity uses left-handed frames with Y as the
|
||||
<https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)>`__ since Unity uses left-handed frames with Y as the
|
||||
vertical axis, while MuJoCo uses right-handed frames with Z as the vertical axis.
|
||||
- Unity transform scaling affects positions, orientations, and scale of the entire game object subtree. However, MuJoCo
|
||||
doesn’t support collision of skewed cylinders and capsules (skewed spheres are supported via the ellipsoid primitive).
|
||||
@@ -145,10 +152,6 @@ effects:
|
||||
material assets for geom RGBA specification.
|
||||
- It allows the importer to handle :ref:`\<include\> <include>` elements without replicating MuJoCo’s file-system
|
||||
workflow.
|
||||
- The current version of MuJoCo generates MJCF files with explicit :ref:`\<inertial\> <body-inertial>` elements, even when
|
||||
the original model uses geoms for implicit definition of the body inertia. If you plan to change geom properties of
|
||||
an imported model, remove these auto-generated ``MjInertial`` components manually. We plan to address this in a
|
||||
future release of MuJoCo.
|
||||
|
||||
In Unity, there is no equivalent to MJCF’s “cascading” :ref:`\<default\> <default>` clauses. Therefore, components in
|
||||
Unity reflect the corresponding elements’ state after applying all the relevant default classes, and the class structure
|
||||
@@ -177,9 +180,10 @@ Scene recreation maintains continuity of physics and state in the following way:
|
||||
persisted.
|
||||
4. The MuJoCo state (for the joints that persisted) is set from the cache, and Unity transforms are synchronized.
|
||||
|
||||
Because the MuJoCo library doesn’t (yet) expose an API for scene editing, adding and removing MuJoCo components causes
|
||||
complete scene recreation. This can be expensive for large models or if it happens frequently. We expect this
|
||||
performance limitation to be lifted in future versions of MuJoCo.
|
||||
MuJoCo has functionality for dynamic scene editing (through :ref:`mjSpec`), however, this is not yet
|
||||
supported in the Unity plugin. Therefore, adding and removing MuJoCo components causes complete scene recreation. This
|
||||
can be expensive for large models or if it happens frequently. We intend to lift this performance limitation to be in a
|
||||
future versions of the plugin.
|
||||
|
||||
Global Settings
|
||||
_______________
|
||||
@@ -323,12 +327,26 @@ The plug-in allows using arbitrary Unity meshes for MuJoCo collision. At model
|
||||
<http://www.qhull.org/>`__ to create a convex hull of the mesh, and uses that for collisions. Currently the computed
|
||||
convex hull is not visible in Unity, but we intend to expose it in future versions.
|
||||
|
||||
Height fields
|
||||
_____________
|
||||
|
||||
MuJoCo hfields are represented in Unity through terrain gameobjects. This allows the use of the terrain editing tools
|
||||
available in Unity to generate shapes for collisions with MuJoCo. When selecting hfield type in the Unity geom
|
||||
component, the right click context menu provides utility to add the corresponding Unity terrain to the scene. The data
|
||||
from the terrain is dynamically kept in sync with the simulation.
|
||||
|
||||
MuJoCo plugins
|
||||
______________
|
||||
|
||||
The current version of the Unity package does not support loading MJCF scenes that use :ref:`MuJoCo plugins<exPlugin>` such as
|
||||
`elasticity <https://github.com/google-deepmind/mujoco/tree/main/plugin/elasticity#readme>`__ . Adding basic functionality to do this will be part of an upcoming release.
|
||||
|
||||
Interaction with External Processes
|
||||
___________________________________
|
||||
|
||||
Roboti’s `MuJoCo plug-in for Unity <https://roboti.us/download.html>`_ steps the simulation in an external Python
|
||||
Roboti’s `MuJoCo plug-in for Unity <https://roboti.us/download.html>`__ steps the simulation in an external Python
|
||||
process, and uses Unity only for rendering. In contrast, our plug-in relies on Unity to step the simulation. It should
|
||||
be possible to use our plug-in while an external process "drives" the simulation, for example by setting ``qpos``,
|
||||
calling ``mj_kinematics``, synchronizing the transforms, and then using Unity to render or compute game logic. In order
|
||||
to establish communication with an external process, you can use Unity's `ML-Agents
|
||||
<https://github.com/Unity-Technologies/ml-agents>`_ package.
|
||||
<https://github.com/Unity-Technologies/ml-agents>`__ package.
|
||||
|
||||
+15
-10
@@ -197,7 +197,8 @@ struct mjData_ {
|
||||
int nf; // number of friction constraints
|
||||
int nl; // number of limit constraints
|
||||
int nefc; // number of constraints
|
||||
int nnzJ; // number of non-zeros in constraint Jacobian
|
||||
int nJ; // number of non-zeros in constraint Jacobian
|
||||
int nA; // number of non-zeros in constraint inverse inertia matrix
|
||||
int nisland; // number of detected constraint islands
|
||||
|
||||
// global properties
|
||||
@@ -291,8 +292,8 @@ struct mjData_ {
|
||||
mjtNum* actuator_length; // actuator lengths (nu x 1)
|
||||
int* moment_rownnz; // number of non-zeros in actuator_moment row (nu x 1)
|
||||
int* moment_rowadr; // row start address in colind array (nu x 1)
|
||||
int* moment_colind; // column indices in sparse Jacobian (nu x nv)
|
||||
mjtNum* actuator_moment; // actuator moments (nu x nv)
|
||||
int* moment_colind; // column indices in sparse Jacobian (nJmom x 1)
|
||||
mjtNum* actuator_moment; // actuator moments (nJmom x 1)
|
||||
|
||||
// computed by mj_fwdPosition/mj_crb
|
||||
mjtNum* crb; // com-based composite inertia and mass (nbody x 10)
|
||||
@@ -301,7 +302,6 @@ struct mjData_ {
|
||||
// computed by mj_fwdPosition/mj_factorM
|
||||
mjtNum* qLD; // L'*D*L factorization of M (sparse) (nM x 1)
|
||||
mjtNum* qLDiagInv; // 1/diag(D) (nv x 1)
|
||||
mjtNum* qLDiagSqrtInv; // 1/sqrt(diag(D)) (nv x 1)
|
||||
|
||||
// computed by mj_collisionTree
|
||||
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
|
||||
@@ -340,12 +340,17 @@ struct mjData_ {
|
||||
int* B_rownnz; // body-dof: non-zeros in each row (nbody x 1)
|
||||
int* B_rowadr; // body-dof: address of each row in B_colind (nbody x 1)
|
||||
int* B_colind; // body-dof: column indices of non-zeros (nB x 1)
|
||||
int* M_rownnz; // inertia: non-zeros in each row (nv x 1)
|
||||
int* M_rowadr; // inertia: address of each row in M_colind (nv x 1)
|
||||
int* M_colind; // inertia: column indices of non-zeros (nM x 1)
|
||||
int* mapM2M; // index mapping from M (legacy) to M (CSR) (nM x 1)
|
||||
int* C_rownnz; // reduced dof-dof: non-zeros in each row (nv x 1)
|
||||
int* C_rowadr; // reduced dof-dof: address of each row in C_colind (nv x 1)
|
||||
int* C_colind; // reduced dof-dof: column indices of non-zeros (nC x 1)
|
||||
int* mapM2C; // index mapping from M to C (nC x 1)
|
||||
int* D_rownnz; // dof-dof: non-zeros in each row (nv x 1)
|
||||
int* D_rowadr; // dof-dof: address of each row in D_colind (nv x 1)
|
||||
int* D_diag; // dof-dof: index of diagonal element (nv x 1)
|
||||
int* D_colind; // dof-dof: column indices of non-zeros (nD x 1)
|
||||
int* mapM2D; // index mapping from M to D (nD x 1)
|
||||
int* mapD2M; // index mapping from D to M (nM x 1)
|
||||
@@ -389,13 +394,13 @@ struct mjData_ {
|
||||
int* efc_J_rownnz; // number of non-zeros in constraint Jacobian row (nefc x 1)
|
||||
int* efc_J_rowadr; // row start address in colind array (nefc x 1)
|
||||
int* efc_J_rowsuper; // number of subsequent rows in supernode (nefc x 1)
|
||||
int* efc_J_colind; // column indices in constraint Jacobian (nnzJ x 1)
|
||||
int* efc_J_colind; // column indices in constraint Jacobian (nJ x 1)
|
||||
int* efc_JT_rownnz; // number of non-zeros in constraint Jacobian row T (nv x 1)
|
||||
int* efc_JT_rowadr; // row start address in colind array T (nv x 1)
|
||||
int* efc_JT_rowsuper; // number of subsequent rows in supernode T (nv x 1)
|
||||
int* efc_JT_colind; // column indices in constraint Jacobian T (nnzJ x 1)
|
||||
mjtNum* efc_J; // constraint Jacobian (nnzJ x 1)
|
||||
mjtNum* efc_JT; // constraint Jacobian transposed (nnzJ x 1)
|
||||
int* efc_JT_colind; // column indices in constraint Jacobian T (nJ x 1)
|
||||
mjtNum* efc_J; // constraint Jacobian (nJ x 1)
|
||||
mjtNum* efc_JT; // constraint Jacobian transposed (nJ x 1)
|
||||
mjtNum* efc_pos; // constraint position (equality, contact) (nefc x 1)
|
||||
mjtNum* efc_margin; // inclusion margin (contact) (nefc x 1)
|
||||
mjtNum* efc_frictionloss; // frictionloss (friction) (nefc x 1)
|
||||
@@ -419,8 +424,8 @@ struct mjData_ {
|
||||
// computed by mj_projectConstraint (PGS solver)
|
||||
int* efc_AR_rownnz; // number of non-zeros in AR (nefc x 1)
|
||||
int* efc_AR_rowadr; // row start address in colind array (nefc x 1)
|
||||
int* efc_AR_colind; // column indices in sparse AR (nefc x nefc)
|
||||
mjtNum* efc_AR; // J*inv(M)*J' + R (nefc x nefc)
|
||||
int* efc_AR_colind; // column indices in sparse AR (nA x 1)
|
||||
mjtNum* efc_AR; // J*inv(M)*J' + R (nA x 1)
|
||||
|
||||
//-------------------- arena-allocated: POSITION, VELOCITY dependent
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define mjMAXIMP 0.9999 // maximum constraint impedance
|
||||
#define mjMAXCONPAIR 50 // maximum number of contacts per geom pair
|
||||
#define mjMAXTREEDEPTH 50 // maximum bounding volume hierarchy depth
|
||||
#define mjMAXFLEXNODES 27 // maximum number of flex nodes
|
||||
|
||||
|
||||
//---------------------------------- sizes ---------------------------------------------------------
|
||||
@@ -43,6 +44,7 @@
|
||||
#define mjNSOLVER 200 // size of one mjData.solver array
|
||||
#define mjNISLAND 20 // number of mjData.solver arrays
|
||||
|
||||
|
||||
//---------------------------------- enum types (mjt) ----------------------------------------------
|
||||
|
||||
typedef enum mjtDisableBit_ { // disable default feature bitflags
|
||||
@@ -62,8 +64,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
|
||||
mjDSBL_MIDPHASE = 1<<13, // mid-phase collision filtering
|
||||
mjDSBL_EULERDAMP = 1<<14, // implicit integration of joint damping in Euler integrator
|
||||
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;
|
||||
|
||||
|
||||
@@ -75,9 +78,8 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
|
||||
// experimental features:
|
||||
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
|
||||
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;
|
||||
|
||||
|
||||
@@ -266,7 +268,9 @@ typedef enum mjtObj_ { // type of MujoCo object
|
||||
mjNOBJECT, // number of object types
|
||||
|
||||
// meta elements, do not appear in mjModel
|
||||
mjOBJ_FRAME = 100 // frame
|
||||
mjOBJ_FRAME = 100, // frame
|
||||
mjOBJ_DEFAULT // default
|
||||
|
||||
} mjtObj;
|
||||
|
||||
|
||||
@@ -347,6 +351,8 @@ typedef enum mjtSensor_ { // type of sensor
|
||||
mjSENS_GEOMFROMTO, // segment between two geoms
|
||||
|
||||
// global sensors
|
||||
mjSENS_E_POTENTIAL, // potential energy
|
||||
mjSENS_E_KINETIC, // kinetic energy
|
||||
mjSENS_CLOCK, // simulation time
|
||||
|
||||
// plugin-controlled sensors
|
||||
@@ -605,6 +611,7 @@ struct mjModel_ {
|
||||
int ncam; // number of cameras
|
||||
int nlight; // number of lights
|
||||
int nflex; // number of flexes
|
||||
int nflexnode; // number of dofs in all flexes
|
||||
int nflexvert; // number of vertices in all flexes
|
||||
int nflexedge; // number of edges in all flexes
|
||||
int nflexelem; // number of elements in all flexes
|
||||
@@ -619,6 +626,9 @@ struct mjModel_ {
|
||||
int nmeshtexcoord; // number of texcoords in all meshes
|
||||
int nmeshface; // number of triangular faces in all meshes
|
||||
int nmeshgraph; // number of ints in mesh auxiliary data
|
||||
int nmeshpoly; // number of polygons in all meshes
|
||||
int nmeshpolyvert; // number of vertices in all polygons
|
||||
int nmeshpolymap; // number of polygons in vertex map
|
||||
int nskin; // number of skins
|
||||
int nskinvert; // number of vertices in all skins
|
||||
int nskintexvert; // number of vertiex with texcoords in all skins
|
||||
@@ -655,19 +665,20 @@ struct mjModel_ {
|
||||
int nuser_actuator; // number of mjtNums in actuator_user
|
||||
int nuser_sensor; // number of mjtNums in sensor_user
|
||||
int nnames; // number of chars in all names
|
||||
int nnames_map; // number of slots in the names hash map
|
||||
int npaths; // number of chars in all paths
|
||||
|
||||
// sizes set after mjModel construction (only affect mjData)
|
||||
// sizes set after mjModel construction
|
||||
int nnames_map; // number of slots in the names hash map
|
||||
int nM; // number of non-zeros in sparse inertia matrix
|
||||
int nB; // number of non-zeros in sparse body-dof matrix
|
||||
int nC; // number of non-zeros in sparse reduced dof-dof matrix
|
||||
int nD; // number of non-zeros in sparse dof-dof matrix
|
||||
int nJmom; // number of non-zeros in sparse actuator_moment matrix
|
||||
int ntree; // number of kinematic trees under world body
|
||||
int ngravcomp; // number of bodies with nonzero gravcomp
|
||||
int nemax; // number of potential equality-constraint rows
|
||||
int njmax; // number of available rows in constraint Jacobian
|
||||
int nconmax; // number of potential contacts in contact list
|
||||
int njmax; // number of available rows in constraint Jacobian (legacy)
|
||||
int nconmax; // number of potential contacts in contact list (legacy)
|
||||
int nuserdata; // number of mjtNums reserved for the user
|
||||
int nsensordata; // number of mjtNums in sensor data vector
|
||||
int npluginstate; // number of mjtNums in plugin state vector
|
||||
@@ -856,6 +867,9 @@ struct mjModel_ {
|
||||
int* flex_dim; // 1: lines, 2: triangles, 3: tetrahedra (nflex x 1)
|
||||
int* flex_matid; // material id for rendering (nflex x 1)
|
||||
int* flex_group; // group for visibility (nflex x 1)
|
||||
int* flex_interp; // interpolation (0: vertex, 1: nodes) (nflex x 1)
|
||||
int* flex_nodeadr; // first node address (nflex x 1)
|
||||
int* flex_nodenum; // number of nodes (nflex x 1)
|
||||
int* flex_vertadr; // first vertex address (nflex x 1)
|
||||
int* flex_vertnum; // number of vertices (nflex x 1)
|
||||
int* flex_edgeadr; // first edge address (nflex x 1)
|
||||
@@ -869,15 +883,19 @@ struct mjModel_ {
|
||||
int* flex_evpairadr; // first evpair address (nflex x 1)
|
||||
int* flex_evpairnum; // number of evpairs (nflex x 1)
|
||||
int* flex_texcoordadr; // address in flex_texcoord; -1: none (nflex x 1)
|
||||
int* flex_nodebodyid; // node body ids (nflexnode x 1)
|
||||
int* flex_vertbodyid; // vertex body ids (nflexvert x 1)
|
||||
int* flex_edge; // edge vertex ids (2 per edge) (nflexedge x 2)
|
||||
int* flex_elem; // element vertex ids (dim+1 per elem) (nflexelemdata x 1)
|
||||
int* flex_elemtexcoord; // element texture coordinates (dim+1) (nflexelemdata x 1)
|
||||
int* flex_elemedge; // element edge ids (nflexelemedge x 1)
|
||||
int* flex_elemlayer; // element distance from surface, 3D only (nflexelem x 1)
|
||||
int* flex_shell; // shell fragment vertex ids (dim per frag) (nflexshelldata x 1)
|
||||
int* flex_evpair; // (element, vertex) collision pairs (nflexevpair x 2)
|
||||
mjtNum* flex_vert; // vertex positions in local body frames (nflexvert x 3)
|
||||
mjtNum* flex_xvert0; // Cartesian vertex positions in qpos0 (nflexvert x 3)
|
||||
mjtNum* flex_vert0; // vertex positions in qpos0 on [0, 1]^d (nflexvert x 3)
|
||||
mjtNum* flex_node; // node positions in local body frames (nflexnode x 3)
|
||||
mjtNum* flex_node0; // Cartesian node positions in qpos0 (nflexnode x 3)
|
||||
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
|
||||
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
|
||||
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
|
||||
@@ -918,6 +936,15 @@ struct mjModel_ {
|
||||
mjtNum* mesh_pos; // translation applied to asset vertices (nmesh x 3)
|
||||
mjtNum* mesh_quat; // rotation applied to asset vertices (nmesh x 4)
|
||||
int* mesh_pathadr; // address of asset path for mesh; -1: none (nmesh x 1)
|
||||
int* mesh_polynum; // number of polygons per mesh (nmesh x 1)
|
||||
int* mesh_polyadr; // first polygon address per mesh (nmesh x 1)
|
||||
mjtNum* mesh_polynormal; // all polygon normals (nmeshpoly x 3)
|
||||
int* mesh_polyvertadr; // polygon vertex start address (nmeshpoly x 1)
|
||||
int* mesh_polyvertnum; // number of vertices per polygon (nmeshpoly x 1)
|
||||
int* mesh_polyvert; // all polygon vertices (nmeshpolyvert x 1)
|
||||
int* mesh_polymapadr; // first polygon address per vertex (nmeshvert x 1)
|
||||
int* mesh_polymapnum; // number of polygons per vertex (nmeshvert x 1)
|
||||
int* mesh_polymap; // vertex to polygon map (nmeshpolymap x 1)
|
||||
|
||||
// skins
|
||||
int* skin_matid; // skin material id; -1: none (nskin x 1)
|
||||
|
||||
@@ -48,7 +48,7 @@ typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* n
|
||||
// callback for checking if the current resource was modified from the time
|
||||
// specified by the timestamp
|
||||
// returns 0 if the resource's timestamp matches the provided timestamp
|
||||
// returns > 0 if the the resource is younger than the given timestamp
|
||||
// returns > 0 if the resource is younger than the given timestamp
|
||||
// returns < 0 if the resource is older than the given timestamp
|
||||
typedef int (*mjfResourceModified)(const mjResource* resource, const char* timestamp);
|
||||
|
||||
|
||||
@@ -115,14 +115,14 @@ struct mjrContext_ { // custom OpenGL context
|
||||
unsigned int auxColor_r[mjNAUX]; // auxiliary color buffer for resolving
|
||||
|
||||
// materials with textures
|
||||
int mat_texid[mjMAXMATERIAL*mjNTEXROLE]; // material texture ids (-1: no texture)
|
||||
int mat_texuniform[mjMAXMATERIAL]; // texture repetition for 2d mapping
|
||||
int mat_texrepeat[mjMAXMATERIAL*2]; // texture repetition for 2d mapping
|
||||
int mat_texid[mjMAXMATERIAL*mjNTEXROLE]; // material texture ids (-1: no texture)
|
||||
int mat_texuniform[mjMAXMATERIAL]; // uniform cube mapping
|
||||
float mat_texrepeat[mjMAXMATERIAL*2]; // texture repetition for 2d mapping
|
||||
|
||||
// texture objects and info
|
||||
int ntexture; // number of allocated textures
|
||||
int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture)
|
||||
unsigned int texture[mjMAXTEXTURE]; // texture names
|
||||
int ntexture; // number of allocated textures
|
||||
int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture)
|
||||
unsigned int texture[mjMAXTEXTURE]; // texture names
|
||||
|
||||
// displaylist starting positions
|
||||
unsigned int basePlane; // all planes from model
|
||||
@@ -161,13 +161,13 @@ struct mjrContext_ { // custom OpenGL context
|
||||
int windowDoublebuffer; // is default/window framebuffer double buffered
|
||||
|
||||
// framebuffer
|
||||
int currentBuffer; // currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN
|
||||
int currentBuffer; // currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN
|
||||
|
||||
// pixel output format
|
||||
int readPixelFormat; // default color pixel format for mjr_readPixels
|
||||
int readPixelFormat; // default color pixel format for mjr_readPixels
|
||||
|
||||
// depth output format
|
||||
int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR
|
||||
int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR
|
||||
};
|
||||
typedef struct mjrContext_ mjrContext;
|
||||
|
||||
|
||||
+22
-15
@@ -62,10 +62,11 @@ typedef enum mjtGeomInertia_ { // type of inertia inference
|
||||
} mjtGeomInertia;
|
||||
|
||||
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjINERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjINERTIA_EXACT, // exact mesh inertia
|
||||
mjINERTIA_LEGACY, // legacy mesh inertia
|
||||
typedef enum mjtMeshInertia_ { // type of mesh inertia
|
||||
mjMESH_INERTIA_CONVEX = 0, // convex mesh inertia
|
||||
mjMESH_INERTIA_EXACT, // exact mesh inertia
|
||||
mjMESH_INERTIA_LEGACY, // legacy mesh inertia
|
||||
mjMESH_INERTIA_SHELL // shell mesh inertia
|
||||
} mjtMeshInertia;
|
||||
|
||||
|
||||
@@ -121,30 +122,34 @@ typedef struct mjsElement_ { // element type, do not modify
|
||||
} mjsElement;
|
||||
|
||||
|
||||
typedef struct mjSpec_ { // model specification
|
||||
mjsElement* element; // element type
|
||||
mjString* modelname; // model name
|
||||
|
||||
// compiler settings
|
||||
typedef struct mjsCompiler_ { // compiler options
|
||||
mjtByte autolimits; // infer "limited" attribute based on range
|
||||
double boundmass; // enforce minimum body mass
|
||||
double boundinertia; // enforce minimum body diagonal inertia
|
||||
double settotalmass; // rescale masses and inertias; <=0: ignore
|
||||
mjtByte balanceinertia; // automatically impose A + B >= C rule
|
||||
mjtByte strippath; // automatically strip paths from mesh files
|
||||
mjtByte fitaabb; // meshfit to aabb instead of inertia box
|
||||
mjtByte degree; // angles in radians or degrees
|
||||
char eulerseq[3]; // sequence for euler rotations
|
||||
mjString* meshdir; // mesh and hfield directory
|
||||
mjString* texturedir; // texture directory
|
||||
mjtByte discardvisual; // discard visual geoms in parser
|
||||
mjtByte convexhull; // compute mesh convex hulls
|
||||
mjtByte usethread; // use multiple threads to speed up compiler
|
||||
mjtByte fusestatic; // fuse static bodies with parent
|
||||
int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom)
|
||||
int inertiagrouprange[2]; // range of geom groups used to compute inertia
|
||||
int alignfree; // align free joints with inertial frame
|
||||
mjLROpt LRopt; // options for lengthrange computation
|
||||
} mjsCompiler;
|
||||
|
||||
|
||||
typedef struct mjSpec_ { // model specification
|
||||
mjsElement* element; // element type
|
||||
mjString* modelname; // model name
|
||||
|
||||
// compiler data
|
||||
mjsCompiler compiler; // compiler options
|
||||
mjtByte strippath; // automatically strip paths from mesh files
|
||||
mjString* meshdir; // mesh and hfield directory
|
||||
mjString* texturedir; // texture directory
|
||||
|
||||
// engine data
|
||||
mjOption option; // physics options
|
||||
@@ -435,10 +440,13 @@ typedef struct mjsFlex_ { // flex specification
|
||||
double thickness; // thickness (2D only)
|
||||
|
||||
// mesh properties
|
||||
mjStringVec* nodebody; // node body names
|
||||
mjStringVec* vertbody; // vertex body names
|
||||
mjDoubleVec* node; // node positions
|
||||
mjDoubleVec* vert; // vertex positions
|
||||
mjIntVec* elem; // element vertex ids
|
||||
mjFloatVec* texcoord; // vertex texture coordinates
|
||||
mjIntVec* elemtexcoord; // element texture coordinates
|
||||
|
||||
// other
|
||||
mjString* info; // message appended to compiler errors
|
||||
@@ -453,14 +461,13 @@ typedef struct mjsMesh_ { // mesh specification
|
||||
double refpos[3]; // reference position
|
||||
double refquat[4]; // reference orientation
|
||||
double scale[3]; // rescale mesh
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact)
|
||||
mjtMeshInertia inertia; // inertia type (convex, legacy, exact, shell)
|
||||
mjtByte smoothnormal; // do not exclude large-angle faces from normals
|
||||
int maxhullvert; // maximum vertex count for the convex hull
|
||||
mjFloatVec* uservert; // user vertex data
|
||||
mjFloatVec* usernormal; // user normal data
|
||||
mjFloatVec* usertexcoord; // user texcoord data
|
||||
mjIntVec* userface; // user vertex indices
|
||||
mjIntVec* userfacenormal; // user normal indices
|
||||
mjIntVec* userfacetexcoord; // user texcoord indices
|
||||
mjsPlugin plugin; // sdf plugin
|
||||
mjString* info; // message appended to compiler errors
|
||||
|
||||
@@ -304,7 +304,7 @@ struct mjUI_ { // entire UI
|
||||
|
||||
// UI sizes (framebuffer units)
|
||||
int width; // width
|
||||
int height; // current heigth
|
||||
int height; // current height
|
||||
int maxheight; // height when all sections open
|
||||
int scroll; // scroll from top of UI
|
||||
|
||||
|
||||
@@ -516,9 +516,14 @@ struct mjvSceneState_ {
|
||||
int* flex_dim;
|
||||
int* flex_matid;
|
||||
int* flex_group;
|
||||
int* flex_interp;
|
||||
int* flex_nodeadr;
|
||||
int* flex_nodenum;
|
||||
int* flex_nodebodyid;
|
||||
int* flex_vertadr;
|
||||
int* flex_vertnum;
|
||||
int* flex_elem;
|
||||
int* flex_elemtexcoord;
|
||||
int* flex_elemlayer;
|
||||
int* flex_elemadr;
|
||||
int* flex_elemnum;
|
||||
@@ -529,8 +534,11 @@ struct mjvSceneState_ {
|
||||
int* flex_texcoordadr;
|
||||
int* flex_bvhadr;
|
||||
int* flex_bvhnum;
|
||||
mjtByte* flex_centered;
|
||||
mjtNum* flex_node;
|
||||
mjtNum* flex_radius;
|
||||
float* flex_rgba;
|
||||
float* flex_texcoord;
|
||||
|
||||
int* hfield_pathadr;
|
||||
|
||||
|
||||
+56
-25
@@ -81,6 +81,7 @@
|
||||
XMJV( ncam ) \
|
||||
XMJV( nlight ) \
|
||||
XMJV( nflex ) \
|
||||
X ( nflexnode ) \
|
||||
XMJV( nflexvert ) \
|
||||
X ( nflexedge ) \
|
||||
X ( nflexelem ) \
|
||||
@@ -95,6 +96,9 @@
|
||||
X ( nmeshtexcoord ) \
|
||||
X ( nmeshface ) \
|
||||
X ( nmeshgraph ) \
|
||||
X ( nmeshpoly ) \
|
||||
X ( nmeshpolyvert ) \
|
||||
X ( nmeshpolymap ) \
|
||||
XMJV( nskin ) \
|
||||
XMJV( nskinvert ) \
|
||||
X ( nskintexvert ) \
|
||||
@@ -137,11 +141,12 @@
|
||||
X ( nB ) \
|
||||
X ( nC ) \
|
||||
X ( nD ) \
|
||||
X ( nJmom ) \
|
||||
XMJV( ntree ) \
|
||||
X ( ngravcomp ) \
|
||||
X ( nemax ) \
|
||||
X ( njmax ) \
|
||||
X ( nconmax ) \
|
||||
XMJV( ntree ) \
|
||||
X ( ngravcomp ) \
|
||||
X ( nuserdata ) \
|
||||
XMJV( nsensordata ) \
|
||||
X ( npluginstate ) \
|
||||
@@ -325,6 +330,9 @@
|
||||
XMJV( int, flex_dim, nflex, 1 ) \
|
||||
XMJV( int, flex_matid, nflex, 1 ) \
|
||||
XMJV( int, flex_group, nflex, 1 ) \
|
||||
XMJV( int, flex_interp, nflex, 1 ) \
|
||||
XMJV( int, flex_nodeadr, nflex, 1 ) \
|
||||
XMJV( int, flex_nodenum, nflex, 1 ) \
|
||||
XMJV( int, flex_vertadr, nflex, 1 ) \
|
||||
XMJV( int, flex_vertnum, nflex, 1 ) \
|
||||
X ( int, flex_edgeadr, nflex, 1 ) \
|
||||
@@ -338,15 +346,19 @@
|
||||
X ( int, flex_evpairadr, nflex, 1 ) \
|
||||
X ( int, flex_evpairnum, nflex, 1 ) \
|
||||
XMJV( int, flex_texcoordadr, nflex, 1 ) \
|
||||
XMJV( int, flex_nodebodyid, nflexnode, 1 ) \
|
||||
X ( int, flex_vertbodyid, nflexvert, 1 ) \
|
||||
X ( int, flex_edge, nflexedge, 2 ) \
|
||||
XMJV( int, flex_elem, nflexelemdata, 1 ) \
|
||||
XMJV( int, flex_elemtexcoord, nflexelemdata, 1 ) \
|
||||
X ( int, flex_elemedge, nflexelemedge, 1 ) \
|
||||
XMJV( int, flex_elemlayer, nflexelem, 1 ) \
|
||||
XMJV( int, flex_shell, nflexshelldata,1 ) \
|
||||
X ( int, flex_evpair, nflexevpair, 2 ) \
|
||||
X ( mjtNum, flex_vert, nflexvert, 3 ) \
|
||||
X ( mjtNum, flex_xvert0, nflexvert, 3 ) \
|
||||
X ( mjtNum, flex_vert0, nflexvert, 3 ) \
|
||||
XMJV( mjtNum, flex_node, nflexnode, 3 ) \
|
||||
X ( mjtNum, flex_node0, nflexnode, 3 ) \
|
||||
X ( mjtNum, flexedge_length0, nflexedge, 1 ) \
|
||||
X ( mjtNum, flexedge_invweight0, nflexedge, 1 ) \
|
||||
XMJV( mjtNum, flex_radius, nflex, 1 ) \
|
||||
@@ -357,12 +369,12 @@
|
||||
X ( mjtByte, flex_edgeequality, nflex, 1 ) \
|
||||
X ( mjtByte, flex_rigid, nflex, 1 ) \
|
||||
X ( mjtByte, flexedge_rigid, nflexedge, 1 ) \
|
||||
X ( mjtByte, flex_centered, nflex, 1 ) \
|
||||
XMJV( mjtByte, flex_centered, nflex, 1 ) \
|
||||
XMJV( mjtByte, flex_flatskin, nflex, 1 ) \
|
||||
XMJV( int, flex_bvhadr, nflex, 1 ) \
|
||||
XMJV( int, flex_bvhnum, nflex, 1 ) \
|
||||
XMJV( float, flex_rgba, nflex, 4 ) \
|
||||
X ( float, flex_texcoord, nflextexcoord, 2 ) \
|
||||
XMJV( float, flex_texcoord, nflextexcoord, 2 ) \
|
||||
X ( int, mesh_vertadr, nmesh, 1 ) \
|
||||
X ( int, mesh_vertnum, nmesh, 1 ) \
|
||||
X ( int, mesh_normaladr, nmesh, 1 ) \
|
||||
@@ -377,14 +389,23 @@
|
||||
X ( mjtNum, mesh_scale, nmesh, 3 ) \
|
||||
X ( mjtNum, mesh_pos, nmesh, 3 ) \
|
||||
X ( mjtNum, mesh_quat, nmesh, 4 ) \
|
||||
X ( float, mesh_vert, nmeshvert, 3 ) \
|
||||
X ( float, mesh_normal, nmeshnormal, 3 ) \
|
||||
X ( float, mesh_texcoord, nmeshtexcoord, 2 ) \
|
||||
X ( int, mesh_face, nmeshface, 3 ) \
|
||||
X ( int, mesh_facenormal, nmeshface, 3 ) \
|
||||
X ( int, mesh_facetexcoord, nmeshface, 3 ) \
|
||||
X ( int, mesh_graph, nmeshgraph, 1 ) \
|
||||
XNV ( float, mesh_vert, nmeshvert, 3 ) \
|
||||
XNV ( float, mesh_normal, nmeshnormal, 3 ) \
|
||||
XNV ( float, mesh_texcoord, nmeshtexcoord, 2 ) \
|
||||
XNV ( int, mesh_face, nmeshface, 3 ) \
|
||||
XNV ( int, mesh_facenormal, nmeshface, 3 ) \
|
||||
XNV ( int, mesh_facetexcoord, nmeshface, 3 ) \
|
||||
XNV ( int, mesh_graph, nmeshgraph, 1 ) \
|
||||
XMJV( int, mesh_pathadr, nmesh, 1 ) \
|
||||
X ( int, mesh_polynum, nmesh, 1 ) \
|
||||
X ( int, mesh_polyadr, nmesh, 1 ) \
|
||||
X ( mjtNum, mesh_polynormal, nmeshpoly, 3 ) \
|
||||
X ( int, mesh_polyvertadr, nmeshpoly, 1 ) \
|
||||
X ( int, mesh_polyvertnum, nmeshpoly, 1 ) \
|
||||
X ( int, mesh_polyvert, nmeshpolyvert, 1 ) \
|
||||
X ( int, mesh_polymapadr, nmeshvert, 1 ) \
|
||||
X ( int, mesh_polymapnum, nmeshvert, 1 ) \
|
||||
X ( int, mesh_polymap, nmeshpolymap, 1 ) \
|
||||
XMJV( int, skin_matid, nskin, 1 ) \
|
||||
XMJV( int, skin_group, nskin, 1 ) \
|
||||
XMJV( float, skin_rgba, nskin, 4 ) \
|
||||
@@ -411,14 +432,14 @@
|
||||
X ( int, hfield_nrow, nhfield, 1 ) \
|
||||
X ( int, hfield_ncol, nhfield, 1 ) \
|
||||
X ( int, hfield_adr, nhfield, 1 ) \
|
||||
X ( float, hfield_data, nhfielddata, 1 ) \
|
||||
XNV ( float, hfield_data, nhfielddata, 1 ) \
|
||||
XMJV( int, hfield_pathadr, nhfield, 1 ) \
|
||||
X ( int, tex_type, ntex, 1 ) \
|
||||
X ( int, tex_height, ntex, 1 ) \
|
||||
X ( int, tex_width, ntex, 1 ) \
|
||||
X ( int, tex_nchannel, ntex, 1 ) \
|
||||
X ( int, tex_adr, ntex, 1 ) \
|
||||
X ( mjtByte, tex_data, ntexdata, 1 ) \
|
||||
XNV ( mjtByte, tex_data, ntexdata, 1 ) \
|
||||
XMJV( int, tex_pathadr, ntex, 1 ) \
|
||||
XMJV( int, mat_texid, nmat, mjNTEXROLE ) \
|
||||
XMJV( mjtByte, mat_texuniform, nmat, 1 ) \
|
||||
@@ -625,13 +646,12 @@
|
||||
X ( mjtNum, actuator_length, nu, 1 ) \
|
||||
X ( int, moment_rownnz, nu, 1 ) \
|
||||
X ( int, moment_rowadr, nu, 1 ) \
|
||||
X ( int, moment_colind, nu, MJ_M(nv) ) \
|
||||
X ( mjtNum, actuator_moment, nu, MJ_M(nv) ) \
|
||||
X ( int, moment_colind, nJmom, 1 ) \
|
||||
X ( mjtNum, actuator_moment, nJmom, 1 ) \
|
||||
X ( mjtNum, crb, nbody, 10 ) \
|
||||
X ( mjtNum, qM, nM, 1 ) \
|
||||
X ( mjtNum, qLD, nM, 1 ) \
|
||||
X ( mjtNum, qLDiagInv, nv, 1 ) \
|
||||
X ( mjtNum, qLDiagSqrtInv, nv, 1 ) \
|
||||
XMJV( mjtNum, bvh_aabb_dyn, nbvhdynamic, 6 ) \
|
||||
XMJV( mjtByte, bvh_active, nbvh, 1 ) \
|
||||
X ( mjtNum, flexedge_velocity, nflexedge, 1 ) \
|
||||
@@ -652,12 +672,17 @@
|
||||
X ( int, B_rownnz, nbody, 1 ) \
|
||||
X ( int, B_rowadr, nbody, 1 ) \
|
||||
X ( int, B_colind, nB, 1 ) \
|
||||
X ( int, M_rownnz, nv, 1 ) \
|
||||
X ( int, M_rowadr, nv, 1 ) \
|
||||
X ( int, M_colind, nM, 1 ) \
|
||||
X ( int, mapM2M, nM, 1 ) \
|
||||
X ( int, C_rownnz, nv, 1 ) \
|
||||
X ( int, C_rowadr, nv, 1 ) \
|
||||
X ( int, C_colind, nC, 1 ) \
|
||||
X ( int, mapM2C, nC, 1 ) \
|
||||
X ( int, D_rownnz, nv, 1 ) \
|
||||
X ( int, D_rowadr, nv, 1 ) \
|
||||
X ( int, D_diag, nv, 1 ) \
|
||||
X ( int, D_colind, nD, 1 ) \
|
||||
X ( int, mapM2D, nD, 1 ) \
|
||||
X ( int, mapD2M, nM, 1 ) \
|
||||
@@ -689,13 +714,13 @@
|
||||
X( int, efc_J_rownnz, MJ_D(nefc), 1 ) \
|
||||
X( int, efc_J_rowadr, MJ_D(nefc), 1 ) \
|
||||
X( int, efc_J_rowsuper, MJ_D(nefc), 1 ) \
|
||||
X( int, efc_J_colind, MJ_D(nnzJ), 1 ) \
|
||||
X( int, efc_J_colind, MJ_D(nJ), 1 ) \
|
||||
X( int, efc_JT_rownnz, MJ_M(nv), 1 ) \
|
||||
X( int, efc_JT_rowadr, MJ_M(nv), 1 ) \
|
||||
X( int, efc_JT_rowsuper, MJ_M(nv), 1 ) \
|
||||
X( int, efc_JT_colind, MJ_D(nnzJ), 1 ) \
|
||||
X( mjtNum, efc_J, MJ_D(nnzJ), 1 ) \
|
||||
X( mjtNum, efc_JT, MJ_D(nnzJ), 1 ) \
|
||||
X( int, efc_JT_colind, MJ_D(nJ), 1 ) \
|
||||
X( mjtNum, efc_J, MJ_D(nJ), 1 ) \
|
||||
X( mjtNum, efc_JT, MJ_D(nJ), 1 ) \
|
||||
X( mjtNum, efc_pos, MJ_D(nefc), 1 ) \
|
||||
X( mjtNum, efc_margin, MJ_D(nefc), 1 ) \
|
||||
X( mjtNum, efc_frictionloss, MJ_D(nefc), 1 ) \
|
||||
@@ -714,8 +739,8 @@
|
||||
#define MJDATA_ARENA_POINTERS_DUAL \
|
||||
X( int, efc_AR_rownnz, MJ_D(nefc), 1 ) \
|
||||
X( int, efc_AR_rowadr, MJ_D(nefc), 1 ) \
|
||||
X( int, efc_AR_colind, MJ_D(nefc), MJ_D(nefc) ) \
|
||||
X( mjtNum, efc_AR, MJ_D(nefc), MJ_D(nefc) )
|
||||
X( int, efc_AR_colind, MJ_D(nA), 1 ) \
|
||||
X( mjtNum, efc_AR, MJ_D(nA), 1 )
|
||||
|
||||
// array fields of mjData that are used for constraint islands
|
||||
#define MJDATA_ARENA_POINTERS_ISLAND \
|
||||
@@ -755,7 +780,8 @@
|
||||
X( int, nf ) \
|
||||
X( int, nl ) \
|
||||
X( int, nefc ) \
|
||||
X( int, nnzJ ) \
|
||||
X( int, nJ ) \
|
||||
X( int, nA ) \
|
||||
X( int, nisland ) \
|
||||
X( mjtNum, time ) \
|
||||
X( uintptr_t, threadpool )
|
||||
@@ -766,7 +792,7 @@
|
||||
X( size_t, maxuse_threadstack, mjMAXTHREAD, 1 ) \
|
||||
X( mjWarningStat, warning, mjNWARNING, 1 ) \
|
||||
X( mjTimerStat, timer, mjNTIMER, 1 ) \
|
||||
X( mjSolverStat, solver, mjNILSAND, mjNSOLVER ) \
|
||||
X( mjSolverStat, solver, mjNISLAND, mjNSOLVER ) \
|
||||
X( int, solver_niter, mjNISLAND, 1 ) \
|
||||
X( int, solver_nnz, mjNISLAND, 1 ) \
|
||||
X( mjtNum, solver_fwdinv, 2, 1 ) \
|
||||
@@ -778,4 +804,9 @@
|
||||
// redefine X to expand to nothing, and XMJV to do what's required
|
||||
#define XMJV X
|
||||
|
||||
// alias XNV to be the same as X
|
||||
// to obtain only X macros for fields that are relevant for mjvScene creation,
|
||||
// redefine XNV to expand to nothing
|
||||
#define XNV X
|
||||
|
||||
#endif // MUJOCO_MJXMACRO_H_
|
||||
|
||||
+62
-38
@@ -16,7 +16,7 @@
|
||||
#define MUJOCO_MUJOCO_H_
|
||||
|
||||
// header version; should match the library version as returned by mj_version()
|
||||
#define mjVERSION_HEADER 325
|
||||
#define mjVERSION_HEADER 331
|
||||
|
||||
// needed to define size_t, fabs and log10
|
||||
#include <stdlib.h>
|
||||
@@ -110,17 +110,18 @@ MJAPI mjModel* mj_compile(mjSpec* s, const mjVFS* vfs);
|
||||
// Recompile spec to model, preserving the state, return 0 on success.
|
||||
MJAPI int mj_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d);
|
||||
|
||||
// Update XML data structures with info from low-level model, save as MJCF.
|
||||
// Update XML data structures with info from low-level model created with mj_loadXML, save as MJCF.
|
||||
// If error is not NULL, it must have size error_sz.
|
||||
MJAPI int mj_saveLastXML(const char* filename, const mjModel* m, char* error, int error_sz);
|
||||
|
||||
// Free last XML model if loaded. Called internally at each load.
|
||||
MJAPI void mj_freeLastXML(void);
|
||||
|
||||
// Save spec to XML string, return 1 on success, 0 otherwise.
|
||||
// Save spec to XML string, return 0 on success, -1 on failure.
|
||||
// If length of the output buffer is too small, returns the required size.
|
||||
MJAPI int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int error_sz);
|
||||
|
||||
// Save spec to XML file, return 1 on success, 0 otherwise.
|
||||
// Save spec to XML file, return 0 on success, -1 otherwise.
|
||||
MJAPI int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz);
|
||||
|
||||
|
||||
@@ -241,6 +242,9 @@ MJAPI void mj_deleteSpec(mjSpec* s);
|
||||
// Activate plugin. Returns 0 on success.
|
||||
MJAPI int mjs_activatePlugin(mjSpec* s, const char* name);
|
||||
|
||||
// Turn deep copy on or off attach. Returns 0 on success.
|
||||
MJAPI int mjs_setDeepCopy(mjSpec* s, int deepcopy);
|
||||
|
||||
|
||||
//---------------------------------- Printing ------------------------------------------------------
|
||||
|
||||
@@ -253,11 +257,11 @@ MJAPI void mj_printModel(const mjModel* m, const char* filename);
|
||||
|
||||
// Print mjData to text file, specifying format.
|
||||
// float_format must be a valid printf-style format string for a single float value
|
||||
MJAPI void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
|
||||
MJAPI void mj_printFormattedData(const mjModel* m, const mjData* d, const char* filename,
|
||||
const char* float_format);
|
||||
|
||||
// Print data to text file.
|
||||
MJAPI void mj_printData(const mjModel* m, mjData* d, const char* filename);
|
||||
MJAPI void mj_printData(const mjModel* m, const mjData* d, const char* filename);
|
||||
|
||||
// Print matrix to screen.
|
||||
MJAPI void mju_printMat(const mjtNum* mat, int nr, int nc);
|
||||
@@ -364,7 +368,8 @@ MJAPI void mj_factorM(const mjModel* m, mjData* d);
|
||||
MJAPI void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
|
||||
|
||||
// Half of linear solve: x = sqrt(inv(D))*inv(L')*y
|
||||
MJAPI void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
|
||||
MJAPI void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y,
|
||||
const mjtNum* sqrtInvD, int n);
|
||||
|
||||
// Compute cvel, cdof_dot.
|
||||
MJAPI void mj_comVel(const mjModel* m, mjData* d);
|
||||
@@ -668,14 +673,6 @@ MJAPI void mjv_defaultFigure(mjvFigure* fig);
|
||||
MJAPI void mjv_initGeom(mjvGeom* geom, int type, const mjtNum size[3],
|
||||
const mjtNum pos[3], const mjtNum mat[9], const float rgba[4]);
|
||||
|
||||
// Set (type, size, pos, mat) for connector-type geom between given points.
|
||||
// Assume that mjv_initGeom was already called to set all other properties.
|
||||
// Width of mjGEOM_LINE is denominated in pixels.
|
||||
// Deprecated: use mjv_connector.
|
||||
MJAPI void mjv_makeConnector(mjvGeom* geom, int type, mjtNum width,
|
||||
mjtNum a0, mjtNum a1, mjtNum a2,
|
||||
mjtNum b0, mjtNum b1, mjtNum b2);
|
||||
|
||||
// Set (type, size, pos, mat) for connector-type geom between given points.
|
||||
// Assume that mjv_initGeom was already called to set all other properties.
|
||||
// Width of mjGEOM_LINE is denominated in pixels.
|
||||
@@ -700,6 +697,9 @@ MJAPI int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOptio
|
||||
const mjvPerturb* pert, mjvCamera* cam, int catmask,
|
||||
mjvScene* scn);
|
||||
|
||||
// Copy mjModel, skip large arrays not required for abstract visualization.
|
||||
MJAPI void mjv_copyModel(mjModel* dest, const mjModel* src);
|
||||
|
||||
// Set default scene state.
|
||||
MJAPI void mjv_defaultSceneState(mjvSceneState* scnstate);
|
||||
|
||||
@@ -975,12 +975,6 @@ MJAPI void mju_mulMatVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3
|
||||
// Multiply transposed 3-by-3 matrix by vector: res = mat' * vec.
|
||||
MJAPI void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
|
||||
|
||||
// Deprecated, use mju_mulMatVec3(res, mat, vec).
|
||||
MJAPI void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
|
||||
|
||||
// Deprecated, use mju_mulMatTVec3(res, mat, vec).
|
||||
MJAPI void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
|
||||
|
||||
// Compute cross-product: res = cross(a, b).
|
||||
MJAPI void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
|
||||
|
||||
@@ -1083,6 +1077,11 @@ MJAPI void mju_transformSpatial(mjtNum res[6], const mjtNum vec[6], int flg_forc
|
||||
|
||||
//---------------------------------- Sparse math ---------------------------------------------------
|
||||
|
||||
// Convert matrix from dense to sparse.
|
||||
// nnz is size of res and colind, return 1 if too small, 0 otherwise.
|
||||
MJAPI int mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
|
||||
int* rownnz, int* rowadr, int* colind, int nnz);
|
||||
|
||||
// Convert matrix from sparse to dense.
|
||||
MJAPI void mju_sparse2dense(mjtNum* res, const mjtNum* mat, int nr, int nc,
|
||||
const int* rownnz, const int* rowadr, const int* colind);
|
||||
@@ -1126,6 +1125,10 @@ MJAPI void mju_quatIntegrate(mjtNum quat[4], const mjtNum vel[3], mjtNum scale);
|
||||
// Construct quaternion performing rotation from z-axis to given vector.
|
||||
MJAPI void mju_quatZ2Vec(mjtNum quat[4], const mjtNum vec[3]);
|
||||
|
||||
// Extract 3D rotation from an arbitrary 3x3 matrix by refining the input quaternion.
|
||||
// Returns the number of iterations required to converge
|
||||
MJAPI int mju_mat2Rot(mjtNum quat[4], const mjtNum mat[9]);
|
||||
|
||||
// Convert sequence of Euler angles (radians) to quaternion.
|
||||
// seq[0,1,2] must be in 'xyzXYZ', lower/upper-case mean intrinsic/extrinsic rotations.
|
||||
MJAPI void mju_euler2Quat(mjtNum quat[4], const mjtNum euler[3], const char* seq);
|
||||
@@ -1423,44 +1426,50 @@ MJAPI mjsFrame* mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
|
||||
MJAPI mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
|
||||
const char* prefix, const char* suffix);
|
||||
|
||||
// Detach body from mjSpec, remove all references and delete the body, return 0 on success.
|
||||
// Attach child frame to a parent site, return the attached frame if success or NULL otherwise.
|
||||
MJAPI mjsFrame* mjs_attachFrameToSite(mjsSite* parent, const mjsFrame* child,
|
||||
const char* prefix, const char* suffix);
|
||||
|
||||
// Delete body and descendants from mjSpec, remove all references, return 0 on success.
|
||||
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b);
|
||||
|
||||
// Delete default class and descendants from mjSpec, remove all references, return 0 on success.
|
||||
MJAPI int mjs_detachDefault(mjSpec* s, mjsDefault* d);
|
||||
|
||||
//---------------------------------- Tree elements -------------------------------------------------
|
||||
|
||||
// Add child body to body, return child.
|
||||
MJAPI mjsBody* mjs_addBody(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add site to body, return site spec.
|
||||
MJAPI mjsSite* mjs_addSite(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsSite* mjs_addSite(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add joint to body.
|
||||
MJAPI mjsJoint* mjs_addJoint(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsJoint* mjs_addJoint(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add freejoint to body.
|
||||
MJAPI mjsJoint* mjs_addFreeJoint(mjsBody* body);
|
||||
|
||||
// Add geom to body.
|
||||
MJAPI mjsGeom* mjs_addGeom(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsGeom* mjs_addGeom(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add camera to body.
|
||||
MJAPI mjsCamera* mjs_addCamera(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsCamera* mjs_addCamera(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add light to body.
|
||||
MJAPI mjsLight* mjs_addLight(mjsBody* body, mjsDefault* def);
|
||||
MJAPI mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def);
|
||||
|
||||
// Add frame to body.
|
||||
MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
|
||||
|
||||
// Delete object corresponding to the given element.
|
||||
MJAPI void mjs_delete(mjsElement* element);
|
||||
// Delete object corresponding to the given element, return 0 on success.
|
||||
MJAPI int mjs_delete(mjsElement* element);
|
||||
|
||||
|
||||
//---------------------------------- Non-tree elements ---------------------------------------------
|
||||
|
||||
// Add actuator.
|
||||
MJAPI mjsActuator* mjs_addActuator(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsActuator* mjs_addActuator(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
// Add sensor.
|
||||
MJAPI mjsSensor* mjs_addSensor(mjSpec* s);
|
||||
@@ -1469,16 +1478,16 @@ MJAPI mjsSensor* mjs_addSensor(mjSpec* s);
|
||||
MJAPI mjsFlex* mjs_addFlex(mjSpec* s);
|
||||
|
||||
// Add contact pair.
|
||||
MJAPI mjsPair* mjs_addPair(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsPair* mjs_addPair(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
// Add excluded body pair.
|
||||
MJAPI mjsExclude* mjs_addExclude(mjSpec* s);
|
||||
|
||||
// Add equality.
|
||||
MJAPI mjsEquality* mjs_addEquality(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsEquality* mjs_addEquality(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
// Add tendon.
|
||||
MJAPI mjsTendon* mjs_addTendon(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsTendon* mjs_addTendon(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
// Wrap site using tendon.
|
||||
MJAPI mjsWrap* mjs_wrapSite(mjsTendon* tendon, const char* name);
|
||||
@@ -1514,7 +1523,7 @@ MJAPI mjsDefault* mjs_addDefault(mjSpec* s, const char* classname, const mjsDefa
|
||||
//---------------------------------- Assets --------------------------------------------------------
|
||||
|
||||
// Add mesh.
|
||||
MJAPI mjsMesh* mjs_addMesh(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsMesh* mjs_addMesh(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
// Add height field.
|
||||
MJAPI mjsHField* mjs_addHField(mjSpec* s);
|
||||
@@ -1526,7 +1535,7 @@ MJAPI mjsSkin* mjs_addSkin(mjSpec* s);
|
||||
MJAPI mjsTexture* mjs_addTexture(mjSpec* s);
|
||||
|
||||
// Add material.
|
||||
MJAPI mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def);
|
||||
MJAPI mjsMaterial* mjs_addMaterial(mjSpec* s, const mjsDefault* def);
|
||||
|
||||
|
||||
//---------------------------------- Find and get utilities ----------------------------------------
|
||||
@@ -1546,6 +1555,12 @@ MJAPI mjsElement* mjs_findElement(mjSpec* s, mjtObj type, const char* name);
|
||||
// Find child body by name.
|
||||
MJAPI mjsBody* mjs_findChild(mjsBody* body, const char* name);
|
||||
|
||||
// Get parent body.
|
||||
MJAPI mjsBody* mjs_getParent(mjsElement* element);
|
||||
|
||||
// Get parent frame.
|
||||
MJAPI mjsFrame* mjs_getFrame(mjsElement* element);
|
||||
|
||||
// Find frame by name.
|
||||
MJAPI mjsFrame* mjs_findFrame(mjSpec* s, const char* name);
|
||||
|
||||
@@ -1623,9 +1638,9 @@ MJAPI const double* mjs_getDouble(const mjDoubleVec* source, int* size);
|
||||
//---------------------------------- Spec utilities ------------------------------------------------
|
||||
|
||||
// Set element's default.
|
||||
MJAPI void mjs_setDefault(mjsElement* element, mjsDefault* def);
|
||||
MJAPI void mjs_setDefault(mjsElement* element, const mjsDefault* def);
|
||||
|
||||
// Set element's enlcosing frame.
|
||||
// Set element's enclosing frame.
|
||||
MJAPI void mjs_setFrame(mjsElement* dest, mjsFrame* frame);
|
||||
|
||||
// Resolve alternative orientations to quat, return error if any.
|
||||
@@ -1635,6 +1650,15 @@ MJAPI const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const c
|
||||
// Transform body into a frame.
|
||||
MJAPI mjsFrame* mjs_bodyToFrame(mjsBody** body);
|
||||
|
||||
// Set user payload, overriding the existing value for the specified key if present.
|
||||
MJAPI void mjs_setUserValue(mjsElement* element, const char* key, const void* data);
|
||||
|
||||
// Return user payload or NULL if none found.
|
||||
MJAPI const void* mjs_getUserValue(mjsElement* element, const char* key);
|
||||
|
||||
// Delete user payload.
|
||||
MJAPI void mjs_deleteUserValue(mjsElement* element, const char* key);
|
||||
|
||||
//---------------------------------- Element initialization ---------------------------------------
|
||||
|
||||
// Default spec attributes.
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
This package is a re-implementation of the
|
||||
[MuJoCo physics engine](https://github.com/google-deepmind/mujoco) in
|
||||
[JAX](https://github.com/google/jax). This library is developed and maintained
|
||||
[JAX](https://github.com/jax-ml/jax). This library is developed and maintained
|
||||
by Google DeepMind, and is kept up-to-date with the latest developments in
|
||||
MuJoCo itself.
|
||||
|
||||
|
||||
+15
-13
@@ -1,13 +1,15 @@
|
||||
-f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
|
||||
|
||||
jax[cuda12_local]==0.4.18; python_version >= '3.9' \
|
||||
--hash=sha256:c3ab72ea2f1c5d8ccf2561e79f6562fb2964629f3e55b3ac1c11c48b64c20336
|
||||
jaxlib==0.4.18+cuda12.cudnn89; python_version >= '3.9' \
|
||||
--hash=sha256:14f74ff081882ea091c121e355051b35932e39cb7ff7242b88a87f3690f3ca90 \
|
||||
--hash=sha256:7c87dc2d68257b02e83c04be88a3c447373ee7077d65f43545bcbda5bfe2231d \
|
||||
--hash=sha256:4d16e9c7592e1aaca0b3d28d2c8beba415a2721bb7001f2947728247951a250d \
|
||||
--hash=sha256:759c08c69f4a5b1e6b39c3e4eff908a04ce3b2b483bb594ed624407c7d12d110 \
|
||||
--hash=sha256:a7a04dbe1851cd50d07691282116aee49a2f0be7838e55b76d7ada86db06be62 \
|
||||
--hash=sha256:2bf842db3d58c8c6c52fbc8ed3fabefd7b91a21746cd59d3eaf3522eea229b53 \
|
||||
--hash=sha256:35d265ef9bb3835a14580cbaa9402060f117e46056f80e0996405fff3964667a \
|
||||
--hash=sha256:0e4352f24d629e912965e6435e140c1b06086243a098651f2d01b75f3738b51c
|
||||
jax[cuda12_local]==0.4.34; python_version >= '3.10' \
|
||||
--hash=sha256:b957ca1fc91f7343f91a186af9f19c7f342c946f95a8c11c7f1e5cdfe2e58d9e
|
||||
jax[cuda12_local]==0.4.30; python_version == '3.9' \
|
||||
--hash=sha256:289b30ae03b52f7f4baf6ef082a9f4e3e29c1080e22d13512c5ecf02d5f1a55b
|
||||
jax-cuda12-plugin==0.4.34; python_version >= '3.10' \
|
||||
--hash=sha256:d035ea72bd9b8a65a6ea621bca1affdd33127fa3a52e7bded7692670d360adab \
|
||||
--hash=sha256:db988b7ba5063483a936ddbf162f04d1b4412e0d64340f11788c7bbc877e8a43 \
|
||||
--hash=sha256:e23721d1654b311b47cd6b35768520284bd036f8c7e6b11600143258b4a0409a \
|
||||
--hash=sha256:b2099a4407225122ff76f6dcdc8dbdae47e6f29343bdfd21460ad337dc34a209
|
||||
jax-cuda12-plugin==0.4.30; python_version == '3.9' \
|
||||
--hash=sha256:d8d196241b9253ecb1144a4409b5deacbb9771624f097b2bbf025da3c7d8f4f8
|
||||
jax-cuda12-pjrt==0.4.34; python_version >= '3.10' \
|
||||
--hash=sha256:0c7cc98f962cc7fc8e0a5ea6331b42a0cee516f202f1c3019f6aa5cd9530cca0
|
||||
jax-cuda12-pjrt==0.4.30; python_version == '3.9' \
|
||||
--hash=sha256:895d0198ad99638fcaf976c47592e2a543eef79ea15fabd24a402d055390c328
|
||||
|
||||
@@ -678,7 +678,7 @@ def _create_contact_manifold(
|
||||
return dist, pos, normal
|
||||
|
||||
|
||||
def _sat_bruteforce(
|
||||
def _box_box_impl(
|
||||
faces_a: jax.Array,
|
||||
faces_b: jax.Array,
|
||||
vertices_a: jax.Array,
|
||||
@@ -688,17 +688,7 @@ def _sat_bruteforce(
|
||||
unique_edges_a: jax.Array,
|
||||
unique_edges_b: jax.Array,
|
||||
) -> Tuple[jax.Array, jax.Array, jax.Array]:
|
||||
"""Runs the Separating Axis Test for a pair of hulls.
|
||||
|
||||
Given two convex hulls, the Separating Axis Test finds a separating axis
|
||||
between all edge pairs and face pairs. Edge pairs create a single contact
|
||||
point and face pairs create a contact manifold (up to four contact points).
|
||||
We return both the edge and face contacts. Valid contacts can be checked with
|
||||
dist < 0. Resulting edge contacts should be preferred over face contacts.
|
||||
|
||||
This method checks all separating axes via a brute force support function, and
|
||||
is thus costly to run over large meshes, but is more performant for smaller
|
||||
meshes (boxes, tetrahedra, etc.).
|
||||
"""Runs the Separating Axis Test for two boxes.
|
||||
|
||||
Args:
|
||||
faces_a: Faces for hull A.
|
||||
@@ -713,18 +703,15 @@ def _sat_bruteforce(
|
||||
Returns:
|
||||
tuple of dist, pos, and normal
|
||||
"""
|
||||
# get the separating axes
|
||||
v_norm = jax.vmap(math.normalize)
|
||||
edge_dir_a = v_norm(unique_edges_a[:, 0] - unique_edges_a[:, 1])
|
||||
edge_dir_b = v_norm(unique_edges_b[:, 0] - unique_edges_b[:, 1])
|
||||
edge_dir_a_r = jp.tile(edge_dir_a, reps=(unique_edges_b.shape[0], 1))
|
||||
edge_dir_b_r = jp.repeat(edge_dir_b, repeats=unique_edges_a.shape[0], axis=0)
|
||||
edge_dir_a, edge_dir_b = unique_edges_a, unique_edges_b
|
||||
edge_dir_a_r = jp.tile(edge_dir_a, reps=(edge_dir_b.shape[0], 1))
|
||||
edge_dir_b_r = jp.repeat(edge_dir_b, repeats=edge_dir_a.shape[0], axis=0)
|
||||
edge_axes = jax.vmap(jp.cross)(edge_dir_a_r, edge_dir_b_r)
|
||||
degenerate_edge_axes = (edge_axes**2).sum(axis=1) < 1e-6
|
||||
edge_axes = jax.vmap(lambda x: math.normalize(x, axis=0))(edge_axes)
|
||||
n_norm = normals_a.shape[0] + normals_b.shape[0]
|
||||
n_face_axes = normals_a.shape[0] + normals_b.shape[0]
|
||||
degenerate_axes = jp.concatenate(
|
||||
[jp.array([False] * n_norm), degenerate_edge_axes]
|
||||
[jp.array([False] * n_face_axes), degenerate_edge_axes]
|
||||
)
|
||||
|
||||
axes = jp.concatenate([normals_a, normals_b, edge_axes])
|
||||
@@ -745,11 +732,16 @@ def _sat_bruteforce(
|
||||
|
||||
support, sign = get_support(axes, degenerate_axes)
|
||||
|
||||
# get the best face axis
|
||||
best_face_idx = jp.argmin(support[:n_face_axes])
|
||||
best_face_axis = axes[best_face_idx]
|
||||
|
||||
# choose the best separating axis
|
||||
best_idx = jp.argmin(support)
|
||||
best_sign = sign[best_idx]
|
||||
best_axis = axes[best_idx]
|
||||
is_edge_contact = best_idx >= (normals_a.shape[0] + normals_b.shape[0])
|
||||
is_edge_contact = best_idx >= n_face_axes
|
||||
is_edge_contact &= jp.abs(best_face_axis.dot(best_axis)) < 0.99 # prefer face
|
||||
|
||||
# get the (reference) face most aligned with the separating axis
|
||||
dist_a = normals_a @ best_axis
|
||||
@@ -788,6 +780,40 @@ def _sat_bruteforce(
|
||||
return dist, pos, normal
|
||||
|
||||
|
||||
def _box_box(b1: ConvexInfo, b2: ConvexInfo) -> Collision:
|
||||
"""Calculates contacts between two boxes."""
|
||||
faces1 = b1.face
|
||||
faces2 = b2.face
|
||||
|
||||
to_local_pos = b2.mat.T @ (b1.pos - b2.pos)
|
||||
to_local_mat = b2.mat.T @ b1.mat
|
||||
|
||||
faces1 = to_local_pos + faces1 @ to_local_mat.T
|
||||
normals1 = b1.face_normal @ to_local_mat.T
|
||||
normals2 = b2.face_normal
|
||||
|
||||
vertices1 = to_local_pos + b1.vert @ to_local_mat.T
|
||||
vertices2 = b2.vert
|
||||
|
||||
dist, pos, normal = _box_box_impl(
|
||||
faces1,
|
||||
faces2,
|
||||
vertices1,
|
||||
vertices2,
|
||||
normals1,
|
||||
normals2,
|
||||
to_local_mat.T,
|
||||
jp.eye(3, dtype=float),
|
||||
)
|
||||
|
||||
# Go back to world frame.
|
||||
pos = b2.pos + pos @ b2.mat.T
|
||||
n = normal @ b2.mat.T
|
||||
dist = jp.where(jp.isinf(dist), jp.finfo(float).max, dist)
|
||||
|
||||
return dist, pos, n
|
||||
|
||||
|
||||
def _arcs_intersect(
|
||||
a: jax.Array, b: jax.Array, c: jax.Array, d: jax.Array
|
||||
) -> jax.Array:
|
||||
@@ -906,7 +932,8 @@ def _sat_gaussmap(
|
||||
return edge_axis * sign, degenerate_edge_axis
|
||||
|
||||
edge_axes, degenerate_edge_axes = jax.vmap(get_normals)(
|
||||
edge_a_dir, edge_a_pt, edge_b_dir)
|
||||
edge_a_dir, edge_a_pt, edge_b_dir
|
||||
)
|
||||
edge_dist = jax.vmap(jp.dot)(edge_axes, edge_b_pt - edge_a_pt)
|
||||
# handle degenerate axis
|
||||
edge_dist = jp.where(degenerate_edge_axes, -jp.inf, edge_dist)
|
||||
@@ -928,51 +955,16 @@ def _sat_gaussmap(
|
||||
dist,
|
||||
)
|
||||
a_closest, b_closest = math.closest_segment_to_segment_points(
|
||||
edge_a_pt[best_edge_idx], edge_a_pt_2[best_edge_idx],
|
||||
edge_b_pt[best_edge_idx], edge_b_pt_2[best_edge_idx])
|
||||
edge_a_pt[best_edge_idx],
|
||||
edge_a_pt_2[best_edge_idx],
|
||||
edge_b_pt[best_edge_idx],
|
||||
edge_b_pt_2[best_edge_idx],
|
||||
)
|
||||
pos = jp.where(
|
||||
is_edge_contact,
|
||||
jp.tile(0.5 * (a_closest + b_closest), (4, 1)), pos)
|
||||
|
||||
return dist, pos, normal
|
||||
|
||||
|
||||
def _box_box(b1: ConvexInfo, b2: ConvexInfo) -> Collision:
|
||||
"""Calculates contacts between two boxes."""
|
||||
faces1 = b1.face
|
||||
faces2 = b2.face
|
||||
|
||||
to_local_pos = b2.mat.T @ (b1.pos - b2.pos)
|
||||
to_local_mat = b2.mat.T @ b1.mat
|
||||
|
||||
faces1 = to_local_pos + faces1 @ to_local_mat.T
|
||||
normals1 = b1.face_normal @ to_local_mat.T
|
||||
normals2 = b2.face_normal
|
||||
|
||||
vertices1 = to_local_pos + b1.vert @ to_local_mat.T
|
||||
vertices2 = b2.vert
|
||||
|
||||
unique_edges1 = jp.take(vertices1, b1.edge_dir, axis=0)
|
||||
unique_edges2 = jp.take(vertices2, b2.edge_dir, axis=0)
|
||||
|
||||
# brute-force SAT is more performant for box-box
|
||||
dist, pos, normal = _sat_bruteforce(
|
||||
faces1,
|
||||
faces2,
|
||||
vertices1,
|
||||
vertices2,
|
||||
normals1,
|
||||
normals2,
|
||||
unique_edges1,
|
||||
unique_edges2,
|
||||
is_edge_contact, jp.tile(0.5 * (a_closest + b_closest), (4, 1)), pos
|
||||
)
|
||||
|
||||
# Go back to world frame.
|
||||
pos = b2.pos + pos @ b2.mat.T
|
||||
n = normal @ b2.mat.T
|
||||
dist = jp.where(jp.isinf(dist), jp.finfo(float).max, dist)
|
||||
|
||||
return dist, pos, n
|
||||
return dist, pos, normal
|
||||
|
||||
|
||||
def _convex_convex(c1: ConvexInfo, c2: ConvexInfo) -> Collision:
|
||||
|
||||
@@ -146,13 +146,13 @@ def geom_pairs(
|
||||
b_end = b_start + m.body_geomnum
|
||||
|
||||
for b1 in range(m.nbody):
|
||||
if not geom_con[b_start[b1]:b_end[b1]].any():
|
||||
if not geom_con[b_start[b1] : b_end[b1]].any():
|
||||
continue
|
||||
w1 = m.body_weldid[b1]
|
||||
w1_p = m.body_weldid[m.body_parentid[w1]]
|
||||
|
||||
for b2 in range(b1, m.nbody):
|
||||
if not geom_con[b_start[b2]:b_end[b2]].any():
|
||||
if not geom_con[b_start[b2] : b_end[b2]].any():
|
||||
continue
|
||||
signature = (b1 << 16) + (b2)
|
||||
if signature in exclude_signature:
|
||||
@@ -272,7 +272,7 @@ def _contact_groups(m: Model, d: Data) -> Dict[FunctionKey, Contact]:
|
||||
jp.clip(m.pair_friction[ip], a_min=eps),
|
||||
m.pair_solref[ip],
|
||||
m.pair_solreffriction[ip],
|
||||
m.pair_solimp[ip]
|
||||
m.pair_solimp[ip],
|
||||
))
|
||||
if geom1.size > 0 and geom2.size > 0:
|
||||
# other contacts get their params from geom fields
|
||||
@@ -377,11 +377,11 @@ def collision(m: Model, d: Data) -> Data:
|
||||
if d.ncon == 0:
|
||||
return d
|
||||
|
||||
groups = _contact_groups(m, d)
|
||||
max_geom_pairs = _numeric(m, 'max_geom_pairs')
|
||||
max_contact_points = _numeric(m, 'max_contact_points')
|
||||
|
||||
# run collision functions on groups
|
||||
groups = _contact_groups(m, d)
|
||||
for key, contact in groups.items():
|
||||
# determine which contacts we'll use for collision testing by running a
|
||||
# broad phase cull if requested
|
||||
|
||||
@@ -49,11 +49,15 @@ def _assert_attr_eq(mjx_d, mj_d, attr, name, atol):
|
||||
|
||||
|
||||
def _collide(
|
||||
mjcf: str, assets: Optional[Dict[str, str]] = None
|
||||
mjcf: str,
|
||||
assets: Optional[Dict[str, str]] = None,
|
||||
keyframe: Optional[int] = None,
|
||||
) -> Tuple[mujoco.MjModel, mujoco.MjData, Model, Data]:
|
||||
m = mujoco.MjModel.from_xml_string(mjcf, assets or {})
|
||||
mx = mjx.put_model(m)
|
||||
d = mujoco.MjData(m)
|
||||
if keyframe is not None:
|
||||
mujoco.mj_resetDataKeyframe(m, d, keyframe)
|
||||
dx = mjx.put_data(m, d)
|
||||
|
||||
mujoco.mj_step(m, d)
|
||||
@@ -218,7 +222,8 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-plane', 1e-5)
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-plane', 1e-5
|
||||
)
|
||||
|
||||
_ELLIPSOID_ELLIPSOID = """
|
||||
<mujoco>
|
||||
@@ -240,7 +245,8 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-ellipsoid', 1e-5)
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-ellipsoid', 1e-2
|
||||
)
|
||||
|
||||
_ELLIPSOID_SPHERE = """
|
||||
<mujoco>
|
||||
@@ -263,7 +269,8 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-sphere', 1e-4)
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-sphere', 1e-4
|
||||
)
|
||||
|
||||
_ELLIPSOID_CAPSULE = """
|
||||
<mujoco>
|
||||
@@ -285,7 +292,8 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-capsule', 1e-4)
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-capsule', 1e-5
|
||||
)
|
||||
|
||||
_ELLIPSOID_CYLINDER = """
|
||||
<mujoco>
|
||||
@@ -308,7 +316,8 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-cylinder', 1e-4)
|
||||
dx.contact, d.contact, field.name, 'ellipsoid-cylinder', 1e-4
|
||||
)
|
||||
|
||||
|
||||
class CapsuleCollisionTest(parameterized.TestCase):
|
||||
@@ -550,7 +559,8 @@ class CylinderTest(absltest.TestCase):
|
||||
|
||||
# cylinder is vertical
|
||||
xml = self._CYLINDER_PLANE.replace(
|
||||
'<geom fromto="-0.1 0 0 0.1 0 0"', '<geom fromto="0 0 -0.1 0 0 0.1"')
|
||||
'<geom fromto="-0.1 0 0 0.1 0 0"', '<geom fromto="0 0 -0.1 0 0 0.1"'
|
||||
)
|
||||
xml = xml.replace('pos="0 0 0.04"', 'pos="0 0 0.095"')
|
||||
d, dx = _collide(xml)
|
||||
|
||||
@@ -579,7 +589,8 @@ class CylinderTest(absltest.TestCase):
|
||||
self.assertLess(dx.contact.dist[0], 0)
|
||||
for field in dataclasses.fields(Contact):
|
||||
_assert_attr_eq(
|
||||
dx.contact, d.contact, field.name, 'sphere-cylinder', 1e-4)
|
||||
dx.contact, d.contact, field.name, 'sphere-cylinder', 1e-4
|
||||
)
|
||||
|
||||
|
||||
class ConvexTest(absltest.TestCase):
|
||||
@@ -641,28 +652,30 @@ class ConvexTest(absltest.TestCase):
|
||||
_BOX_BOX = """
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body pos="0.0 1.0 0.2">
|
||||
<joint axis="1 0 0" type="free"/>
|
||||
<geom size="0.2 0.2 0.2" type="box"/>
|
||||
</body>
|
||||
<body pos="0.1 1.0 0.495" euler="0.1 -0.1 0">
|
||||
<joint axis="1 0 0" type="free"/>
|
||||
<geom size="0.1 0.1 0.1" type="box"/>
|
||||
</body>
|
||||
<light name="top" pos="0 0 1"/>
|
||||
<geom type="box" size="0.025 0.025 0.025" pos="0 0 0.025"/>
|
||||
<body name="peg" pos="0 0 0.06">
|
||||
<freejoint/>
|
||||
<geom name="peg" size="0.048 0.01 0.01" type="box"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<keyframe>
|
||||
<!-- Boxes are penetrating with a slightly off-axis face contact -->
|
||||
<key qpos='-0.00234853 0.0112999 0.0533649 0.474162 0.472141 0.524886 0.526069'/>
|
||||
</keyframe>
|
||||
</mujoco>
|
||||
"""
|
||||
|
||||
def test_box_box(self):
|
||||
"""Tests a face contact for a box-box collision."""
|
||||
d, dx = _collide(self._BOX_BOX)
|
||||
d, dx = _collide(self._BOX_BOX, keyframe=0)
|
||||
c = dx.contact
|
||||
|
||||
self.assertEqual(c.pos.shape[0], 4)
|
||||
np.testing.assert_array_less(c.dist, 0)
|
||||
np.testing.assert_array_almost_equal(c.pos[:, 2], np.array([0.39] * 4), 2)
|
||||
np.testing.assert_array_almost_equal(c.pos[:, 2], np.array([0.05] * 4), 2)
|
||||
np.testing.assert_array_almost_equal(
|
||||
c.frame[:, 0, :], np.array([[0.0, 0.0, 1.0]] * 4)
|
||||
c.frame[:, 0, :], np.array([[0.0, 0.0, 1.0]] * 4), decimal=2
|
||||
)
|
||||
np.testing.assert_array_almost_equal(
|
||||
c.frame.reshape((-1, 9)), d.contact.frame[:4, :]
|
||||
@@ -760,11 +773,14 @@ class ConvexTest(absltest.TestCase):
|
||||
np.testing.assert_array_less(0, c.dist[1:])
|
||||
np.testing.assert_array_almost_equal(c.frame[0, 0], np.array([0, 0, 1]))
|
||||
np.testing.assert_array_almost_equal(
|
||||
c.pos[0], np.array([0, 2, 1.3155]), decimal=5)
|
||||
c.pos[0], np.array([0, 2, 1.3155]), decimal=5
|
||||
)
|
||||
|
||||
_, dx = _collide(
|
||||
self._CONVEX_CONVEX_THIN.replace(
|
||||
'pos="0.0 2.0 0.35"', 'pos="0.0 2.0 0"'))
|
||||
'pos="0.0 2.0 0.35"', 'pos="0.0 2.0 0"'
|
||||
)
|
||||
)
|
||||
c = dx.contact
|
||||
self.assertTrue((c.dist > 0).all())
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from mujoco.mjx._src.types import Model
|
||||
|
||||
def collider(ncon: int):
|
||||
"""Wraps collision functions for use by collision_driver."""
|
||||
|
||||
def wrapper(func):
|
||||
def collide(m: Model, d: Data, _, geom: jax.Array) -> Collision:
|
||||
g1, g2 = geom.T
|
||||
@@ -119,7 +120,7 @@ def plane_cylinder(plane: GeomInfo, cylinder: GeomInfo) -> Collision:
|
||||
# disk parallel to plane: pick x-axis of cylinder, scale by radius
|
||||
cylinder.mat[:, 0] * cylinder.size[0],
|
||||
# general configuration: normalize vector, scale by radius
|
||||
vec / len_ * cylinder.size[0]
|
||||
vec / len_ * cylinder.size[0],
|
||||
)
|
||||
|
||||
# project vector on normal
|
||||
@@ -138,11 +139,15 @@ def plane_cylinder(plane: GeomInfo, cylinder: GeomInfo) -> Collision:
|
||||
d1 = dist0 + prjaxis + prjvec
|
||||
d2 = dist0 + prjaxis + prjvec1
|
||||
dist = jp.array([d1, d2, d2])
|
||||
pos = cylinder.pos + axis + jp.array([
|
||||
vec - n * d1 * 0.5,
|
||||
vec1 + vec * -0.5 - n * d2 * 0.5,
|
||||
-vec1 + vec * -0.5 - n * d2 * 0.5,
|
||||
])
|
||||
pos = (
|
||||
cylinder.pos
|
||||
+ axis
|
||||
+ jp.array([
|
||||
vec - n * d1 * 0.5,
|
||||
vec1 + vec * -0.5 - n * d2 * 0.5,
|
||||
-vec1 + vec * -0.5 - n * d2 * 0.5,
|
||||
])
|
||||
)
|
||||
|
||||
# cylinder parallel to plane
|
||||
cond = jp.abs(prjaxis) < 1e-3
|
||||
|
||||
@@ -41,6 +41,7 @@ SDFFn = Callable[[jax.Array], jax.Array]
|
||||
|
||||
def collider(ncon: int):
|
||||
"""Wraps collision functions for use by collision_driver."""
|
||||
|
||||
def wrapper(func):
|
||||
def collide(m: Model, d: Data, _, geom: jax.Array) -> Collision:
|
||||
g1, g2 = geom.T
|
||||
@@ -81,7 +82,7 @@ def _capsule(pos: jax.Array, size: jax.Array):
|
||||
|
||||
def _ellipsoid(pos: jax.Array, size: jax.Array) -> jax.Array:
|
||||
k0 = math.norm(pos / size)
|
||||
k1 = math.norm(pos / (size*size))
|
||||
k1 = math.norm(pos / (size * size))
|
||||
return k0 * (k0 - 1.0) / (k1 + (k1 == 0.0) * 1e-12)
|
||||
|
||||
|
||||
@@ -96,12 +97,12 @@ def _cylinder(pos: jax.Array, size: jax.Array) -> jax.Array:
|
||||
|
||||
def _cylinder_grad(x: jax.Array, size: jax.Array) -> jax.Array:
|
||||
"""Gradient of the cylinder SDF wrt query point and singularities removed."""
|
||||
c = jp.sqrt(x[0]*x[0]+x[1]*x[1])
|
||||
c = jp.sqrt(x[0] * x[0] + x[1] * x[1])
|
||||
e = jp.abs(x[2])
|
||||
a = jp.array([c - size[0], e - size[1]])
|
||||
b = jp.array([jp.maximum(a[0], 0), jp.maximum(a[1], 0)])
|
||||
j = jp.argmax(a)
|
||||
bnorm = jp.sqrt(b[0]*b[0] + b[1]*b[1])
|
||||
bnorm = jp.sqrt(b[0] * b[0] + b[1] * b[1])
|
||||
bnorm += jp.allclose(bnorm, 0) * 1e-12
|
||||
grada = jp.array([
|
||||
x[0] / (c + jp.allclose(c, 0) * 1e-12),
|
||||
@@ -151,7 +152,7 @@ def _gradient_step(objective: SDFFn, state: GradientState) -> GradientState:
|
||||
"""Performs a step of gradient descent."""
|
||||
# TODO: find better parameters
|
||||
amin = 1e-4 # minimum value for line search factor scaling the gradient
|
||||
amax = 2. # maximum value for line search factor scaling the gradient
|
||||
amax = 2.0 # maximum value for line search factor scaling the gradient
|
||||
nlinesearch = 10 # line search points
|
||||
grad = jax.grad(objective)(state.x)
|
||||
alpha = jp.geomspace(amin, amax, nlinesearch).reshape(nlinesearch, -1)
|
||||
@@ -179,7 +180,11 @@ def _gradient_descent(
|
||||
|
||||
|
||||
def _optim(
|
||||
d1, d2, info1: GeomInfo, info2: GeomInfo, x0: jax.Array,
|
||||
d1,
|
||||
d2,
|
||||
info1: GeomInfo,
|
||||
info2: GeomInfo,
|
||||
x0: jax.Array,
|
||||
) -> Collision:
|
||||
"""Optimizes the clearance function."""
|
||||
d1 = functools.partial(d1, size=info1.size)
|
||||
@@ -198,14 +203,14 @@ def _optim(
|
||||
|
||||
@collider(ncon=1)
|
||||
def sphere_ellipsoid(s: GeomInfo, e: GeomInfo) -> Collision:
|
||||
""""Calculates contact between a sphere and an ellipsoid."""
|
||||
"""Calculates contact between a sphere and an ellipsoid."""
|
||||
x0 = 0.5 * (s.pos + e.pos)
|
||||
return _optim(_sphere, _ellipsoid, s, e, x0)
|
||||
|
||||
|
||||
@collider(ncon=1)
|
||||
def sphere_cylinder(s: GeomInfo, c: GeomInfo) -> Collision:
|
||||
""""Calculates contact between a sphere and a cylinder."""
|
||||
"""Calculates contact between a sphere and a cylinder."""
|
||||
# TODO: implement analytical version.
|
||||
x0 = 0.5 * (s.pos + c.pos)
|
||||
return _optim(_sphere, _cylinder, s, c, x0)
|
||||
@@ -213,14 +218,14 @@ def sphere_cylinder(s: GeomInfo, c: GeomInfo) -> Collision:
|
||||
|
||||
@collider(ncon=1)
|
||||
def capsule_ellipsoid(c: GeomInfo, e: GeomInfo) -> Collision:
|
||||
""""Calculates contact between a capsule and an ellipsoid."""
|
||||
""" "Calculates contact between a capsule and an ellipsoid."""
|
||||
x0 = 0.5 * (c.pos + e.pos)
|
||||
return _optim(_capsule, _ellipsoid, c, e, x0)
|
||||
|
||||
|
||||
@collider(ncon=2)
|
||||
def capsule_cylinder(ca: GeomInfo, cy: GeomInfo) -> Collision:
|
||||
""""Calculates contact between a capsule and a cylinder."""
|
||||
"""Calculates contact between a capsule and a cylinder."""
|
||||
# TODO: improve robustness
|
||||
# Near sharp corners, the SDF might give the penetration depth with respect
|
||||
# to a surface that is not in collision. Possible solutions is to find the
|
||||
@@ -235,21 +240,21 @@ def capsule_cylinder(ca: GeomInfo, cy: GeomInfo) -> Collision:
|
||||
|
||||
@collider(ncon=1)
|
||||
def ellipsoid_ellipsoid(e1: GeomInfo, e2: GeomInfo) -> Collision:
|
||||
""""Calculates contact between two ellipsoids."""
|
||||
"""Calculates contact between two ellipsoids."""
|
||||
x0 = 0.5 * (e1.pos + e2.pos)
|
||||
return _optim(_ellipsoid, _ellipsoid, e1, e2, x0)
|
||||
|
||||
|
||||
@collider(ncon=1)
|
||||
def ellipsoid_cylinder(e: GeomInfo, c: GeomInfo) -> Collision:
|
||||
""""Calculates contact between and ellipsoid and a cylinder."""
|
||||
"""Calculates contact between and ellipsoid and a cylinder."""
|
||||
x0 = 0.5 * (e.pos + c.pos)
|
||||
return _optim(_ellipsoid, _cylinder, e, c, x0)
|
||||
|
||||
|
||||
@collider(ncon=4)
|
||||
def cylinder_cylinder(c1: GeomInfo, c2: GeomInfo) -> Collision:
|
||||
""""Calculates contact between a cylinder and a cylinder."""
|
||||
"""Calculates contact between a cylinder and a cylinder."""
|
||||
# TODO: improve robustness
|
||||
# Near sharp corners, the SDF might give the penetration depth with respect
|
||||
# to a surface that is not in collision. Possible solutions is to find the
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"""Collision base types."""
|
||||
|
||||
import dataclasses
|
||||
from typing import Optional, Tuple
|
||||
from typing import Tuple
|
||||
import jax
|
||||
from mujoco.mjx._src.dataclasses import PyTreeNode # pylint: disable=g-importing-member
|
||||
import numpy as np
|
||||
@@ -46,7 +46,6 @@ class ConvexInfo(PyTreeNode):
|
||||
face_normal: jax.Array
|
||||
edge: jax.Array
|
||||
edge_face_normal: jax.Array
|
||||
edge_dir: Optional[jax.Array] = None
|
||||
|
||||
|
||||
class HFieldInfo(PyTreeNode):
|
||||
@@ -73,6 +72,7 @@ class FunctionKey:
|
||||
resulting constraint jacobian is determined at compile time.
|
||||
subgrid_size: the size determines the hfield subgrid to collide with
|
||||
"""
|
||||
|
||||
types: Tuple[int, int]
|
||||
data_ids: Tuple[int, int]
|
||||
condim: int
|
||||
|
||||
@@ -39,6 +39,7 @@ import numpy as np
|
||||
|
||||
class _Efc(PyTreeNode):
|
||||
"""Support data for creating constraint matrices."""
|
||||
|
||||
J: jax.Array
|
||||
pos_aref: jax.Array
|
||||
pos_imp: jax.Array
|
||||
@@ -107,7 +108,9 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
|
||||
return None
|
||||
|
||||
@jax.vmap
|
||||
def rows(is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp):
|
||||
def rows(
|
||||
is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp, active
|
||||
):
|
||||
anchor1, anchor2 = data[0:3], data[3:6]
|
||||
|
||||
pos1 = d.xmat[body1id] @ anchor1 + d.xpos[body1id]
|
||||
@@ -128,7 +131,8 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
|
||||
invweight = m.body_invweight0[body1id, 0] + m.body_invweight0[body2id, 0]
|
||||
zero = jp.zeros_like(pos)
|
||||
|
||||
return _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
|
||||
efc = _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
|
||||
return jax.tree_util.tree_map(lambda x: x * active, efc)
|
||||
|
||||
is_site = m.eq_objtype == ObjType.SITE
|
||||
body1id = np.copy(m.eq_obj1id)
|
||||
@@ -147,6 +151,7 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
|
||||
m.eq_data,
|
||||
m.eq_solref,
|
||||
m.eq_solimp,
|
||||
d.eq_active,
|
||||
)
|
||||
args = jax.tree_util.tree_map(lambda x: x[eq_id], args)
|
||||
# concatenate to drop row grouping
|
||||
@@ -161,7 +166,9 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
|
||||
return None
|
||||
|
||||
@jax.vmap
|
||||
def rows(is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp):
|
||||
def rows(
|
||||
is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp, active
|
||||
):
|
||||
anchor1, anchor2 = data[0:3], data[3:6]
|
||||
relpose, torquescale = data[6:10], data[10]
|
||||
|
||||
@@ -208,7 +215,8 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
|
||||
invweight = jp.repeat(invweight, 3, axis=0)
|
||||
zero = jp.zeros_like(pos)
|
||||
|
||||
return _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
|
||||
efc = _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
|
||||
return jax.tree_util.tree_map(lambda x: x * active, efc)
|
||||
|
||||
is_site = m.eq_objtype == ObjType.SITE
|
||||
body1id = np.copy(m.eq_obj1id)
|
||||
@@ -227,6 +235,7 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
|
||||
m.eq_data,
|
||||
m.eq_solref,
|
||||
m.eq_solimp,
|
||||
d.eq_active,
|
||||
)
|
||||
args = jax.tree_util.tree_map(lambda x: x[eq_id], args)
|
||||
# concatenate to drop row grouping
|
||||
@@ -242,7 +251,9 @@ def _efc_equality_joint(m: Model, d: Data) -> Optional[_Efc]:
|
||||
return None
|
||||
|
||||
@jax.vmap
|
||||
def rows(obj2id, data, solref, solimp, dofadr1, dofadr2, qposadr1, qposadr2):
|
||||
def rows(
|
||||
obj2id, data, solref, solimp, active, dofadr1, dofadr2, qposadr1, qposadr2
|
||||
):
|
||||
pos1, pos2 = d.qpos[qposadr1], d.qpos[qposadr2]
|
||||
ref1, ref2 = m.qpos0[qposadr1], m.qpos0[qposadr2]
|
||||
dif = (pos2 - ref2) * (obj2id > -1)
|
||||
@@ -255,9 +266,11 @@ def _efc_equality_joint(m: Model, d: Data) -> Optional[_Efc]:
|
||||
invweight += m.dof_invweight0[dofadr2] * (obj2id > -1)
|
||||
zero = jp.zeros_like(pos)
|
||||
|
||||
return _row(j, pos, pos, invweight, solref, solimp, zero, zero)
|
||||
efc = _row(j, pos, pos, invweight, solref, solimp, zero, zero)
|
||||
return jax.tree_util.tree_map(lambda x: x * active, efc)
|
||||
|
||||
args = (m.eq_obj1id, m.eq_obj2id, m.eq_data, m.eq_solref, m.eq_solimp)
|
||||
args += (d.eq_active,)
|
||||
args = jax.tree_util.tree_map(lambda x: x[eq_id], args)
|
||||
dofadr1, dofadr2 = m.jnt_dofadr[args[0]], m.jnt_dofadr[args[1]]
|
||||
qposadr1, qposadr2 = m.jnt_qposadr[args[0]], m.jnt_qposadr[args[1]]
|
||||
@@ -274,7 +287,7 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
|
||||
if (m.opt.disableflags & DisableBit.EQUALITY) or eq_id.size == 0:
|
||||
return None
|
||||
|
||||
obj1id, obj2id, data, solref, solimp = jax.tree_util.tree_map(
|
||||
obj1id, obj2id, data, solref, solimp, active = jax.tree_util.tree_map(
|
||||
lambda x: x[eq_id],
|
||||
(
|
||||
m.eq_obj1id,
|
||||
@@ -282,11 +295,14 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
|
||||
m.eq_data,
|
||||
m.eq_solref,
|
||||
m.eq_solimp,
|
||||
d.eq_active,
|
||||
),
|
||||
)
|
||||
|
||||
@jax.vmap
|
||||
def rows(obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2):
|
||||
def rows(
|
||||
obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2, active
|
||||
):
|
||||
dif = pos2 * (obj2id > -1)
|
||||
dif_power = jp.power(dif, jp.arange(0, 5))
|
||||
pos = pos1 - jp.dot(data[:5], dif_power)
|
||||
@@ -294,7 +310,8 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
|
||||
j = jac1 + jac2 * -deriv
|
||||
zero = jp.zeros_like(pos)
|
||||
|
||||
return _row(j, pos, pos, invweight, solref, solimp, zero, zero)
|
||||
efc = _row(j, pos, pos, invweight, solref, solimp, zero, zero)
|
||||
return jax.tree_util.tree_map(lambda x: x * active, efc)
|
||||
|
||||
inv1, inv2 = m.tendon_invweight0[obj1id], m.tendon_invweight0[obj2id]
|
||||
jac1, jac2 = d.ten_J[obj1id], d.ten_J[obj2id]
|
||||
@@ -302,7 +319,9 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
|
||||
pos2 = d.ten_length[obj2id] - m.tendon_length0[obj2id]
|
||||
invweight = inv1 + inv2 * (obj2id > -1)
|
||||
|
||||
return rows(obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2)
|
||||
return rows(
|
||||
obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2, active
|
||||
)
|
||||
|
||||
|
||||
def _efc_friction(m: Model, d: Data) -> Optional[_Efc]:
|
||||
|
||||
@@ -30,7 +30,7 @@ _TOLERANCE = 5e-5
|
||||
|
||||
|
||||
def _assert_eq(a, b, name):
|
||||
tol = _TOLERANCE * 10 # avoid test noise
|
||||
tol = _TOLERANCE * 10 # avoid test noise
|
||||
err_msg = f'mismatch: {name}'
|
||||
np.testing.assert_allclose(a, b, err_msg=err_msg, atol=tol, rtol=tol)
|
||||
|
||||
@@ -41,10 +41,17 @@ def _assert_attr_eq(a, b, attr):
|
||||
|
||||
class ConstraintTest(parameterized.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
np.random.seed(42)
|
||||
|
||||
@parameterized.parameters(
|
||||
mujoco.mjtCone.mjCONE_PYRAMIDAL, mujoco.mjtCone.mjCONE_ELLIPTIC
|
||||
{'cone': mujoco.mjtCone.mjCONE_PYRAMIDAL, 'rand_eq_active': False},
|
||||
{'cone': mujoco.mjtCone.mjCONE_ELLIPTIC, 'rand_eq_active': False},
|
||||
{'cone': mujoco.mjtCone.mjCONE_PYRAMIDAL, 'rand_eq_active': True},
|
||||
{'cone': mujoco.mjtCone.mjCONE_ELLIPTIC, 'rand_eq_active': True},
|
||||
)
|
||||
def test_constraints(self, cone):
|
||||
def test_constraints(self, cone, rand_eq_active):
|
||||
"""Test constraints."""
|
||||
m = test_util.load_test_file('constraints.xml')
|
||||
m.opt.cone = cone
|
||||
@@ -53,6 +60,8 @@ class ConstraintTest(parameterized.TestCase):
|
||||
# sample a mix of active/inactive constraints at different timesteps
|
||||
for key in range(3):
|
||||
mujoco.mj_resetDataKeyframe(m, d, key)
|
||||
if rand_eq_active:
|
||||
d.eq_active[:] = np.random.randint(0, 2, size=m.neq)
|
||||
mujoco.mj_forward(m, d)
|
||||
mx = mjx.put_model(m)
|
||||
dx = mjx.put_data(m, d)
|
||||
@@ -66,6 +75,7 @@ class ConstraintTest(parameterized.TestCase):
|
||||
_assert_eq(0, dx.efc_aref[order][d.nefc :], 'efc_aref')
|
||||
_assert_eq(d.efc_D, dx.efc_D[order][: d.nefc], 'efc_D')
|
||||
_assert_eq(d.efc_pos, dx.efc_pos[order][: d.nefc], 'efc_pos')
|
||||
_assert_eq(dx.efc_pos[order][d.nefc :], 0, 'efc_pos')
|
||||
_assert_eq(
|
||||
d.efc_frictionloss,
|
||||
dx.efc_frictionloss[order][: d.nefc],
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import copy
|
||||
import dataclasses
|
||||
|
||||
import typing
|
||||
from typing import Dict, Optional, Sequence, Tuple, TypeVar, Union
|
||||
import jax
|
||||
@@ -35,7 +34,7 @@ def _jax_in_args(typ) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def dataclass(clz: _T) -> _T:
|
||||
def dataclass(clz: _T, register_as_pytree: bool) -> _T:
|
||||
"""Wraps a dataclass with metadata for which fields are pytrees.
|
||||
|
||||
This is based off flax.struct.dataclass, but instead of using field
|
||||
@@ -49,54 +48,51 @@ def dataclass(clz: _T) -> _T:
|
||||
the resulting dataclass, registered with Jax
|
||||
"""
|
||||
data_clz = dataclasses.dataclass(frozen=True)(clz)
|
||||
meta_fields, data_fields = [], []
|
||||
for field in dataclasses.fields(data_clz):
|
||||
if _jax_in_args(field.type):
|
||||
data_fields.append(field)
|
||||
else:
|
||||
meta_fields.append(field)
|
||||
data_clz.replace = dataclasses.replace
|
||||
|
||||
def replace(self, **updates):
|
||||
""""Returns a new object replacing the specified fields with new values."""
|
||||
return dataclasses.replace(self, **updates)
|
||||
|
||||
data_clz.replace = replace
|
||||
|
||||
def iterate_clz_with_keys(x):
|
||||
def to_meta(field, obj):
|
||||
val = getattr(obj, field.name)
|
||||
# numpy arrays are not hashable so return raw bytes instead
|
||||
if isinstance(val, np.ndarray):
|
||||
return (val.tobytes(), val.dtype, val.shape)
|
||||
if register_as_pytree:
|
||||
meta_fields, data_fields = [], []
|
||||
for field in dataclasses.fields(data_clz):
|
||||
if _jax_in_args(field.type):
|
||||
data_fields.append(field)
|
||||
else:
|
||||
return val
|
||||
meta_fields.append(field)
|
||||
|
||||
def to_data(field, obj):
|
||||
return (jax.tree_util.GetAttrKey(field.name), getattr(obj, field.name))
|
||||
def iterate_clz_with_keys(x):
|
||||
def to_meta(field, obj):
|
||||
val = getattr(obj, field.name)
|
||||
# numpy arrays are not hashable so return raw bytes instead
|
||||
if isinstance(val, np.ndarray):
|
||||
return (val.tobytes(), val.dtype, val.shape)
|
||||
else:
|
||||
return val
|
||||
|
||||
data = tuple(to_data(f, x) for f in data_fields)
|
||||
meta = tuple(to_meta(f, x) for f in meta_fields)
|
||||
return data, meta
|
||||
def to_data(field, obj):
|
||||
return (jax.tree_util.GetAttrKey(field.name), getattr(obj, field.name))
|
||||
|
||||
def clz_from_iterable(meta, data):
|
||||
data = tuple(to_data(f, x) for f in data_fields)
|
||||
meta = tuple(to_meta(f, x) for f in meta_fields)
|
||||
return data, meta
|
||||
|
||||
def from_meta(field, meta):
|
||||
if field.type is np.ndarray:
|
||||
arr = np.frombuffer(meta[0], dtype=meta[1]).reshape(meta[2])
|
||||
return (field.name, arr)
|
||||
else:
|
||||
return (field.name, meta)
|
||||
def clz_from_iterable(meta, data):
|
||||
|
||||
from_data = lambda field, meta: (field.name, meta)
|
||||
def from_meta(field, meta):
|
||||
if field.type is np.ndarray:
|
||||
arr = np.frombuffer(meta[0], dtype=meta[1]).reshape(meta[2])
|
||||
return (field.name, arr)
|
||||
else:
|
||||
return (field.name, meta)
|
||||
|
||||
meta_args = tuple(from_meta(f, m) for f, m in zip(meta_fields, meta))
|
||||
data_args = tuple(from_data(f, m) for f, m in zip(data_fields, data))
|
||||
from_data = lambda field, meta: (field.name, meta)
|
||||
|
||||
return data_clz(**dict(meta_args + data_args))
|
||||
meta_args = tuple(from_meta(f, m) for f, m in zip(meta_fields, meta))
|
||||
data_args = tuple(from_data(f, m) for f, m in zip(data_fields, data))
|
||||
|
||||
jax.tree_util.register_pytree_with_keys(
|
||||
data_clz, iterate_clz_with_keys, clz_from_iterable
|
||||
)
|
||||
return data_clz(**dict(meta_args + data_args))
|
||||
|
||||
jax.tree_util.register_pytree_with_keys(
|
||||
data_clz, iterate_clz_with_keys, clz_from_iterable
|
||||
)
|
||||
|
||||
return data_clz
|
||||
|
||||
@@ -110,8 +106,9 @@ class PyTreeNode:
|
||||
This base class additionally avoids type checking errors when using PyType.
|
||||
"""
|
||||
|
||||
def __init_subclass__(cls):
|
||||
dataclass(cls)
|
||||
def __init_subclass__(cls, register_as_pytree: bool = True, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
dataclass(cls, register_as_pytree=register_as_pytree)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# stub for pytype
|
||||
|
||||
@@ -115,6 +115,8 @@ def fwd_actuation(m: Model, d: Data) -> Data:
|
||||
act_dot = ctrl
|
||||
elif dyn_typ in (DynType.FILTER, DynType.FILTEREXACT):
|
||||
act_dot = (ctrl - act) / jp.clip(dyn_prm[0], mujoco.mjMINVAL)
|
||||
elif dyn_typ == DynType.MUSCLE:
|
||||
act_dot = support.muscle_dynamics(ctrl, act, dyn_prm)
|
||||
else:
|
||||
raise NotImplementedError(f'dyntype {dyn_typ.name} not implemented.')
|
||||
return act_dot
|
||||
@@ -139,13 +141,15 @@ def fwd_actuation(m: Model, d: Data) -> Data:
|
||||
ctrl_act = jp.where(m.actuator_actadr == -1, ctrl, act_last_dim)
|
||||
|
||||
def get_force(*args):
|
||||
gain_t, gain_p, bias_t, bias_p, len_, vel, ctrl_act = args
|
||||
gain_t, gain_p, bias_t, bias_p, len_, vel, ctrl_act, len_range, acc0 = args
|
||||
|
||||
typ, prm = GainType(gain_t), gain_p
|
||||
if typ == GainType.FIXED:
|
||||
gain = prm[0]
|
||||
elif typ == GainType.AFFINE:
|
||||
gain = prm[0] + prm[1] * len_ + prm[2] * vel
|
||||
elif typ == GainType.MUSCLE:
|
||||
gain = support.muscle_gain(len_, vel, len_range, acc0, prm)
|
||||
else:
|
||||
raise RuntimeError(f'unrecognized gaintype {typ.name}.')
|
||||
|
||||
@@ -153,13 +157,15 @@ def fwd_actuation(m: Model, d: Data) -> Data:
|
||||
bias = jp.array(0.0)
|
||||
if typ == BiasType.AFFINE:
|
||||
bias = prm[0] + prm[1] * len_ + prm[2] * vel
|
||||
elif typ == BiasType.MUSCLE:
|
||||
bias = support.muscle_bias(len_, len_range, acc0, prm)
|
||||
|
||||
return gain * ctrl_act + bias
|
||||
|
||||
force = scan.flat(
|
||||
m,
|
||||
get_force,
|
||||
'uuuuuuu',
|
||||
'uuuuuuuuu',
|
||||
'u',
|
||||
m.actuator_gaintype,
|
||||
m.actuator_gainprm,
|
||||
@@ -168,6 +174,8 @@ def fwd_actuation(m: Model, d: Data) -> Data:
|
||||
d.actuator_length,
|
||||
d.actuator_velocity,
|
||||
ctrl_act,
|
||||
jp.array(m.actuator_lengthrange),
|
||||
jp.array(m.actuator_acc0),
|
||||
group_by='u',
|
||||
)
|
||||
forcerange = jp.where(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"""Tests for forward functions."""
|
||||
|
||||
from absl.testing import absltest
|
||||
from absl.testing import parameterized
|
||||
import jax
|
||||
import mujoco
|
||||
from mujoco import mjx
|
||||
@@ -167,40 +168,28 @@ class ForwardTest(absltest.TestCase):
|
||||
np.testing.assert_allclose(dx.qvel, 1 + m.opt.timestep)
|
||||
|
||||
|
||||
class ActuatorTest(absltest.TestCase):
|
||||
_DYN_XML = """
|
||||
<mujoco>
|
||||
<compiler autolimits="true"/>
|
||||
<worldbody>
|
||||
<body name="box">
|
||||
<joint name="slide1" type="slide" axis="1 0 0" />
|
||||
<joint name="slide2" type="slide" axis="0 1 0" />
|
||||
<joint name="slide3" type="slide" axis="0 0 1" />
|
||||
<joint name="slide4" type="slide" axis="1 1 0" />
|
||||
<geom type="box" size=".05 .05 .05" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="slide1" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide2" dyntype="integrator" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide3" dyntype="filter" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide4" dyntype="filterexact" dynprm="0.1" gainprm="1.1" />
|
||||
</actuator>
|
||||
</mujoco>
|
||||
"""
|
||||
class ActuatorTest(parameterized.TestCase):
|
||||
|
||||
def test_dyntype(self):
|
||||
m = mujoco.MjModel.from_xml_string(self._DYN_XML)
|
||||
@parameterized.parameters(
|
||||
'actuator/arm21.xml',
|
||||
'actuator/arm26.xml',
|
||||
'actuator/general_dyntype.xml',
|
||||
)
|
||||
def test_actuator(self, fname):
|
||||
m = test_util.load_test_file(fname)
|
||||
d = mujoco.MjData(m)
|
||||
d.ctrl = np.array([1.5, 1.5, 1.5, 1.5])
|
||||
d.act = np.array([0.5, 0.5, 0.5])
|
||||
|
||||
mujoco.mj_step(m, d)
|
||||
d.ctrl = 1.5 * np.random.random(m.nu)
|
||||
d.act = 0.5 * np.random.random(m.na)
|
||||
mx = mjx.put_model(m)
|
||||
dx = mjx.put_data(m, d)
|
||||
|
||||
mujoco.mj_fwdActuation(m, d)
|
||||
dx = jax.jit(mjx.fwd_actuation)(mx, dx)
|
||||
|
||||
_assert_attr_eq(d, dx, 'act_dot')
|
||||
_assert_attr_eq(d, dx, 'qfrc_actuator')
|
||||
_assert_attr_eq(d, dx, 'actuator_force')
|
||||
|
||||
mujoco.mj_Euler(m, d)
|
||||
dx = jax.jit(mjx.euler)(mx, dx)
|
||||
|
||||
+179
-83
@@ -34,31 +34,36 @@ def _strip_weak_type(tree):
|
||||
if isinstance(leaf, jax.Array):
|
||||
return leaf.astype(jax.dtypes.canonicalize_dtype(leaf.dtype))
|
||||
return leaf
|
||||
|
||||
return jax.tree_util.tree_map(f, tree)
|
||||
|
||||
|
||||
def _make_option(o: mujoco.MjOption) -> types.Option:
|
||||
def _make_option(
|
||||
o: mujoco.MjOption, _full_compat: bool = False # pylint: disable=invalid-name
|
||||
) -> types.Option:
|
||||
"""Returns mjx.Option given mujoco.MjOption."""
|
||||
if o.integrator not in set(types.IntegratorType):
|
||||
raise NotImplementedError(f'{mujoco.mjtIntegrator(o.integrator)}')
|
||||
if not _full_compat:
|
||||
if o.integrator not in set(types.IntegratorType):
|
||||
raise NotImplementedError(f'{mujoco.mjtIntegrator(o.integrator)}')
|
||||
|
||||
if o.cone not in set(types.ConeType):
|
||||
raise NotImplementedError(f'{mujoco.mjtCone(o.cone)}')
|
||||
if o.cone not in set(types.ConeType):
|
||||
raise NotImplementedError(f'{mujoco.mjtCone(o.cone)}')
|
||||
|
||||
if o.jacobian not in set(types.JacobianType):
|
||||
raise NotImplementedError(f'{mujoco.mjtJacobian(o.jacobian)}')
|
||||
if o.jacobian not in set(types.JacobianType):
|
||||
raise NotImplementedError(f'{mujoco.mjtJacobian(o.jacobian)}')
|
||||
|
||||
if o.solver not in set(types.SolverType):
|
||||
raise NotImplementedError(f'{mujoco.mjtSolver(o.solver)}')
|
||||
if o.solver not in set(types.SolverType):
|
||||
raise NotImplementedError(f'{mujoco.mjtSolver(o.solver)}')
|
||||
|
||||
for i in range(mujoco.mjtEnableBit.mjNENABLE):
|
||||
if o.enableflags & 2**i:
|
||||
raise NotImplementedError(f'{mujoco.mjtEnableBit(2 ** i)}')
|
||||
for i in range(mujoco.mjtEnableBit.mjNENABLE):
|
||||
if o.enableflags & 2**i:
|
||||
raise NotImplementedError(f'{mujoco.mjtEnableBit(2 ** i)}')
|
||||
|
||||
has_fluid_params = o.density > 0 or o.viscosity > 0 or o.wind.any()
|
||||
implicitfast = o.integrator == mujoco.mjtIntegrator.mjINT_IMPLICITFAST
|
||||
if implicitfast and has_fluid_params:
|
||||
raise NotImplementedError('implicitfast not implemented for fluid drag.')
|
||||
if not _full_compat:
|
||||
if implicitfast and has_fluid_params:
|
||||
raise NotImplementedError('implicitfast not implemented for fluid drag.')
|
||||
|
||||
fields = {f.name: getattr(o, f.name, None) for f in types.Option.fields()}
|
||||
fields['integrator'] = types.IntegratorType(o.integrator)
|
||||
@@ -91,7 +96,7 @@ def put_model(
|
||||
m: the model to put onto device
|
||||
device: which device to use - if unspecified picks the default device
|
||||
_full_compat: put all MjModel fields onto device irrespective of MJX support
|
||||
This is an experimental feature. Avoid using it for now.
|
||||
This is an experimental feature. Avoid using it for now.
|
||||
|
||||
Returns:
|
||||
an mjx.Model placed on device
|
||||
@@ -118,29 +123,20 @@ def put_model(
|
||||
if t == mujoco.mjtGeom.mjGEOM_MESH:
|
||||
mesh_geomid.add(g)
|
||||
|
||||
# check for spatial tendon internal geom wrapping
|
||||
if m.ntendon:
|
||||
# find sphere or cylinder geoms (if any exist)
|
||||
(wrap_id_geom,) = np.nonzero(
|
||||
(m.wrap_type == mujoco.mjtWrap.mjWRAP_SPHERE)
|
||||
| (m.wrap_type == mujoco.mjtWrap.mjWRAP_CYLINDER)
|
||||
# check for unsupported sensor and equality constraint combinations
|
||||
sensor_rne_postconstraint = (
|
||||
np.any(m.sensor_type == types.SensorType.ACCELEROMETER)
|
||||
| np.any(m.sensor_type == types.SensorType.FORCE)
|
||||
| np.any(m.sensor_type == types.SensorType.TORQUE)
|
||||
)
|
||||
eq_connect_weld = np.any(m.eq_type == types.EqType.CONNECT) | np.any(
|
||||
m.eq_type == types.EqType.WELD
|
||||
)
|
||||
if sensor_rne_postconstraint and eq_connect_weld:
|
||||
raise NotImplementedError(
|
||||
'rne_postconstraint not implemented with equality constraints:'
|
||||
' connect, weld.'
|
||||
)
|
||||
wrap_objid_geom = m.wrap_objid[wrap_id_geom]
|
||||
geom_pos = m.geom_pos[wrap_objid_geom]
|
||||
geom_size = m.geom_size[wrap_objid_geom, 0]
|
||||
|
||||
# find sidesites (if any exist)
|
||||
side_id = np.round(m.wrap_prm[wrap_id_geom]).astype(int)
|
||||
side = m.site_pos[side_id]
|
||||
|
||||
# check for sidesite inside geom
|
||||
if np.any(
|
||||
(np.linalg.norm(side - geom_pos, axis=1) < geom_size) & (side_id >= 0)
|
||||
):
|
||||
raise NotImplementedError(
|
||||
'Internal wrapping with sphere and cylinder geoms is not'
|
||||
' implemented for spatial tendons.'
|
||||
)
|
||||
|
||||
for enum_field, enum_type, mj_type in (
|
||||
(m.actuator_biastype, types.BiasType, mujoco.mjtBias),
|
||||
@@ -163,20 +159,54 @@ def put_model(
|
||||
if f.metadata.get('restricted_to') != 'mjx'
|
||||
}
|
||||
fields = {f: getattr(m, f) for f in mj_field_names}
|
||||
|
||||
# zero out fields restricted to MuJoCo
|
||||
if not _full_compat:
|
||||
for f in types.Model.fields():
|
||||
if f.metadata.get('restricted_to') == 'mujoco' and isinstance(
|
||||
fields[f.name], np.ndarray
|
||||
):
|
||||
fields[f.name] = np.zeros((0,), dtype=fields[f.name].dtype)
|
||||
|
||||
fields['dof_hasfrictionloss'] = fields['dof_frictionloss'] > 0
|
||||
fields['tendon_hasfrictionloss'] = fields['tendon_frictionloss'] > 0
|
||||
fields['geom_rbound_hfield'] = fields['geom_rbound']
|
||||
fields['cam_mat0'] = fields['cam_mat0'].reshape((-1, 3, 3))
|
||||
fields['opt'] = _make_option(m.opt)
|
||||
fields['opt'] = _make_option(m.opt, _full_compat=_full_compat)
|
||||
fields['stat'] = _make_statistic(m.stat)
|
||||
|
||||
# spatial tendon wrap inside
|
||||
fields['wrap_inside_maxiter'] = 5
|
||||
fields['wrap_inside_tolerance'] = 1.0e-4
|
||||
fields['wrap_inside_z_init'] = 1.0 - 1.0e-5
|
||||
fields['is_wrap_inside'] = np.zeros(0, dtype=bool)
|
||||
if m.nsite:
|
||||
# find sphere or cylinder geoms (if any exist)
|
||||
(wrap_id_geom,) = np.nonzero(
|
||||
(m.wrap_type == mujoco.mjtWrap.mjWRAP_SPHERE)
|
||||
| (m.wrap_type == mujoco.mjtWrap.mjWRAP_CYLINDER)
|
||||
)
|
||||
wrap_objid_geom = m.wrap_objid[wrap_id_geom]
|
||||
geom_pos = m.geom_pos[wrap_objid_geom]
|
||||
geom_size = m.geom_size[wrap_objid_geom, 0]
|
||||
|
||||
# find sidesites (if any exist)
|
||||
side_id = np.round(m.wrap_prm[wrap_id_geom]).astype(int)
|
||||
side = m.site_pos[side_id]
|
||||
|
||||
# wrap inside flag
|
||||
fields['is_wrap_inside'] = np.array(
|
||||
(np.linalg.norm(side - geom_pos, axis=1) < geom_size) & (side_id >= 0)
|
||||
)
|
||||
|
||||
# Pre-compile meshes for MJX collisions.
|
||||
fields['mesh_convex'] = [None] * m.nmesh
|
||||
for i in mesh_geomid:
|
||||
dataid = m.geom_dataid[i]
|
||||
if fields['mesh_convex'][dataid] is None:
|
||||
fields['mesh_convex'][dataid] = mesh.convex(m, dataid) # pytype: disable=unsupported-operands
|
||||
fields['mesh_convex'] = tuple(fields['mesh_convex'])
|
||||
if not _full_compat:
|
||||
for i in mesh_geomid:
|
||||
dataid = m.geom_dataid[i]
|
||||
if fields['mesh_convex'][dataid] is None:
|
||||
fields['mesh_convex'][dataid] = mesh.convex(m, dataid) # pytype: disable=unsupported-operands
|
||||
fields['mesh_convex'] = tuple(fields['mesh_convex'])
|
||||
|
||||
model = types.Model(**{k: copy.copy(v) for k, v in fields.items()})
|
||||
|
||||
@@ -195,8 +225,8 @@ def make_data(
|
||||
m: the model to use
|
||||
device: which device to use - if unspecified picks the default device
|
||||
_full_compat: create all MjData fields on device irrespective of MJX support
|
||||
This is an experimental feature. Avoid using it for now.
|
||||
If using this flag, also use _full_compat for put_model.
|
||||
This is an experimental feature. Avoid using it for now. If using this
|
||||
flag, also use _full_compat for put_model.
|
||||
|
||||
Returns:
|
||||
an initialized mjx.Data placed on device
|
||||
@@ -218,12 +248,19 @@ def make_data(
|
||||
solreffriction=jp.zeros((ncon, mujoco.mjNREF), dtype=float),
|
||||
solimp=jp.zeros((ncon, mujoco.mjNIMP), dtype=float),
|
||||
dim=dim,
|
||||
geom1=jp.full((ncon,), -1, dtype=jp.int32),
|
||||
geom2=jp.full((ncon,), -1, dtype=jp.int32),
|
||||
geom=jp.full((ncon, 2), -1, dtype=jp.int32),
|
||||
# let jax pick contact.geom int precision, for interop with
|
||||
# jax_enable_x64
|
||||
geom1=jp.full((ncon,), -1, dtype=int),
|
||||
geom2=jp.full((ncon,), -1, dtype=int),
|
||||
geom=jp.full((ncon, 2), -1, dtype=int),
|
||||
efc_address=efc_address,
|
||||
)
|
||||
|
||||
if m.opt.cone == types.ConeType.ELLIPTIC and np.any(contact.dim == 1):
|
||||
raise NotImplementedError(
|
||||
'condim=1 with ConeType.ELLIPTIC not implemented.'
|
||||
)
|
||||
|
||||
zero_fields = {
|
||||
'solver_niter': (int,),
|
||||
'time': (float,),
|
||||
@@ -233,7 +270,6 @@ def make_data(
|
||||
'ctrl': (m.nu, float),
|
||||
'qfrc_applied': (m.nv, float),
|
||||
'xfrc_applied': (m.nbody, 6, float),
|
||||
'eq_active': (m.neq, jp.uint8),
|
||||
'mocap_pos': (m.nmocap, 3, float),
|
||||
'mocap_quat': (m.nmocap, 4, float),
|
||||
'qacc': (m.nv, float),
|
||||
@@ -275,12 +311,14 @@ def make_data(
|
||||
'wrap_obj': (m.nwrap, 2, jp.int32),
|
||||
'wrap_xpos': (m.nwrap, 6, float),
|
||||
'actuator_length': (m.nu, float),
|
||||
'moment_rownnz': (m.nu, jp.int32),
|
||||
'moment_rowadr': (m.nu, jp.int32),
|
||||
'moment_colind': (m.nJmom, jp.int32),
|
||||
'actuator_moment': (m.nu, m.nv, float),
|
||||
'crb': (m.nbody, 10, float),
|
||||
'qM': (m.nM, float) if support.is_sparse(m) else (m.nv, m.nv, float),
|
||||
'qLD': (m.nM, float) if support.is_sparse(m) else (m.nv, m.nv, float),
|
||||
'qLDiagInv': (m.nv, float) if support.is_sparse(m) else (0, float),
|
||||
'qLDiagSqrtInv': (m.nv, float),
|
||||
'bvh_aabb_dyn': (m.nbvhdynamic, 6, float),
|
||||
'bvh_active': (m.nbvh, jp.uint8),
|
||||
'flexedge_velocity': (m.nflexedge, float),
|
||||
@@ -298,12 +336,23 @@ def make_data(
|
||||
'subtree_angmom': (m.nbody, 3, float),
|
||||
'qH': (m.nM, float) if support.is_sparse(m) else (m.nv, m.nv, float),
|
||||
'qHDiagInv': (m.nv, float),
|
||||
'D_rownnz': (m.nv, jp.int32),
|
||||
'D_rowadr': (m.nv, jp.int32),
|
||||
'D_colind': (m.nD, jp.int32),
|
||||
'B_rownnz': (m.nbody, jp.int32),
|
||||
'B_rowadr': (m.nbody, jp.int32),
|
||||
'B_colind': (m.nB, jp.int32),
|
||||
'M_rownnz': (m.nv, jp.int32),
|
||||
'M_rowadr': (m.nv, jp.int32),
|
||||
'M_colind': (m.nM, jp.int32),
|
||||
'mapM2M': (m.nM, jp.int32),
|
||||
'C_rownnz': (m.nv, jp.int32),
|
||||
'C_rowadr': (m.nv, jp.int32),
|
||||
'C_colind': (m.nC, jp.int32),
|
||||
'mapM2C': (m.nC, jp.int32),
|
||||
'D_rownnz': (m.nv, jp.int32),
|
||||
'D_rowadr': (m.nv, jp.int32),
|
||||
'D_diag': (m.nv, jp.int32),
|
||||
'D_colind': (m.nD, jp.int32),
|
||||
'mapM2D': (m.nD, jp.int32),
|
||||
'mapD2M': (m.nM, jp.int32),
|
||||
'qDeriv': (m.nD, float),
|
||||
'qLU': (m.nD, float),
|
||||
'actuator_force': (m.nu, float),
|
||||
@@ -345,7 +394,8 @@ def make_data(
|
||||
qpos=jp.array(m.qpos0),
|
||||
contact=contact,
|
||||
efc_type=efc_type,
|
||||
**zero_fields
|
||||
eq_active=m.eq_active0,
|
||||
**zero_fields,
|
||||
)
|
||||
|
||||
return d
|
||||
@@ -408,7 +458,7 @@ def get_data_into(
|
||||
ncon = (d_i.contact.dist <= 0).sum()
|
||||
efc_active = (d_i.efc_J != 0).any(axis=1)
|
||||
nefc = int(efc_active.sum())
|
||||
result_i.nnzJ = nefc * m.nv
|
||||
result_i.nJ = nefc * m.nv
|
||||
if ncon != result_i.ncon or nefc != result_i.nefc:
|
||||
mujoco._functions._realloc_con_efc(result_i, ncon=ncon, nefc=nefc) # pylint: disable=protected-access
|
||||
result_i.efc_J_rownnz[:] = np.repeat(m.nv, nefc)
|
||||
@@ -427,6 +477,26 @@ def get_data_into(
|
||||
result_i.contact.efc_address[:] = efc_map[result_i.contact.efc_address]
|
||||
continue
|
||||
|
||||
# MuJoCo actuator_moment is sparse, MJX uses a dense representation.
|
||||
if field.name == 'actuator_moment':
|
||||
moment_rownnz = np.zeros(m.nu, dtype=np.int32)
|
||||
moment_rowadr = np.zeros(m.nu, dtype=np.int32)
|
||||
moment_colind = np.zeros(m.nJmom, dtype=np.int32)
|
||||
actuator_moment = np.zeros(m.nJmom)
|
||||
if m.nu:
|
||||
mujoco.mju_dense2sparse(
|
||||
actuator_moment,
|
||||
d_i.actuator_moment,
|
||||
moment_rownnz,
|
||||
moment_rowadr,
|
||||
moment_colind,
|
||||
)
|
||||
result_i.moment_rownnz[:] = moment_rownnz
|
||||
result_i.moment_rowadr[:] = moment_rowadr
|
||||
result_i.moment_colind[:] = moment_colind
|
||||
result_i.actuator_moment[:] = actuator_moment
|
||||
continue
|
||||
|
||||
value = getattr(d_i, field.name)
|
||||
|
||||
if field.name in ('nefc', 'ncon'):
|
||||
@@ -440,14 +510,22 @@ def get_data_into(
|
||||
elif field.name == 'qM' and not support.is_sparse(m):
|
||||
value = value[dof_i, dof_j]
|
||||
elif field.name == 'qLD' and not support.is_sparse(m):
|
||||
value = value[dof_i, dof_j]
|
||||
# TODO(erikfrey): provide correct qLDs
|
||||
value = np.zeros(m.nM)
|
||||
elif field.name == 'qLDiagInv' and not support.is_sparse(m):
|
||||
value = np.ones(m.nv)
|
||||
|
||||
if isinstance(value, np.ndarray) and value.shape:
|
||||
if restricted_to in ('mujoco', 'mjx') and value.shape == (0,):
|
||||
if restricted_to in ('mujoco', 'mjx'):
|
||||
continue # don't copy fields that are mujoco-only or MJX-only
|
||||
getattr(result_i, field.name)[:] = value
|
||||
else:
|
||||
result_field = getattr(result_i, field.name)
|
||||
if result_field.shape != value.shape:
|
||||
raise ValueError(
|
||||
f'Input field {field.name} has shape {value.shape}, but output'
|
||||
f' has shape {result_field.shape}'
|
||||
)
|
||||
result_field[:] = value
|
||||
else:
|
||||
setattr(result_i, field.name, value)
|
||||
|
||||
@@ -464,7 +542,7 @@ def _make_contact(
|
||||
# if we have fewer Contacts for a condim range, pad the range with zeros
|
||||
|
||||
# build a map for where to find a dim-matching contact, or -1 if none
|
||||
contact_map = np.zeros_like(dim) - 1
|
||||
contact_map = -np.ones_like(dim)
|
||||
for i, di in enumerate(fields['dim']):
|
||||
space = [j for j, dj in enumerate(dim) if di == dj and contact_map[j] == -1]
|
||||
if not space:
|
||||
@@ -497,8 +575,8 @@ def put_data(
|
||||
d: the data to put on device
|
||||
device: which device to use - if unspecified picks the default device
|
||||
_full_compat: put all MjModel fields onto device irrespective of MJX support
|
||||
This is an experimental feature. Avoid using it for now.
|
||||
If using this flag, also use _full_compat for put_model.
|
||||
This is an experimental feature. Avoid using it for now. If using this
|
||||
flag, also use _full_compat for put_model.
|
||||
|
||||
Returns:
|
||||
an mjx.Data placed on device
|
||||
@@ -532,6 +610,17 @@ def put_data(
|
||||
# MJX does not support islanding, so only transfer the first solver_niter
|
||||
fields['solver_niter'] = fields['solver_niter'][0]
|
||||
|
||||
# convert sparse representation of actuator_moment to dense matrix
|
||||
moment = np.zeros((m.nu, m.nv))
|
||||
mujoco.mju_sparse2dense(
|
||||
moment,
|
||||
d.actuator_moment,
|
||||
d.moment_rownnz,
|
||||
d.moment_rowadr,
|
||||
d.moment_colind,
|
||||
)
|
||||
fields['actuator_moment'] = moment
|
||||
|
||||
contact, contact_map = _make_contact(d.contact, dim, efc_address)
|
||||
|
||||
# pad efc fields: MuJoCo efc arrays are sparse for inactive constraints.
|
||||
@@ -539,12 +628,14 @@ def put_data(
|
||||
# neither: it contains zeros for inactive constraints, and efc_J is always
|
||||
# (nefc, nv). this may change in the future.
|
||||
if mujoco.mj_isSparse(m):
|
||||
nr = d.efc_J_rownnz.shape[0]
|
||||
efc_j = np.zeros((nr, m.nv))
|
||||
for i in range(nr):
|
||||
rowadr = d.efc_J_rowadr[i]
|
||||
for j in range(d.efc_J_rownnz[i]):
|
||||
efc_j[i, d.efc_J_colind[rowadr + j]] = fields['efc_J'][rowadr + j]
|
||||
efc_j = np.zeros((d.efc_J_rownnz.shape[0], m.nv))
|
||||
mujoco.mju_sparse2dense(
|
||||
efc_j,
|
||||
fields['efc_J'],
|
||||
d.efc_J_rownnz,
|
||||
d.efc_J_rowadr,
|
||||
d.efc_J_colind,
|
||||
)
|
||||
fields['efc_J'] = efc_j
|
||||
else:
|
||||
fields['efc_J'] = fields['efc_J'].reshape((-1 if m.nv else 0, m.nv))
|
||||
@@ -574,10 +665,28 @@ def put_data(
|
||||
if num_rows > 1 and m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL:
|
||||
num_rows = (num_rows - 1) * 2
|
||||
efc_i, efc_o = d.contact.efc_address[id_from], efc_address[id_to]
|
||||
value[efc_o:efc_o + num_rows] = fields[fname][efc_i:efc_i + num_rows]
|
||||
if efc_i == -1:
|
||||
continue
|
||||
value[efc_o : efc_o + num_rows] = fields[fname][efc_i : efc_i + num_rows]
|
||||
|
||||
fields[fname] = value
|
||||
|
||||
if _full_compat:
|
||||
# full compatibility mode, we store sparse qM regardless of jacobian setting
|
||||
fields['_qM_sparse'] = fields['qM']
|
||||
fields['_qLD_sparse'] = fields['qLD']
|
||||
fields['_qLDiagInv_sparse'] = fields['qLDiagInv']
|
||||
else:
|
||||
fields['_qM_sparse'] = jp.zeros(0, dtype=float)
|
||||
fields['_qLD_sparse'] = jp.zeros(0, dtype=float)
|
||||
fields['_qLDiagInv_sparse'] = jp.zeros(0, dtype=float)
|
||||
# otherwise clear out unused arrays
|
||||
for f in types.Data.fields():
|
||||
if f.metadata.get('restricted_to') == 'mujoco' and isinstance(
|
||||
fields[f.name], np.ndarray
|
||||
):
|
||||
fields[f.name] = np.zeros(0, dtype=fields[f.name].dtype)
|
||||
|
||||
# convert qM and qLD if jacobian is dense
|
||||
if not support.is_sparse(m):
|
||||
fields['qM'] = np.zeros((m.nv, m.nv))
|
||||
@@ -590,24 +699,11 @@ def put_data(
|
||||
fields['qLD'] = np.zeros((m.nv, m.nv))
|
||||
fields['qLDiagInv'] = np.zeros(0)
|
||||
|
||||
if _full_compat:
|
||||
# full compatibility mode, we store sparse qM regardless of jacobian setting
|
||||
fields['_qM_sparse'] = fields['qM']
|
||||
fields['_qLD_sparse'] = fields['qLD']
|
||||
fields['_qLDiagInv_sparse'] = fields['qLDiagInv']
|
||||
else:
|
||||
fields['_qM_sparse'] = jp.zeros(0, dtype=float)
|
||||
fields['_qLD_sparse'] = jp.zeros(0, dtype=float)
|
||||
fields['_qLDiagInv_sparse'] = jp.zeros(0, dtype=float)
|
||||
# otherwise clear out unused arrays
|
||||
for f in types.Data.fields():
|
||||
if f.metadata.get('restricted_to') == 'mujoco':
|
||||
fields[f.name] = np.zeros(0, dtype=fields[f.name].dtype)
|
||||
|
||||
fields['contact'] = contact
|
||||
fields.update(ne=ne, nf=nf, nl=nl, nefc=nefc, ncon=ncon, efc_type=efc_type)
|
||||
|
||||
# copy because device_put is async:
|
||||
data = types.Data(**{k: copy.copy(v) for k, v in fields.items()})
|
||||
|
||||
return jax.device_put(data, device=device)
|
||||
data = jax.device_put(data, device=device)
|
||||
return _strip_weak_type(data)
|
||||
|
||||
+114
-30
@@ -20,6 +20,10 @@ import jax
|
||||
from jax import numpy as jp
|
||||
import mujoco
|
||||
from mujoco import mjx
|
||||
from mujoco.mjx._src import test_util
|
||||
# pylint: disable=g-importing-member
|
||||
from mujoco.mjx._src.types import ConeType
|
||||
# pylint: enable=g-importing-member
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -91,12 +95,11 @@ _MULTIPLE_CONSTRAINTS = """
|
||||
class ModelIOTest(parameterized.TestCase):
|
||||
"""IO tests for mjx.Model."""
|
||||
|
||||
@parameterized.parameters(
|
||||
_MULTIPLE_CONVEX_OBJECTS, _MULTIPLE_CONSTRAINTS
|
||||
)
|
||||
@parameterized.parameters(_MULTIPLE_CONVEX_OBJECTS, _MULTIPLE_CONSTRAINTS)
|
||||
def test_put_model(self, xml):
|
||||
m = mujoco.MjModel.from_xml_string(xml)
|
||||
mx = mjx.put_model(m)
|
||||
|
||||
def assert_not_weak_type(x):
|
||||
if isinstance(x, jax.Array):
|
||||
assert not x.weak_type
|
||||
@@ -117,6 +120,9 @@ class ModelIOTest(parameterized.TestCase):
|
||||
self.assertEqual(mx.nM, m.nM)
|
||||
self.assertAlmostEqual(mx.opt.timestep, m.opt.timestep)
|
||||
|
||||
# fields restricted to MuJoCo should not be populated
|
||||
self.assertEqual(mx.bvh_aabb.shape, (0,))
|
||||
|
||||
np.testing.assert_allclose(mx.body_parentid, m.body_parentid)
|
||||
np.testing.assert_allclose(mx.geom_type, m.geom_type)
|
||||
np.testing.assert_allclose(mx.geom_bodyid, m.geom_bodyid)
|
||||
@@ -166,33 +172,6 @@ class ModelIOTest(parameterized.TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
def test_spatial_tendon_not_implemented(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body name="arm">
|
||||
<joint name="arm" axis="0 1 0"/>
|
||||
<geom name="shoulder" type="sphere" size=".05"/>
|
||||
<site name="arm" pos="-.1 0 .05"/>
|
||||
<site name="sidesite" pos="0 0 0"/>
|
||||
</body>
|
||||
<body name="slider" pos=".05 0 -.2">
|
||||
<joint name="slider" type="slide" damping="1"/>
|
||||
<geom name="slider" type="box" size=".01 .01 .01"/>
|
||||
<site name="slider" pos="0 0 .01"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<tendon>
|
||||
<spatial name="rope" range="0 .35">
|
||||
<site site="slider"/>
|
||||
<geom geom="shoulder" sidesite="sidesite"/>
|
||||
<site site="arm"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
</mujoco>"""))
|
||||
|
||||
def test_margin_gap_mesh_not_implemented(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
||||
@@ -220,6 +199,20 @@ class ModelIOTest(parameterized.TestCase):
|
||||
<worldbody/>
|
||||
</mujoco>"""))
|
||||
|
||||
def test_wrap_inside(self):
|
||||
m = test_util.load_test_file('tendon/wrap_sidesite.xml')
|
||||
mx0 = mjx.put_model(m)
|
||||
np.testing.assert_equal(
|
||||
mx0.is_wrap_inside,
|
||||
np.array([1, 0, 1, 0, 1, 1, 0]),
|
||||
)
|
||||
m.site_pos[2] = m.site_pos[1]
|
||||
mx1 = mjx.put_model(m)
|
||||
np.testing.assert_equal(
|
||||
mx1.is_wrap_inside,
|
||||
np.array([0, 0, 1, 0, 1, 0, 0]),
|
||||
)
|
||||
|
||||
|
||||
class DataIOTest(parameterized.TestCase):
|
||||
"""IO tests for mjx.Data."""
|
||||
@@ -310,6 +303,15 @@ class DataIOTest(parameterized.TestCase):
|
||||
np.testing.assert_allclose(dx.cvel, d.cvel)
|
||||
np.testing.assert_allclose(dx.cdof_dot, d.cdof_dot)
|
||||
|
||||
# check that there are no weak types
|
||||
self.assertFalse(
|
||||
any(
|
||||
jax.tree_util.tree_flatten(
|
||||
jax.tree_util.tree_map(lambda x: x.weak_type, dx)
|
||||
)[0]
|
||||
)
|
||||
)
|
||||
|
||||
# check that qM is transformed properly
|
||||
qm = np.zeros((m.nv, m.nv), dtype=np.float64)
|
||||
mujoco.mj_fullM(m, qm, d.qM)
|
||||
@@ -422,6 +424,26 @@ class DataIOTest(parameterized.TestCase):
|
||||
np.testing.assert_allclose(d_2.efc_aref, d.efc_aref)
|
||||
np.testing.assert_allclose(d_2.contact.efc_address, d.contact.efc_address)
|
||||
|
||||
def test_get_data_runs(self):
|
||||
xml = """
|
||||
<mujoco>
|
||||
<compiler autolimits="true"/>
|
||||
<worldbody>
|
||||
<body name="box">
|
||||
<joint name="slide1" type="slide" axis="1 0 0" />
|
||||
<geom type="box" size=".05 .05 .05" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<motor joint="slide1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
"""
|
||||
m = mujoco.MjModel.from_xml_string(xml)
|
||||
d = mujoco.MjData(m)
|
||||
dx = mjx.put_data(m, d)
|
||||
mjx.get_data(m, dx)
|
||||
|
||||
def test_get_data_batched(self):
|
||||
"""Test that get_data makes correct List[MjData] for batched Data."""
|
||||
|
||||
@@ -460,6 +482,18 @@ class DataIOTest(parameterized.TestCase):
|
||||
self.assertEqual(d_2.contact.frame.shape, (1, 9))
|
||||
np.testing.assert_allclose(d_2.contact.frame, d.contact.frame)
|
||||
|
||||
def test_get_data_into_wrong_shape(self):
|
||||
"""Tests that get_data_into throwsif input and output shapes don't match."""
|
||||
|
||||
m = mujoco.MjModel.from_xml_string(_MULTIPLE_CONSTRAINTS)
|
||||
d = mujoco.MjData(m)
|
||||
mujoco.mj_step(m, d, 2)
|
||||
dx = mjx.put_data(m, d)
|
||||
m_2 = mujoco.MjModel.from_xml_string(_MULTIPLE_CONVEX_OBJECTS)
|
||||
d_2 = mujoco.MjData(m_2)
|
||||
with self.assertRaisesRegex(ValueError, r'Input field.*has shape.*'):
|
||||
mjx.get_data_into(d_2, m, dx)
|
||||
|
||||
def test_make_matches_put(self):
|
||||
"""Test that make_data produces a pytree that matches put_data."""
|
||||
|
||||
@@ -475,5 +509,55 @@ class DataIOTest(parameterized.TestCase):
|
||||
# calling make_data. they should be interchangeable for jax functions:
|
||||
step_fn_jit(mjx.make_data(m))
|
||||
|
||||
def test_contact_elliptic_condim1(self):
|
||||
"""Test that condim=1 with ConeType.ELLIPTIC is not implemented."""
|
||||
m = mujoco.MjModel.from_xml_string("""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom size="0 0 1e-5" type="plane" condim="1"/>
|
||||
<body>
|
||||
<freejoint/>
|
||||
<geom size="0.1" condim="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
""")
|
||||
m.opt.cone = ConeType.ELLIPTIC
|
||||
with self.assertRaises(NotImplementedError):
|
||||
mjx.make_data(m)
|
||||
|
||||
@parameterized.product(
|
||||
sensor=['accelerometer', 'force', 'torque'], equality=['connect', 'weld']
|
||||
)
|
||||
def test_sensor_constraint_compatibility(self, sensor, equality):
|
||||
"""Test unsupported sensor and equality constraint combinations."""
|
||||
equality_constraint = f'{equality} body1="body1" body2="body2"'
|
||||
if equality == 'connect':
|
||||
equality_constraint += ' anchor="0 0 0"'
|
||||
m = mujoco.MjModel.from_xml_string(f"""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body name="body1">
|
||||
<freejoint/>
|
||||
<geom size="0.1"/>
|
||||
<site name="site1"/>
|
||||
</body>
|
||||
<body name="body2">
|
||||
<freejoint/>
|
||||
<geom size="0.1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<equality>
|
||||
<{equality_constraint}/>
|
||||
</equality>
|
||||
<sensor>
|
||||
<{sensor} site="site1"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
""")
|
||||
with self.assertRaises(NotImplementedError):
|
||||
mjx.put_model(m)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
|
||||
@@ -28,6 +28,7 @@ def matmul_unroll(a: jax.Array, b: jax.Array) -> jax.Array:
|
||||
Args:
|
||||
a: left hand of matmul operand
|
||||
b: right hand of matmul operand
|
||||
|
||||
Returns:
|
||||
the matrix product of the inputs.
|
||||
"""
|
||||
@@ -50,7 +51,7 @@ def norm(
|
||||
"""Calculates a linalg.norm(x) that's safe for gradients at x=0.
|
||||
|
||||
Avoids a poorly defined gradient for jnp.linal.norm(0) see
|
||||
https://github.com/google/jax/issues/3058 for details
|
||||
https://github.com/jax-ml/jax/issues/3058 for details
|
||||
Args:
|
||||
x: A jnp.array
|
||||
axis: The axis along which to compute the norm
|
||||
|
||||
@@ -53,42 +53,6 @@ def _get_face_norm(vert: np.ndarray, face: np.ndarray) -> np.ndarray:
|
||||
return face_norm
|
||||
|
||||
|
||||
def _get_unique_edge_dir(vert: np.ndarray, face: np.ndarray) -> np.ndarray:
|
||||
"""Returns unique edge directions.
|
||||
|
||||
Args:
|
||||
vert: (n_vert, 3) vertices
|
||||
face: (n_face, n_vert) face index array
|
||||
|
||||
Returns:
|
||||
edges: tuples of vertex indexes for each edge
|
||||
"""
|
||||
r_face = np.roll(face, 1, axis=1)
|
||||
edges = np.concatenate(np.array([face, r_face]).T)
|
||||
|
||||
# do a first pass to remove duplicates
|
||||
edges.sort(axis=1)
|
||||
edges = np.unique(edges, axis=0)
|
||||
edges = edges[edges[:, 0] != edges[:, 1]] # get rid of edges from padded face
|
||||
|
||||
# get normalized edge directions
|
||||
edge_vert = vert.take(edges, axis=0)
|
||||
edge_dir = edge_vert[:, 0] - edge_vert[:, 1]
|
||||
norms = np.sqrt(np.sum(edge_dir**2, axis=1))
|
||||
edge_dir = edge_dir / norms.reshape((-1, 1))
|
||||
|
||||
# get the first unique edge for all pairwise comparisons
|
||||
diff1 = edge_dir[:, None, :] - edge_dir[None, :, :]
|
||||
diff2 = edge_dir[:, None, :] + edge_dir[None, :, :]
|
||||
matches = (np.linalg.norm(diff1, axis=-1) < 1e-6) | (
|
||||
np.linalg.norm(diff2, axis=-1) < 1e-6
|
||||
)
|
||||
matches = np.tril(matches).sum(axis=-1)
|
||||
unique_edge_idx = np.where(matches == 1)[0]
|
||||
|
||||
return edges[unique_edge_idx]
|
||||
|
||||
|
||||
def _get_edge_normals(
|
||||
face: np.ndarray, face_norm: np.ndarray
|
||||
) -> Tuple[np.ndarray, np.ndarray]:
|
||||
@@ -172,8 +136,8 @@ def _merge_coplanar(
|
||||
|
||||
# resize faces that exceed max polygon vertices
|
||||
if face.shape[0] > _MAX_HULL_FACE_VERTICES:
|
||||
name = m.names[m.name_meshadr[meshid]:]
|
||||
name = name[:name.find(b'\x00')].decode('utf-8')
|
||||
name = m.names[m.name_meshadr[meshid] :]
|
||||
name = name[: name.find(b'\x00')].decode('utf-8')
|
||||
warnings.warn(
|
||||
f'Mesh "{name}" has a coplanar face with more than '
|
||||
f'{_MAX_HULL_FACE_VERTICES} vertices. This may lead to performance '
|
||||
@@ -221,7 +185,6 @@ def box(info: GeomInfo) -> ConvexInfo:
|
||||
# pyformat: enable
|
||||
face_normal = _get_face_norm(vert, face)
|
||||
edge, edge_face_normal = _get_edge_normals(face, face_normal)
|
||||
edge_dir = _get_unique_edge_dir(vert, face)
|
||||
face = vert[face] # materialize full nface x nvert matrix
|
||||
|
||||
c = ConvexInfo(
|
||||
@@ -233,7 +196,6 @@ def box(info: GeomInfo) -> ConvexInfo:
|
||||
face_normal,
|
||||
edge,
|
||||
edge_face_normal,
|
||||
edge_dir,
|
||||
)
|
||||
c = jax.tree_util.tree_map(jp.array, c)
|
||||
vert = jax.vmap(jp.multiply, in_axes=(None, 0))(c.vert, info.size)
|
||||
@@ -356,7 +318,6 @@ def hfield_prism(vert: jax.Array) -> ConvexInfo:
|
||||
face_norm,
|
||||
edges,
|
||||
face_norm[edge_face_norm],
|
||||
None,
|
||||
)
|
||||
|
||||
return jax.tree_util.tree_map(jp.array, c)
|
||||
|
||||
@@ -53,22 +53,14 @@ class MeshTest(absltest.TestCase):
|
||||
map_ = {v: k for k, v in enumerate(vidx)}
|
||||
h_face = np.vectorize(map_.get)(convex_face)
|
||||
face_verts = sorted([tuple(sorted(set(s))) for s in h_face.tolist()])
|
||||
expected_face_verts = sorted([
|
||||
(0, 3, 4), (1, 3, 4), (0, 2, 4), (0, 1, 2, 3), (1, 2, 4)])
|
||||
expected_face_verts = sorted(
|
||||
[(0, 3, 4), (1, 3, 4), (0, 2, 4), (0, 1, 2, 3), (1, 2, 4)]
|
||||
)
|
||||
self.assertSequenceEqual(
|
||||
face_verts,
|
||||
expected_face_verts,
|
||||
)
|
||||
|
||||
# check edges
|
||||
edge_dir = mesh._get_unique_edge_dir(convex_vert, convex_face)
|
||||
unique_edge = np.vectorize(map_.get)(edge_dir)
|
||||
unique_edge = np.array(sorted(unique_edge.tolist()))
|
||||
np.testing.assert_array_equal(
|
||||
unique_edge,
|
||||
np.array([[0, 2], [0, 3], [0, 4], [1, 4], [2, 4], [3, 4]]),
|
||||
)
|
||||
|
||||
# face normals
|
||||
face_normal = mesh._get_face_norm(convex_vert, convex_face)
|
||||
self.assertEqual(face_normal.shape, (5, 3))
|
||||
@@ -142,19 +134,5 @@ class ConvexHull2DTest(absltest.TestCase):
|
||||
np.testing.assert_array_almost_equal(normal, expected)
|
||||
|
||||
|
||||
class UniqueEdgesTest(absltest.TestCase):
|
||||
|
||||
def test_tetrahedron_edges(self):
|
||||
"""Tests unique edges for a tetrahedron."""
|
||||
vert = np.array(
|
||||
[[-0.1, 0.0, -0.1], [0.0, 0.1, 0.1], [0.1, 0.0, -0.1], [0.0, -0.1, 0.1]]
|
||||
)
|
||||
face = np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1], [2, 1, 3]])
|
||||
idx = mesh._get_unique_edge_dir(vert, face)
|
||||
np.testing.assert_array_equal(
|
||||
idx, np.array([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]])
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
|
||||
@@ -31,6 +31,7 @@ from mujoco.mjx._src.types import Model
|
||||
|
||||
def _spring_damper(m: Model, d: Data) -> jax.Array:
|
||||
"""Applies joint level spring and damping forces."""
|
||||
|
||||
def fn(jnt_typs, stiffness, qpos_spring, qpos):
|
||||
qpos_i = 0
|
||||
qfrcs = []
|
||||
|
||||
@@ -108,6 +108,29 @@ def _ray_capsule(
|
||||
return x
|
||||
|
||||
|
||||
def _ray_ellipsoid(
|
||||
size: jax.Array,
|
||||
pnt: jax.Array,
|
||||
vec: jax.Array,
|
||||
) -> jax.Array:
|
||||
"""Returns the distance at which a ray intersects with an ellipsoid."""
|
||||
|
||||
# invert size^2
|
||||
s = 1 / jp.square(size)
|
||||
|
||||
# (x*lvec+lpnt)' * diag(1/size^2) * (x*lvec+lpnt) = 1
|
||||
svec = s * vec
|
||||
a = svec @ vec
|
||||
b = svec @ pnt
|
||||
c = (s * pnt) @ pnt - 1
|
||||
|
||||
# solve a*x^2 + 2*b*x + c = 0
|
||||
x0, x1 = _ray_quad(a, b, c)
|
||||
x = jp.where(jp.isinf(x0), x1, x0)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def _ray_box(
|
||||
size: jax.Array,
|
||||
pnt: jax.Array,
|
||||
@@ -201,6 +224,7 @@ _RAY_FUNC = {
|
||||
GeomType.PLANE: _ray_plane,
|
||||
GeomType.SPHERE: _ray_sphere,
|
||||
GeomType.CAPSULE: _ray_capsule,
|
||||
GeomType.ELLIPSOID: _ray_ellipsoid,
|
||||
GeomType.BOX: _ray_box,
|
||||
GeomType.MESH: _ray_mesh,
|
||||
}
|
||||
@@ -233,8 +257,6 @@ def ray(
|
||||
|
||||
dists, ids = [], []
|
||||
geom_filter = m.geom_bodyid != bodyexclude
|
||||
geom_filter &= (m.geom_matid != -1) | (m.geom_rgba[:, 3] != 0)
|
||||
geom_filter &= (m.geom_matid == -1) | (m.mat_rgba[m.geom_matid, 3] != 0)
|
||||
geom_filter &= flg_static | (m.body_weldid[m.geom_bodyid] != 0)
|
||||
if geomgroup:
|
||||
geomgroup = np.array(geomgroup, dtype=bool)
|
||||
@@ -244,8 +266,10 @@ def ray(
|
||||
geom_pnts = jax.vmap(lambda x, y: x.T @ (pnt - y))(d.geom_xmat, d.geom_xpos)
|
||||
geom_vecs = jax.vmap(lambda x: x.T @ vec)(d.geom_xmat)
|
||||
|
||||
geom_filter_dyn = (m.geom_matid != -1) | (m.geom_rgba[:, 3] != 0)
|
||||
geom_filter_dyn &= (m.geom_matid == -1) | (m.mat_rgba[m.geom_matid, 3] != 0)
|
||||
for geom_type, fn in _RAY_FUNC.items():
|
||||
id_, = np.nonzero(geom_filter & (m.geom_type == geom_type))
|
||||
(id_,) = np.nonzero(geom_filter & (m.geom_type == geom_type))
|
||||
|
||||
if id_.size == 0:
|
||||
continue
|
||||
@@ -257,6 +281,7 @@ def ray(
|
||||
else:
|
||||
dist = jax.vmap(fn)(*args)
|
||||
|
||||
dist = jp.where(geom_filter_dyn[id_], dist, jp.inf)
|
||||
dists, ids = dists + [dist], ids + [id_]
|
||||
|
||||
if not ids:
|
||||
|
||||
+11
-15
@@ -144,13 +144,11 @@ def _check_input(m: Model, args: Any, in_types: str) -> None:
|
||||
}
|
||||
for idx, (arg, typ) in enumerate(zip(args, in_types)):
|
||||
if len(arg) != size[typ]:
|
||||
raise IndexError(
|
||||
(
|
||||
f'f argument "{idx}" with type "{typ}" has length "{len(arg)}"'
|
||||
f' which does not match the in_types[{idx}] expected length of '
|
||||
f'"{size[typ]}".'
|
||||
)
|
||||
)
|
||||
raise IndexError((
|
||||
f'f argument "{idx}" with type "{typ}" has length "{len(arg)}"'
|
||||
f' which does not match the in_types[{idx}] expected length of '
|
||||
f'"{size[typ]}".'
|
||||
))
|
||||
|
||||
|
||||
def _check_output(
|
||||
@@ -158,13 +156,11 @@ def _check_output(
|
||||
) -> None:
|
||||
"""Checks that scan output has the right shape."""
|
||||
if y.shape[0] != take_ids.shape[0]:
|
||||
raise IndexError(
|
||||
(
|
||||
f'f output "{idx}" with type "{typ}" has shape "{y.shape[0]}" '
|
||||
f'which does not match the out_types[{idx}] expected size of'
|
||||
f' "{take_ids.shape[0]}".'
|
||||
)
|
||||
)
|
||||
raise IndexError((
|
||||
f'f output "{idx}" with type "{typ}" has shape "{y.shape[0]}" '
|
||||
f'which does not match the out_types[{idx}] expected size of'
|
||||
f' "{take_ids.shape[0]}".'
|
||||
))
|
||||
|
||||
|
||||
def flat(
|
||||
@@ -400,7 +396,7 @@ def body_tree(
|
||||
if t == 'b':
|
||||
continue
|
||||
elif t == 'j':
|
||||
key += (tuple(m.jnt_type[np.nonzero(m.jnt_bodyid == id_)[0]]))
|
||||
key += tuple(m.jnt_type[np.nonzero(m.jnt_bodyid == id_)[0]])
|
||||
elif t == 'v':
|
||||
key += (len(np.nonzero(m.dof_bodyid == id_)[0]),)
|
||||
elif t == 'q':
|
||||
|
||||
@@ -90,6 +90,7 @@ class ScanTest(absltest.TestCase):
|
||||
if tuple(jnt_types) == (JointType.FREE,):
|
||||
return None
|
||||
return val + sum(jnt_types)
|
||||
|
||||
b_expect = jp.array([[0, 0], [3, 3], [8, 8]])
|
||||
b_out = scan.flat(m, no_free, 'jb', 'b', m.jnt_type, b_in)
|
||||
np.testing.assert_equal(np.array(b_out), np.array(b_expect))
|
||||
@@ -99,6 +100,7 @@ class ScanTest(absltest.TestCase):
|
||||
if jnt_types.size == 0:
|
||||
self.fail('world has no dofs, should not be called')
|
||||
return val + sum(jnt_types)
|
||||
|
||||
v_in = jp.ones((m.nv, 1))
|
||||
scan.flat(m, no_world, 'jv', 'v', m.jnt_type, v_in)
|
||||
|
||||
@@ -141,6 +143,7 @@ class ScanTest(absltest.TestCase):
|
||||
return None
|
||||
carry = jp.zeros_like(val) if carry is None else carry
|
||||
return carry + val + sum(jnt_types)
|
||||
|
||||
b_expect = jp.array([[0, 0], [3, 3], [8, 8]])
|
||||
b_out = scan.body_tree(m, no_free, 'jb', 'b', m.jnt_type, b_in)
|
||||
np.testing.assert_equal(np.array(b_out), np.array(b_expect))
|
||||
|
||||
@@ -461,7 +461,7 @@ def sensor_acc(m: Model, d: Data) -> Data:
|
||||
force, condim_id = support.contact_force_dim(m, d, dim)
|
||||
forces.append(force)
|
||||
condim_ids.append(condim_id)
|
||||
forces = jp.concatenate(forces)[jp.concatenate(condim_ids)]
|
||||
forces = jp.concatenate(forces)[np.argsort(np.concatenate(condim_ids))]
|
||||
|
||||
# get bodies of contact geoms
|
||||
conbody = jp.array(m.geom_bodyid)[d.contact.geom]
|
||||
@@ -483,14 +483,14 @@ def sensor_acc(m: Model, d: Data) -> Data:
|
||||
conray = jp.where(conbody1[..., None], -conray, conray)
|
||||
|
||||
# compute distance, mapping over sites and contacts
|
||||
def _distance(
|
||||
site_size, site_xpos, site_xmat, site_type, contact_pos, conray
|
||||
):
|
||||
return jax.vmap(
|
||||
lambda site_size, site_xpos, site_xmat, conray: jax.vmap(
|
||||
lambda pnt, vec: ray.ray_geom(site_size, pnt, vec, site_type)
|
||||
)((contact_pos - site_xpos) @ site_xmat, conray @ site_xmat)
|
||||
)(site_size, site_xpos, site_xmat, conray)
|
||||
def _distance(site_size, site_xpos, site_xmat, site_type, pos, conray):
|
||||
def dist(size, xpos, xmat, conray):
|
||||
pnt = (pos - xpos) @ xmat
|
||||
vec = conray @ xmat
|
||||
ray_geom_ = lambda pnt, vec: ray.ray_geom(size, pnt, vec, site_type)
|
||||
return jax.vmap(ray_geom_)(pnt, vec)
|
||||
|
||||
return jax.vmap(dist)(site_size, site_xpos, site_xmat, conray)
|
||||
|
||||
dist = []
|
||||
dist_id = []
|
||||
@@ -506,8 +506,7 @@ def sensor_acc(m: Model, d: Data) -> Data:
|
||||
)
|
||||
dist.append(jp.where(jp.isinf(dist_site), 0, dist_site))
|
||||
dist_id.append(dist_id_site)
|
||||
|
||||
dist = jp.vstack(dist)[np.concatenate(dist_id)]
|
||||
dist = jp.vstack(dist)[np.argsort(np.concatenate(dist_id))]
|
||||
|
||||
# accumulate normal forces for each site
|
||||
sensor = jp.dot((dist > 0) & contacts, forces[:, 0])
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
from absl.testing import absltest
|
||||
from absl.testing import parameterized
|
||||
import jax
|
||||
|
||||
from jax import numpy as jp
|
||||
import mujoco
|
||||
from mujoco import mjx
|
||||
from mujoco.mjx._src import test_util
|
||||
from mujoco.mjx._src.types import ConeType
|
||||
import numpy as np
|
||||
|
||||
# tolerance for difference between MuJoCo and MJX smooth calculations - mostly
|
||||
@@ -41,10 +41,14 @@ def _assert_attr_eq(a, b, attr):
|
||||
|
||||
class SensorTest(parameterized.TestCase):
|
||||
|
||||
@parameterized.parameters('sensor/model.xml', 'sensor/sensor.xml')
|
||||
def test_sensor(self, filename):
|
||||
@parameterized.product(
|
||||
filename=['sensor/model.xml', 'sensor/sensor.xml'],
|
||||
cone_type=list(ConeType),
|
||||
)
|
||||
def test_sensor(self, filename, cone_type):
|
||||
"""Tests MJX sensor functions match MuJoCo sensor functions."""
|
||||
m = test_util.load_test_file(filename)
|
||||
m.opt.cone = cone_type
|
||||
d = mujoco.MjData(m)
|
||||
# give the system a little kick to ensure we have non-identity rotations
|
||||
d.qvel = 0.1 * np.random.random(m.nv)
|
||||
@@ -101,17 +105,14 @@ class SensorTest(parameterized.TestCase):
|
||||
<body>
|
||||
<joint type="hinge"/>
|
||||
<geom name="geom0" size="0.1"/>
|
||||
<site name="site0"/>
|
||||
<body>
|
||||
<joint type="hinge"/>
|
||||
<geom name="geom1" size="0.25"/>
|
||||
<site name="site1"/>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
<sensor>
|
||||
<distance name="distance" geom1="geom0" geom2="geom1"/>
|
||||
<touch name="touch" site="site0"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
""")
|
||||
|
||||
@@ -24,6 +24,7 @@ from mujoco.mjx._src import support
|
||||
from mujoco.mjx._src.types import CamLightType
|
||||
from mujoco.mjx._src.types import Data
|
||||
from mujoco.mjx._src.types import DisableBit
|
||||
from mujoco.mjx._src.types import EqType
|
||||
from mujoco.mjx._src.types import JointType
|
||||
from mujoco.mjx._src.types import Model
|
||||
from mujoco.mjx._src.types import TrnType
|
||||
@@ -291,7 +292,7 @@ def crb(m: Model, d: Data) -> Data:
|
||||
crb_cdof = jax.vmap(math.inert_mul)(crb_dof, d.cdof)
|
||||
qm = support.make_m(m, crb_cdof, d.cdof, m.dof_armature)
|
||||
d = d.replace(qM=qm)
|
||||
if support.is_sparse(m):
|
||||
if support.is_sparse(m) and d._qM_sparse.size > 0: # pylint: disable=protected-access
|
||||
d = d.replace(_qM_sparse=qm)
|
||||
|
||||
return d
|
||||
@@ -333,7 +334,7 @@ def factor_m(m: Model, d: Data) -> Data:
|
||||
pivots = []
|
||||
out = []
|
||||
|
||||
for (b, e, madr_d, madr_ij) in updates:
|
||||
for b, e, madr_d, madr_ij in updates:
|
||||
width = e - b
|
||||
rows.append(np.arange(madr_ij, madr_ij + width))
|
||||
madr_ijs.append(np.full((width,), madr_ij))
|
||||
@@ -353,7 +354,10 @@ def factor_m(m: Model, d: Data) -> Data:
|
||||
qld = (qld / qld[jp.array(madr_ds)]).at[m.dof_Madr].set(qld_diag)
|
||||
|
||||
d = d.replace(qLD=qld, qLDiagInv=1 / qld_diag)
|
||||
d = d.replace(_qLD_sparse=d.qLD, _qLDiagInv_sparse=d.qLDiagInv)
|
||||
if d._qLD_sparse.size > 0: # pylint: disable=protected-access
|
||||
d = d.replace(_qLD_sparse=d.qLD)
|
||||
if d._qLDiagInv_sparse.size > 0: # pylint: disable=protected-access
|
||||
d = d.replace(_qLDiagInv_sparse=d.qLDiagInv)
|
||||
|
||||
return d
|
||||
|
||||
@@ -507,7 +511,6 @@ def subtree_vel(m: Model, d: Data) -> Data:
|
||||
angmom_child, mom_parent_child = carry
|
||||
return angmom + mom + angmom_child + mom_parent_child, mom_parent
|
||||
|
||||
|
||||
subtree_angmom, _ = scan.body_tree(
|
||||
m,
|
||||
_subtree_angmom,
|
||||
@@ -531,6 +534,7 @@ def subtree_vel(m: Model, d: Data) -> Data:
|
||||
|
||||
def rne(m: Model, d: Data) -> Data:
|
||||
"""Computes inverse dynamics using the recursive Newton-Euler algorithm."""
|
||||
|
||||
# forward scan over tree: accumulate link center of mass acceleration
|
||||
def cacc_fn(cacc, cdof_dot, qvel):
|
||||
if cacc is None:
|
||||
@@ -632,6 +636,10 @@ def rne_postconstraint(m: Model, d: Data) -> Data:
|
||||
)
|
||||
|
||||
# TODO(taylorhowell): connect and weld constraints
|
||||
if np.any(m.eq_type == EqType.CONNECT):
|
||||
raise NotImplementedError('Connect constraints are not implemented.')
|
||||
if np.any(m.eq_type == EqType.WELD):
|
||||
raise NotImplementedError('Weld constraints are not implemented.')
|
||||
|
||||
# forward pass over bodies: compute cacc, cfrc_int
|
||||
def _forward(carry, cfrc_ext, cinert, cvel, body_dofadr, body_dofnum):
|
||||
@@ -726,7 +734,9 @@ def tendon(m: Model, d: Data) -> Data:
|
||||
for adr, num in zip(m.tendon_adr, m.tendon_num):
|
||||
for id_pulley in wrap_id_pulley:
|
||||
if adr <= id_pulley < adr + num:
|
||||
divisor[id_pulley : adr + num] = m.wrap_prm[id_pulley]
|
||||
divisor[id_pulley : adr + num] = np.maximum(
|
||||
mujoco.mjMINVAL, m.wrap_prm[id_pulley]
|
||||
)
|
||||
|
||||
# process spatial tendon sites
|
||||
(wrap_id_site,) = np.nonzero(m.wrap_type == WrapType.SITE)
|
||||
@@ -802,6 +812,7 @@ def tendon(m: Model, d: Data) -> Data:
|
||||
geom_xmat = d.geom_xmat[wrap_objid_geom]
|
||||
geom_size = m.geom_size[wrap_objid_geom, 0]
|
||||
geom_type = m.wrap_type[wrap_id_geom]
|
||||
is_sphere = geom_type == WrapType.SPHERE
|
||||
|
||||
# get body ids for site-geom-site instances
|
||||
body_id_site0 = m.site_bodyid[wrap_objid_site0]
|
||||
@@ -813,17 +824,52 @@ def tendon(m: Model, d: Data) -> Data:
|
||||
side = d.site_xpos[side_id]
|
||||
has_sidesite = np.expand_dims(np.array(side_id >= 0), -1)
|
||||
|
||||
# wrap inside
|
||||
# TODO(taylorhowell): check that is_wrap_inside is consistent with
|
||||
# site and geom relative positions
|
||||
(wrap_inside_id,) = np.nonzero(m.is_wrap_inside)
|
||||
(wrap_outside_id,) = np.nonzero(~m.is_wrap_inside)
|
||||
|
||||
# compute geom wrap length and connect points (if wrap occurs)
|
||||
lengths_geomgeom, geom_pnt0, geom_pnt1 = jax.vmap(support.wrap)(
|
||||
site_pnt0,
|
||||
site_pnt1,
|
||||
geom_xpos,
|
||||
geom_xmat,
|
||||
geom_size,
|
||||
side,
|
||||
has_sidesite,
|
||||
geom_type == WrapType.SPHERE,
|
||||
v_wrap = jax.vmap(
|
||||
support.wrap, in_axes=(0, 0, 0, 0, 0, 0, 0, 0, None, None, None, None)
|
||||
)
|
||||
lengths_inside, pnt0_inside, pnt1_inside = v_wrap(
|
||||
site_pnt0[wrap_inside_id],
|
||||
site_pnt1[wrap_inside_id],
|
||||
geom_xpos[wrap_inside_id],
|
||||
geom_xmat[wrap_inside_id],
|
||||
geom_size[wrap_inside_id],
|
||||
side[wrap_inside_id],
|
||||
has_sidesite[wrap_inside_id],
|
||||
is_sphere[wrap_inside_id],
|
||||
True,
|
||||
m.wrap_inside_maxiter,
|
||||
m.wrap_inside_tolerance,
|
||||
m.wrap_inside_z_init,
|
||||
)
|
||||
|
||||
lengths_outside, pnt0_outside, pnt1_outside = v_wrap(
|
||||
site_pnt0[wrap_outside_id],
|
||||
site_pnt1[wrap_outside_id],
|
||||
geom_xpos[wrap_outside_id],
|
||||
geom_xmat[wrap_outside_id],
|
||||
geom_size[wrap_outside_id],
|
||||
side[wrap_outside_id],
|
||||
has_sidesite[wrap_outside_id],
|
||||
is_sphere[wrap_outside_id],
|
||||
False,
|
||||
m.wrap_inside_maxiter,
|
||||
m.wrap_inside_tolerance,
|
||||
m.wrap_inside_z_init,
|
||||
)
|
||||
|
||||
wrap_id = np.argsort(np.concatenate([wrap_inside_id, wrap_outside_id]))
|
||||
vstack_ = lambda x, y: jp.vstack([x, y])[wrap_id]
|
||||
lengths_geomgeom = vstack_(lengths_inside, lengths_outside)
|
||||
geom_pnt0 = vstack_(pnt0_inside, pnt0_outside)
|
||||
geom_pnt1 = vstack_(pnt1_inside, pnt1_outside)
|
||||
|
||||
lengths_geomgeom = lengths_geomgeom.reshape(-1)
|
||||
|
||||
# identify geoms where wrap does not occur
|
||||
@@ -980,7 +1026,7 @@ def _site_dof_mask(m: Model) -> np.ndarray:
|
||||
mask = np.ones((m.nu, m.nv))
|
||||
for i in np.nonzero(m.actuator_trnid[:, 1] != -1)[0]:
|
||||
id_, refid = m.actuator_trnid[i]
|
||||
# intialize last dof address for each body
|
||||
# initialize last dof address for each body
|
||||
b0 = m.body_weldid[m.site_bodyid[id_]]
|
||||
b1 = m.body_weldid[m.site_bodyid[refid]]
|
||||
dofadr0 = m.body_dofadr[b0] + m.body_dofnum[b0] - 1
|
||||
|
||||
@@ -20,6 +20,7 @@ import jax
|
||||
import mujoco
|
||||
from mujoco import mjx
|
||||
from mujoco.mjx._src import test_util
|
||||
from mujoco.mjx._src.types import ConeType
|
||||
import numpy as np
|
||||
|
||||
# tolerance for difference between MuJoCo and MJX smooth calculations - mostly
|
||||
@@ -87,10 +88,16 @@ class SmoothTest(absltest.TestCase):
|
||||
dx = jax.jit(mjx.crb)(mx, mjx.put_data(m, d))
|
||||
_assert_attr_eq(d, dx, 'crb')
|
||||
_assert_attr_eq(d, dx, 'qM')
|
||||
_assert_eq(dx._qM_sparse, np.zeros(0), '_qM_sparse')
|
||||
# factor_m
|
||||
dx = jax.jit(mjx.factor_m)(mx, mjx.put_data(m, d))
|
||||
_assert_attr_eq(d, dx, 'qLD')
|
||||
qLDLegacy = np.zeros(mx.nM) # pylint:disable=invalid-name
|
||||
for i in range(m.nM):
|
||||
qLDLegacy[d.mapM2M[i]] = d.qLD[i]
|
||||
_assert_eq(qLDLegacy, dx.qLD, 'qLD')
|
||||
_assert_attr_eq(d, dx, 'qLDiagInv')
|
||||
_assert_eq(dx._qLD_sparse, np.zeros(0), '_qLD_sparse')
|
||||
_assert_eq(dx._qLDiagInv_sparse, np.zeros(0), '_qLDiagInv_sparse')
|
||||
# com_vel
|
||||
dx = jax.jit(mjx.com_vel)(mx, mjx.put_data(m, d))
|
||||
_assert_attr_eq(d, dx, 'cvel')
|
||||
@@ -113,7 +120,17 @@ class SmoothTest(absltest.TestCase):
|
||||
# transmission
|
||||
dx = jax.jit(mjx.transmission)(mx, dx)
|
||||
_assert_attr_eq(d, dx, 'actuator_length')
|
||||
_assert_attr_eq(d, dx, 'actuator_moment')
|
||||
|
||||
# convert sparse actuator_moment to dense representation
|
||||
moment = np.zeros((m.nu, m.nv))
|
||||
mujoco.mju_sparse2dense(
|
||||
moment,
|
||||
d.actuator_moment,
|
||||
d.moment_rownnz,
|
||||
d.moment_rowadr,
|
||||
d.moment_colind,
|
||||
)
|
||||
_assert_eq(moment, dx.actuator_moment, 'actuator_moment')
|
||||
|
||||
def test_disable_gravity(self):
|
||||
m = mujoco.MjModel.from_xml_string("""
|
||||
@@ -174,7 +191,17 @@ class SmoothTest(absltest.TestCase):
|
||||
mujoco.mj_transmission(m, d)
|
||||
dx = jax.jit(mjx.transmission)(mx, dx)
|
||||
_assert_attr_eq(d, dx, 'actuator_length')
|
||||
_assert_attr_eq(d, dx, 'actuator_moment')
|
||||
|
||||
# convert sparse actuator_moment to dense representation
|
||||
moment = np.zeros((m.nu, m.nv))
|
||||
mujoco.mju_sparse2dense(
|
||||
moment,
|
||||
d.actuator_moment,
|
||||
d.moment_rownnz,
|
||||
d.moment_rowadr,
|
||||
d.moment_colind,
|
||||
)
|
||||
_assert_eq(moment, dx.actuator_moment, 'actuator_moment')
|
||||
|
||||
def test_subtree_vel(self):
|
||||
"""Tests MJX subtree_vel function matches MuJoCo mj_subtreeVel."""
|
||||
@@ -195,27 +222,42 @@ class SmoothTest(absltest.TestCase):
|
||||
_assert_attr_eq(d, dx, 'subtree_linvel')
|
||||
_assert_attr_eq(d, dx, 'subtree_angmom')
|
||||
|
||||
def test_rnepostconstraint(self):
|
||||
|
||||
class RnePostConstraintTest(parameterized.TestCase):
|
||||
|
||||
@parameterized.parameters(ConeType)
|
||||
def test_rnepostconstraint(self, cone_type):
|
||||
"""Tests MJX rne_postconstraint function to match MuJoCo mj_rnePostConstraint."""
|
||||
|
||||
m = mujoco.MjModel.from_xml_string("""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom name="floor" size="0 0 .05" type="plane"/>
|
||||
<geom name="floor" size="10 10 .05" type="plane"/>
|
||||
<body pos="0 0 1">
|
||||
<joint type="ball" damping="1"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0" condim="1"/>
|
||||
<body pos="0.5 0 0">
|
||||
<joint type="ball" damping="1"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0" condim="3"/>
|
||||
</body>
|
||||
</body>
|
||||
<body pos="0 1 1">
|
||||
<joint type="ball" damping="1"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0" condim="6"/>
|
||||
<body pos="0.5 0 0">
|
||||
<joint type="ball" damping="1"/>
|
||||
<geom type="capsule" size="0.1 0.5" fromto="0 0 0 0.5 0 0" condim="3"/>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
<keyframe>
|
||||
<key qpos='0.424577 0.450592 0.451703 -0.642391 0.729379 0.545151 0.407756 0.0674697'/>
|
||||
<key qpos='0.424577 0.450592 0.451703 -0.642391 0.729379 0.545151 0.407756 0.0674697 0.424577 1.450592 0.451703 -0.642391 0.729379 0.545151 0.407756 0.0674697'/>
|
||||
</keyframe>
|
||||
</mujoco>
|
||||
""")
|
||||
# set cone type
|
||||
m.opt.cone = cone_type
|
||||
# create data and set to keyframe
|
||||
d = mujoco.MjData(m)
|
||||
mujoco.mj_resetDataKeyframe(m, d, 0)
|
||||
# apply external forces
|
||||
|
||||
@@ -52,6 +52,7 @@ class _Context(PyTreeNode):
|
||||
u: friction cone (normal and tangents) (num(con.dim > 1), 6)
|
||||
h: cone hessian (num(con.dim > 1), 6, 6)
|
||||
"""
|
||||
|
||||
qacc: jax.Array
|
||||
qfrc_constraint: jax.Array
|
||||
Jaref: jax.Array # pylint: disable=invalid-name
|
||||
@@ -225,6 +226,7 @@ class _LSContext(PyTreeNode):
|
||||
|
||||
def _while_loop_scan(cond_fun, body_fun, init_val, max_iter):
|
||||
"""Scan-based implementation (jit ok, reverse-mode autodiff ok)."""
|
||||
|
||||
def _iter(val):
|
||||
next_val = body_fun(val)
|
||||
next_cond = cond_fun(next_val)
|
||||
@@ -279,7 +281,12 @@ def _update_constraint(m: Model, d: Data, ctx: _Context) -> _Context:
|
||||
friction = d.contact.friction[d.contact.dim > 1]
|
||||
efc_address = d.contact.efc_address[d.contact.dim > 1]
|
||||
dim = d.contact.dim[d.contact.dim > 1]
|
||||
slice_fn = jax.vmap(lambda x: jax.lax.dynamic_slice(ctx.Jaref, (x,), (6,)))
|
||||
# to prevent out of range append zeros to ctx.Jaref
|
||||
slice_fn = jax.vmap(
|
||||
lambda x: jax.lax.dynamic_slice(
|
||||
jp.concatenate((ctx.Jaref, jp.zeros((3)))), (x,), (6,)
|
||||
)
|
||||
)
|
||||
u = slice_fn(efc_address) * ctx.fri
|
||||
mu, n, t = ctx.fri[:, 0], u[:, 0], jax.vmap(math.norm)(u[:, 1:])
|
||||
|
||||
@@ -377,7 +384,7 @@ def _update_gradient(m: Model, d: Data, ctx: _Context) -> _Context:
|
||||
# set efc of cone H along diagonal
|
||||
for i, (condim, addr) in enumerate(zip(dim, efc_address)):
|
||||
h_cone = ctx.h[i, :condim, :condim]
|
||||
cm = cm.at[addr:addr+condim, addr:addr+condim].add(h_cone)
|
||||
cm = cm.at[addr : addr + condim, addr : addr + condim].add(h_cone)
|
||||
h = d.efc_J.T @ cm @ d.efc_J
|
||||
else:
|
||||
h = (d.efc_J.T * d.efc_D * ctx.active) @ d.efc_J
|
||||
@@ -433,7 +440,12 @@ def _linesearch(m: Model, d: Data, ctx: _Context) -> _Context:
|
||||
quad = quad.at[jp.array(efc_con)].add(quad[jp.array(efc_fri)])
|
||||
|
||||
# rescale to make primal cone circular
|
||||
jv_fn = jax.vmap(lambda x: jax.lax.dynamic_slice(jv, (x,), (6,)))
|
||||
# to prevent out of range append zeros to jv
|
||||
jv_fn = jax.vmap(
|
||||
lambda x: jax.lax.dynamic_slice(
|
||||
jp.concatenate((jv, jp.zeros(3))), (x,), (6,)
|
||||
)
|
||||
)
|
||||
efc_elliptic = d.contact.efc_address[mask]
|
||||
v = jv_fn(efc_elliptic) * ctx.fri
|
||||
uu = jp.sum(ctx.u[:, 1:] * ctx.u[:, 1:], axis=1)
|
||||
|
||||
@@ -21,6 +21,7 @@ import mujoco
|
||||
from mujoco import mjx
|
||||
from mujoco.mjx._src import solver
|
||||
from mujoco.mjx._src import test_util
|
||||
from mujoco.mjx._src.types import ConeType
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -146,6 +147,24 @@ class SolverTest(parameterized.TestCase):
|
||||
nnz = dx.efc_J.any(axis=1)
|
||||
_assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force')
|
||||
|
||||
# TODO(taylorhowell): condim=1 with ConeType.ELLIPTIC
|
||||
@parameterized.product(condim=(3, 4, 6), cone=tuple(ConeType))
|
||||
def test_condim(self, condim, cone):
|
||||
"""Test contact dimension."""
|
||||
m = mujoco.MjModel.from_xml_string(f"""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom size="0 0 1e-5" type="plane" condim="1"/>
|
||||
<body pos="0 0 0.09">
|
||||
<freejoint/>
|
||||
<geom size="0.1" condim="{condim}"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
""")
|
||||
m.opt.cone = cone
|
||||
solver.solve(mjx.put_model(m), mjx.put_data(m, mujoco.MjData(m)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
|
||||
+593
-21
@@ -13,16 +13,20 @@
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
"""Engine support functions."""
|
||||
from collections.abc import Iterable, Sequence
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
import jax
|
||||
from jax import numpy as jp
|
||||
import mujoco
|
||||
from mujoco.introspect import mjxmacro
|
||||
from mujoco.mjx._src import math
|
||||
from mujoco.mjx._src import scan
|
||||
# pylint: disable=g-importing-member
|
||||
from mujoco.mjx._src.types import ConeType
|
||||
from mujoco.mjx._src.types import Data
|
||||
from mujoco.mjx._src.types import JacobianType
|
||||
from mujoco.mjx._src.types import JointType
|
||||
from mujoco.mjx._src.types import Model
|
||||
# pylint: enable=g-importing-member
|
||||
import numpy as np
|
||||
@@ -235,7 +239,7 @@ def _getadr(
|
||||
def id2name(
|
||||
m: Union[Model, mujoco.MjModel], typ: mujoco._enums.mjtObj, i: int
|
||||
) -> Optional[str]:
|
||||
"""Gets the name of an object with the specified mjtObj type and id.
|
||||
"""Gets the name of an object with the specified mjtObj type and ids.
|
||||
|
||||
See mujoco.id2name for more info.
|
||||
|
||||
@@ -283,6 +287,268 @@ def name2id(
|
||||
return names_map.get(name, -1)
|
||||
|
||||
|
||||
class BindModel(object):
|
||||
"""Class holding the requested MJX Model and spec id for binding a spec to Model."""
|
||||
|
||||
def __init__(self, model: Model, specs: Sequence[mujoco.MjStruct]):
|
||||
self.model = model
|
||||
self.prefix = ''
|
||||
ids = []
|
||||
for spec in specs:
|
||||
if not spec.name:
|
||||
raise KeyError(f'cannot bind spec with empty name')
|
||||
elif isinstance(spec, mujoco.MjsBody):
|
||||
self.prefix = 'body_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_BODY, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsJoint):
|
||||
self.prefix = 'jnt_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_JOINT, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsGeom):
|
||||
self.prefix = 'geom_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_GEOM, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsSite):
|
||||
self.prefix = 'site_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_SITE, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsLight):
|
||||
self.prefix = 'light_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_LIGHT, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsCamera):
|
||||
self.prefix = 'cam_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_CAMERA, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsMesh):
|
||||
self.prefix = 'mesh_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_MESH, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsHField):
|
||||
self.prefix = 'hfield_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_HFIELD, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsPair):
|
||||
self.prefix = 'pair_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_PAIR, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsTendon):
|
||||
self.prefix = 'tendon_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_TENDON, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsActuator):
|
||||
self.prefix = 'actuator_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_ACTUATOR, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsSensor):
|
||||
self.prefix = 'sensor_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_SENSOR, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsNumeric):
|
||||
self.prefix = 'numeric_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_NUMERIC, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsText):
|
||||
self.prefix = 'text_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_TEXT, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsTuple):
|
||||
self.prefix = 'tuple_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_TUPLE, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsKey):
|
||||
self.prefix = 'key_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_KEY, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsEquality):
|
||||
self.prefix = 'eq_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_EQUALITY, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsExclude):
|
||||
self.prefix = 'exclude_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_EXCLUDE, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsSkin):
|
||||
self.prefix = 'skin_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_SKIN, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsMaterial):
|
||||
self.prefix = 'material_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_MATERIAL, spec.name)
|
||||
else:
|
||||
raise ValueError('invalid spec type')
|
||||
if idx < 0:
|
||||
raise KeyError(f'invalid name: {spec.name}') # pytype: disable=attribute-error
|
||||
ids.append(idx)
|
||||
if len(ids) == 1:
|
||||
self.id = ids[0]
|
||||
else:
|
||||
self.id = ids
|
||||
|
||||
def _slice(self, name: str, idx: Union[int, slice, Sequence[int]]):
|
||||
_, expected_dim = mjxmacro.MJMODEL[name]
|
||||
var = getattr(self.model, name)
|
||||
if expected_dim == '1':
|
||||
return var[..., idx]
|
||||
elif expected_dim == '9':
|
||||
return var[..., idx, :, :]
|
||||
return var[..., idx, :]
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
return self._slice(self.prefix + name, self.id)
|
||||
|
||||
|
||||
def _bind_model(
|
||||
self: Model, obj: mujoco.MjStruct | Iterable[mujoco.MjStruct]
|
||||
) -> BindModel:
|
||||
"""Bind a Mujoco spec to an MJX Model."""
|
||||
if isinstance(obj, mujoco.MjStruct):
|
||||
obj = (obj,)
|
||||
else:
|
||||
obj = tuple(obj)
|
||||
return BindModel(self, obj)
|
||||
|
||||
|
||||
class BindData(object):
|
||||
"""Class holding the requested MJX Data and spec id for binding a spec to Data."""
|
||||
|
||||
def __init__(
|
||||
self, data: Data, model: Model, specs: Sequence[mujoco.MjStruct]
|
||||
):
|
||||
self.data = data
|
||||
self.model = model
|
||||
self.prefix = ''
|
||||
ids = []
|
||||
for spec in specs:
|
||||
if not spec.name:
|
||||
raise KeyError(f'cannot bind spec with empty name')
|
||||
elif isinstance(spec, mujoco.MjsBody):
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_BODY, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsJoint):
|
||||
self.prefix = 'jnt_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_JOINT, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsGeom):
|
||||
self.prefix = 'geom_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_GEOM, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsSite):
|
||||
self.prefix = 'site_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_SITE, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsLight):
|
||||
self.prefix = 'light_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_LIGHT, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsCamera):
|
||||
self.prefix = 'cam_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_CAMERA, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsTendon):
|
||||
self.prefix = 'ten_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_TENDON, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsActuator):
|
||||
self.prefix = 'actuator_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_ACTUATOR, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsSensor):
|
||||
self.prefix = 'sensor_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_SENSOR, spec.name)
|
||||
elif isinstance(spec, mujoco.MjsEquality):
|
||||
self.prefix = 'eq_'
|
||||
idx = name2id(model, mujoco.mjtObj.mjOBJ_EQUALITY, spec.name)
|
||||
else:
|
||||
raise ValueError('invalid spec type')
|
||||
if idx < 0:
|
||||
raise KeyError(f'invalid name: {spec.name}') # pytype: disable=attribute-error
|
||||
ids.append(idx)
|
||||
if len(ids) == 1:
|
||||
self.id = ids[0]
|
||||
else:
|
||||
self.id = ids
|
||||
|
||||
def __getname(self, name: str):
|
||||
"""Get the name of the attribute and check if the type is correct."""
|
||||
if name == 'sensordata':
|
||||
if self.prefix == 'sensor_':
|
||||
return name
|
||||
else:
|
||||
raise AttributeError('sensordata is not available for this type')
|
||||
if name == 'ctrl':
|
||||
if self.prefix == 'actuator_':
|
||||
return name
|
||||
else:
|
||||
raise AttributeError('ctrl is not available for this type')
|
||||
if name == 'qpos' or name == 'qvel' or name == 'qacc':
|
||||
if self.prefix == 'jnt_':
|
||||
return name
|
||||
else:
|
||||
raise AttributeError('qpos, qvel, qacc are not available for this type')
|
||||
else:
|
||||
return self.prefix + name
|
||||
|
||||
def _slice(self, name: str, idx: Union[int, slice, Sequence[int]]):
|
||||
_, expected_dim = mjxmacro.MJDATA[name]
|
||||
var = getattr(self.data, name)
|
||||
if expected_dim == '1':
|
||||
return var[..., idx]
|
||||
elif expected_dim == '9':
|
||||
return var[..., idx, :, :]
|
||||
return var[..., idx, :]
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
if name in ('sensordata', 'qpos', 'qvel', 'qacc'):
|
||||
adr = num = 0
|
||||
if name == 'sensordata':
|
||||
adr = self.model.sensor_adr[self.id]
|
||||
num = self.model.sensor_dim[self.id]
|
||||
elif name == 'qpos':
|
||||
adr = self.model.jnt_qposadr[self.id]
|
||||
typ = self.model.jnt_type[self.id]
|
||||
num = sum((typ == jt) * jt.qpos_width() for jt in JointType)
|
||||
elif name == 'qvel' or name == 'qacc':
|
||||
adr = self.model.jnt_dofadr[self.id]
|
||||
typ = self.model.jnt_type[self.id]
|
||||
num = sum((typ == jt) * jt.dof_width() for jt in JointType)
|
||||
if isinstance(self.id, list):
|
||||
idx = []
|
||||
for a, n in zip(adr, num):
|
||||
idx.extend(a + j for j in range(n))
|
||||
return self._slice(self.__getname(name), idx)
|
||||
elif num > 1:
|
||||
return self._slice(self.__getname(name), slice(adr, adr + num))
|
||||
else:
|
||||
return self._slice(self.__getname(name), adr)
|
||||
return self._slice(self.__getname(name), self.id)
|
||||
|
||||
def set(self, name: str, value: jax.Array) -> Data:
|
||||
"""Set the value of an array in an MJX Data."""
|
||||
if name == 'sensordata':
|
||||
raise AttributeError('sensordata is readonly')
|
||||
array = getattr(self.data, self.__getname(name))
|
||||
dim = 1 if len(array.shape) == 1 else array.shape[-1]
|
||||
try:
|
||||
iter(value)
|
||||
except TypeError:
|
||||
value = [value]
|
||||
if name in ('qpos', 'qvel', 'qacc'):
|
||||
adr = num = 0
|
||||
if name == 'qpos':
|
||||
adr = self.model.jnt_qposadr[self.id]
|
||||
typ = self.model.jnt_type[self.id]
|
||||
num = sum((typ == jt) * jt.qpos_width() for jt in JointType)
|
||||
elif name == 'qvel' or name == 'qacc':
|
||||
adr = self.model.jnt_dofadr[self.id]
|
||||
typ = self.model.jnt_type[self.id]
|
||||
num = sum((typ == jt) * jt.dof_width() for jt in JointType)
|
||||
if not isinstance(self.id, list):
|
||||
adr = [adr]
|
||||
num = [num]
|
||||
elif isinstance(self.id, list):
|
||||
adr = self.id * dim
|
||||
num = [dim for _ in range(len(self.id))]
|
||||
else:
|
||||
adr = [self.id * dim]
|
||||
num = [dim]
|
||||
i = 0
|
||||
for a, n in zip(adr, num):
|
||||
shape = array.shape
|
||||
array = array.flatten().at[a : a + n].set(value[i : i + n]).reshape(shape)
|
||||
i += n
|
||||
return self.data.replace(**{self.__getname(name): array})
|
||||
|
||||
|
||||
def _bind_data(
|
||||
self: Data, model: Model, obj: mujoco.MjStruct | Iterable[mujoco.MjStruct]
|
||||
) -> BindData:
|
||||
"""Bind a Mujoco spec to an MJX Data."""
|
||||
if isinstance(obj, mujoco.MjStruct):
|
||||
obj = (obj,)
|
||||
else:
|
||||
obj = tuple(obj)
|
||||
return BindData(self, model, obj)
|
||||
|
||||
|
||||
Model.bind = _bind_model
|
||||
Data.bind = _bind_data
|
||||
|
||||
|
||||
def _decode_pyramid(
|
||||
pyramid: jax.Array, mu: jax.Array, condim: int
|
||||
) -> jax.Array:
|
||||
@@ -307,12 +573,13 @@ def contact_force(
|
||||
"""Extract 6D force:torque for one contact, in contact frame by default."""
|
||||
efc_address = d.contact.efc_address[contact_id]
|
||||
condim = d.contact.dim[contact_id]
|
||||
if m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL:
|
||||
if m.opt.cone == ConeType.PYRAMIDAL:
|
||||
force = _decode_pyramid(
|
||||
d.efc_force[efc_address:], d.contact.friction[contact_id], condim
|
||||
)
|
||||
elif m.opt.cone == mujoco.mjtCone.mjCONE_ELLIPTIC:
|
||||
raise NotImplementedError('Elliptic cone force is not implemented yet.')
|
||||
elif m.opt.cone == ConeType.ELLIPTIC:
|
||||
force = d.efc_force[efc_address : efc_address + condim]
|
||||
force = jp.concatenate([force, jp.zeros((6 - condim))])
|
||||
else:
|
||||
raise ValueError(f'Unknown cone type: {m.opt.cone}')
|
||||
|
||||
@@ -331,7 +598,7 @@ def contact_force_dim(
|
||||
idx_dim = (d.contact.efc_address >= 0) & (d.contact.dim == dim)
|
||||
|
||||
# contact force from efc
|
||||
if m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL:
|
||||
if m.opt.cone == ConeType.PYRAMIDAL:
|
||||
efc_address = (
|
||||
d.contact.efc_address[idx_dim, None]
|
||||
+ np.arange(np.where(dim == 1, 1, 2 * (dim - 1)))[None]
|
||||
@@ -340,12 +607,13 @@ def contact_force_dim(
|
||||
force = jax.vmap(_decode_pyramid, in_axes=(0, 0, None))(
|
||||
efc_force, d.contact.friction[idx_dim], dim
|
||||
)
|
||||
return force, np.where(idx_dim)[0]
|
||||
elif m.opt.cone == mujoco.mjtCone.mjCONE_ELLIPTIC:
|
||||
# TODO(taylorhowell): add support for elliptic cone
|
||||
raise NotImplementedError('Elliptic cone force is not implemented yet.')
|
||||
elif m.opt.cone == ConeType.ELLIPTIC:
|
||||
efc_address = d.contact.efc_address[idx_dim, None] + np.arange(dim)[None]
|
||||
force = d.efc_force[efc_address]
|
||||
force = jp.hstack([force, jp.zeros((force.shape[0], 6 - dim))])
|
||||
else:
|
||||
raise ValueError(f'Unknown cone type: {m.opt.cone}.')
|
||||
return force, np.where(idx_dim)[0]
|
||||
|
||||
|
||||
def _length_circle(
|
||||
@@ -356,7 +624,9 @@ def _length_circle(
|
||||
p0n = math.normalize(p0).reshape(-1)
|
||||
p1n = math.normalize(p1).reshape(-1)
|
||||
|
||||
angle = jp.arccos(jp.dot(p0n, p1n))
|
||||
# clip input to closed interval for jp.arccos to prevent potential nan
|
||||
# TODO(taylorhowell): add test for case where clip is necessary
|
||||
angle = jp.arccos(jp.clip(jp.dot(p0n, p1n), -1, 1))
|
||||
|
||||
# flip if necessary
|
||||
cross = p0[1] * p1[0] - p0[0] * p1[1]
|
||||
@@ -381,7 +651,11 @@ def _is_intersect(
|
||||
(p2[0] - p1[0]) * (p1[1] - p3[1]) - (p2[1] - p1[1]) * (p1[0] - p3[0])
|
||||
) / det
|
||||
|
||||
return (a >= 0) & (a <= 1) & (b >= 0) & (b <= 1)
|
||||
return jp.where(
|
||||
jp.abs(det) < mujoco.mjMINVAL,
|
||||
0,
|
||||
(a >= 0) & (a <= 1) & (b >= 0) & (b <= 1),
|
||||
)
|
||||
|
||||
|
||||
def wrap_circle(
|
||||
@@ -394,7 +668,9 @@ def wrap_circle(
|
||||
sqrad = rad * rad
|
||||
dif = jp.array([d[2] - d[0], d[3] - d[1]])
|
||||
dd = dif[0] ** 2 + dif[1] ** 2
|
||||
a = jp.clip(-(dif[0] * d[0] + dif[1] * d[1]) / dd, 0, 1)
|
||||
a = jp.clip(
|
||||
-(dif[0] * d[0] + dif[1] * d[1]) / jp.maximum(mujoco.mjMINVAL, dd), 0, 1
|
||||
)
|
||||
seg = jp.array([a * dif[0] + d[0], a * dif[1] + d[1]])
|
||||
|
||||
point_inside0 = sqlen0 < sqrad
|
||||
@@ -408,13 +684,21 @@ def wrap_circle(
|
||||
|
||||
# construct the two solutions, compute goodness
|
||||
def _sol(sgn):
|
||||
sqrt0 = jp.sqrt(sqlen0 - sqrad)
|
||||
sqrt1 = jp.sqrt(sqlen1 - sqrad)
|
||||
sqrt0 = jp.sqrt(jp.maximum(mujoco.mjMINVAL, sqlen0 - sqrad))
|
||||
sqrt1 = jp.sqrt(jp.maximum(mujoco.mjMINVAL, sqlen1 - sqrad))
|
||||
|
||||
d00 = (d[0] * sqrad + sgn * rad * d[1] * sqrt0) / sqlen0
|
||||
d01 = (d[1] * sqrad - sgn * rad * d[0] * sqrt0) / sqlen0
|
||||
d10 = (d[2] * sqrad - sgn * rad * d[3] * sqrt1) / sqlen1
|
||||
d11 = (d[3] * sqrad + sgn * rad * d[2] * sqrt1) / sqlen1
|
||||
d00 = (d[0] * sqrad + sgn * rad * d[1] * sqrt0) / jp.maximum(
|
||||
mujoco.mjMINVAL, sqlen0
|
||||
)
|
||||
d01 = (d[1] * sqrad - sgn * rad * d[0] * sqrt0) / jp.maximum(
|
||||
mujoco.mjMINVAL, sqlen0
|
||||
)
|
||||
d10 = (d[2] * sqrad - sgn * rad * d[3] * sqrt1) / jp.maximum(
|
||||
mujoco.mjMINVAL, sqlen1
|
||||
)
|
||||
d11 = (d[3] * sqrad + sgn * rad * d[2] * sqrt1) / jp.maximum(
|
||||
mujoco.mjMINVAL, sqlen1
|
||||
)
|
||||
|
||||
sol = jp.array([[d00, d01], [d10, d11]])
|
||||
|
||||
@@ -463,6 +747,136 @@ def wrap_circle(
|
||||
return wlen, pnt
|
||||
|
||||
|
||||
def wrap_inside(
|
||||
end: jax.Array,
|
||||
radius: jax.Array,
|
||||
maxiter: int,
|
||||
tolerance: float,
|
||||
z_init: float,
|
||||
) -> Tuple[jax.Array, jax.Array]:
|
||||
"""Compute 2D inside wrap point.
|
||||
|
||||
Args:
|
||||
end: 2D points
|
||||
radius: radius of circle
|
||||
|
||||
Returns:
|
||||
status: 0 if wrap, else -1
|
||||
concatentated 2D wrap points: jax.Array
|
||||
"""
|
||||
mjMINVAL = mujoco.mjMINVAL # pylint: disable=invalid-name
|
||||
|
||||
# constants
|
||||
len0 = math.norm(end[:2])
|
||||
len1 = math.norm(end[2:])
|
||||
dif = jp.array([end[2] - end[0], end[3] - end[1]])
|
||||
dd = dif[0] * dif[0] + dif[1] * dif[1]
|
||||
|
||||
# either point inside circle or circle too small: no wrap
|
||||
no_wrap0 = (
|
||||
(len0 <= radius)
|
||||
| (len1 <= radius)
|
||||
| (radius < mjMINVAL)
|
||||
| (len0 < mjMINVAL)
|
||||
| (len1 < mjMINVAL)
|
||||
)
|
||||
|
||||
# find nearest point on line segment to origin: d0 + a*dif
|
||||
a = -1 * (dif[0] * end[0] + dif[1] * end[1]) / jp.maximum(mjMINVAL, dd)
|
||||
tmp = end[:2] + a * dif
|
||||
|
||||
# segment-circle intersection: no wrap
|
||||
no_wrap1 = (dd > mjMINVAL) & (a > 0) & (a < 1) & (math.norm(tmp) <= radius)
|
||||
|
||||
# prepare default in case of numerical failure: average
|
||||
pnt_avg = 0.5 * jp.array([end[0] + end[2], end[1] + end[3]])
|
||||
pnt_avg = radius * math.normalize(pnt_avg)
|
||||
|
||||
# compute function parameters: asin(A*z) + asin(B*z) - 2*asin(z) + G = 0
|
||||
A = radius / jp.maximum(mjMINVAL, len0) # pylint: disable=invalid-name
|
||||
B = radius / jp.maximum(mjMINVAL, len1) # pylint: disable=invalid-name
|
||||
cosG = (len0 * len0 + len1 * len1 - dd) / jp.maximum(mjMINVAL, 2 * len0 * len1) # pylint: disable=invalid-name
|
||||
|
||||
no_wrap2 = cosG < -1 + mjMINVAL
|
||||
early_return0 = cosG > 1 - mjMINVAL
|
||||
|
||||
G = jp.arccos(cosG) # pylint: disable=invalid-name
|
||||
|
||||
# initialize solver
|
||||
z = jp.array([z_init])
|
||||
f = jp.arcsin(A * z) + jp.arcsin(B * z) - 2 * jp.arcsin(z) + G
|
||||
|
||||
# make sure initialization is not on the other side
|
||||
early_return1 = f > 0
|
||||
|
||||
# iteratively solve with Newton's method
|
||||
def _newton(carry, _):
|
||||
# unpack
|
||||
z, f, status_prev = carry
|
||||
|
||||
# check current solution
|
||||
converged = jp.abs(f) <= tolerance
|
||||
|
||||
# compute derivative
|
||||
df = (
|
||||
A / jp.maximum(mjMINVAL, jp.sqrt(1 - z * z * A * A))
|
||||
+ B / jp.maximum(mjMINVAL, jp.sqrt(1 - z * z * B * B))
|
||||
- 2 / jp.maximum(mjMINVAL, jp.sqrt(1 - z * z))
|
||||
)
|
||||
|
||||
# check sign; SHOULD NOT OCCUR
|
||||
status0 = df > -mjMINVAL
|
||||
|
||||
# new point
|
||||
z_next = z - (1 - converged) * f / jp.where(
|
||||
jp.abs(df) < mjMINVAL, mjMINVAL, df
|
||||
)
|
||||
|
||||
# make sure we are moving to the left; SHOULD NOT OCCUR
|
||||
status1 = z_next > z
|
||||
|
||||
# evaluate solution
|
||||
f_next = (
|
||||
jp.arcsin(A * z_next)
|
||||
+ jp.arcsin(B * z_next)
|
||||
- 2 * jp.arcsin(z_next)
|
||||
+ G
|
||||
)
|
||||
|
||||
# exit if positive; SHOULD NOT OCCUR
|
||||
status2 = f_next > tolerance
|
||||
|
||||
return (
|
||||
z_next,
|
||||
f_next,
|
||||
status_prev | status0 | status1 | status2,
|
||||
), None
|
||||
|
||||
# TODO(taylorhowell): compare performance of jax.lax.scan and jax.lax.while_loop
|
||||
z, _, early_return2 = jax.lax.scan(
|
||||
_newton, (z, f, jp.array([False])), None, maxiter
|
||||
)[0]
|
||||
|
||||
# finalize: rotation by ang from vec = a or b, depending on cross(a,b) sign
|
||||
sign = end[0] * end[3] - end[1] * end[2] > 0
|
||||
vec = jp.where(sign, end[:2], end[2:])
|
||||
vec = math.normalize(vec)
|
||||
ang = jp.arcsin(z) - jp.where(sign, jp.arcsin(A * z), jp.arcsin(B * z))
|
||||
pnt_sol = radius * jp.array([
|
||||
jp.cos(ang) * vec[0] - jp.sin(ang) * vec[1],
|
||||
jp.sin(ang) * vec[0] + jp.cos(ang) * vec[1],
|
||||
]).reshape(-1)
|
||||
|
||||
no_wrap = no_wrap0 | no_wrap1 | no_wrap2
|
||||
early_return = early_return0 | early_return1 | early_return2
|
||||
status = -1 * no_wrap * jp.ones(1)
|
||||
|
||||
pnt = jp.where(early_return, pnt_avg, pnt_sol)
|
||||
pnt = jp.where(no_wrap, jp.zeros(2), pnt)
|
||||
|
||||
return status, jp.concatenate([pnt, pnt])
|
||||
|
||||
|
||||
def wrap(
|
||||
x0: jax.Array,
|
||||
x1: jax.Array,
|
||||
@@ -472,7 +886,11 @@ def wrap(
|
||||
side: jax.Array,
|
||||
sidesite: jax.Array,
|
||||
is_sphere: jax.Array,
|
||||
):
|
||||
is_wrap_inside: bool,
|
||||
wrap_inside_maxiter: int,
|
||||
wrap_inside_tolerance: float,
|
||||
wrap_inside_z_init: float,
|
||||
) -> Tuple[jax.Array, jax.Array, jax.Array]:
|
||||
"""Wrap tendon around sphere or cylinder."""
|
||||
# map sites to wrap object's local frame
|
||||
p0 = xmat.T @ (x0 - xpos)
|
||||
@@ -520,8 +938,13 @@ def wrap(
|
||||
sd = jp.array([jp.dot(s, axis0), jp.dot(s, axis1)])
|
||||
sd = math.normalize(sd) * size
|
||||
|
||||
# TODO(taylorhowell): implement wrap_inside for internal wrapping case
|
||||
wlen, pnt = wrap_circle(d, sd, sidesite, size)
|
||||
if is_wrap_inside:
|
||||
wlen, pnt = wrap_inside(
|
||||
d, size, wrap_inside_maxiter, wrap_inside_tolerance, wrap_inside_z_init
|
||||
)
|
||||
else:
|
||||
wlen, pnt = wrap_circle(d, sd, sidesite, size)
|
||||
|
||||
no_wrap = wlen < 0
|
||||
|
||||
# reconstruct 3D points in local frame: res
|
||||
@@ -557,3 +980,152 @@ def wrap(
|
||||
wpnt1 = jp.where(invalid, jp.zeros(3), wpnt1)
|
||||
|
||||
return wlen, wpnt0, wpnt1
|
||||
|
||||
|
||||
def muscle_gain_length(
|
||||
length: jax.Array, lmin: jax.Array, lmax: jax.Array
|
||||
) -> jax.Array:
|
||||
"""Normalized muscle length-gain curve."""
|
||||
# mid-ranges (maximum is at 1.0)
|
||||
a = 0.5 * (lmin + 1)
|
||||
b = 0.5 * (1 + lmax)
|
||||
|
||||
out0 = 0.5 * jp.square(
|
||||
(length - lmin) / jp.maximum(mujoco.mjMINVAL, a - lmin)
|
||||
)
|
||||
out1 = 1 - 0.5 * jp.square((1 - length) / jp.maximum(mujoco.mjMINVAL, 1 - a))
|
||||
out2 = 1 - 0.5 * jp.square((length - 1) / jp.maximum(mujoco.mjMINVAL, b - 1))
|
||||
out3 = 0.5 * jp.square(
|
||||
(lmax - length) / jp.maximum(mujoco.mjMINVAL, lmax - b)
|
||||
)
|
||||
|
||||
out = jp.where(length <= b, out2, out3)
|
||||
out = jp.where(length <= 1, out1, out)
|
||||
out = jp.where(length <= a, out0, out)
|
||||
out = jp.where((lmin <= length) & (length <= lmax), out, 0.0)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def muscle_gain(
|
||||
length: jax.Array,
|
||||
vel: jax.Array,
|
||||
lengthrange: jax.Array,
|
||||
acc0: jax.Array,
|
||||
prm: jax.Array,
|
||||
) -> jax.Array:
|
||||
"""Muscle active force."""
|
||||
# unpack parameters
|
||||
lrange = prm[:2]
|
||||
force, scale, lmin, lmax, vmax, _, fvmax = prm[2:9]
|
||||
|
||||
force = jp.where(force < 0, scale / jp.maximum(mujoco.mjMINVAL, acc0), force)
|
||||
|
||||
# optimum length
|
||||
L0 = (lengthrange[1] - lengthrange[0]) / jp.maximum( # pylint:disable=invalid-name
|
||||
mujoco.mjMINVAL, lrange[1] - lrange[0]
|
||||
)
|
||||
|
||||
# normalized length and velocity
|
||||
L = lrange[0] + (length - lengthrange[0]) / jp.maximum(mujoco.mjMINVAL, L0) # pylint:disable=invalid-name
|
||||
V = vel / jp.maximum(mujoco.mjMINVAL, L0 * vmax) # pylint:disable=invalid-name
|
||||
|
||||
# length curve
|
||||
FL = muscle_gain_length(L, lmin, lmax) # pylint:disable=invalid-name
|
||||
|
||||
# velocity curve
|
||||
y = fvmax - 1
|
||||
FV = jp.where( # pylint:disable=invalid-name
|
||||
V <= y, fvmax - jp.square(y - V) / jp.maximum(mujoco.mjMINVAL, y), fvmax
|
||||
)
|
||||
FV = jp.where(V <= 0, jp.square(V + 1), FV) # pylint:disable=invalid-name
|
||||
FV = jp.where(V <= -1, 0, FV) # pylint:disable=invalid-name
|
||||
|
||||
# compute FVL and scale, make it negative
|
||||
return -force * FL * FV
|
||||
|
||||
|
||||
def muscle_bias(
|
||||
length: jax.Array, lengthrange: jax.Array, acc0: jax.Array, prm: jax.Array
|
||||
) -> jax.Array:
|
||||
"""Muscle passive force."""
|
||||
# unpack parameters
|
||||
lrange = prm[:2]
|
||||
force, scale, _, lmax, _, fpmax = prm[2:8]
|
||||
|
||||
force = jp.where(force < 0, scale / jp.maximum(mujoco.mjMINVAL, acc0), force)
|
||||
|
||||
# optimum length
|
||||
L0 = (lengthrange[1] - lengthrange[0]) / jp.maximum( # pylint:disable=invalid-name
|
||||
mujoco.mjMINVAL, lrange[1] - lrange[0]
|
||||
)
|
||||
|
||||
# normalized length
|
||||
L = lrange[0] + (length - lengthrange[0]) / jp.maximum(mujoco.mjMINVAL, L0) # pylint:disable=invalid-name
|
||||
|
||||
# half-quadratic to (L0 + lmax) / 2, linear beyond
|
||||
b = 0.5 * (1 + lmax)
|
||||
|
||||
out1 = (
|
||||
-force
|
||||
* fpmax
|
||||
* 0.5
|
||||
* jp.square((L - 1) / jp.maximum(mujoco.mjMINVAL, b - 1))
|
||||
)
|
||||
out2 = -force * fpmax * (0.5 + (L - b) / jp.maximum(mujoco.mjMINVAL, b - 1))
|
||||
|
||||
out = jp.where(L <= b, out1, out2)
|
||||
out = jp.where(L <= 1, 0.0, out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def muscle_dynamics_timescale(
|
||||
dctrl: jax.Array,
|
||||
tau_act: jax.Array,
|
||||
tau_deact: jax.Array,
|
||||
smoothing_width: jax.Array,
|
||||
) -> jax.Array:
|
||||
"""Muscle time constant with optional smoothing."""
|
||||
# hard switching
|
||||
tau_hard = jp.where(dctrl > 0, tau_act, tau_deact)
|
||||
|
||||
def _sigmoid(x):
|
||||
# sigmoid function over 0 <= x <= 1 using quintic polynomial
|
||||
# sigmoid: f(x) = 6 * x^5 - 15 * x^4 + 10 * x^3
|
||||
# solution of f(0) = f'(0) = f''(0) = 0, f(1) = 1, f'(1) = f''(1) = 0
|
||||
sol = x * x * x * (3 * x * (2 * x - 5) + 10)
|
||||
sol = jp.where(x <= 0, 0, sol)
|
||||
sol = jp.where(x >= 1, 1, sol)
|
||||
return sol
|
||||
|
||||
# smooth switching
|
||||
# scale by width, center around 0.5 midpoint, rescale to bounds
|
||||
tau_smooth = tau_deact + (tau_act - tau_deact) * _sigmoid(
|
||||
dctrl / smoothing_width + 0.5
|
||||
)
|
||||
|
||||
return jp.where(smoothing_width < mujoco.mjMINVAL, tau_hard, tau_smooth)
|
||||
|
||||
|
||||
def muscle_dynamics(
|
||||
ctrl: jax.Array, act: jax.Array, prm: jax.Array
|
||||
) -> jax.Array:
|
||||
"""Muscle activation dynamics."""
|
||||
# clamp control
|
||||
ctrlclamp = jp.clip(ctrl, 0, 1)
|
||||
|
||||
# clamp activation
|
||||
actclamp = jp.clip(act, 0, 1)
|
||||
|
||||
# compute timescales as in Millard et at. (2013)
|
||||
# https://doi.org/10.1115/1.4023390
|
||||
tau_act = prm[0] * (0.5 + 1.5 * actclamp) # activation timescale
|
||||
tau_deact = prm[1] / (0.5 + 1.5 * actclamp) # deactivation timescale
|
||||
smoothing_width = prm[2] # width of smoothing sigmoid
|
||||
dctrl = ctrlclamp - act # excess excitation
|
||||
|
||||
tau = muscle_dynamics_timescale(dctrl, tau_act, tau_deact, smoothing_width)
|
||||
|
||||
# filter output
|
||||
return dctrl / jp.maximum(mujoco.mjMINVAL, tau)
|
||||
|
||||
@@ -157,6 +157,199 @@ class SupportTest(parameterized.TestCase):
|
||||
i = i if n is not None else -1
|
||||
self.assertEqual(support.name2id(mx, obj, n), i)
|
||||
|
||||
def test_bind(self):
|
||||
xml = """
|
||||
<mujoco model="test_bind_model">
|
||||
<worldbody>
|
||||
<body pos="10 20 30" name="body1">
|
||||
<joint axis="1 0 0" type="ball" name="joint1"/>
|
||||
<geom size="1 2 3" type="box" name="geom1" pos="0 1 0"/>
|
||||
</body>
|
||||
<body pos="40 50 60" name="body2">
|
||||
<joint axis="0 1 0" type="slide" name="joint2"/>
|
||||
<geom size="4 5 6" type="box" name="geom2"/>
|
||||
</body>
|
||||
<body pos="70 80 90" name="body3">
|
||||
<joint axis="0 0 1" type="slide" name="joint3"/>
|
||||
<geom size="7 8 9" type="box" name="geom3"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<motor name="actuator1" joint="joint1"/>
|
||||
<motor name="actuator2" joint="joint2"/>
|
||||
<motor name="actuator3" joint="joint3"/>
|
||||
</actuator>
|
||||
|
||||
<sensor>
|
||||
<framepos name="sensor1" objtype="body" objname="body1"/>
|
||||
<framepos name="sensor2" objtype="body" objname="body2"/>
|
||||
<framepos name="sensor3" objtype="body" objname="body3"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
"""
|
||||
|
||||
s = mujoco.MjSpec.from_string(xml)
|
||||
m = s.compile()
|
||||
d = mujoco.MjData(m)
|
||||
mx = mjx.put_model(m)
|
||||
dx = mjx.put_data(m, d)
|
||||
mujoco.mj_step(m, d)
|
||||
dx = mjx.step(mx, dx)
|
||||
|
||||
# test getting
|
||||
np.testing.assert_array_equal(mx.bind(s.bodies).pos, m.body_pos)
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.bodies).xpos, d.xpos)
|
||||
np.testing.assert_array_equal(m.bind(s.bodies[0]).mass, m.body_mass[0])
|
||||
np.testing.assert_array_equal(m.bind(s.bodies[0:1]).mass, [m.body_mass[0]])
|
||||
np.testing.assert_array_equal(mx.bind(s.bodies[0]).mass, m.body_mass[0])
|
||||
np.testing.assert_array_equal(mx.bind(s.bodies[0:1]).mass, [m.body_mass[0]])
|
||||
for i in range(m.nbody):
|
||||
np.testing.assert_array_equal(m.bind(s.bodies[i]).pos, m.body_pos[i, :])
|
||||
np.testing.assert_array_equal(mx.bind(s.bodies[i]).pos, m.body_pos[i, :])
|
||||
np.testing.assert_array_equal(d.bind(s.bodies[i]).xpos, d.xpos[i, :])
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.bodies[i]).xpos, d.xpos[i, :]
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.bodies[i]).xfrc_applied, d.xfrc_applied[i, :]
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(mx.bind(s.geoms).size, m.geom_size)
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.geoms).xpos, d.geom_xpos)
|
||||
for i in range(m.ngeom):
|
||||
np.testing.assert_array_equal(m.bind(s.geoms[i]).size, m.geom_size[i, :])
|
||||
np.testing.assert_array_equal(mx.bind(s.geoms[i]).size, m.geom_size[i, :])
|
||||
np.testing.assert_array_equal(d.bind(s.geoms[i]).xpos, d.geom_xpos[i, :])
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.geoms[i]).xpos, d.geom_xpos[i, :]
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(mx.bind(s.joints).axis, m.jnt_axis)
|
||||
np.testing.assert_array_equal(mx.bind(s.joints).qposadr, m.jnt_qposadr)
|
||||
np.testing.assert_array_equal(mx.bind(s.joints).dofadr, m.jnt_dofadr)
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.joints[1]).id, 1)
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.joints[1:2]).id, [1])
|
||||
qposnum = [4, 1, 1] # one ball joint (4) and two slide joints (1)
|
||||
dofnum = [3, 1, 1] # one ball joint (3) and two slide joints (1)
|
||||
for i in range(m.njnt):
|
||||
np.testing.assert_array_equal(m.bind(s.joints[i]).axis, m.jnt_axis[i, :])
|
||||
np.testing.assert_array_equal(mx.bind(s.joints[i]).axis, m.jnt_axis[i, :])
|
||||
np.testing.assert_array_almost_equal(
|
||||
dx.bind(mx, s.joints[i]).qpos,
|
||||
d.qpos[m.jnt_qposadr[i]:m.jnt_qposadr[i] + qposnum[i]], decimal=6
|
||||
)
|
||||
np.testing.assert_array_almost_equal(
|
||||
dx.bind(mx, s.joints[i]).qvel,
|
||||
d.qvel[m.jnt_dofadr[i]:m.jnt_dofadr[i] + dofnum[i]], decimal=6
|
||||
)
|
||||
np.testing.assert_array_almost_equal(
|
||||
dx.bind(mx, s.joints[i]).qacc,
|
||||
d.qacc[m.jnt_dofadr[i]:m.jnt_dofadr[i] + dofnum[i]], decimal=6
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, d.ctrl)
|
||||
for i in range(m.nu):
|
||||
np.testing.assert_array_equal(d.bind(s.actuators[i]).ctrl, d.ctrl[i])
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.actuators[i]).ctrl, d.ctrl[i]
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.sensors).sensordata, d.sensordata
|
||||
)
|
||||
for i in range(m.nsensor):
|
||||
np.testing.assert_array_equal(
|
||||
dx.bind(mx, s.sensors[i]).sensordata,
|
||||
d.sensordata[m.sensor_adr[i] : m.sensor_adr[i] + m.sensor_dim[i]],
|
||||
)
|
||||
|
||||
# test setting
|
||||
np.testing.assert_array_equal(d.ctrl, [0, 0, 0])
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, d.ctrl)
|
||||
dx2 = dx.bind(mx, s.actuators).set('ctrl', [1, 2, 3])
|
||||
np.testing.assert_array_equal(dx2.bind(mx, s.actuators).ctrl, [1, 2, 3])
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, [0, 0, 0])
|
||||
dx3 = dx.bind(mx, s.actuators[1:]).set('ctrl', [4, 5])
|
||||
np.testing.assert_array_equal(dx3.bind(mx, s.actuators).ctrl, [0, 4, 5])
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, [0, 0, 0])
|
||||
dx4 = dx.bind(mx, s.actuators[1]).set('ctrl', [6])
|
||||
np.testing.assert_array_equal(dx4.bind(mx, s.actuators).ctrl, [0, 6, 0])
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, [0, 0, 0])
|
||||
dx5 = dx.bind(mx, s.actuators[1]).set('ctrl', 7)
|
||||
np.testing.assert_array_equal(dx5.bind(mx, s.actuators).ctrl, [0, 7, 0])
|
||||
np.testing.assert_array_equal(dx.bind(mx, s.actuators).ctrl, [0, 0, 0])
|
||||
|
||||
qpos_1step = [1.00000e00, -3.67875e-06, 0, 0, 0, -3.924e-05]
|
||||
qpos_desired = [1, 0, 0, 0, 0, 8]
|
||||
np.testing.assert_array_almost_equal(d.qpos, qpos_1step)
|
||||
np.testing.assert_array_almost_equal(dx.bind(mx, s.joints).qpos, d.qpos)
|
||||
dx6 = dx.bind(mx, s.joints[::2]).set('qpos', [1, 0, 0, 0, 8])
|
||||
np.testing.assert_array_equal(dx6.bind(mx, s.joints).qpos, qpos_desired)
|
||||
np.testing.assert_array_almost_equal(dx.bind(mx, s.joints).qpos, d.qpos)
|
||||
dx6a = dx.bind(mx, s.joints[0]).set('qpos', qpos_desired[:4])
|
||||
np.testing.assert_array_equal(
|
||||
dx6a.bind(mx, s.joints[0]).qpos, qpos_desired[:4]
|
||||
)
|
||||
dx7 = dx.bind(mx, s.joints[::2]).set('qvel', [2.0, -1.2, 0.5, 0.3])
|
||||
np.testing.assert_array_almost_equal(
|
||||
dx7.bind(mx, s.joints).qvel, [2.0, -1.2, 0.5, 0.0, 0.3], decimal=6
|
||||
)
|
||||
dx8 = dx.bind(mx, s.joints[::2]).set('qacc', [3.0, -2.1, 0.6, 0.4])
|
||||
np.testing.assert_array_almost_equal(
|
||||
dx8.bind(mx, s.joints).qacc, [3.0, -2.1, 0.6, 0.0, 0.4], decimal=6
|
||||
)
|
||||
|
||||
dx9 = dx.bind(mx, s.bodies[1]).set('xfrc_applied', [1, 2, 3, 4, 5, 6])
|
||||
np.testing.assert_array_equal(
|
||||
dx9.bind(mx, s.bodies[1]).xfrc_applied, [1, 2, 3, 4, 5, 6]
|
||||
)
|
||||
for body in s.bodies[:1] + s.bodies[2:]:
|
||||
np.testing.assert_array_equal(
|
||||
dx7.bind(mx, body).xfrc_applied, [0, 0, 0, 0, 0, 0]
|
||||
)
|
||||
|
||||
# test invalid name
|
||||
with self.assertRaises(
|
||||
AttributeError, msg='ctrl is not available for this type'
|
||||
):
|
||||
print(dx.bind(mx, s.geoms).ctrl)
|
||||
with self.assertRaises(
|
||||
KeyError, msg='actuator_actuator_ctrl'
|
||||
):
|
||||
print(dx.bind(mx, s.actuators).actuator_ctrl)
|
||||
with self.assertRaises(
|
||||
AttributeError, msg='actuator_actuator_ctrl'
|
||||
):
|
||||
print(dx.bind(mx, s.actuators).set('actuator_ctrl', [1, 2, 3]))
|
||||
with self.assertRaises(
|
||||
AttributeError, msg='qpos, qvel, qacc are not available for this type'
|
||||
):
|
||||
print(dx.bind(mx, s.geoms).qpos)
|
||||
with self.assertRaises(KeyError, msg='invalid name: invalid_actuator_name'):
|
||||
s.actuators[0].name = 'invalid_actuator_name'
|
||||
print(dx.bind(mx, s.actuators).set('ctrl', [1, 2, 3]))
|
||||
with self.assertRaises(KeyError, msg='invalid name: invalid_geom_name'):
|
||||
s.geoms[0].name = 'invalid_geom_name'
|
||||
print(mx.bind(s.geoms).pos)
|
||||
|
||||
# test batched data
|
||||
batch_size = 16
|
||||
ds = [d for _ in range(batch_size)]
|
||||
vdx = jax.vmap(lambda xpos: dx.replace(xpos=xpos))(
|
||||
jp.array([d.xpos for d in ds], device=jax.devices('cpu')[0]))
|
||||
for i in range(m.nbody):
|
||||
np.testing.assert_array_equal(
|
||||
vdx.bind(mx, s.bodies[i]).xpos, [d.xpos[i, :]] * batch_size
|
||||
)
|
||||
|
||||
# test emtpy name
|
||||
s.worldbody.add_body()
|
||||
m = s.compile()
|
||||
mx = mjx.put_model(m)
|
||||
with self.assertRaises(KeyError, msg='cannot bind spec with empty name'):
|
||||
mx.bind(s.bodies)
|
||||
|
||||
_CONTACTS = """
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -221,6 +414,332 @@ class SupportTest(parameterized.TestCase):
|
||||
force = force.at[3:].set(dx.contact.frame[j] @ force[3:])
|
||||
np.testing.assert_allclose(result, force, rtol=1e-5, atol=2)
|
||||
|
||||
def test_wrap_inside(self):
|
||||
maxiter = 5
|
||||
tolerance = 1.0e-4
|
||||
z_init = 1.0 - 1.0e-5
|
||||
|
||||
# len0 <= radius
|
||||
np.testing.assert_equal(
|
||||
support.wrap_inside(
|
||||
jp.array([1.0, 0, 0, 0]),
|
||||
jp.array([1.0]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)[0],
|
||||
jp.array([-1]),
|
||||
)
|
||||
|
||||
# len1 <= radius
|
||||
np.testing.assert_equal(
|
||||
support.wrap_inside(
|
||||
jp.array([0, 0, 1.0, 0]),
|
||||
jp.array([1.0]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)[0],
|
||||
jp.array([-1]),
|
||||
)
|
||||
|
||||
# radius < mjMINVAL
|
||||
np.testing.assert_equal(
|
||||
support.wrap_inside(
|
||||
jp.array([1, 0, 0, 1]), jp.array([0.1 * mujoco.mjMINVAL]), maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)[0],
|
||||
jp.array([-1]),
|
||||
)
|
||||
|
||||
# len0 < mjMINVAL and radius < mjMINVAL
|
||||
np.testing.assert_equal(
|
||||
support.wrap_inside(
|
||||
jp.array([0.1 * mujoco.mjMINVAL, 0, 0, 0]),
|
||||
jp.array([0.1 * mujoco.mjMINVAL]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)[0],
|
||||
jp.array([-1]),
|
||||
)
|
||||
|
||||
# len1 < mjMINVAL and radius < mjMINVAL
|
||||
np.testing.assert_equal(
|
||||
support.wrap_inside(
|
||||
jp.array([0, 0, 0.1 * mujoco.mjMINVAL, 0]),
|
||||
jp.array([0.1 * mujoco.mjMINVAL]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)[0],
|
||||
jp.array([-1]),
|
||||
)
|
||||
|
||||
# wrap: p0 = [1, 0], p1 = [0, 1]
|
||||
status, pnt = support.wrap_inside(
|
||||
jp.array([1, 0, 0, 1]), jp.array([0.5]), maxiter, tolerance, z_init
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
pnt,
|
||||
jp.array([0.353553, 0.353553, 0.353553, 0.353553]),
|
||||
atol=1e-3,
|
||||
rtol=1e-3,
|
||||
)
|
||||
np.testing.assert_equal(status, jp.array([0]))
|
||||
|
||||
# no wrap, point on circle: p0 = [1, 0], p1 = [0, 0.5]
|
||||
status, pnt = support.wrap_inside(
|
||||
jp.array([1, 0, 0, 0.5]), jp.array([0.5]), maxiter, tolerance, z_init
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
pnt,
|
||||
jp.zeros(4),
|
||||
atol=1e-3,
|
||||
rtol=1e-3,
|
||||
)
|
||||
np.testing.assert_equal(status, jp.array([-1]))
|
||||
|
||||
# no wrap, segment-circle intersection: p0 = [0.75, 0], p1 = [0, 0.51]
|
||||
status, pnt = support.wrap_inside(
|
||||
jp.array([0.75, 0, 0, 0.51]),
|
||||
jp.array([0.5]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
pnt,
|
||||
jp.zeros(4),
|
||||
atol=1e-3,
|
||||
rtol=1e-3,
|
||||
)
|
||||
np.testing.assert_equal(status, jp.array([-1]))
|
||||
|
||||
# wrap: p0 = [-0.5, 1], p1 = [0.5, 1]
|
||||
status, pnt = support.wrap_inside(
|
||||
jp.array([-0.5, 1, 0.5, 1]),
|
||||
jp.array([0.5]),
|
||||
maxiter,
|
||||
tolerance,
|
||||
z_init,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
pnt,
|
||||
jp.array([0, 0.5, 0, 0.5]),
|
||||
atol=1e-3,
|
||||
rtol=1e-3,
|
||||
)
|
||||
np.testing.assert_equal(status, jp.array([0]))
|
||||
|
||||
# TODO(taylorhowell): improve wrap_inside testing with additional test cases
|
||||
|
||||
def test_muscle_gain_length(self):
|
||||
lmin = 0.5
|
||||
lmax = 1.5
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(0, lmin, lmax),
|
||||
jp.zeros(1),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(0.5, lmin, lmax),
|
||||
jp.zeros(1),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(0.6, lmin, lmax),
|
||||
jp.array([0.08]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(0.75, lmin, lmax),
|
||||
jp.array([0.5]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(1.0, lmin, lmax),
|
||||
jp.ones(1),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(1.25, lmin, lmax),
|
||||
jp.array([0.5]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(1.5, lmin, lmax),
|
||||
jp.zeros(1),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain_length(2.0, lmin, lmax),
|
||||
jp.zeros(1),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
def test_muscle_gain(self):
|
||||
length = jp.array([1.0])
|
||||
lengthrange = jp.array([0.0, 1.0])
|
||||
acc0 = jp.array([1.0])
|
||||
prm = jp.array([0.0, 1.0, 1.0, 200.0, 0.5, 3.0, 1.0, 0.0, 2.0, 0.0])
|
||||
|
||||
# V <= -1
|
||||
vel = jp.array([-1.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain(length, vel, lengthrange, acc0, prm),
|
||||
jp.array([-0.0]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# V <= 0
|
||||
vel = jp.array([-0.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain(length, vel, lengthrange, acc0, prm),
|
||||
jp.array([-0.25]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# V <= y
|
||||
vel = jp.array([0.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain(length, vel, lengthrange, acc0, prm),
|
||||
jp.array([-1.75]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# V > y
|
||||
vel = jp.array([1.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain(length, vel, lengthrange, acc0, prm),
|
||||
jp.array([-2.0]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# force < 0
|
||||
prm = prm.at[2].set(-1.0)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_gain(length, vel, lengthrange, acc0, prm),
|
||||
jp.array([-400.0]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
def test_muscle_bias(self):
|
||||
lengthrange = jp.array([0.0, 1.0])
|
||||
acc0 = jp.array([1.0])
|
||||
prm = jp.array([0.0, 1.0, 1.0, 200.0, 0.5, 3.0, 1.5, 1.3, 1.2, 0.0])
|
||||
|
||||
# L <= 1
|
||||
length = jp.array([0.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_bias(length, lengthrange, acc0, prm),
|
||||
jp.array([0.0]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# L <= b
|
||||
length = jp.array([1.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_bias(length, lengthrange, acc0, prm),
|
||||
jp.array([-0.1625]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# L > b
|
||||
length = jp.array([2.5])
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_bias(length, lengthrange, acc0, prm),
|
||||
jp.array([-1.3]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# force < 0
|
||||
prm = prm.at[2].set(-1.0)
|
||||
np.testing.assert_allclose(
|
||||
support.muscle_bias(length, lengthrange, acc0, prm),
|
||||
jp.array([-260.0]),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
def test_smooth_muscle_dynamics(self):
|
||||
# compute time constant as in Millard et al. (2013)
|
||||
# https://doi.org/10.1115/1.4023390
|
||||
def _muscle_dynamics_millard(ctrl, act, prm):
|
||||
ctrlclamp = jp.clip(ctrl, 0, 1)
|
||||
actclamp = jp.clip(act, 0, 1)
|
||||
|
||||
tau0 = prm[0] * (0.5 + 1.5 * actclamp)
|
||||
tau1 = prm[1] / (0.5 + 1.5 * actclamp)
|
||||
tau = jp.where(ctrlclamp > act, tau0, tau1)
|
||||
|
||||
return (ctrlclamp - act) / jp.maximum(mujoco.mjMINVAL, tau)
|
||||
|
||||
prm = jp.array([0.01, 0.04, 0.0])
|
||||
|
||||
# exact equality if tau_smooth = 0
|
||||
for ctrl in [-0.1, 0.0, 0.4, 0.5, 1.0, 1.0]:
|
||||
for act in [-0.1, 0.0, 0.4, 0.5, 1.0, 1.1]:
|
||||
actdot_old = _muscle_dynamics_millard(ctrl, act, prm)
|
||||
actdot_new = support.muscle_dynamics(ctrl, act, prm)
|
||||
np.testing.assert_allclose(actdot_old, actdot_new, rtol=1e-5, atol=1e-5)
|
||||
|
||||
# positive tau_smooth
|
||||
tau_smooth = 0.2
|
||||
prm = prm.at[2].set(tau_smooth)
|
||||
act = 0.5
|
||||
eps = 1.0e-6
|
||||
|
||||
ctrl = 0.4 - eps # smaller than act by just over 0.5 * tau_smooth
|
||||
np.testing.assert_allclose(
|
||||
_muscle_dynamics_millard(ctrl, act, prm),
|
||||
support.muscle_dynamics(ctrl, act, prm),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
ctrl = 0.6 + eps # larger than act by just over 0.5 * tau_smooth
|
||||
np.testing.assert_allclose(
|
||||
_muscle_dynamics_millard(ctrl, act, prm),
|
||||
support.muscle_dynamics(ctrl, act, prm),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
# right in the middle should give average of time constants
|
||||
tau_act = 0.2
|
||||
tau_deact = 0.3
|
||||
for dctrl in [0.0, 0.1, 0.2, 1.0, 1.1]:
|
||||
lower = support.muscle_dynamics_timescale(
|
||||
-dctrl, tau_act, tau_deact, tau_smooth
|
||||
)
|
||||
upper = support.muscle_dynamics_timescale(
|
||||
dctrl, tau_act, tau_deact, tau_smooth
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
0.5 * (upper + lower),
|
||||
0.5 * (tau_act + tau_deact),
|
||||
rtol=1e-5,
|
||||
atol=1e-5,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
|
||||
@@ -53,7 +53,7 @@ def benchmark(
|
||||
nstep: int = 1000,
|
||||
batch_size: int = 1024,
|
||||
unroll_steps: int = 1,
|
||||
solver: str = 'cg',
|
||||
solver: str = 'newton',
|
||||
iterations: int = 1,
|
||||
ls_iterations: int = 4,
|
||||
) -> Tuple[float, float, int]:
|
||||
@@ -116,7 +116,7 @@ def efc_order(m: mujoco.MjModel, d: mujoco.MjData, dx: Data) -> np.ndarray:
|
||||
if dx.contact.dim[i] > 1 and m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL:
|
||||
num_rows = (dx.contact.dim[i] - 1) * 2
|
||||
if dx.contact.dist[i] > 0: # move empty contacts to end
|
||||
order = np.append(order, np.repeat(2 ** 16, num_rows))
|
||||
order = np.append(order, np.repeat(2**16, num_rows))
|
||||
continue
|
||||
contact_match = (d.contact.geom == dx.contact.geom[i]).all(axis=-1)
|
||||
contact_match &= (d.contact.pos == dx.contact.pos[i]).all(axis=-1)
|
||||
|
||||
+139
-30
@@ -47,6 +47,7 @@ class DisableBit(enum.IntFlag):
|
||||
REFSAFE: integrator safety: make ref[0]>=2*timestep
|
||||
SENSOR: sensors
|
||||
"""
|
||||
|
||||
CONSTRAINT = mujoco.mjtDisableBit.mjDSBL_CONSTRAINT
|
||||
EQUALITY = mujoco.mjtDisableBit.mjDSBL_EQUALITY
|
||||
FRICTIONLOSS = mujoco.mjtDisableBit.mjDSBL_FRICTIONLOSS
|
||||
@@ -73,6 +74,7 @@ class JointType(enum.IntEnum):
|
||||
SLIDE: sliding distance along body-fixed axis (1,)
|
||||
HINGE: rotation angle (rad) around body-fixed axis (1,)
|
||||
"""
|
||||
|
||||
FREE = mujoco.mjtJoint.mjJNT_FREE
|
||||
BALL = mujoco.mjtJoint.mjJNT_BALL
|
||||
SLIDE = mujoco.mjtJoint.mjJNT_SLIDE
|
||||
@@ -93,6 +95,7 @@ class IntegratorType(enum.IntEnum):
|
||||
RK4: 4th-order Runge Kutta
|
||||
IMPLICITFAST: implicit in velocity, no rne derivative
|
||||
"""
|
||||
|
||||
EULER = mujoco.mjtIntegrator.mjINT_EULER
|
||||
RK4 = mujoco.mjtIntegrator.mjINT_RK4
|
||||
IMPLICITFAST = mujoco.mjtIntegrator.mjINT_IMPLICITFAST
|
||||
@@ -113,6 +116,7 @@ class GeomType(enum.IntEnum):
|
||||
MESH: mesh
|
||||
SDF: signed distance field
|
||||
"""
|
||||
|
||||
PLANE = mujoco.mjtGeom.mjGEOM_PLANE
|
||||
HFIELD = mujoco.mjtGeom.mjGEOM_HFIELD
|
||||
SPHERE = mujoco.mjtGeom.mjGEOM_SPHERE
|
||||
@@ -134,6 +138,7 @@ class ConvexMesh(PyTreeNode):
|
||||
edge: edge indexes for all edges in the convex mesh
|
||||
edge_face_normal: indexes for face normals adjacent to edges in `edge`
|
||||
"""
|
||||
|
||||
vert: jax.Array
|
||||
face: jax.Array
|
||||
face_normal: jax.Array
|
||||
@@ -148,6 +153,7 @@ class ConeType(enum.IntEnum):
|
||||
PYRAMIDAL: pyramidal
|
||||
ELLIPTIC: elliptic
|
||||
"""
|
||||
|
||||
PYRAMIDAL = mujoco.mjtCone.mjCONE_PYRAMIDAL
|
||||
ELLIPTIC = mujoco.mjtCone.mjCONE_ELLIPTIC
|
||||
|
||||
@@ -160,6 +166,7 @@ class JacobianType(enum.IntEnum):
|
||||
SPARSE: sparse
|
||||
AUTO: sparse if nv>60 and device is TPU, dense otherwise
|
||||
"""
|
||||
|
||||
DENSE = mujoco.mjtJacobian.mjJAC_DENSE
|
||||
SPARSE = mujoco.mjtJacobian.mjJAC_SPARSE
|
||||
AUTO = mujoco.mjtJacobian.mjJAC_AUTO
|
||||
@@ -172,6 +179,7 @@ class SolverType(enum.IntEnum):
|
||||
CG: Conjugate gradient (primal)
|
||||
NEWTON: Newton (primal)
|
||||
"""
|
||||
|
||||
# unsupported: PGS
|
||||
CG = mujoco.mjtSolver.mjSOL_CG
|
||||
NEWTON = mujoco.mjtSolver.mjSOL_NEWTON
|
||||
@@ -186,6 +194,7 @@ class EqType(enum.IntEnum):
|
||||
JOINT: couple the values of two scalar joints with cubic
|
||||
TENDON: couple the lengths of two tendons with cubic
|
||||
"""
|
||||
|
||||
CONNECT = mujoco.mjtEq.mjEQ_CONNECT
|
||||
WELD = mujoco.mjtEq.mjEQ_WELD
|
||||
JOINT = mujoco.mjtEq.mjEQ_JOINT
|
||||
@@ -203,6 +212,7 @@ class WrapType(enum.IntEnum):
|
||||
SPHERE: wrap around sphere
|
||||
CYLINDER: wrap around (infinite) cylinder
|
||||
"""
|
||||
|
||||
JOINT = mujoco.mjtWrap.mjWRAP_JOINT
|
||||
PULLEY = mujoco.mjtWrap.mjWRAP_PULLEY
|
||||
SITE = mujoco.mjtWrap.mjWRAP_SITE
|
||||
@@ -219,6 +229,7 @@ class TrnType(enum.IntEnum):
|
||||
TENDON: force on tendon
|
||||
SITE: force on site
|
||||
"""
|
||||
|
||||
JOINT = mujoco.mjtTrn.mjTRN_JOINT
|
||||
JOINTINPARENT = mujoco.mjtTrn.mjTRN_JOINTINPARENT
|
||||
SITE = mujoco.mjtTrn.mjTRN_SITE
|
||||
@@ -234,12 +245,15 @@ class DynType(enum.IntEnum):
|
||||
INTEGRATOR: integrator: da/dt = u
|
||||
FILTER: linear filter: da/dt = (u-a) / tau
|
||||
FILTEREXACT: linear filter: da/dt = (u-a) / tau, with exact integration
|
||||
MUSCLE: piece-wise linear filter with two time constants
|
||||
"""
|
||||
|
||||
NONE = mujoco.mjtDyn.mjDYN_NONE
|
||||
INTEGRATOR = mujoco.mjtDyn.mjDYN_INTEGRATOR
|
||||
FILTER = mujoco.mjtDyn.mjDYN_FILTER
|
||||
FILTEREXACT = mujoco.mjtDyn.mjDYN_FILTEREXACT
|
||||
# unsupported: MUSCLE, USER
|
||||
MUSCLE = mujoco.mjtDyn.mjDYN_MUSCLE
|
||||
# unsupported: USER
|
||||
|
||||
|
||||
class GainType(enum.IntEnum):
|
||||
@@ -248,10 +262,13 @@ class GainType(enum.IntEnum):
|
||||
Members:
|
||||
FIXED: fixed gain
|
||||
AFFINE: const + kp*length + kv*velocity
|
||||
MUSCLE: muscle FLV curve computed by muscle_gain
|
||||
"""
|
||||
|
||||
FIXED = mujoco.mjtGain.mjGAIN_FIXED
|
||||
AFFINE = mujoco.mjtGain.mjGAIN_AFFINE
|
||||
# unsupported: MUSCLE, USER
|
||||
MUSCLE = mujoco.mjtGain.mjGAIN_MUSCLE
|
||||
# unsupported: USER
|
||||
|
||||
|
||||
class BiasType(enum.IntEnum):
|
||||
@@ -260,10 +277,13 @@ class BiasType(enum.IntEnum):
|
||||
Members:
|
||||
NONE: no bias
|
||||
AFFINE: const + kp*length + kv*velocity
|
||||
MUSCLE: muscle passive force computed by muscle_bias
|
||||
"""
|
||||
|
||||
NONE = mujoco.mjtBias.mjBIAS_NONE
|
||||
AFFINE = mujoco.mjtBias.mjBIAS_AFFINE
|
||||
# unsupported: MUSCLE, USER
|
||||
MUSCLE = mujoco.mjtBias.mjBIAS_MUSCLE
|
||||
# unsupported: USER
|
||||
|
||||
|
||||
class ConstraintType(enum.IntEnum):
|
||||
@@ -276,6 +296,7 @@ class ConstraintType(enum.IntEnum):
|
||||
CONTACT_FRICTIONLESS: frictionless contact
|
||||
CONTACT_PYRAMIDAL: frictional contact, pyramidal friction cone
|
||||
"""
|
||||
|
||||
EQUALITY = mujoco.mjtConstraint.mjCNSTR_EQUALITY
|
||||
FRICTION_DOF = mujoco.mjtConstraint.mjCNSTR_FRICTION_DOF
|
||||
FRICTION_TENDON = mujoco.mjtConstraint.mjCNSTR_FRICTION_TENDON
|
||||
@@ -296,6 +317,7 @@ class CamLightType(enum.IntEnum):
|
||||
TARGETBODY: pos fixed in body, rot tracks target body
|
||||
TARGETBODYCOM: pos fixed in body, rot tracks target subtree com
|
||||
"""
|
||||
|
||||
FIXED = mujoco.mjtCamLight.mjCAMLIGHT_FIXED
|
||||
TRACK = mujoco.mjtCamLight.mjCAMLIGHT_TRACK
|
||||
TRACKCOM = mujoco.mjtCamLight.mjCAMLIGHT_TRACKCOM
|
||||
@@ -340,6 +362,7 @@ class SensorType(enum.IntEnum):
|
||||
FRAMELINACC: 3D linear acceleration
|
||||
FRAMEANGACC: 3D angular acceleration
|
||||
"""
|
||||
|
||||
MAGNETOMETER = mujoco.mjtSensor.mjSENS_MAGNETOMETER
|
||||
CAMPROJECTION = mujoco.mjtSensor.mjSENS_CAMPROJECTION
|
||||
RANGEFINDER = mujoco.mjtSensor.mjSENS_RANGEFINDER
|
||||
@@ -385,6 +408,7 @@ class ObjType(PyTreeNode):
|
||||
SITE: site
|
||||
CAMERA: camera
|
||||
"""
|
||||
|
||||
UNKNOWN = mujoco.mjtObj.mjOBJ_UNKNOWN
|
||||
BODY = mujoco.mjtObj.mjOBJ_BODY
|
||||
XBODY = mujoco.mjtObj.mjOBJ_XBODY
|
||||
@@ -431,7 +455,7 @@ class Option(PyTreeNode):
|
||||
disableactuator: bit flags for disabling actuators by group id (not used)
|
||||
sdf_initpoints: number of starting points for gradient descent (not used)
|
||||
sdf_iterations: max number of iterations for gradient descent (not used)
|
||||
"""
|
||||
""" # fmt: skip
|
||||
timestep: jax.Array
|
||||
apirate: jax.Array = _restricted_to('mujoco')
|
||||
impratio: jax.Array
|
||||
@@ -474,6 +498,7 @@ class Statistic(PyTreeNode):
|
||||
extent: spatial extent (not used)
|
||||
center: center of model (not used)
|
||||
"""
|
||||
|
||||
meaninertia: jax.Array
|
||||
meanmass: jax.Array
|
||||
meansize: jax.Array
|
||||
@@ -514,6 +539,8 @@ class Model(PyTreeNode):
|
||||
nmeshgraph: number of ints in mesh auxiliary data
|
||||
nhfield: number of heightfields
|
||||
nhfielddata: number of data points in all heightfields
|
||||
ntex: number of textures
|
||||
ntexdata: number of bytes in texture rgb data
|
||||
nmat: number of materials
|
||||
npair: number of predefined geom pairs
|
||||
nexclude: number of excluded geom pairs
|
||||
@@ -528,6 +555,9 @@ class Model(PyTreeNode):
|
||||
nM: number of non-zeros in sparse inertia matrix
|
||||
nD: number of non-zeros in sparse dof-dof matrix
|
||||
nB: number of non-zeros in sparse body-dof matrix
|
||||
nC: number of non-zeros in sparse reduced dof-dof matrix
|
||||
nD: number of non-zeros in sparse dof-dof matrix
|
||||
nJmom: number of non-zeros in sparse actuator_moment matrix
|
||||
ntree: number of kinematic trees under world body
|
||||
ngravcomp: number of bodies with nonzero gravcomp
|
||||
nuserdata: size of userdata array
|
||||
@@ -635,11 +665,14 @@ class Model(PyTreeNode):
|
||||
light_mode: light tracking mode (mjtCamLight) (nlight,)
|
||||
light_bodyid: id of light's body (nlight,)
|
||||
light_targetbodyid: id of targeted body; -1: none (nlight,)
|
||||
light_directional: directional light (nlight,)
|
||||
light_castshadow: does light cast shadows (nlight,)
|
||||
light_pos: position rel. to body frame (nlight, 3)
|
||||
light_dir: direction rel. to body frame (nlight, 3)
|
||||
light_poscom0: global position rel. to sub-com in qpos0 (nlight, 3)
|
||||
light_pos0: global position rel. to body in qpos0 (nlight, 3)
|
||||
light_dir0: global direction in qpos0 (nlight, 3)
|
||||
light_cutoff: OpenGL cutoff (nlight,)
|
||||
flex_contype: flex contact type (nflex,)
|
||||
flex_conaffinity: flex contact affinity (nflex,)
|
||||
flex_condim: contact dimensionality (1, 3, 4, 6) (nflex,)
|
||||
@@ -692,12 +725,22 @@ class Model(PyTreeNode):
|
||||
mesh_pos: translation applied to asset vertices (nmesh, 3)
|
||||
mesh_quat: rotation applied to asset vertices (nmesh, 4)
|
||||
mesh_convex: pre-compiled convex mesh info for MJX (nmesh,)
|
||||
mesh_texcoordadr: texcoord data address; -1: no texcoord (nmesh,)
|
||||
mesh_texcoordnum: number of texcoord (nmesh,)
|
||||
mesh_texcoord: vertex texcoords for all meshes (nmeshtexcoord, 2)
|
||||
hfield_size: (x, y, z_top, z_bottom) (nhfield,)
|
||||
hfield_nrow: number of rows in grid (nhfield,)
|
||||
hfield_ncol: number of columns in grid (nhfield,)
|
||||
hfield_adr: address in hfield_data (nhfield,)
|
||||
hfield_data: elevation data (nhfielddata,)
|
||||
tex_type: texture type (mjtTexture) (ntex,)
|
||||
tex_height: number of rows in texture image (ntex,)
|
||||
tex_width: number of columns in texture image (ntex,)
|
||||
tex_nchannel: number of channels in texture image (ntex,)
|
||||
tex_adr: start address in tex_data (ntex,)
|
||||
tex_data: pixel values (ntexdata,)
|
||||
mat_rgba: rgba (nmat, 4)
|
||||
mat_texid: indices of textures; -1: none (nmat, mjNTEXROLE)
|
||||
pair_dim: contact dimensionality (npair,)
|
||||
pair_geom1: id of geom1 (npair,)
|
||||
pair_geom2: id of geom2 (npair,)
|
||||
@@ -736,6 +779,10 @@ class Model(PyTreeNode):
|
||||
wrap_type: wrap object type (mjtWrap) (nwrap,)
|
||||
wrap_objid: object id: geom, site, joint (nwrap,)
|
||||
wrap_prm: divisor, joint coef, or site id (nwrap,)
|
||||
wrap_inside_maxiter: maximum iterations for wrap_inside
|
||||
wrap_inside_tolerance: tolerance for wrap_inside
|
||||
wrap_inside_z_init: initialization for wrap_inside
|
||||
is_wrap_inside: spatial tendon sidesite inside geom (nwrapinside,)
|
||||
actuator_trntype: transmission type (mjtTrn) (nu,)
|
||||
actuator_dyntype: dynamics type (mjtDyn) (nu,)
|
||||
actuator_gaintype: gain type (mjtGain) (nu,)
|
||||
@@ -775,6 +822,13 @@ class Model(PyTreeNode):
|
||||
tuple_objtype: array of object types in all tuples (ntupledata,)
|
||||
tuple_objid: array of object ids in all tuples (ntupledata,)
|
||||
tuple_objprm: array of object params in all tuples (ntupledata,)
|
||||
key_time: key time (nkey,)
|
||||
key_qpos: key position (nkey, nq)
|
||||
key_qvel: key velocity (nkey, nv)
|
||||
key_act: key activation (nkey, na)
|
||||
key_mpos: key mocap position (nkey, nmocap, 3)
|
||||
key_mquat: key mocap quaternion (nkey, nmocap, 4)
|
||||
key_ctrl: key control (nkey, nu)
|
||||
name_bodyadr: body name pointers (nbody,)
|
||||
name_jntadr: joint name pointers (njnt,)
|
||||
name_geomadr: geom name pointers (ngeom,)
|
||||
@@ -791,6 +845,7 @@ class Model(PyTreeNode):
|
||||
name_keyadr: keyframe name pointers (nkey,)
|
||||
names: names of all objects, 0-terminated (nnames,)
|
||||
"""
|
||||
|
||||
nq: int
|
||||
nv: int
|
||||
nu: int
|
||||
@@ -820,6 +875,8 @@ class Model(PyTreeNode):
|
||||
nmeshgraph: int
|
||||
nhfield: int
|
||||
nhfielddata: int
|
||||
ntex: int
|
||||
ntexdata: int
|
||||
nmat: int
|
||||
npair: int
|
||||
nexclude: int
|
||||
@@ -832,8 +889,10 @@ class Model(PyTreeNode):
|
||||
nkey: int
|
||||
nmocap: int
|
||||
nM: int # pylint:disable=invalid-name
|
||||
nD: int # pylint:disable=invalid-name
|
||||
nB: int # pylint:disable=invalid-name
|
||||
nC: int # pylint:disable=invalid-name
|
||||
nD: int # pylint:disable=invalid-name
|
||||
nJmom: int # pylint:disable=invalid-name
|
||||
ntree: int = _restricted_to('mujoco')
|
||||
ngravcomp: int
|
||||
nuserdata: int
|
||||
@@ -910,7 +969,7 @@ class Model(PyTreeNode):
|
||||
geom_sameframe: np.ndarray
|
||||
geom_dataid: np.ndarray
|
||||
geom_group: np.ndarray
|
||||
geom_matid: np.ndarray
|
||||
geom_matid: jax.Array
|
||||
geom_priority: np.ndarray
|
||||
geom_solmix: jax.Array
|
||||
geom_solref: jax.Array
|
||||
@@ -925,7 +984,7 @@ class Model(PyTreeNode):
|
||||
geom_margin: jax.Array
|
||||
geom_gap: jax.Array
|
||||
geom_fluid: np.ndarray
|
||||
geom_rgba: np.ndarray
|
||||
geom_rgba: jax.Array
|
||||
site_type: np.ndarray
|
||||
site_bodyid: np.ndarray
|
||||
site_sameframe: np.ndarray
|
||||
@@ -944,14 +1003,17 @@ class Model(PyTreeNode):
|
||||
cam_resolution: np.ndarray
|
||||
cam_sensorsize: np.ndarray
|
||||
cam_intrinsic: np.ndarray
|
||||
light_mode: np.ndarray = _restricted_to('mujoco')
|
||||
light_mode: np.ndarray
|
||||
light_bodyid: np.ndarray = _restricted_to('mujoco')
|
||||
light_targetbodyid: np.ndarray = _restricted_to('mujoco')
|
||||
light_pos: np.ndarray = _restricted_to('mujoco')
|
||||
light_dir: np.ndarray = _restricted_to('mujoco')
|
||||
light_poscom0: np.ndarray = _restricted_to('mujoco')
|
||||
light_pos0: np.ndarray = _restricted_to('mujoco')
|
||||
light_dir0: np.ndarray = _restricted_to('mujoco')
|
||||
light_directional: jax.Array
|
||||
light_castshadow: jax.Array
|
||||
light_pos: jax.Array
|
||||
light_dir: jax.Array
|
||||
light_poscom0: jax.Array
|
||||
light_pos0: np.ndarray
|
||||
light_dir0: np.ndarray
|
||||
light_cutoff: jax.Array
|
||||
flex_contype: np.ndarray = _restricted_to('mujoco')
|
||||
flex_conaffinity: np.ndarray = _restricted_to('mujoco')
|
||||
flex_condim: np.ndarray = _restricted_to('mujoco')
|
||||
@@ -1004,12 +1066,22 @@ class Model(PyTreeNode):
|
||||
mesh_pos: np.ndarray
|
||||
mesh_quat: np.ndarray
|
||||
mesh_convex: Tuple[ConvexMesh, ...] = _restricted_to('mjx')
|
||||
mesh_texcoordadr: np.ndarray
|
||||
mesh_texcoordnum: np.ndarray
|
||||
mesh_texcoord: np.ndarray
|
||||
hfield_size: np.ndarray
|
||||
hfield_nrow: np.ndarray
|
||||
hfield_ncol: np.ndarray
|
||||
hfield_adr: np.ndarray
|
||||
hfield_data: jax.Array
|
||||
mat_rgba: np.ndarray
|
||||
tex_type: np.ndarray
|
||||
tex_height: np.ndarray
|
||||
tex_width: np.ndarray
|
||||
tex_nchannel: np.ndarray
|
||||
tex_adr: np.ndarray
|
||||
tex_data: jax.Array
|
||||
mat_rgba: jax.Array
|
||||
mat_texid: np.ndarray
|
||||
pair_dim: np.ndarray
|
||||
pair_geom1: np.ndarray
|
||||
pair_geom2: np.ndarray
|
||||
@@ -1048,6 +1120,10 @@ class Model(PyTreeNode):
|
||||
wrap_type: np.ndarray
|
||||
wrap_objid: np.ndarray
|
||||
wrap_prm: np.ndarray
|
||||
wrap_inside_maxiter: int = _restricted_to('mjx')
|
||||
wrap_inside_tolerance: float = _restricted_to('mjx')
|
||||
wrap_inside_z_init: float = _restricted_to('mjx')
|
||||
is_wrap_inside: np.ndarray = _restricted_to('mjx')
|
||||
actuator_trntype: np.ndarray
|
||||
actuator_dyntype: np.ndarray
|
||||
actuator_gaintype: np.ndarray
|
||||
@@ -1068,7 +1144,7 @@ class Model(PyTreeNode):
|
||||
actuator_actrange: jax.Array
|
||||
actuator_gear: jax.Array
|
||||
actuator_cranklength: np.ndarray
|
||||
actuator_acc0: np.ndarray
|
||||
actuator_acc0: jax.Array
|
||||
actuator_lengthrange: np.ndarray
|
||||
actuator_plugin: np.ndarray = _restricted_to('mujoco')
|
||||
sensor_type: np.ndarray
|
||||
@@ -1088,6 +1164,13 @@ class Model(PyTreeNode):
|
||||
tuple_objtype: np.ndarray
|
||||
tuple_objid: np.ndarray
|
||||
tuple_objprm: np.ndarray
|
||||
key_time: np.ndarray
|
||||
key_qpos: np.ndarray
|
||||
key_qvel: np.ndarray
|
||||
key_act: np.ndarray
|
||||
key_mpos: np.ndarray
|
||||
key_mquat: np.ndarray
|
||||
key_ctrl: np.ndarray
|
||||
name_bodyadr: np.ndarray
|
||||
name_jntadr: np.ndarray
|
||||
name_geomadr: np.ndarray
|
||||
@@ -1124,7 +1207,7 @@ class Contact(PyTreeNode):
|
||||
geom2: id of geom 2; deprecated, use geom[1]
|
||||
geom: geom ids (2,)
|
||||
efc_address: address in efc; -1: not included
|
||||
"""
|
||||
""" # fmt: skip
|
||||
dist: jax.Array
|
||||
pos: jax.Array
|
||||
frame: jax.Array
|
||||
@@ -1177,7 +1260,7 @@ class Data(PyTreeNode):
|
||||
geom_xpos: Cartesian geom position (ngeom, 3)
|
||||
geom_xmat: Cartesian geom orientation (ngeom, 3, 3)
|
||||
site_xpos: Cartesian site position (nsite, 3)
|
||||
site_xmat: Cartesian site orientation (nsite, 9)
|
||||
site_xmat: Cartesian site orientation (nsite, 3, 3)
|
||||
cam_xpos: Cartesian camera position (ncam, 3)
|
||||
cam_xmat: Cartesian camera orientation (ncam, 3, 3)
|
||||
light_xpos: Cartesian light position (nlight, 3)
|
||||
@@ -1202,7 +1285,10 @@ class Data(PyTreeNode):
|
||||
wrap_obj: geom id; -1: site; -2: pulley (nwrap*2,)
|
||||
wrap_xpos: Cartesian 3D points in all path (nwrap*2, 3)
|
||||
actuator_length: actuator lengths (nu,)
|
||||
actuator_moment: actuator moments (nu, nv)
|
||||
moment_rownnz: number of non-zeros in actuator_moment row (nu,)
|
||||
moment_rowadr: row start address in colind array (nu,)
|
||||
moment_colind: column indices in sparse Jacobian (nJmom,)
|
||||
actuator_moment: actuator moments (nJmom,)
|
||||
crb: com-based composite inertia and mass (nbody, 10)
|
||||
qM: total inertia if sparse: (nM,)
|
||||
if dense: (nv, nv)
|
||||
@@ -1210,7 +1296,6 @@ class Data(PyTreeNode):
|
||||
if dense: (nv, nv)
|
||||
qLDiagInv: 1/diag(D) if sparse: (nv,)
|
||||
if dense: (0,)
|
||||
qLDiagSqrtInv: 1/sqrt(diag(D)) (nv,)
|
||||
bvh_aabb_dyn: global bounding box (center, size) (nbvhdynamic, 6)
|
||||
bvh_active: volume has been added to collisions (nbvh,)
|
||||
flexedge_velocity: flex edge velocities (nflexedge,)
|
||||
@@ -1228,12 +1313,23 @@ class Data(PyTreeNode):
|
||||
subtree_angmom: angular momentum about subtree com (nbody, 3)
|
||||
qH: L'*D*L factorization of modified M (nM,)
|
||||
qHDiagInv: 1/diag(D) of modified M (nv,)
|
||||
D_rownnz: non-zeros in each row (nv,)
|
||||
D_rowadr: address of each row in D_colind (nv,)
|
||||
D_colind: column indices of non-zeros (nD,)
|
||||
B_rownnz: non-zeros in each row (nbody,)
|
||||
B_rowadr: address of each row in B_colind (nbody,)
|
||||
B_colind: column indices of non-zeros (nB,)
|
||||
B_rownnz: body-dof: non-zeros in each row (nbody,)
|
||||
B_rowadr: body-dof: address of each row in B_colind (nbody,)
|
||||
B_colind: body-dof: column indices of non-zeros (nB,)
|
||||
M_rownnz: inertia: non-zeros in each row (nv,)
|
||||
M_rowadr: inertia: address of each row in M_colind (nv,)
|
||||
M_colind: inertia: column indices of non-zeros (nM,)
|
||||
mapM2M: index mapping from M (legacy) to M (CSR) (nM,)
|
||||
C_rownnz: reduced dof-dof: non-zeros in each row (nv,)
|
||||
C_rowadr: reduced dof-dof: address of each row in C_colind (nv,)
|
||||
C_colind: reduced dof-dof: column indices of non-zeros (nC,)
|
||||
mapM2C: index mapping from M to C (nC,)
|
||||
D_rownnz: dof-dof: non-zeros in each row (nv,)
|
||||
D_rowadr: dof-dof: address of each row in D_colind (nv,)
|
||||
D_diag: dof-dof: index of diagonal element (nv,)
|
||||
D_colind: dof-dof: column indices of non-zeros (nD,)
|
||||
mapM2D: index mapping from M to D (nD,)
|
||||
mapD2M: index mapping from D to M (nM,)
|
||||
qDeriv: d (passive + actuator - bias) / d qvel (nD,)
|
||||
qLU: sparse LU of (qM - dt*qDeriv) (nD,)
|
||||
actuator_force: actuator force in actuation space (nu,)
|
||||
@@ -1258,7 +1354,7 @@ class Data(PyTreeNode):
|
||||
_qM_sparse: qM in sparse representation (nM,)
|
||||
_qLD_sparse: qLD in sparse representation (nM,)
|
||||
_qLDiagInv_sparse: qLDiagInv in sparse representation (nv,)
|
||||
"""
|
||||
""" # fmt: skip
|
||||
# constant sizes:
|
||||
ne: int
|
||||
nf: int
|
||||
@@ -1324,12 +1420,14 @@ class Data(PyTreeNode):
|
||||
wrap_obj: jax.Array
|
||||
wrap_xpos: jax.Array
|
||||
actuator_length: jax.Array
|
||||
moment_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
moment_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
moment_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
actuator_moment: jax.Array
|
||||
crb: jax.Array
|
||||
qM: jax.Array # pylint:disable=invalid-name
|
||||
qLD: jax.Array # pylint:disable=invalid-name
|
||||
qLDiagInv: jax.Array # pylint:disable=invalid-name
|
||||
qLDiagSqrtInv: jax.Array # pylint:disable=invalid-name
|
||||
bvh_aabb_dyn: jax.Array = _restricted_to('mujoco')
|
||||
bvh_active: jax.Array = _restricted_to('mujoco')
|
||||
# position, velocity dependent:
|
||||
@@ -1348,12 +1446,23 @@ class Data(PyTreeNode):
|
||||
subtree_angmom: jax.Array
|
||||
qH: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
qHDiagInv: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
B_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
B_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
B_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
M_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
M_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
M_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
mapM2M: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
C_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
C_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
C_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
mapM2C: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_rownnz: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_rowadr: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_diag: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
D_colind: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
mapM2D: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
mapD2M: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
qDeriv: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
qLU: jax.Array = _restricted_to('mujoco') # pylint:disable=invalid-name
|
||||
# position, velocity, control & acceleration dependent:
|
||||
|
||||
@@ -68,10 +68,27 @@ class TransmissionIntegrationTest(parameterized.TestCase):
|
||||
mujoco.mj_transmission(m, d)
|
||||
dx = transmission_jit_fn(mx, dx)
|
||||
|
||||
for field in ['actuator_length', 'actuator_moment']:
|
||||
_assert_attr_eq(
|
||||
d, dx, field, seed, f'transmission{seed}', atol=1e-4
|
||||
)
|
||||
_assert_attr_eq(
|
||||
d, dx, 'actuator_length', seed, f'transmission{seed}', atol=1e-4
|
||||
)
|
||||
|
||||
# convert sparse actuator_moment to dense representation
|
||||
moment = np.zeros((m.nu, m.nv))
|
||||
mujoco.mju_sparse2dense(
|
||||
moment,
|
||||
d.actuator_moment,
|
||||
d.moment_rownnz,
|
||||
d.moment_rowadr,
|
||||
d.moment_colind,
|
||||
)
|
||||
_assert_eq(
|
||||
moment,
|
||||
dx.actuator_moment,
|
||||
'actuator_moment',
|
||||
seed,
|
||||
f'transmission{seed}',
|
||||
atol=1e-4,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -13,17 +13,25 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="2D grid with pins">
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<size memory="10M"/>
|
||||
|
||||
<mujoco model="2-link 1-muscle arm">
|
||||
<worldbody>
|
||||
<composite type="grid" count="9 9 1" spacing="0.05" offset="0 0 1">
|
||||
<skin rgba=".6 .1 .6 1" inflate="0.001" subgrid="3"/>
|
||||
<pin coord="0 0"/>
|
||||
<pin coord="8 0"/>
|
||||
<geom size=".02"/>
|
||||
</composite>
|
||||
<body>
|
||||
<geom type="capsule" size="0.01" fromto="0 0 0 1 0 0"/>
|
||||
<site name="s0" pos="0 0 0.01"/>
|
||||
<joint type="slide" axis="1 0 0"/>
|
||||
<body pos="1 0 0">
|
||||
<geom type="capsule" size="0.01" fromto="0 0 0 1 0 0"/>
|
||||
<site name="s1" pos="1 0 0.01"/>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
<tendon>
|
||||
<spatial name="tendon" width="0.01">
|
||||
<site site="s0"/>
|
||||
<site site="s1"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
<actuator>
|
||||
<muscle tendon="tendon" lengthrange="0 2" force="1" ctrllimited="true" ctrlrange="0 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,118 @@
|
||||
<!-- Copyright 2021 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="2-link 6-muscle arm">
|
||||
<option timestep="0.005" iterations="50" solver="Newton" tolerance="1e-10"/>
|
||||
|
||||
<visual>
|
||||
<rgba haze=".3 .3 .3 1"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<joint type="hinge" pos="0 0 0" axis="0 0 1" limited="true" range="0 120" damping="0.1"/>
|
||||
<muscle ctrllimited="true" ctrlrange="0 1"/>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.6 0.6 0.6" rgb2="0 0 0" width="512" height="512"/>
|
||||
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".25 .25 .25" rgb2=".3 .3 .3" width="512" height="512" mark="cross" markrgb=".8 .8 .8"/>
|
||||
|
||||
<material name="matplane" reflectance="0.3" texture="texplane" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<geom name="floor" pos="0 0 -0.5" size="0 0 1" type="plane" material="matplane"/>
|
||||
|
||||
<light directional="true" diffuse=".8 .8 .8" specular=".2 .2 .2" pos="0 0 5" dir="0 0 -1"/>
|
||||
|
||||
<site name="s0" pos="-0.15 0 0" size="0.02"/>
|
||||
<site name="x0" pos="0 -0.15 0" size="0.02" rgba="0 .7 0 1" group="1"/>
|
||||
|
||||
<body pos="0 0 0">
|
||||
<geom name="upper arm" type="capsule" size="0.045" fromto="0 0 0 0.5 0 0" rgba=".5 .1 .1 1"/>
|
||||
<joint name="shoulder"/>
|
||||
<geom name="shoulder" type="cylinder" pos="0 0 0" size=".1 .05" rgba=".5 .1 .8 .5" mass="0" group="1"/>
|
||||
|
||||
<site name="s1" pos="0.15 0.06 0" size="0.02"/>
|
||||
<site name="s2" pos="0.15 -0.06 0" size="0.02"/>
|
||||
<site name="s3" pos="0.4 0.06 0" size="0.02"/>
|
||||
<site name="s4" pos="0.4 -0.06 0" size="0.02"/>
|
||||
<site name="s5" pos="0.25 0.1 0" size="0.02"/>
|
||||
<site name="s6" pos="0.25 -0.1 0" size="0.02"/>
|
||||
<site name="x1" pos="0.5 -0.15 0" size="0.02" rgba="0 .7 0 1" group="1"/>
|
||||
|
||||
<body pos="0.5 0 0">
|
||||
<geom name="forearm" type="capsule" size="0.035" fromto="0 0 0 0.5 0 0" rgba=".5 .1 .1 1"/>
|
||||
<joint name="elbow"/>
|
||||
<geom name="elbow" type="cylinder" pos="0 0 0" size=".08 .05" rgba=".5 .1 .8 .5" mass="0" group="1"/>
|
||||
|
||||
<site name="s7" pos="0.11 0.05 0" size="0.02"/>
|
||||
<site name="s8" pos="0.11 -0.05 0" size="0.02"/>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<tendon>
|
||||
<spatial name="SF" width="0.01">
|
||||
<site site="s0"/>
|
||||
<geom geom="shoulder"/>
|
||||
<site site="s1"/>
|
||||
</spatial>
|
||||
|
||||
<spatial name="SE" width="0.01">
|
||||
<site site="s0"/>
|
||||
<geom geom="shoulder" sidesite="x0"/>
|
||||
<site site="s2"/>
|
||||
</spatial>
|
||||
|
||||
<spatial name="EF" width="0.01">
|
||||
<site site="s3"/>
|
||||
<geom geom="elbow"/>
|
||||
<site site="s7"/>
|
||||
</spatial>
|
||||
|
||||
<spatial name="EE" width="0.01">
|
||||
<site site="s4"/>
|
||||
<geom geom="elbow" sidesite="x1"/>
|
||||
<site site="s8"/>
|
||||
</spatial>
|
||||
|
||||
<spatial name="BF" width="0.009" rgba=".4 .6 .4 1">
|
||||
<site site="s0"/>
|
||||
<geom geom="shoulder"/>
|
||||
<site site="s5"/>
|
||||
<geom geom="elbow"/>
|
||||
<site site="s7"/>
|
||||
</spatial>
|
||||
|
||||
<spatial name="BE" width="0.009" rgba=".4 .6 .4 1">
|
||||
<site site="s0"/>
|
||||
<geom geom="shoulder" sidesite="x0"/>
|
||||
<site site="s6"/>
|
||||
<geom geom="elbow" sidesite="x1"/>
|
||||
<site site="s8"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
|
||||
<actuator>
|
||||
<muscle name="SF" tendon="SF"/>
|
||||
<muscle name="SE" tendon="SE"/>
|
||||
<muscle name="EF" tendon="EF"/>
|
||||
<muscle name="EE" tendon="EE"/>
|
||||
<muscle name="BF" tendon="BF"/>
|
||||
<muscle name="BE" tendon="BE"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,18 @@
|
||||
<mujoco>
|
||||
<compiler autolimits="true"/>
|
||||
<worldbody>
|
||||
<body name="box">
|
||||
<joint name="slide1" type="slide" axis="1 0 0" />
|
||||
<joint name="slide2" type="slide" axis="0 1 0" />
|
||||
<joint name="slide3" type="slide" axis="0 0 1" />
|
||||
<joint name="slide4" type="slide" axis="1 1 0" />
|
||||
<geom type="box" size=".05 .05 .05" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="slide1" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide2" dyntype="integrator" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide3" dyntype="filter" dynprm="0.1" gainprm="1.1" />
|
||||
<general joint="slide4" dyntype="filterexact" dynprm="0.1" gainprm="1.1" />
|
||||
</actuator>
|
||||
</mujoco>
|
||||
@@ -102,15 +102,27 @@
|
||||
<geom size="0.1"/>
|
||||
</body>
|
||||
|
||||
<!-- plane and body for touch sensors -->
|
||||
<geom type="plane" size="1 1 .1" pos="-20 -20 -20"/>
|
||||
<body pos="-20 -20 -19.985">
|
||||
<joint type="slide" axis="0 0 1"/>
|
||||
<joint type="hinge" axis="0 1 0"/>
|
||||
<geom type="capsule" fromto="-.5 0 0 .5 0 0" size="0.0125"/>
|
||||
<!-- plane and bodies for touch sensors -->
|
||||
<geom type="plane" size="2 2 .1" pos="-20 -20 -20"/>
|
||||
<body pos="-20 -20 -20">
|
||||
<freejoint/>
|
||||
<geom type="capsule" fromto="-.5 0 0 .5 0 0" size="0.0125" condim="1"/>
|
||||
<site name="touch_sphere" type="sphere" pos="-0.5 0 0" size="0.025"/>
|
||||
<site name="touch_capsule" type="capsule" fromto="-0.1 0 0.01 0.1 0 0.01" size="0.025"/>
|
||||
<site name="touch_box" pos="0.5 0 0" type="box" size="0.025 0.025 0.025"/>
|
||||
</body>
|
||||
<body pos="-20 -20.25 -20">
|
||||
<freejoint/>
|
||||
<geom type="capsule" fromto="-.5 0 0 .5 0 0" size="0.0125" condim="3"/>
|
||||
<site name="touch_capsule" type="capsule" fromto="0.4 0 0.0 0.5 0 0.0" size="0.025"/>
|
||||
</body>
|
||||
<body pos="-20 -20.5 -20">
|
||||
<freejoint/>
|
||||
<geom type="capsule" fromto="-.5 0 0 .5 0 0" size="0.0125" condim="6"/>
|
||||
<site name="touch_box" pos="-0.5 0 0" type="box" size="0.025 0.025 0.025"/>
|
||||
</body>
|
||||
<body pos="-20 -20.75 -20">
|
||||
<freejoint/>
|
||||
<geom type="capsule" fromto="-.5 0 0 .5 0 0" size="0.0125" condim="3"/>
|
||||
<site name="touch_ellipsoid" type="ellipsoid" pos="0.5 0 0.0" size="0.05 0.01 0.02"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
@@ -141,7 +153,8 @@
|
||||
<velocimeter name="velocimeter0" site="site0"/>
|
||||
<velocimeter name="velocimeter0cutoff" site="site0" cutoff="3e-4"/>
|
||||
<gyro name="gyro0" site="site0"/>
|
||||
<touch name="touch_box" site="touch_box"/>
|
||||
<touch name="touch_box1" site="touch_box"/>
|
||||
<touch name="touch_box2" site="touch_box"/>
|
||||
<gyro name="gyro0cutoff" site="site0" cutoff="2e-3"/>
|
||||
<rangefinder name="rangefinder0" site="site_rangefinder0"/>
|
||||
<accelerometer name="accelerometer1" site="site1"/>
|
||||
@@ -152,7 +165,8 @@
|
||||
<jointpos name="jointpos0cutoff" joint="hinge0" cutoff="1e-4"/>
|
||||
<jointvel name="jointvel0" joint="hinge0"/>
|
||||
<jointvel name="jointvel0cutoff" joint="hinge0" cutoff="1e-3"/>
|
||||
<touch name="touch_capsule" site="touch_capsule"/>
|
||||
<touch name="touch_capsule1" site="touch_capsule"/>
|
||||
<touch name="touch_capsule2" site="touch_capsule"/>
|
||||
<actuatorfrc name="actuatorfrc0" actuator="motor0"/>
|
||||
<framelinvel name="framelinvel3" objtype="site" objname="site3"/>
|
||||
<framelinvel name="framelinvel3ref" objtype="site" objname="site3" reftype="body" refname="body2"/>
|
||||
@@ -169,6 +183,7 @@
|
||||
<framepos name="framepos0" objtype="site" objname="site0"/>
|
||||
<velocimeter name="velocimeter1" site="site1"/>
|
||||
<gyro name="gyro1" site="site1"/>
|
||||
<touch name="touch_ellipsoid" site="touch_ellipsoid"/>
|
||||
<frameangvel name="frameangvel3" objtype="site" objname="site3"/>
|
||||
<frameangvel name="frameangvel3ref" objtype="site" objname="site3" reftype="body" refname="body2"/>
|
||||
<frameangvel name="frameangvel3cutoff" objtype="site" objname="site3" cutoff="1e-3"/>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<site name="site0" pos="0.25 0 0.1" size="0.025"/>
|
||||
<geom name="sphere0" type="sphere" pos="0.5 0 0.125" size="0.085"/>
|
||||
<site name="sidesite0" pos="0.5 0 0.25"/>
|
||||
<site name="inside_sidesite0" pos="0.5 0 0.125"/>
|
||||
<body pos="0.5 0 0">
|
||||
<joint name="joint1" type="hinge" axis="0 1 0"/>
|
||||
<geom type="capsule" size="0.05 0.5" fromto="0 0 0 0.5 0 0"/>
|
||||
@@ -21,6 +22,7 @@
|
||||
<site name="site2" pos="0.25 0 0.1" size="0.025"/>
|
||||
<geom name="cylinder2" type="cylinder" contype="0" conaffinity="0" pos="0.5 0 0.25" euler="90 0 0" size="0.085 0.1"/>
|
||||
<site name="sidesite2" pos="0.5 0 0.5"/>
|
||||
<site name="inside_sidesite2" pos="0.5 0 0.25"/>
|
||||
<body pos="0.5 0 0">
|
||||
<joint name="joint3" type="hinge" axis="0 1 0"/>
|
||||
<geom type="capsule" size="0.05 0.5" fromto="0 0 0 0.5 0 0"/>
|
||||
@@ -34,16 +36,23 @@
|
||||
<tendon>
|
||||
<spatial width="0.0125">
|
||||
<site site="site0"/>
|
||||
<geom geom="sphere0" sidesite="sidesite0"/>
|
||||
<geom geom="sphere0" sidesite="inside_sidesite0"/>
|
||||
<site site="site1"/>
|
||||
<geom geom="sphere1"/>
|
||||
<site site="site2"/>
|
||||
<geom geom="cylinder2" sidesite="sidesite2"/>
|
||||
<geom geom="cylinder2" sidesite="inside_sidesite2"/>
|
||||
<site site="site3"/>
|
||||
</spatial>
|
||||
<spatial width="0.0125">
|
||||
<site site="site0"/>
|
||||
<geom geom="sphere0"/>
|
||||
<geom geom="sphere0" sidesite="sidesite0"/>
|
||||
<site site="site2"/>
|
||||
<geom geom="cylinder2" sidesite="inside_sidesite2"/>
|
||||
<site site="site3"/>
|
||||
</spatial>
|
||||
<spatial width="0.0125">
|
||||
<site site="site0"/>
|
||||
<geom geom="sphere0" sidesite="inside_sidesite0"/>
|
||||
<site site="site2"/>
|
||||
<geom geom="cylinder2" sidesite="sidesite2"/>
|
||||
<site site="site3"/>
|
||||
|
||||
@@ -22,7 +22,9 @@ from etils import epath
|
||||
import mujoco
|
||||
from mujoco import mjx
|
||||
|
||||
_MJCF = flags.DEFINE_string('mjcf', None, 'path to model `.xml` or `.mjb`', required=True)
|
||||
_MJCF = flags.DEFINE_string(
|
||||
'mjcf', None, 'path to model `.xml` or `.mjb`', required=True
|
||||
)
|
||||
_BASE_PATH = flags.DEFINE_string(
|
||||
'base_path', None, 'base path, defaults to mujoco.mjx resource path'
|
||||
)
|
||||
|
||||
@@ -28,8 +28,9 @@ import mujoco.viewer
|
||||
|
||||
|
||||
_JIT = flags.DEFINE_bool('jit', True, 'To jit or not to jit.')
|
||||
_MODEL_PATH = flags.DEFINE_string('mjcf', None, 'Path to a MuJoCo MJCF file.',
|
||||
required=True)
|
||||
_MODEL_PATH = flags.DEFINE_string(
|
||||
'mjcf', None, 'Path to a MuJoCo MJCF file.', required=True
|
||||
)
|
||||
|
||||
|
||||
_VIEWER_GLOBAL_STATE = {
|
||||
|
||||
+25
-4
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name="mujoco-mjx"
|
||||
version = "3.2.5"
|
||||
version = "3.3.1"
|
||||
authors = [
|
||||
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
||||
]
|
||||
@@ -30,7 +30,7 @@ dependencies = [
|
||||
"etils[epath]",
|
||||
"jax",
|
||||
"jaxlib",
|
||||
"mujoco>=3.2.5.dev0",
|
||||
"mujoco>=3.3.1.dev0",
|
||||
"scipy",
|
||||
"trimesh",
|
||||
]
|
||||
@@ -41,6 +41,27 @@ mjx-viewer = "mujoco.mjx.viewer:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
||||
Documentation = "https://mujoco.readthedocs.io/en/3.2.5"
|
||||
Documentation = "https://mujoco.readthedocs.io/en/3.3.1"
|
||||
Repository = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
||||
Changelog = "https://mujoco.readthedocs.io/en/3.2.5/changelog.html"
|
||||
Changelog = "https://mujoco.readthedocs.io/en/3.3.1/changelog.html"
|
||||
|
||||
[tool.isort]
|
||||
force_single_line = true
|
||||
force_sort_within_sections = true
|
||||
lexicographical = true
|
||||
single_line_exclusions = ["typing"]
|
||||
order_by_type = false
|
||||
group_by_package = true
|
||||
line_length = 120
|
||||
use_parentheses = true
|
||||
multi_line_output = 3
|
||||
skip_glob = ["**/*.ipynb"]
|
||||
|
||||
[tool.pyink]
|
||||
line-length = 80
|
||||
unstable = true
|
||||
pyink-indentation = 2
|
||||
pyink-use-majority-quotes = true
|
||||
extend-exclude = '''(
|
||||
.ipynb$
|
||||
)'''
|
||||
|
||||
+166
-129
@@ -1,141 +1,178 @@
|
||||
absl-py==2.0.0 \
|
||||
--hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3
|
||||
etils[epath]==1.5.1; python_version >= '3.9' \
|
||||
--hash=sha256:2c1bfa2817eb4881cb509097f1e65ac6160126ba74ec47b3bb47ee678628d8c8
|
||||
jax==0.4.18; python_version >= '3.9' \
|
||||
--hash=sha256:2ded3f558b74593c3533036a90c20d41ea35f35c74b25ca0fc86f4aafc388746
|
||||
jaxlib==0.4.18; python_version >= '3.9' \
|
||||
--hash=sha256:f0d5414bc79bdd667b81ee3c5836641bbd52d6d9c0054043dec8e025857e1260 \
|
||||
--hash=sha256:ed1ba86c1a2adea8235269f3e1f5561696068ca60c68b8e5a6f0eb3b978a305a \
|
||||
--hash=sha256:dfe84a294ab3de2557c49a48c0d83c555018a5190aefa1134d5cb3219865edcd \
|
||||
--hash=sha256:c429a15165b6b5ded5b0c46c5861d0b978a82aaa2200b2e517366d220f3f01ee \
|
||||
--hash=sha256:85572a9fa84a17cffd05b771d528012297be4c0e227a07e7dd082c15094749b8 \
|
||||
--hash=sha256:055950e663fdc101b544597c1361596ff82816575640685effd4779949c8cf06 \
|
||||
--hash=sha256:4ce7b001fb070e2b7926553bedb9490b7671b3dcc176fd7b52df3932c3593cb0 \
|
||||
--hash=sha256:f3a8ce7096b3eadd531773c5ef7f7c3bb7552cdf163d682ccd0b0c7f7240d109 \
|
||||
--hash=sha256:e73b17ac3a6a3e034bca5e5752b1bf035a2eb50ced721a4651b287f5e2c672f7 \
|
||||
--hash=sha256:02895bc15ec578d3bdbdf2c3a2195852d45870f611b26e2e7261cc6b0353a928 \
|
||||
--hash=sha256:a72ee7baf663ed5b9c6a426c1919f3755d4d71d4db6abf1979e6ce1f2451fb7f \
|
||||
--hash=sha256:3293689a8bef495c7837a82ca3038b92c4f21204cadbad6f497306c58aa554e1 \
|
||||
--hash=sha256:43287e8ece61f69b1d2a13d7f5e4d540c6edf4ab60bc1606b2b5f9321a9e8471 \
|
||||
--hash=sha256:89fff93b90d054715db0bc3d3b572b799071e63f1fb44edfb1630c5f53631cfd \
|
||||
--hash=sha256:e0d78703fd1219d9875f20c6c692bba0973b744d66791e0b3e3cdb230c65a2a5 \
|
||||
--hash=sha256:4771e8439c48d1c3cf65e01016da02c6592a310bf973c9609fc3be7df9b49b22 \
|
||||
--hash=sha256:f7787a5531d226d6cc9ec2baa7141260bb713435e1cfc053cb9f5cefa9756ac3 \
|
||||
--hash=sha256:6cb20bbbdafd90e71ad0deb9295519a0175c108c8c557b84fb9fe94f751daee4 \
|
||||
--hash=sha256:116a0d6aedd3e856b52493d7e392fb1b40952b84fb72448fde1c1ab5687db667 \
|
||||
--hash=sha256:9593ff69f424947567e206f3e356b2a2df55ca68e6d815d5adc6cae308e8f652
|
||||
pip==23.3.1 \
|
||||
--hash=sha256:55eb67bb6171d37447e82213be585b75fe2b12b359e993773aca4de9247a052b
|
||||
pytest==7.4.2 \
|
||||
--hash=sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002
|
||||
pytest-xdist==3.3.1 \
|
||||
--hash=sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2
|
||||
scipy==1.11.3; python_version >= '3.9' \
|
||||
--hash=sha256:90271dbde4be191522b3903fc97334e3956d7cfb9cce3f0718d0ab4fd7d8bfd6 \
|
||||
--hash=sha256:d2f6dee6cbb0e263b8142ed587bc93e3ed5e777f1f75448d24fb923d9fd4dce6 \
|
||||
--hash=sha256:bae66a2d7d5768eaa33008fa5a974389f167183c87bf39160d3fefe6664f8ddc \
|
||||
--hash=sha256:0d3a136ae1ff0883fffbb1b05b0b2fea251cb1046a5077d0b435a1839b3e52b7 \
|
||||
--hash=sha256:dfcc1552add7cb7c13fb70efcb2389d0624d571aaf2c80b04117e2755a0c5d15 \
|
||||
--hash=sha256:e1f97cd89c0fe1a0685f8f89d85fa305deb3067d0668151571ba50913e445820 \
|
||||
--hash=sha256:5f290cf561a4b4edfe8d1001ee4be6da60c1c4ea712985b58bf6bc62badee221 \
|
||||
--hash=sha256:00f325434b6424952fbb636506f0567898dca7b0f7654d48f1c382ea338ce9a3 \
|
||||
--hash=sha256:5664e364f90be8219283eeb844323ff8cd79d7acbd64e15eb9c46b9bc7f6a42a \
|
||||
--hash=sha256:925c6f09d0053b1c0f90b2d92d03b261e889b20d1c9b08a3a51f61afc5f58165 \
|
||||
--hash=sha256:033c3fd95d55012dd1148b201b72ae854d5086d25e7c316ec9850de4fe776929 \
|
||||
--hash=sha256:3e1a8a4657673bfae1e05e1e1d6e94b0cabe5ed0c7c144c8aa7b7dbb774ce5c1 \
|
||||
--hash=sha256:e04aa19acc324a1a076abb4035dabe9b64badb19f76ad9c798bde39d41025cdc \
|
||||
--hash=sha256:9885e3e4f13b2bd44aaf2a1a6390a11add9f48d5295f7a592393ceb8991577a3 \
|
||||
--hash=sha256:370f569c57e1d888304052c18e58f4a927338eafdaef78613c685ca2ea0d1fa0 \
|
||||
--hash=sha256:4b4bb134c7aa457e26cc6ea482b016fef45db71417d55cc6d8f43d799cdf9ef2 \
|
||||
--hash=sha256:c77da50c9a91e23beb63c2a711ef9e9ca9a2060442757dffee34ea41847d8156 \
|
||||
--hash=sha256:9ea7f579182d83d00fed0e5c11a4aa5ffe01460444219dedc448a36adf0c3917 \
|
||||
--hash=sha256:5305792c7110e32ff155aed0df46aa60a60fc6e52cd4ee02cdeb67eaccd5356e \
|
||||
--hash=sha256:a63d1ec9cadecce838467ce0631c17c15c7197ae61e49429434ba01d618caa83 \
|
||||
--hash=sha256:715c9966eb8906bc67e450e962bd07a5254420077178f98258904da4004a172f \
|
||||
--hash=sha256:d4d88a6fc091614b842a739b3db6ae15f95c77b308113bd6daefd4b05539b103 \
|
||||
--hash=sha256:cf0dbc4d3fe3107358868a60f263c9d8c2e9ba5de8a934cac4164124f727e6ca
|
||||
setuptools==70.3.0 \
|
||||
--hash=sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc
|
||||
trimesh==4.0.0 \
|
||||
--hash=sha256:c1600e2a1121cff069e89d1403c85c88e49c9dc5b636e96c977a924de432ee50
|
||||
wheel==0.41.2 \
|
||||
--hash=sha256:75909db2664838d015e3d9139004ee16711748a52c8f336b52882266540215d8
|
||||
absl-py==2.1.0 \
|
||||
--hash=sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308
|
||||
etils[epath]==1.10.0; python_version >= '3.10' \
|
||||
--hash=sha256:0777fe60a234b4c65ca53470fc64f2dd2d0c6bca7fcc623fdaa8d7fa5a317098
|
||||
etils[epath]==1.5.2; python_version == '3.9' \
|
||||
--hash=sha256:6dc882d355e1e98a5d1a148d6323679dc47c9a5792939b9de72615aa4737eb0b
|
||||
jax==0.4.34; python_version >= '3.10' \
|
||||
--hash=sha256:b957ca1fc91f7343f91a186af9f19c7f342c946f95a8c11c7f1e5cdfe2e58d9e
|
||||
jax==0.4.30; python_version == '3.9' \
|
||||
--hash=sha256:289b30ae03b52f7f4baf6ef082a9f4e3e29c1080e22d13512c5ecf02d5f1a55b
|
||||
jaxlib==0.4.34; python_version >= '3.10' \
|
||||
--hash=sha256:6b43a974c5d91a19912d138f2658dd8dbb7d30dcdff5c961d896c673e872b611 \
|
||||
--hash=sha256:87f25a477cd279840e53718403f97092eba0e8a945fcab47bcf435b6f9119dda \
|
||||
--hash=sha256:7be673a876ebd1aef440fb7e3ebaf99a91abeb550c9728c644b7d7c7b5d7c108 \
|
||||
--hash=sha256:c303f5acaf6c56ce5ff133a923c9b6247bdebedde15bd2c893c24be4d8f71306 \
|
||||
--hash=sha256:72e22e99a5dc890a64443c3fc12f13f20091f578c405a76de077ba42b4c62cd7 \
|
||||
--hash=sha256:901cb4040ed24eae40071d8114ea8d10dff436277fa74a1a5b9e7206f641151c \
|
||||
--hash=sha256:48272e9034ff868d4328cf0055a07882fd2be93f59dfb6283af7de491f9d1290 \
|
||||
--hash=sha256:1a30771d85fa77f9ab8f18e63240f455ab3a3f87660ed7b8d5eea6ceecbe5c1e \
|
||||
--hash=sha256:096f0ca309d41fa692a9d1f2f9baab1c5c8ca0749876ebb3f748e738a27c7ff4 \
|
||||
--hash=sha256:c7b3e724a30426a856070aba0192b5d199e95b4411070e7ad96ad8b196877b10 \
|
||||
--hash=sha256:133070d4fec5525ffea4dc72956398c1cf647a04dcb37f8a935ee82af78d9965 \
|
||||
--hash=sha256:3bcfa639ca3cfaf86c8ceebd5fc0d47300fd98a078014a1d0cc03133e1523d5f \
|
||||
--hash=sha256:571ef03259835458111596a71a2f4a6fabf4ec34595df4cea555035362ac5bf0 \
|
||||
--hash=sha256:c9d3adcae43a33aad4332be9c2aedc5ef751d1e755f917a5afb30c7872eacaa8 \
|
||||
--hash=sha256:8ee3f93836e53c86556ccd9449a4ea43516ee05184d031a71dd692e81259f7d9 \
|
||||
--hash=sha256:b0001c8f0e2b1c7bc99e4f314b524a340d25653505c1a1484d4041a9d3617f6f \
|
||||
--hash=sha256:d840e64b85f8865404d6d225b9bb340e158df1457152a361b05680e24792b232 \
|
||||
--hash=sha256:3e60bc826933082e99b19b87c21818a8d26fcdb01f418d47cedff554746fd6cc \
|
||||
--hash=sha256:45d719a2ce0ebf21255a277b71d756f3609b7b5be70cddc5d88fd58c35219de0 \
|
||||
--hash=sha256:b7a212a3cb5c6acc201c32ae4f4b5f5a9ac09457fbb77ba8db5ce7e7d4adc214
|
||||
jaxlib==0.4.30; python_version == '3.9' \
|
||||
--hash=sha256:54987e97a22db70f3829b437b9329e4799d653634bacc8b398554d3b90c76b2a \
|
||||
--hash=sha256:f74a6b0e09df4b5e2ee399ebb9f0e01190e26e84ccb0a758fadb516415c07f18 \
|
||||
--hash=sha256:11602d5556e8baa2f16314c36518e9be4dfae0c2c256a361403fb29dc9dc79a4 \
|
||||
--hash=sha256:3d31e01191ce8052bd611aaf16ff967d8d0ec0b63f1ea4b199020cecb248d667 \
|
||||
--hash=sha256:ea3a00005faafbe3c18b178d3b534208b3b4027b2be6230227e7b87ce399fc29
|
||||
pip==24.3.1 \
|
||||
--hash=sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed
|
||||
pytest==8.3.3 \
|
||||
--hash=sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2
|
||||
pytest-xdist==3.6.1 \
|
||||
--hash=sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7
|
||||
scipy==1.14.1; python_version >= '3.10' \
|
||||
--hash=sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84 \
|
||||
--hash=sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e \
|
||||
--hash=sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d \
|
||||
--hash=sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e \
|
||||
--hash=sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73 \
|
||||
--hash=sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e \
|
||||
--hash=sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79 \
|
||||
--hash=sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f \
|
||||
--hash=sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066 \
|
||||
--hash=sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310 \
|
||||
--hash=sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc \
|
||||
--hash=sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5 \
|
||||
--hash=sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07 \
|
||||
--hash=sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d \
|
||||
--hash=sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94 \
|
||||
--hash=sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2 \
|
||||
--hash=sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37 \
|
||||
--hash=sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8 \
|
||||
--hash=sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617 \
|
||||
--hash=sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2 \
|
||||
--hash=sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675 \
|
||||
--hash=sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5 \
|
||||
--hash=sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69 \
|
||||
--hash=sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d \
|
||||
--hash=sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3 \
|
||||
--hash=sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0 \
|
||||
--hash=sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3 \
|
||||
--hash=sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389
|
||||
scipy==1.13.1; python_version == '3.9' \
|
||||
--hash=sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2 \
|
||||
--hash=sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d \
|
||||
--hash=sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004 \
|
||||
--hash=sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24 \
|
||||
--hash=sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5
|
||||
setuptools==75.5.0 \
|
||||
--hash=sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829
|
||||
trimesh==4.5.2 \
|
||||
--hash=sha256:2e50f3a7fd135c3045da887a1b9f91230528f3ce11d2ec1ba44750d82d6b4f73
|
||||
wheel==0.45.0 \
|
||||
--hash=sha256:52f0baa5e6522155090a09c6bd95718cc46956d1b51d537ea5454249edb671c7
|
||||
|
||||
# Transitive dependencies of etils[epath]
|
||||
fsspec==2023.9.2 \
|
||||
--hash=sha256:603dbc52c75b84da501b9b2ec8c11e1f61c25984c4a0dda1f129ef391fbfc9b4
|
||||
importlib-resources==6.1.0 \
|
||||
--hash=sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83
|
||||
typing_extensions==4.8.0 \
|
||||
--hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0
|
||||
zipp==3.19.1 \
|
||||
--hash=sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091 \
|
||||
--hash=sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f
|
||||
fsspec==2024.10.0 \
|
||||
--hash=sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871
|
||||
importlib-resources==6.4.5 \
|
||||
--hash=sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717
|
||||
typing_extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d
|
||||
zipp==3.21.0 \
|
||||
--hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931
|
||||
|
||||
# Transitive dependencies of jax and jaxlib
|
||||
importlib-metadata==6.8.0; python_version < '3.10' \
|
||||
--hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb
|
||||
ml-dtypes==0.3.1; python_version >= '3.9' \
|
||||
--hash=sha256:3d8ca0acbd377082792d8b97081ba580abdad67c6afb7f827012c675b052f058 \
|
||||
--hash=sha256:4828b62fa3bf1ae35faa40f3db9a38ec72fbce02f328a1d14c3a9da4606af364 \
|
||||
--hash=sha256:d1a8dc3bac1da2a17d0e2e4cba36ee89721d0bd33ea4765af2eefb5f41409e0f \
|
||||
--hash=sha256:a777928dcba8865ab4a8157eeb25d23aed7bc82e5fd74e1d5eca821d3f148b39 \
|
||||
--hash=sha256:5e0b0b6bb07fa5ad11bb61d174667176bee5e05857225067aabfc5adc1b51d23 \
|
||||
--hash=sha256:5727effa7650f7ab10906542d137cfb3244fdc3b2b519beff42f82def8ba59be \
|
||||
--hash=sha256:42a8980afd8b7c8e270e8b5c260237286b5b26acd276fcb758d13cd7cb567e99 \
|
||||
--hash=sha256:cb0c404e0dd3e25b56362c1c1e5de0ef717f727dde59fa721af4ce6ab2acca44 \
|
||||
--hash=sha256:510d249a91face47211762eb294d6fe64f325356b965fb6388c1bf51bd339267 \
|
||||
--hash=sha256:f83ff080df8910c0f987f615b03e4f8198638e0c00c6e679ea8892dda909763b \
|
||||
--hash=sha256:fcae2c69715410d96906e1dfe8f017d9f78a0d10e0df91aae52e91f51fdfe45e \
|
||||
--hash=sha256:da274599e4950a9b488d21571061f49a185537cc77f2d3f8121151d58a9e9f16 \
|
||||
--hash=sha256:438437e2e614a3c91d75581653b6c40ec890e8b5994d7190a90c931740151c95 \
|
||||
--hash=sha256:70984b473db6489ec1d8c79b082a1322105155193049d08a3b0c515094e9777b \
|
||||
--hash=sha256:4d94b2d1bed77284694f7fd0479640fa7aa5d96433dca3cbcec407a5ef752e77 \
|
||||
--hash=sha256:979d7d196d9a17e0135ae22878f74241fbd3522cef58d7b292f1fd5b32282201
|
||||
numpy==1.26.0; python_version >= '3.9' \
|
||||
--hash=sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388 \
|
||||
--hash=sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581 \
|
||||
--hash=sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb \
|
||||
--hash=sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505 \
|
||||
--hash=sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112 \
|
||||
--hash=sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a \
|
||||
--hash=sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd \
|
||||
--hash=sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208 \
|
||||
--hash=sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c \
|
||||
--hash=sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99 \
|
||||
--hash=sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd \
|
||||
--hash=sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292 \
|
||||
--hash=sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68 \
|
||||
--hash=sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be \
|
||||
--hash=sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91 \
|
||||
--hash=sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2 \
|
||||
--hash=sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8 \
|
||||
--hash=sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f \
|
||||
--hash=sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c \
|
||||
--hash=sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2 \
|
||||
--hash=sha256:d6fa6d17727169ff1385ad3cb8f290bbcc3f2097322d90507c1956a4f9f870fc
|
||||
opt-einsum==3.3.0 \
|
||||
--hash=sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147
|
||||
importlib-metadata==8.5.0; python_version == '3.9' \
|
||||
--hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b
|
||||
ml-dtypes==0.5.0 \
|
||||
--hash=sha256:cb5cc7b25acabd384f75bbd78892d0c724943f3e2e1986254665a1aa10982e07 \
|
||||
--hash=sha256:54415257f00eb44fbcc807454efac3356f75644f1cbfc2d4e5522a72ae1dacab \
|
||||
--hash=sha256:e04fde367b2fe901b1d47234426fe8819909bd1dd862a5adb630f27789c20599 \
|
||||
--hash=sha256:d3b3db9990c3840986a0e70524e122cfa32b91139c3653df76121ba7776e015f \
|
||||
--hash=sha256:afa08343069874a30812871d639f9c02b4158ace065601406a493a8511180c02 \
|
||||
--hash=sha256:a38df8df61194aeaae1ab7579075779b4ad32cd1cffd012c28be227fa7f2a70a \
|
||||
--hash=sha256:a988bac6572630e1e9c2edd9b1277b4eefd1c86209e52b0d061b775ac33902ff \
|
||||
--hash=sha256:d4b1a70a3e5219790d6b55b9507606fc4e02911d1497d16c18dd721eb7efe7d0 \
|
||||
--hash=sha256:dc74fd9995513d33eac63d64e436240f5494ec74d522a9f0920194942fc3d2d7 \
|
||||
--hash=sha256:2e7534392682c3098bc7341648c650864207169c654aed83143d7a19c67ae06f \
|
||||
--hash=sha256:76942f6aeb5c40766d5ea62386daa4148e6a54322aaf5b53eae9e7553240222f \
|
||||
--hash=sha256:60275f2b51b56834e840c4809fca840565f9bf8e9a73f6d8c94f5b5935701215 \
|
||||
--hash=sha256:968fede07d1f9b926a63df97d25ac656cac1a57ebd33701734eaf704bc55d8d8 \
|
||||
--hash=sha256:c7a9152f5876fef565516aa5dd1dccd6fc298a5891b2467973905103eb5c7856 \
|
||||
--hash=sha256:ab046f2ff789b1f11b2491909682c5d089934835f9a760fafc180e47dcb676b8 \
|
||||
--hash=sha256:8c32138975797e681eb175996d64356bcfa124bdbb6a70460b9768c2b35a6fa4 \
|
||||
--hash=sha256:7ee9c320bb0f9ffdf9f6fa6a696ef2e005d1f66438d6f1c1457338e00a02e8cf \
|
||||
--hash=sha256:a03fc861b86cc586728e3d093ba37f0cc05e65330c3ebd7688e7bae8290f8859 \
|
||||
--hash=sha256:099e09edd54e676903b4538f3815b5ab96f5b119690514602d96bfdb67172cbe \
|
||||
--hash=sha256:5f2b59233a0dbb6a560b3137ed6125433289ccba2f8d9c3695a52423a369ed15
|
||||
numpy==2.1.3; python_version >= '3.10' \
|
||||
--hash=sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed \
|
||||
--hash=sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56 \
|
||||
--hash=sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43 \
|
||||
--hash=sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe \
|
||||
--hash=sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57 \
|
||||
--hash=sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598 \
|
||||
--hash=sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f \
|
||||
--hash=sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a \
|
||||
--hash=sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b \
|
||||
--hash=sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512 \
|
||||
--hash=sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564 \
|
||||
--hash=sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8 \
|
||||
--hash=sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958 \
|
||||
--hash=sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e \
|
||||
--hash=sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2 \
|
||||
--hash=sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b \
|
||||
--hash=sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a \
|
||||
--hash=sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09 \
|
||||
--hash=sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9 \
|
||||
--hash=sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41 \
|
||||
--hash=sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d \
|
||||
--hash=sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0 \
|
||||
--hash=sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098 \
|
||||
--hash=sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3 \
|
||||
--hash=sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd \
|
||||
--hash=sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1 \
|
||||
--hash=sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5 \
|
||||
--hash=sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff \
|
||||
numpy==2.0.2; python_version == '3.9' \
|
||||
--hash=sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73 \
|
||||
--hash=sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd \
|
||||
--hash=sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1 \
|
||||
--hash=sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729 \
|
||||
--hash=sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b \
|
||||
--hash=sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd \
|
||||
--hash=sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c
|
||||
opt-einsum==3.4.0 \
|
||||
--hash=sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd
|
||||
|
||||
# Transitive dependencies of pytest
|
||||
attrs==23.1.0; platform_system == 'Windows' \
|
||||
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04
|
||||
attrs==24.2.0; platform_system == 'Windows' \
|
||||
--hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
|
||||
colorama==0.4.6; platform_system == 'Windows' \
|
||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||
exceptiongroup==1.1.3; python_version < '3.11' \
|
||||
--hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3
|
||||
exceptiongroup==1.2.2; python_version < '3.11' \
|
||||
--hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b
|
||||
iniconfig==2.0.0 \
|
||||
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
|
||||
packaging==23.2 \
|
||||
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
|
||||
pluggy==1.3.0 \
|
||||
--hash=sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7
|
||||
pyparsing==3.1.1 \
|
||||
--hash=sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb
|
||||
tomli==2.0.1; python_version < '3.11' \
|
||||
--hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc
|
||||
packaging==24.2 \
|
||||
--hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759
|
||||
pluggy==1.5.0 \
|
||||
--hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669
|
||||
tomli==2.1.0; python_version < '3.11' \
|
||||
--hash=sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391
|
||||
|
||||
# Transitive dependencies of pytest-xdist
|
||||
execnet==2.0.2 \
|
||||
--hash=sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41
|
||||
execnet==2.1.1 \
|
||||
--hash=sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc
|
||||
|
||||
+25
-3
@@ -66,10 +66,10 @@
|
||||
"$$\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\frac{\\partial x_t}{\\partial \\theta} = \\textcolor{Navy}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial x_{t-1}}}\\frac{\\partial x_{t-1}}{\\partial \\theta} + \\textcolor{Navy}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial a_{t-1}}} \\frac{\\partial a_{t-1}}{\\partial \\theta}\n",
|
||||
"\\frac{\\partial x_t}{\\partial \\theta} = \\color{blue}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial x_{t-1}}}\\frac{\\partial x_{t-1}}{\\partial \\theta} + \\color{blue}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial a_{t-1}}} \\frac{\\partial a_{t-1}}{\\partial \\theta}\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"The navy-colored terms in the above expression are enabled by MJX's differentiability and are the key difference between FoPG's and ZoPG's. An important consideration is what these jacobians look like near contact points. To see why certain gradients within the jacobian can be pathological, imagine a hard sphere falling toward a block of marble. How does its velocity change with respect to distance ($\\frac{\\partial \\dot{z}_t}{\\partial z_t}$), the instant before it touches the ground? This is the case of an **uninformative gradient**, due to [hard contact](https://arxiv.org/html/2404.02887v1). Fortunately, the default contact settings in Mujoco are sufficiently [soft](https://mujoco.readthedocs.io/en/stable/computation/index.html#soft-contact-model) for learning via FoPG's. With soft contacts, the ground applies an increasing force on the ball as it penetrates it, unlike rigid contacts, which instantly provide enough force for deflection.\n",
|
||||
"The blue-colored terms in the above expression are enabled by MJX's differentiability and are the key difference between FoPG's and ZoPG's. An important consideration is what these jacobians look like near contact points. To see why certain gradients within the jacobian can be pathological, imagine a hard sphere falling toward a block of marble. How does its velocity change with respect to distance ($\\frac{\\partial \\dot{z}_t}{\\partial z_t}$), the instant before it touches the ground? This is the case of an **uninformative gradient**, due to [hard contact](https://arxiv.org/html/2404.02887v1). Fortunately, the default contact settings in Mujoco are sufficiently [soft](https://mujoco.readthedocs.io/en/stable/computation/index.html#soft-contact-model) for learning via FoPG's. With soft contacts, the ground applies an increasing force on the ball as it penetrates it, unlike rigid contacts, which instantly provide enough force for deflection.\n",
|
||||
"\n",
|
||||
"A helpful way to think about FoPG's is via the chain rule and computation graphs, as illustrated below for how $r_2$ influences the policy gradient, again for the case that the reward does not depend on action:\n",
|
||||
"\n",
|
||||
@@ -85,7 +85,29 @@
|
||||
"\n",
|
||||
"Last, despite the sample efficiency, FoPG methods can still struggle with wall-clock time. Because the gradients have low variance, they do not benefit significantly from massive parallelization of data collection - unlike [RL](https://arxiv.org/abs/2109.11978). Additionally, the policy gradient is typically calculated via autodifferentiation. This can be 3-5x slower than unrolling the simulation forward, and memory intensive, with memory requirements scaling with $O(m \\cdot (m+n) \\cdot T)$, where m and n are the state and control dimensions, $m \\cdot (m+n)$ is the jacobian dimension, and T is the number of steps propogated through.\n",
|
||||
"\n",
|
||||
"Note that with certain models, using autodifferentiation through mjx.step currently causes [nan gradients](https://github.com/google-deepmind/mujoco/issues/1517). For now, we address this issue by using double-precision floats, at the cost of doubling the memory requirements and training time."
|
||||
"Note that with certain models, using autodifferentiation through mjx.step currently causes [nan gradients](https://github.com/google-deepmind/mujoco/issues/1517). For now, we address this issue by using double-precision floats, at the cost of doubling the memory requirements and training time.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"**Publications**\n",
|
||||
"\n",
|
||||
"If you use this work in an academic context, please cite the following publication:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"@misc{luo2024residual,\n",
|
||||
" title={Residual Policy Learning for Perceptive Quadruped Control Using Differentiable Simulation},\n",
|
||||
" author={Luo, Jing Yuan and Song, Yunlong and Klemm, Victor and Shi, Fan and Scaramuzza, Davide and Hutter, Marco},\n",
|
||||
" year={2024},\n",
|
||||
" eprint={2410.03076},\n",
|
||||
" archivePrefix={arXiv},\n",
|
||||
" primaryClass={cs.RO},\n",
|
||||
" url={https://doi.org/10.48550/arXiv.2410.03076}\n",
|
||||
"}\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+20
-343
@@ -321,7 +321,7 @@
|
||||
"source": [
|
||||
"Now let's run the same exact simulation on the GPU device using MJX!\n",
|
||||
"\n",
|
||||
"In the example below, we use `mjx.step` instead of `mujoco.mj_step`, and we also [`jax.jit`](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html) the `mjx.step` so that it runs efficiently on the GPU. After each step, we convert the `mjx.Data` back to `mjData` so that we can use the MuJoCo renderer.\n"
|
||||
"In the example below, we use `mjx.step` instead of `mujoco.mj_step`, and we also [`jax.jit`](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html) the `mjx.step` so that it runs efficiently on the GPU. For each frame, we convert the `mjx.Data` back to `mjData` so that we can use the MuJoCo renderer.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -416,13 +416,14 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "mtGMYNLE3QJN"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title Humanoid Env\n",
|
||||
"\n",
|
||||
"HUMANOID_ROOT_PATH = epath.Path(epath.resource_path('mujoco')) / 'mjx/test_data/humanoid'\n",
|
||||
"\n",
|
||||
"class Humanoid(PipelineEnv):\n",
|
||||
"\n",
|
||||
" def __init__(\n",
|
||||
@@ -436,11 +437,9 @@
|
||||
" exclude_current_positions_from_observation=True,\n",
|
||||
" **kwargs,\n",
|
||||
" ):\n",
|
||||
" path = epath.Path(epath.resource_path('mujoco')) / (\n",
|
||||
" 'mjx/test_data/humanoid'\n",
|
||||
" )\n",
|
||||
"#\n",
|
||||
" mj_model = mujoco.MjModel.from_xml_path(\n",
|
||||
" (path / 'humanoid.xml').as_posix())\n",
|
||||
" (HUMANOID_ROOT_PATH / 'humanoid.xml').as_posix())\n",
|
||||
" mj_model.opt.solver = mujoco.mjtSolver.mjSOL_CG\n",
|
||||
" mj_model.opt.iterations = 6\n",
|
||||
" mj_model.opt.ls_iterations = 6\n",
|
||||
@@ -563,7 +562,7 @@
|
||||
"\n",
|
||||
"Let's instantiate the environment and visualize a short rollout.\n",
|
||||
"\n",
|
||||
"NOTE: Since episodes terminates early if the torso is below the healthy z-range, the only relevant contacts for this task are between the feet and the plane. We turn off other contacts."
|
||||
"NOTE: Since episodes terminate early if the torso is below the healthy z-range, the only relevant contacts for this task are between the feet and the plane. We turn off other contacts."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -887,7 +886,9 @@
|
||||
"source": [
|
||||
"## Quadruped Env\n",
|
||||
"\n",
|
||||
"Let's define a quadruped environment that takes advantage of the domain randomization function. Here we use the [Barkour vb Quadruped](https://github.com/google-deepmind/mujoco_menagerie/tree/main/google_barkour_vb) from [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie). We implement an environment that trains a joystick policy with Brax."
|
||||
"Let's define a quadruped environment that takes advantage of the domain randomization function. Here we use the [Barkour vb Quadruped](https://github.com/google-deepmind/mujoco_menagerie/tree/main/google_barkour_vb) from [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie). We implement an environment that trains a joystick policy with Brax.\n",
|
||||
"\n",
|
||||
"NOTE: for a full suite of robotic environments, many of which were transferred onto robots, check out [MuJoCo Playground](https://github.com/google-deepmind/mujoco_playground)!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1605,345 +1606,22 @@
|
||||
" fps=1.0 / eval_env.dt / render_every)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-Q5gsOOaBYd1"
|
||||
},
|
||||
"source": [
|
||||
"# MuJoCo Playground: Robotics locomotion and manipulation environments + Sim-to-Real!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gluTlHURuC6i"
|
||||
},
|
||||
"source": [
|
||||
"# Manipulation Environments and Policies\n",
|
||||
"\n",
|
||||
"By now, we have shown how MJX can be used to train policies for classic control and robotic locomotion. MJX can also be used for robotic manipulation!\n",
|
||||
"\n",
|
||||
"We demonstrate a task on the Franka Panda below, which trains a policy to pickup a cube and bring it to a mocap target position in about 3 minutes on an A100. We will be adding more support for manipulation environments in MJX (i.e. more performant collisions), so stay tuned!\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"id": "RCv16hZIu5Dm"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%shell\n",
|
||||
"if [ ! -d \"mujoco_menagerie\" ]; then\n",
|
||||
" git clone https://github.com/google-deepmind/mujoco_menagerie\n",
|
||||
"fi\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "AuU-9nUquEu4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title Franka Panda BringToTarget Environment\n",
|
||||
"\n",
|
||||
"FRANKA_PANDA_ROOT_PATH = epath.Path('mujoco_menagerie/franka_emika_panda')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def default_config():\n",
|
||||
" \"\"\"Returns reward config for the environment.\"\"\"\n",
|
||||
"\n",
|
||||
" return config_dict.create(\n",
|
||||
" # Environment timestep. Should match the robot decision frequency.\n",
|
||||
" dt=0.02,\n",
|
||||
" # Lowers action magnitude for less-jerky motion. Also sometimes helps\n",
|
||||
" # sample efficiency.\n",
|
||||
" action_scale=0.04,\n",
|
||||
" # The coefficients for all reward terms used for training.\n",
|
||||
" reward_scales=config_dict.create(\n",
|
||||
" # Gripper goes to the box.\n",
|
||||
" gripper_box=4.0,\n",
|
||||
" # Box goes to the target mocap.\n",
|
||||
" box_target=8.0,\n",
|
||||
" # Do not collide the gripper with the floor.\n",
|
||||
" no_floor_collision=0.25,\n",
|
||||
" # Arm stays close to target pose.\n",
|
||||
" robot_target_qpos=0.3,\n",
|
||||
" ),\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _load_sys(path: epath.Path) -> base.System:\n",
|
||||
" \"\"\"Load a mujoco model from a path.\"\"\"\n",
|
||||
" assets = {}\n",
|
||||
" for f in path.parent.glob('*.xml'):\n",
|
||||
" assets[f.name] = f.read_bytes()\n",
|
||||
" for f in (path.parent / 'assets').glob('*'):\n",
|
||||
" assets[f.name] = f.read_bytes()\n",
|
||||
" xml = path.read_text()\n",
|
||||
" model = mujoco.MjModel.from_xml_string(xml, assets)\n",
|
||||
" return mjcf.load_model(model)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _get_collision_info(\n",
|
||||
" contact: Any, geom1: int, geom2: int) -> Tuple[jax.Array, jax.Array]:\n",
|
||||
" if geom1 > geom2:\n",
|
||||
" geom1, geom2 = geom2, geom1\n",
|
||||
" mask = (jp.array([geom1, geom2]) == contact.geom).all(axis=1)\n",
|
||||
" idx = jp.where(mask, contact.dist, 1e4).argmin()\n",
|
||||
" dist = contact.dist[idx] * mask[idx]\n",
|
||||
" normal = (dist < 0) * contact.frame[idx, 0, :3]\n",
|
||||
" return dist, normal\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _geoms_colliding(\n",
|
||||
" state: Optional[State], geom1: int, geom2: int\n",
|
||||
") -> jax.Array:\n",
|
||||
" return _get_collision_info(state.contact, geom1, geom2)[0] < 0\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class PandaBringToTarget(PipelineEnv):\n",
|
||||
" \"\"\"Environment for training franka panda to bring an object to target.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, **kwargs):\n",
|
||||
" global root_path\n",
|
||||
" sys = _load_sys(FRANKA_PANDA_ROOT_PATH / 'mjx_single_cube.xml')\n",
|
||||
" self._config = config = default_config()\n",
|
||||
" nsteps = int(np.round(config.dt / sys.opt.timestep))\n",
|
||||
" kwargs['backend'] = 'mjx'\n",
|
||||
" kwargs['n_frames'] = nsteps\n",
|
||||
" super().__init__(sys, **kwargs)\n",
|
||||
"\n",
|
||||
" # define constants\n",
|
||||
" model = sys.mj_model\n",
|
||||
" arm_joints = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5',\n",
|
||||
" 'joint6', 'joint7']\n",
|
||||
" finger_joints = ['finger_joint1', 'finger_joint2']\n",
|
||||
" all_joints = arm_joints + finger_joints\n",
|
||||
" self._robot_arm_qposadr = np.array([\n",
|
||||
" model.jnt_qposadr[model.joint(j).id] for j in arm_joints])\n",
|
||||
" self._robot_qposadr = np.array([\n",
|
||||
" model.jnt_qposadr[model.joint(j).id] for j in all_joints])\n",
|
||||
" self._gripper_site = model.site('gripper').id\n",
|
||||
" self._left_finger_geom = model.geom('left_finger_pad').id\n",
|
||||
" self._right_finger_geom = model.geom('right_finger_pad').id\n",
|
||||
" self._hand_geom = model.geom('hand_capsule').id\n",
|
||||
" self._box_body = model.body('box').id\n",
|
||||
" self._box_qposadr = model.jnt_qposadr[model.body('box').jntadr[0]]\n",
|
||||
" # TODO(btaba): replace with mocap_pos once MJX version 3.2.3 is released.\n",
|
||||
" self._target_id = model.body('mocap_target').id\n",
|
||||
" self._floor_geom = model.geom('floor').id\n",
|
||||
" self._init_q = sys.mj_model.keyframe('home').qpos\n",
|
||||
" self._init_box_pos = jp.array(\n",
|
||||
" self._init_q[self._box_qposadr : self._box_qposadr + 3],\n",
|
||||
" dtype=jp.float32)\n",
|
||||
" self._init_ctrl = sys.mj_model.keyframe('home').ctrl\n",
|
||||
" self._lowers = model.actuator_ctrlrange[:, 0]\n",
|
||||
" self._uppers = model.actuator_ctrlrange[:, 1]\n",
|
||||
"\n",
|
||||
" def reset(self, rng: jax.Array) -> State:\n",
|
||||
" rng, rng_box, rng_target = jax.random.split(rng, 3)\n",
|
||||
"\n",
|
||||
" # intialize box position\n",
|
||||
" box_pos = jax.random.uniform(\n",
|
||||
" rng_box, (3,),\n",
|
||||
" minval=jp.array([-0.2, -0.2, 0.0]),\n",
|
||||
" maxval=jp.array([0.2, 0.2, 0.0])) + self._init_box_pos\n",
|
||||
"\n",
|
||||
" # initialize target position\n",
|
||||
" target_pos = jax.random.uniform(\n",
|
||||
" rng_target, (3,),\n",
|
||||
" minval=jp.array([-0.2, -0.2, 0.2]),\n",
|
||||
" maxval=jp.array([0.2, 0.2, 0.4])) + self._init_box_pos\n",
|
||||
"\n",
|
||||
" # initialize pipeline state\n",
|
||||
" init_q = jp.array(self._init_q).at[\n",
|
||||
" self._box_qposadr : self._box_qposadr + 3].set(box_pos)\n",
|
||||
" pipeline_state = self.pipeline_init(\n",
|
||||
" init_q, jp.zeros(self.sys.nv)\n",
|
||||
" )\n",
|
||||
" pipeline_state = pipeline_state.replace(ctrl=self._init_ctrl)\n",
|
||||
" # set target mocap position\n",
|
||||
" # TODO(btaba): replace with mocap_pos once MJX version 3.2.3 is released.\n",
|
||||
" pipeline_state = pipeline_state.replace(\n",
|
||||
" xpos=pipeline_state.xpos.at[self._target_id, :].set(target_pos))\n",
|
||||
"\n",
|
||||
" # initialize env state and info\n",
|
||||
" metrics = {\n",
|
||||
" 'out_of_bounds': jp.array(0.0),\n",
|
||||
" **{k: 0.0 for k in self._config.reward_scales.keys()},\n",
|
||||
" }\n",
|
||||
" info = {'rng': rng, 'target_pos': target_pos, 'reached_box': 0.0}\n",
|
||||
" obs = self._get_obs(pipeline_state, info)\n",
|
||||
" reward, done = jp.zeros(2)\n",
|
||||
" state = State(pipeline_state, obs, reward, done, metrics, info)\n",
|
||||
" return state\n",
|
||||
"\n",
|
||||
" def step(self, state: State, action: jax.Array) -> State:\n",
|
||||
" delta = action * self._config.action_scale\n",
|
||||
" ctrl = state.pipeline_state.ctrl + delta\n",
|
||||
" ctrl = jp.clip(ctrl, self._lowers, self._uppers)\n",
|
||||
"\n",
|
||||
" # step the physics\n",
|
||||
" data = self.pipeline_step(state.pipeline_state, ctrl)\n",
|
||||
"\n",
|
||||
" # compute reward terms\n",
|
||||
" target_pos = state.info['target_pos']\n",
|
||||
" box_pos = data.xpos[self._box_body]\n",
|
||||
" gripper_pos = data.site_xpos[self._gripper_site]\n",
|
||||
" box_target = 1 - jp.tanh(5 * jp.linalg.norm(target_pos - box_pos))\n",
|
||||
" gripper_box = 1 - jp.tanh(5 * jp.linalg.norm(box_pos - gripper_pos))\n",
|
||||
" robot_target_qpos = 1 - jp.tanh(\n",
|
||||
" jp.linalg.norm(\n",
|
||||
" state.pipeline_state.qpos[self._robot_arm_qposadr]\n",
|
||||
" - self._init_q[self._robot_arm_qposadr]\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" hand_floor_collision = [\n",
|
||||
" _geoms_colliding(state.pipeline_state, self._floor_geom, g)\n",
|
||||
" for g in [\n",
|
||||
" self._left_finger_geom,\n",
|
||||
" self._right_finger_geom,\n",
|
||||
" self._hand_geom,\n",
|
||||
" ]\n",
|
||||
" ]\n",
|
||||
" floor_collision = sum(hand_floor_collision) > 0\n",
|
||||
" no_floor_collision = 1 - floor_collision\n",
|
||||
"\n",
|
||||
" state.info['reached_box'] = 1.0 * jp.maximum(\n",
|
||||
" state.info['reached_box'],\n",
|
||||
" (jp.linalg.norm(box_pos - gripper_pos) < 0.012),\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" rewards = {\n",
|
||||
" 'box_target': box_target * state.info['reached_box'],\n",
|
||||
" 'gripper_box': gripper_box,\n",
|
||||
" 'no_floor_collision': no_floor_collision,\n",
|
||||
" 'robot_target_qpos': robot_target_qpos,\n",
|
||||
" }\n",
|
||||
" rewards = {k: v * self._config.reward_scales[k] for k, v in rewards.items()}\n",
|
||||
" reward = jp.clip(sum(rewards.values()), -1e4, 1e4)\n",
|
||||
"\n",
|
||||
" out_of_bounds = jp.any(jp.abs(box_pos) > 1.0)\n",
|
||||
" out_of_bounds |= box_pos[2] < 0.0\n",
|
||||
" state.metrics.update(\n",
|
||||
" out_of_bounds=out_of_bounds.astype(float),\n",
|
||||
" **rewards)\n",
|
||||
"\n",
|
||||
" obs = self._get_obs(data, state.info)\n",
|
||||
" done = out_of_bounds | jp.isnan(data.qpos).any() | jp.isnan(data.qvel).any()\n",
|
||||
" done = done.astype(float)\n",
|
||||
" state = State(data, obs, reward, done, state.metrics, state.info)\n",
|
||||
"\n",
|
||||
" return state\n",
|
||||
"\n",
|
||||
" def _get_obs(self, data: PipelineState, info: dict[str, Any]) -> jax.Array:\n",
|
||||
" gripper_pos = data.site_xpos[self._gripper_site]\n",
|
||||
" gripper_mat = data.site_xmat[self._gripper_site].ravel()\n",
|
||||
" obs = jp.concatenate([\n",
|
||||
" data.qpos,\n",
|
||||
" data.qvel,\n",
|
||||
" gripper_pos,\n",
|
||||
" gripper_mat[3:],\n",
|
||||
" data.xmat[self._box_body].ravel()[3:],\n",
|
||||
" data.xpos[self._box_body] - data.site_xpos[self._gripper_site],\n",
|
||||
" info['target_pos'] - data.xpos[self._box_body],\n",
|
||||
" data.ctrl - data.qpos[self._robot_qposadr[:-1]],\n",
|
||||
" ])\n",
|
||||
"\n",
|
||||
" return obs\n",
|
||||
"\n",
|
||||
"envs.register_environment('PandaBringToTarget', PandaBringToTarget)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"id": "76g9uILMQVkc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# instantiate the environment\n",
|
||||
"env_name = 'PandaBringToTarget'\n",
|
||||
"env = envs.get_environment(env_name)\n",
|
||||
"\n",
|
||||
"# define the jit reset/step functions\n",
|
||||
"jit_reset = jax.jit(env.reset)\n",
|
||||
"jit_step = jax.jit(env.step)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"id": "10_vs9IDvnke"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title Train Pick-up-cube Policy\n",
|
||||
"\n",
|
||||
"make_networks_factory = functools.partial(\n",
|
||||
" ppo_networks.make_ppo_networks,\n",
|
||||
" policy_hidden_layer_sizes=(32, 32, 32, 32))\n",
|
||||
"\n",
|
||||
"train_fn = functools.partial(\n",
|
||||
" ppo.train, num_timesteps=20_000_000, num_evals=4, reward_scaling=0.1,\n",
|
||||
" episode_length=150, normalize_observations=True, action_repeat=1,\n",
|
||||
" unroll_length=10, num_minibatches=32, num_updates_per_batch=8,\n",
|
||||
" discounting=0.97, learning_rate=1e-3, entropy_cost=2e-2, num_envs=2048,\n",
|
||||
" batch_size=512, num_resets_per_eval=1,\n",
|
||||
" network_factory=make_networks_factory, seed=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"x_data, y_data, y_dataerr = [], [], []\n",
|
||||
"times = [datetime.now()]\n",
|
||||
"def progress(num_steps, metrics):\n",
|
||||
" times.append(datetime.now())\n",
|
||||
" x_data.append(num_steps)\n",
|
||||
" y_data.append(metrics['eval/episode_reward'])\n",
|
||||
" y_dataerr.append(metrics['eval/episode_reward_std'])\n",
|
||||
"\n",
|
||||
" plt.xlim([0, train_fn.keywords['num_timesteps'] * 1.25])\n",
|
||||
" plt.ylim([0, 2000])\n",
|
||||
" plt.xlabel('# environment steps')\n",
|
||||
" plt.ylabel('reward per episode')\n",
|
||||
" plt.title(f'y={y_data[-1]:.3f}')\n",
|
||||
" plt.errorbar(x_data, y_data, yerr=y_dataerr)\n",
|
||||
" plt.show()\n",
|
||||
"\n",
|
||||
"make_inference_fn, params, _= train_fn(environment=env, progress_fn=progress)\n",
|
||||
"jit_inference_fn = jax.jit(make_inference_fn(params, deterministic=True))\n",
|
||||
"\n",
|
||||
"print(f'time to jit: {times[1] - times[0]}')\n",
|
||||
"print(f'time to train: {times[-1] - times[1]}')\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 0,
|
||||
"metadata": {
|
||||
"id": "jDJLcI0Bv5lD"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# initialize the state\n",
|
||||
"rng = jax.random.PRNGKey(0)\n",
|
||||
"state = jit_reset(rng)\n",
|
||||
"rollout = [state.pipeline_state]\n",
|
||||
"\n",
|
||||
"# grab a trajectory\n",
|
||||
"n_steps = 150\n",
|
||||
"render_every = 2\n",
|
||||
"\n",
|
||||
"for i in range(n_steps):\n",
|
||||
" act_rng, rng = jax.random.split(rng)\n",
|
||||
" ctrl, _ = jit_inference_fn(state.obs, act_rng)\n",
|
||||
" state = jit_step(state, ctrl)\n",
|
||||
" rollout.append(state.pipeline_state)\n",
|
||||
"\n",
|
||||
" if state.done:\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"media.show_video(env.render(rollout[::render_every]), fps=1.0 / env.dt / render_every)"
|
||||
"By now, we have shown how MJX can be used to train policies for classic control and robotic locomotion. For a full suite of robotic locomotion and manipulation environments we encourage you to check out [MuJoCo Playground](https://github.com/google-deepmind/mujoco_playground). Many of the robotic environments have been transferred onto robots, as described on the [website](https://playground.mujoco.org/) and [technical report](https://playground.mujoco.org/assets/playground_technical_report.pdf).\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -1953,8 +1631,7 @@
|
||||
"gpuClass": "premium",
|
||||
"gpuType": "V100",
|
||||
"machine_shape": "hm",
|
||||
"private_outputs": true,
|
||||
"toc_visible": true
|
||||
"private_outputs": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
|
||||
@@ -94,9 +94,18 @@
|
||||
<site name="pin_right" pos=".025 0 .025"/>
|
||||
</body>
|
||||
|
||||
<composite type="particle" count="4 4 4" spacing="0.025" offset=".11 .01 .1">
|
||||
<geom size=".012" rgba=".5 .8 .5 1" solref=".005 1"/>
|
||||
</composite>
|
||||
<replicate count="4" offset=".025 0 0">
|
||||
<replicate count="4" offset="0 .025 0">
|
||||
<replicate count="4" offset="0 0 .025">
|
||||
<body pos=".06 -.04 .05">
|
||||
<joint type="slide" axis="1 0 0"/>
|
||||
<joint type="slide" axis="0 1 0"/>
|
||||
<joint type="slide" axis="0 0 1"/>
|
||||
<geom type="sphere" size=".012" rgba=".5 .8 .5 1" solref=".005 1" condim="1"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
|
||||
<equality>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
@@ -1,45 +0,0 @@
|
||||
<!-- Copyright 2021 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Particle">
|
||||
<!-- Degree of Freedom: 3000
|
||||
Actuators: 0
|
||||
-->
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".01"/>
|
||||
|
||||
<size memory="1G"/>
|
||||
|
||||
<visual>
|
||||
<map stiffness="100"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<default class="wall">
|
||||
<geom type="plane" size=".5 .5 .05"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom name="+x" class="wall" zaxis="1 0 0" pos="-.5 0 -.25"/>
|
||||
<geom name="-x" class="wall" zaxis="-1 0 0" pos=".5 0 -.25"/>
|
||||
<geom name="+y" class="wall" zaxis="0 1 0" pos="0 -.5 -.25"/>
|
||||
<geom name="-y" class="wall" zaxis="0 -1 0" pos="0 .5 -.25"/>
|
||||
<composite type="particle" count="10 10 10" spacing="0.07" offset="0 0 1">
|
||||
<geom size=".025" rgba=".8 .2 .1 1"/>
|
||||
</composite>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,617 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object cap.obj
|
||||
#
|
||||
# Vertices: 217
|
||||
# Faces: 384
|
||||
#
|
||||
####
|
||||
v -0.00000000 0.00000000 0.07411810
|
||||
v 0.09659259 0.00000000 -0.00000001
|
||||
v 0.04829629 0.08365163 0.00000001
|
||||
v -0.04829629 0.08365163 0.00000001
|
||||
v -0.09659259 0.00000000 -0.00000001
|
||||
v -0.04829629 -0.08365163 0.00000001
|
||||
v 0.04829629 -0.08365163 0.00000001
|
||||
v 0.05341240 -0.00000000 0.05865872
|
||||
v 0.08365164 0.04829629 -0.00000001
|
||||
v 0.02670619 0.04625649 0.05865872
|
||||
v 0.00000000 0.09659259 -0.00000001
|
||||
v -0.02670619 0.04625649 0.05865872
|
||||
v -0.08365164 0.04829629 -0.00000001
|
||||
v -0.05341240 0.00000000 0.05865872
|
||||
v -0.08365164 -0.04829629 -0.00000001
|
||||
v -0.02670619 -0.04625649 0.05865872
|
||||
v 0.00000000 -0.09659259 -0.00000001
|
||||
v 0.02670619 -0.04625649 0.05865872
|
||||
v 0.08365164 -0.04829629 -0.00000001
|
||||
v 0.06724097 0.02381060 0.04420168
|
||||
v 0.05424107 0.04632709 0.04420168
|
||||
v 0.04028573 0.02325897 0.06263974
|
||||
v 0.01299990 0.07013769 0.04420167
|
||||
v -0.01299990 0.07013769 0.04420168
|
||||
v -0.00000000 0.04651795 0.06263975
|
||||
v -0.05424108 0.04632709 0.04420166
|
||||
v -0.06724098 0.02381060 0.04420166
|
||||
v -0.04028573 0.02325897 0.06263974
|
||||
v -0.06724097 -0.02381060 0.04420168
|
||||
v -0.05424107 -0.04632709 0.04420168
|
||||
v -0.04028573 -0.02325897 0.06263974
|
||||
v -0.01299990 -0.07013769 0.04420167
|
||||
v 0.01299990 -0.07013769 0.04420168
|
||||
v 0.00000000 -0.04651795 0.06263975
|
||||
v 0.05424108 -0.04632709 0.04420166
|
||||
v 0.06724098 -0.02381060 0.04420166
|
||||
v 0.04028573 -0.02325897 0.06263974
|
||||
v 0.02678733 -0.00000000 0.07046351
|
||||
v 0.01339367 0.02319851 0.07046351
|
||||
v 0.07868688 -0.00000000 0.03583015
|
||||
v 0.09330128 0.02500000 -0.00000001
|
||||
v 0.06830128 0.06830128 -0.00000004
|
||||
v 0.03934343 0.06814483 0.03583015
|
||||
v -0.01339367 0.02319851 0.07046351
|
||||
v 0.02500000 0.09330128 -0.00000001
|
||||
v -0.02500000 0.09330128 -0.00000001
|
||||
v -0.03934344 0.06814483 0.03583015
|
||||
v -0.02678734 0.00000000 0.07046351
|
||||
v -0.06830128 0.06830128 -0.00000004
|
||||
v -0.09330128 0.02500000 -0.00000001
|
||||
v -0.07868689 0.00000000 0.03583013
|
||||
v -0.01339367 -0.02319851 0.07046351
|
||||
v -0.09330128 -0.02500000 -0.00000001
|
||||
v -0.06830128 -0.06830128 -0.00000004
|
||||
v -0.03934343 -0.06814483 0.03583015
|
||||
v 0.01339367 -0.02319851 0.07046351
|
||||
v -0.02500000 -0.09330128 -0.00000001
|
||||
v 0.02500000 -0.09330128 -0.00000001
|
||||
v 0.03934344 -0.06814483 0.03583015
|
||||
v 0.06830128 -0.06830128 -0.00000004
|
||||
v 0.09330128 -0.02500000 -0.00000001
|
||||
v 0.06102579 0.03523326 0.04507211
|
||||
v 0.04719808 0.03485627 0.05509577
|
||||
v 0.05378547 0.02344661 0.05509576
|
||||
v -0.00000000 0.07046651 0.04507211
|
||||
v -0.00658737 0.05830287 0.05509577
|
||||
v 0.00658736 0.05830287 0.05509577
|
||||
v -0.06102579 0.03523326 0.04507211
|
||||
v -0.05378547 0.02344662 0.05509576
|
||||
v -0.04719809 0.03485627 0.05509576
|
||||
v -0.06102579 -0.03523326 0.04507211
|
||||
v -0.04719808 -0.03485627 0.05509577
|
||||
v -0.05378547 -0.02344661 0.05509576
|
||||
v 0.00000000 -0.07046651 0.04507211
|
||||
v 0.00658737 -0.05830287 0.05509577
|
||||
v -0.00658736 -0.05830287 0.05509577
|
||||
v 0.06102579 -0.03523326 0.04507211
|
||||
v 0.05378547 -0.02344662 0.05509576
|
||||
v 0.04719809 -0.03485627 0.05509576
|
||||
v 0.03349884 0.01162469 0.06762048
|
||||
v 0.02681670 0.02319850 0.06762048
|
||||
v 0.02009635 0.01160263 0.07138842
|
||||
v 0.08512369 0.01233582 0.02512579
|
||||
v 0.08049080 0.02435185 0.02823111
|
||||
v 0.07300231 0.01205527 0.04138871
|
||||
v 0.06133472 0.05753115 0.02823112
|
||||
v 0.05324498 0.06755137 0.02512579
|
||||
v 0.04694132 0.05719421 0.04138872
|
||||
v 0.00668214 0.03482319 0.06762048
|
||||
v -0.00668214 0.03482319 0.06762048
|
||||
v -0.00000000 0.02320527 0.07138842
|
||||
v 0.03187871 0.07988720 0.02512577
|
||||
v 0.01915608 0.08188300 0.02823112
|
||||
v 0.02606098 0.06924948 0.04138871
|
||||
v -0.01915608 0.08188300 0.02823112
|
||||
v -0.03187871 0.07988719 0.02512579
|
||||
v -0.02606099 0.06924949 0.04138871
|
||||
v -0.02681670 0.02319850 0.06762048
|
||||
v -0.03349884 0.01162470 0.06762048
|
||||
v -0.02009636 0.01160263 0.07138842
|
||||
v -0.05324498 0.06755137 0.02512577
|
||||
v -0.06133472 0.05753115 0.02823112
|
||||
v -0.04694132 0.05719422 0.04138871
|
||||
v -0.08049081 0.02435185 0.02823111
|
||||
v -0.08512370 0.01233582 0.02512577
|
||||
v -0.07300231 0.01205527 0.04138870
|
||||
v -0.03349884 -0.01162469 0.06762048
|
||||
v -0.02681670 -0.02319850 0.06762048
|
||||
v -0.02009635 -0.01160263 0.07138842
|
||||
v -0.08512370 -0.01233582 0.02512577
|
||||
v -0.08049080 -0.02435185 0.02823111
|
||||
v -0.07300231 -0.01205527 0.04138871
|
||||
v -0.06133472 -0.05753115 0.02823112
|
||||
v -0.05324498 -0.06755137 0.02512579
|
||||
v -0.04694132 -0.05719421 0.04138872
|
||||
v -0.00668214 -0.03482319 0.06762048
|
||||
v 0.00668214 -0.03482319 0.06762048
|
||||
v 0.00000000 -0.02320527 0.07138842
|
||||
v -0.03187871 -0.07988720 0.02512577
|
||||
v -0.01915608 -0.08188300 0.02823112
|
||||
v -0.02606098 -0.06924948 0.04138871
|
||||
v 0.01915608 -0.08188300 0.02823112
|
||||
v 0.03187871 -0.07988719 0.02512579
|
||||
v 0.02606099 -0.06924949 0.04138871
|
||||
v 0.02681670 -0.02319850 0.06762048
|
||||
v 0.03349884 -0.01162470 0.06762048
|
||||
v 0.02009636 -0.01160263 0.07138842
|
||||
v 0.05324498 -0.06755137 0.02512577
|
||||
v 0.06133472 -0.05753115 0.02823112
|
||||
v 0.04694132 -0.05719422 0.04138871
|
||||
v 0.08049081 -0.02435185 0.02823111
|
||||
v 0.08512369 -0.01233582 0.02512579
|
||||
v 0.07300231 -0.01205527 0.04138871
|
||||
v 0.06011157 0.01183397 0.05315326
|
||||
v 0.04687075 0.01169339 0.06167607
|
||||
v 0.07504904 0.03594581 0.02957566
|
||||
v 0.06865450 0.04702147 0.02957566
|
||||
v 0.04030430 0.04614115 0.05315326
|
||||
v 0.03356215 0.03474456 0.06167607
|
||||
v 0.01980726 0.05797512 0.05315328
|
||||
v 0.01330860 0.04643795 0.06167607
|
||||
v 0.00639453 0.08296728 0.02957566
|
||||
v -0.00639453 0.08296728 0.02957566
|
||||
v -0.01980726 0.05797513 0.05315326
|
||||
v -0.01330860 0.04643795 0.06167607
|
||||
v -0.04030430 0.04614115 0.05315326
|
||||
v -0.03356215 0.03474456 0.06167607
|
||||
v -0.06865451 0.04702147 0.02957564
|
||||
v -0.07504904 0.03594581 0.02957565
|
||||
v -0.06011157 0.01183397 0.05315326
|
||||
v -0.04687076 0.01169339 0.06167606
|
||||
v -0.06011157 -0.01183397 0.05315326
|
||||
v -0.04687075 -0.01169339 0.06167607
|
||||
v -0.07504904 -0.03594581 0.02957566
|
||||
v -0.06865450 -0.04702147 0.02957566
|
||||
v -0.04030430 -0.04614115 0.05315326
|
||||
v -0.03356215 -0.03474456 0.06167607
|
||||
v -0.01980726 -0.05797512 0.05315328
|
||||
v -0.01330860 -0.04643795 0.06167607
|
||||
v -0.00639453 -0.08296728 0.02957566
|
||||
v 0.00639453 -0.08296728 0.02957566
|
||||
v 0.01980726 -0.05797513 0.05315326
|
||||
v 0.01330860 -0.04643795 0.06167607
|
||||
v 0.04030430 -0.04614115 0.05315326
|
||||
v 0.03356215 -0.03474456 0.06167607
|
||||
v 0.06865451 -0.04702147 0.02957564
|
||||
v 0.07504904 -0.03594581 0.02957565
|
||||
v 0.06011157 -0.01183397 0.05315326
|
||||
v 0.04687075 -0.01169339 0.06167607
|
||||
v 0.01339461 -0.00000000 0.07321696
|
||||
v 0.00669730 0.01160007 0.07321696
|
||||
v 0.04015301 -0.00000000 0.06570269
|
||||
v 0.02007650 0.03477353 0.06570269
|
||||
v 0.06638318 -0.00000000 0.04890629
|
||||
v 0.08950821 -0.00000000 0.01870825
|
||||
v 0.09576622 0.01260786 -0.00000001
|
||||
v 0.08923991 0.03696438 -0.00000001
|
||||
v 0.03319159 0.05748951 0.04890630
|
||||
v 0.07663205 0.05880184 -0.00000001
|
||||
v 0.05880184 0.07663205 -0.00000001
|
||||
v 0.04475410 0.07751638 0.01870825
|
||||
v -0.00669731 0.01160007 0.07321696
|
||||
v -0.02007651 0.03477353 0.06570269
|
||||
v 0.03696438 0.08923991 -0.00000001
|
||||
v 0.01260786 0.09576622 -0.00000001
|
||||
v -0.03319159 0.05748951 0.04890630
|
||||
v -0.01260786 0.09576622 -0.00000001
|
||||
v -0.03696438 0.08923991 -0.00000001
|
||||
v -0.04475411 0.07751638 0.01870823
|
||||
v -0.01339461 0.00000000 0.07321696
|
||||
v -0.04015301 0.00000000 0.06570269
|
||||
v -0.05880184 0.07663205 -0.00000001
|
||||
v -0.07663205 0.05880184 -0.00000001
|
||||
v -0.06638319 0.00000000 0.04890629
|
||||
v -0.08923991 0.03696438 -0.00000001
|
||||
v -0.09576622 0.01260786 -0.00000001
|
||||
v -0.08950821 0.00000000 0.01870823
|
||||
v -0.00669730 -0.01160007 0.07321696
|
||||
v -0.02007650 -0.03477353 0.06570269
|
||||
v -0.09576622 -0.01260786 -0.00000001
|
||||
v -0.08923991 -0.03696438 -0.00000001
|
||||
v -0.03319159 -0.05748951 0.04890630
|
||||
v -0.07663205 -0.05880184 -0.00000001
|
||||
v -0.05880184 -0.07663205 -0.00000001
|
||||
v -0.04475410 -0.07751638 0.01870825
|
||||
v 0.00669731 -0.01160007 0.07321696
|
||||
v 0.02007651 -0.03477353 0.06570269
|
||||
v -0.03696438 -0.08923991 -0.00000001
|
||||
v -0.01260786 -0.09576622 -0.00000001
|
||||
v 0.03319159 -0.05748951 0.04890630
|
||||
v 0.01260786 -0.09576622 -0.00000001
|
||||
v 0.03696438 -0.08923991 -0.00000001
|
||||
v 0.04475411 -0.07751638 0.01870823
|
||||
v 0.05880184 -0.07663205 -0.00000001
|
||||
v 0.07663205 -0.05880184 -0.00000001
|
||||
v 0.08923991 -0.03696438 -0.00000001
|
||||
v 0.09576622 -0.01260786 -0.00000001
|
||||
# 217 vertices, 0 vertices normals
|
||||
|
||||
f 62 63 64
|
||||
f 65 66 67
|
||||
f 68 69 70
|
||||
f 71 72 73
|
||||
f 74 75 76
|
||||
f 77 78 79
|
||||
f 80 81 82
|
||||
f 83 84 85
|
||||
f 86 87 88
|
||||
f 89 90 91
|
||||
f 92 93 94
|
||||
f 95 96 97
|
||||
f 98 99 100
|
||||
f 101 102 103
|
||||
f 104 105 106
|
||||
f 107 108 109
|
||||
f 110 111 112
|
||||
f 113 114 115
|
||||
f 116 117 118
|
||||
f 119 120 121
|
||||
f 122 123 124
|
||||
f 125 126 127
|
||||
f 128 129 130
|
||||
f 131 132 133
|
||||
f 134 64 135
|
||||
f 136 137 62
|
||||
f 63 138 139
|
||||
f 140 67 141
|
||||
f 142 143 65
|
||||
f 66 144 145
|
||||
f 146 70 147
|
||||
f 148 149 68
|
||||
f 69 150 151
|
||||
f 152 73 153
|
||||
f 154 155 71
|
||||
f 72 156 157
|
||||
f 158 76 159
|
||||
f 160 161 74
|
||||
f 75 162 163
|
||||
f 164 79 165
|
||||
f 166 167 77
|
||||
f 78 168 169
|
||||
f 170 82 171
|
||||
f 172 135 80
|
||||
f 81 139 173
|
||||
f 174 85 134
|
||||
f 175 176 83
|
||||
f 84 177 136
|
||||
f 138 88 178
|
||||
f 137 179 86
|
||||
f 87 180 181
|
||||
f 171 91 182
|
||||
f 173 141 89
|
||||
f 90 145 183
|
||||
f 178 94 140
|
||||
f 181 184 92
|
||||
f 93 185 142
|
||||
f 144 97 186
|
||||
f 143 187 95
|
||||
f 96 188 189
|
||||
f 182 100 190
|
||||
f 183 147 98
|
||||
f 99 151 191
|
||||
f 186 103 146
|
||||
f 189 192 101
|
||||
f 102 193 148
|
||||
f 150 106 194
|
||||
f 149 195 104
|
||||
f 105 196 197
|
||||
f 190 109 198
|
||||
f 191 153 107
|
||||
f 108 157 199
|
||||
f 194 112 152
|
||||
f 197 200 110
|
||||
f 111 201 154
|
||||
f 156 115 202
|
||||
f 155 203 113
|
||||
f 114 204 205
|
||||
f 198 118 206
|
||||
f 199 159 116
|
||||
f 117 163 207
|
||||
f 202 121 158
|
||||
f 205 208 119
|
||||
f 120 209 160
|
||||
f 162 124 210
|
||||
f 161 211 122
|
||||
f 123 212 213
|
||||
f 206 127 170
|
||||
f 207 165 125
|
||||
f 126 169 172
|
||||
f 210 130 164
|
||||
f 213 214 128
|
||||
f 129 215 166
|
||||
f 168 133 174
|
||||
f 167 216 131
|
||||
f 132 217 175
|
||||
f 20 62 64
|
||||
f 62 21 63
|
||||
f 64 63 22
|
||||
f 23 65 67
|
||||
f 65 24 66
|
||||
f 67 66 25
|
||||
f 26 68 70
|
||||
f 68 27 69
|
||||
f 70 69 28
|
||||
f 29 71 73
|
||||
f 71 30 72
|
||||
f 73 72 31
|
||||
f 32 74 76
|
||||
f 74 33 75
|
||||
f 76 75 34
|
||||
f 35 77 79
|
||||
f 77 36 78
|
||||
f 79 78 37
|
||||
f 38 80 82
|
||||
f 80 22 81
|
||||
f 82 81 39
|
||||
f 40 83 85
|
||||
f 83 41 84
|
||||
f 85 84 20
|
||||
f 21 86 88
|
||||
f 86 42 87
|
||||
f 88 87 43
|
||||
f 39 89 91
|
||||
f 89 25 90
|
||||
f 91 90 44
|
||||
f 43 92 94
|
||||
f 92 45 93
|
||||
f 94 93 23
|
||||
f 24 95 97
|
||||
f 95 46 96
|
||||
f 97 96 47
|
||||
f 44 98 100
|
||||
f 98 28 99
|
||||
f 100 99 48
|
||||
f 47 101 103
|
||||
f 101 49 102
|
||||
f 103 102 26
|
||||
f 27 104 106
|
||||
f 104 50 105
|
||||
f 106 105 51
|
||||
f 48 107 109
|
||||
f 107 31 108
|
||||
f 109 108 52
|
||||
f 51 110 112
|
||||
f 110 53 111
|
||||
f 112 111 29
|
||||
f 30 113 115
|
||||
f 113 54 114
|
||||
f 115 114 55
|
||||
f 52 116 118
|
||||
f 116 34 117
|
||||
f 118 117 56
|
||||
f 55 119 121
|
||||
f 119 57 120
|
||||
f 121 120 32
|
||||
f 33 122 124
|
||||
f 122 58 123
|
||||
f 124 123 59
|
||||
f 56 125 127
|
||||
f 125 37 126
|
||||
f 127 126 38
|
||||
f 59 128 130
|
||||
f 128 60 129
|
||||
f 130 129 35
|
||||
f 36 131 133
|
||||
f 131 61 132
|
||||
f 133 132 40
|
||||
f 8 134 135
|
||||
f 134 20 64
|
||||
f 135 64 22
|
||||
f 20 136 62
|
||||
f 136 9 137
|
||||
f 62 137 21
|
||||
f 22 63 139
|
||||
f 63 21 138
|
||||
f 139 138 10
|
||||
f 10 140 141
|
||||
f 140 23 67
|
||||
f 141 67 25
|
||||
f 23 142 65
|
||||
f 142 11 143
|
||||
f 65 143 24
|
||||
f 25 66 145
|
||||
f 66 24 144
|
||||
f 145 144 12
|
||||
f 12 146 147
|
||||
f 146 26 70
|
||||
f 147 70 28
|
||||
f 26 148 68
|
||||
f 148 13 149
|
||||
f 68 149 27
|
||||
f 28 69 151
|
||||
f 69 27 150
|
||||
f 151 150 14
|
||||
f 14 152 153
|
||||
f 152 29 73
|
||||
f 153 73 31
|
||||
f 29 154 71
|
||||
f 154 15 155
|
||||
f 71 155 30
|
||||
f 31 72 157
|
||||
f 72 30 156
|
||||
f 157 156 16
|
||||
f 16 158 159
|
||||
f 158 32 76
|
||||
f 159 76 34
|
||||
f 32 160 74
|
||||
f 160 17 161
|
||||
f 74 161 33
|
||||
f 34 75 163
|
||||
f 75 33 162
|
||||
f 163 162 18
|
||||
f 18 164 165
|
||||
f 164 35 79
|
||||
f 165 79 37
|
||||
f 35 166 77
|
||||
f 166 19 167
|
||||
f 77 167 36
|
||||
f 37 78 169
|
||||
f 78 36 168
|
||||
f 169 168 8
|
||||
f 1 170 171
|
||||
f 170 38 82
|
||||
f 171 82 39
|
||||
f 38 172 80
|
||||
f 172 8 135
|
||||
f 80 135 22
|
||||
f 39 81 173
|
||||
f 81 22 139
|
||||
f 173 139 10
|
||||
f 8 174 134
|
||||
f 174 40 85
|
||||
f 134 85 20
|
||||
f 40 175 83
|
||||
f 175 2 176
|
||||
f 83 176 41
|
||||
f 20 84 136
|
||||
f 84 41 177
|
||||
f 136 177 9
|
||||
f 10 138 178
|
||||
f 138 21 88
|
||||
f 178 88 43
|
||||
f 21 137 86
|
||||
f 137 9 179
|
||||
f 86 179 42
|
||||
f 43 87 181
|
||||
f 87 42 180
|
||||
f 181 180 3
|
||||
f 1 171 182
|
||||
f 171 39 91
|
||||
f 182 91 44
|
||||
f 39 173 89
|
||||
f 173 10 141
|
||||
f 89 141 25
|
||||
f 44 90 183
|
||||
f 90 25 145
|
||||
f 183 145 12
|
||||
f 10 178 140
|
||||
f 178 43 94
|
||||
f 140 94 23
|
||||
f 43 181 92
|
||||
f 181 3 184
|
||||
f 92 184 45
|
||||
f 23 93 142
|
||||
f 93 45 185
|
||||
f 142 185 11
|
||||
f 12 144 186
|
||||
f 144 24 97
|
||||
f 186 97 47
|
||||
f 24 143 95
|
||||
f 143 11 187
|
||||
f 95 187 46
|
||||
f 47 96 189
|
||||
f 96 46 188
|
||||
f 189 188 4
|
||||
f 1 182 190
|
||||
f 182 44 100
|
||||
f 190 100 48
|
||||
f 44 183 98
|
||||
f 183 12 147
|
||||
f 98 147 28
|
||||
f 48 99 191
|
||||
f 99 28 151
|
||||
f 191 151 14
|
||||
f 12 186 146
|
||||
f 186 47 103
|
||||
f 146 103 26
|
||||
f 47 189 101
|
||||
f 189 4 192
|
||||
f 101 192 49
|
||||
f 26 102 148
|
||||
f 102 49 193
|
||||
f 148 193 13
|
||||
f 14 150 194
|
||||
f 150 27 106
|
||||
f 194 106 51
|
||||
f 27 149 104
|
||||
f 149 13 195
|
||||
f 104 195 50
|
||||
f 51 105 197
|
||||
f 105 50 196
|
||||
f 197 196 5
|
||||
f 1 190 198
|
||||
f 190 48 109
|
||||
f 198 109 52
|
||||
f 48 191 107
|
||||
f 191 14 153
|
||||
f 107 153 31
|
||||
f 52 108 199
|
||||
f 108 31 157
|
||||
f 199 157 16
|
||||
f 14 194 152
|
||||
f 194 51 112
|
||||
f 152 112 29
|
||||
f 51 197 110
|
||||
f 197 5 200
|
||||
f 110 200 53
|
||||
f 29 111 154
|
||||
f 111 53 201
|
||||
f 154 201 15
|
||||
f 16 156 202
|
||||
f 156 30 115
|
||||
f 202 115 55
|
||||
f 30 155 113
|
||||
f 155 15 203
|
||||
f 113 203 54
|
||||
f 55 114 205
|
||||
f 114 54 204
|
||||
f 205 204 6
|
||||
f 1 198 206
|
||||
f 198 52 118
|
||||
f 206 118 56
|
||||
f 52 199 116
|
||||
f 199 16 159
|
||||
f 116 159 34
|
||||
f 56 117 207
|
||||
f 117 34 163
|
||||
f 207 163 18
|
||||
f 16 202 158
|
||||
f 202 55 121
|
||||
f 158 121 32
|
||||
f 55 205 119
|
||||
f 205 6 208
|
||||
f 119 208 57
|
||||
f 32 120 160
|
||||
f 120 57 209
|
||||
f 160 209 17
|
||||
f 18 162 210
|
||||
f 162 33 124
|
||||
f 210 124 59
|
||||
f 33 161 122
|
||||
f 161 17 211
|
||||
f 122 211 58
|
||||
f 59 123 213
|
||||
f 123 58 212
|
||||
f 213 212 7
|
||||
f 1 206 170
|
||||
f 206 56 127
|
||||
f 170 127 38
|
||||
f 56 207 125
|
||||
f 207 18 165
|
||||
f 125 165 37
|
||||
f 38 126 172
|
||||
f 126 37 169
|
||||
f 172 169 8
|
||||
f 18 210 164
|
||||
f 210 59 130
|
||||
f 164 130 35
|
||||
f 59 213 128
|
||||
f 213 7 214
|
||||
f 128 214 60
|
||||
f 35 129 166
|
||||
f 129 60 215
|
||||
f 166 215 19
|
||||
f 8 168 174
|
||||
f 168 36 133
|
||||
f 174 133 40
|
||||
f 36 167 131
|
||||
f 167 19 216
|
||||
f 131 216 61
|
||||
f 40 132 175
|
||||
f 132 61 217
|
||||
f 175 217 2
|
||||
# 384 faces, 0 coords texture
|
||||
|
||||
# End of File
|
||||
|
Before Width: | Height: | Size: 634 KiB After Width: | Height: | Size: 634 KiB |
@@ -0,0 +1,42 @@
|
||||
<!-- Copyright 2024 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Trilinear">
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".001" integrator="implicitfast"/>
|
||||
|
||||
<size memory="100M"/>
|
||||
|
||||
<visual>
|
||||
<map stiffness="100"/>
|
||||
</visual>
|
||||
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="press" type="slide" axis="0 0 1" damping="500"/>
|
||||
<geom type="box" size=".02 .2 .2" pos="0 0 .5"/>
|
||||
</body>
|
||||
<flexcomp type="mesh" file="bunny.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
||||
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
|
||||
<elasticity young="1e3" poisson="0.1" damping="0.001"/>
|
||||
<contact selfcollide="none" internal="false"/>
|
||||
</flexcomp>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<position name="press" joint="press" gear="-1 0 0 0 0 0" ctrlrange="-1 1" kp="1000"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- Copyright 2024 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Trilinear">
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".001" integrator="implicitfast"/>
|
||||
|
||||
<size memory="100M"/>
|
||||
|
||||
<visual>
|
||||
<map stiffness="100"/>
|
||||
</visual>
|
||||
|
||||
|
||||
<asset>
|
||||
<texture name="texsponge" type="2d" file="sponge.png"/>
|
||||
<material name="matsponge" texture="texsponge" specular="0.3"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="press" type="slide" axis="0 0 1" damping="500"/>
|
||||
<geom type="box" size=".02 .2 .2" pos="0 0 .5"/>
|
||||
</body>
|
||||
<flexcomp type="mesh" file="bunny_with_uv.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
||||
radius=".001" material="matsponge" mass=".05" name="softbody" dof="trilinear">
|
||||
<elasticity young="1e3" poisson="0.1" damping="0.001"/>
|
||||
<contact selfcollide="none" internal="false"/>
|
||||
</flexcomp>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<position name="press" joint="press" gear="-1 0 0 0 0 0" ctrlrange="-1 1" kp="1000"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user