From e13ddfa2ab976e940008433fddb64799e475690e Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Fri, 12 Jul 2024 11:04:20 -0700 Subject: [PATCH] Improve compiler-related documentation. PiperOrigin-RevId: 651827545 Change-Id: I85c12dc12967e49087c342445dbfa049c5cdbcdf --- doc/APIreference/APItypes.rst | 16 ++-- doc/APIreference/functions.rst | 15 +++- doc/APIreference/functions_override.rst | 18 ++++- doc/changelog.rst | 51 +++++++------ doc/includes/roles.rst | 15 ++++ doc/programming/index.rst | 8 +- doc/programming/modeledit.rst | 99 +++++++++++++++++++++---- doc/python.rst | 43 +++++++++++ 8 files changed, 213 insertions(+), 52 deletions(-) diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index 4d04d1db..75f9a7c1 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -1078,12 +1078,13 @@ is initialized, others change at runtime. .. _tySpecStructure: -mjSpec -^^^^^^ +Model Editing +^^^^^^^^^^^^^ -The strucs below are defined in `mjspec.h `__ -and, with the exception of the top level :ref:`mjSpec` struct, begin with the ``mjs`` prefix. -For more details, see the :doc:`Model Editing <../programming/modeledit>` chapter. +The strucs below are defined in +`mjspec.h `__ and, with the exception of +the top level :ref:`mjSpec` struct, begin with the ``mjs`` prefix. For more details, see the :doc:`Model Editing +<../programming/modeledit>` chapter. .. _mjSpec: @@ -1375,6 +1376,8 @@ Plugin specification. .. mujoco-include:: mjsPlugin +.. _ArrayHandles: + .. _mjString: .. _mjStringVec: @@ -1392,7 +1395,8 @@ Plugin specification. Array handles ~~~~~~~~~~~~~ -Explain how handles work. +C handles for C++ strings and vector types. When using from C, use the provided :ref:`getters` and +:ref:`setters`. .. code-block:: C++ diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 1e543270..5d588e54 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -56,7 +56,17 @@ mj_recompile .. mujoco-include:: mj_recompile -Recompile spec to model, preserving the state. +Recompile spec to model, preserving the state. Like :ref:`mj_compile`, this function compiles an :ref:`mjSpec` to an +:ref:`mjModel`, with two differences. First, rather than returning an entirely new model, it will +reallocate existing :ref:`mjModel` and :ref:`mjData` instances in-place. Second, it will preserve the +:ref:`integration state`, as given in the provided :ref:`mjData` instance, while accounting for +newly added or removed degrees of freedom. This allows the user to continue simulation with the same model and data +struct pointers while editing the model programmatically. + +.. admonition:: Incomplete implementation + :class: attention + + This function is currently incomplete, preserving only ``mjData.qpos`` and ``mjData.qvel``. .. _mj_saveLastXML: @@ -2682,7 +2692,8 @@ Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math: All outputs are optional. -These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. +These functions provide high level manipulation for the :ref:`mjSpec` struct, which represents an +uncompiled :ref:`mjModel`. .. _Plugins-api: diff --git a/doc/APIreference/functions_override.rst b/doc/APIreference/functions_override.rst index cdc4593e..6b25f81d 100644 --- a/doc/APIreference/functions_override.rst +++ b/doc/APIreference/functions_override.rst @@ -29,6 +29,20 @@ The key function here is :ref:`mj_loadXML`. It invokes the built-in parser and c a valid mjModel, or NULL - in which case the user should check the error information in the user-provided string. The model and all files referenced in it can be loaded from disk or from a VFS when provided. +.. _mj_recompile: + +Recompile spec to model, preserving the state. Like :ref:`mj_compile`, this function compiles an :ref:`mjSpec` to an +:ref:`mjModel`, with two differences. First, rather than returning an entirely new model, it will +reallocate existing :ref:`mjModel` and :ref:`mjData` instances in-place. Second, it will preserve the +:ref:`integration state`, as given in the provided :ref:`mjData` instance, while accounting for +newly added or removed degrees of freedom. This allows the user to continue simulation with the same model and data +struct pointers while editing the model programmatically. + +.. admonition:: Incomplete implementation + :class: attention + + This function is currently incomplete, preserving only ``mjData.qpos`` and ``mjData.qvel``. + .. _Mainsimulation: These are the main entry points to the simulator. Most users will only need to call :ref:`mj_step`, which computes @@ -638,5 +652,5 @@ All outputs are optional. .. _SpecManip: -These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. - +These functions provide high level manipulation for the :ref:`mjSpec` struct, which represents an +uncompiled :ref:`mjModel`. diff --git a/doc/changelog.rst b/doc/changelog.rst index 52c2c13e..d84fb4a3 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -5,65 +5,68 @@ Changelog Upcoming version (not yet released) ----------------------------------- +New features +^^^^^^^^^^^^ + +1. Introduced a major new feature: **procedural model creation and editing**, using a new top-level data-structure + :ref:`mjSpec`. See the :doc:`Model Editing` chapter for details. + Note that as of this release this feature is still in testing and subject to future breaking changes. + Fixes :github:issue:`364`. + General ^^^^^^^ + .. admonition:: Breaking API changes :class: attention - 1. Removed deprecated ``mj_makeEmptyFileVFS`` and ``mj_findFileVFS`` functions. The constants ``mjMAXVFS`` and + 2. Removed deprecated ``mj_makeEmptyFileVFS`` and ``mj_findFileVFS`` functions. The constants ``mjMAXVFS`` and ``mjMAXVFSNAME`` are also removed as they are no longer needed. **Migration:** Use :ref:`mj_addBufferVFS` to copy a buffer into a VFS file directly. - 2. Calls to :ref:`mj_defaultVFS` may allocate memory inside VFS, and the corresponding + 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. - 3. Deprecated :ref:`mju_rotVecMat` and :ref:`mju_rotVecMatT` in favor of :ref:`mju_mulMatVec3` and + 4. Deprecated :ref:`mju_rotVecMat` and :ref:`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. - 4. Removed the ``actuator_actdim`` callback from actuator plugins. They now have the ``actdim`` attribute, which + 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 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. -5. The :ref:`VFS` implementation has been rewritten in C++ and is now considerably more efficient in +6. Added :ref:`attach` meta-element to MJCF, which allows :ref:`attaching` a subtree from a + different model to a body in the current model. +7. The :ref:`VFS` implementation has been rewritten in C++ and is now considerably more efficient in speed and memory footprint. -6. Added a new API for :doc:`procedural model manipulation`. Fixes :github:issue:`364`. - Still missing: - - - Detailed documentation. - .. youtube:: ZXBTEIDWHhs :align: right :width: 240px -7. Add :ref:`attach` meta-element to MJCF, which allows attaching a model to a body. 8. Added support for orthographic cameras. This is available for both fixed cameras and the free camera, using the :ref:`camera/orthographic` and :ref:`global/orthographic` attributes, respectively. 9. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. 10. Added :ref:`mj_setKeyframe` for saving the current state into a model keyframe. 11. Added support for ``ball`` joints in the URDF parser ("spherical" in URDF). -12. Deprecated :ref:`mju_rotVecMat` and :ref:`mju_rotVecMatT` in favor of :ref:`mju_mulMatVec3` and - :ref:`mju_mulMatTVec3`. These functions names and argument ordering are more consistent with the rest of the API. -13. Replaced ``mjUSEDOUBLE`` which was previously hard-coded in +12. Replaced ``mjUSEDOUBLE`` which was previously hard-coded in `mjtnum.h `__ with the build-time flag ``mjUSESINGLE``. If this symbol is not defined, MuJoCo will use double-precision floating point, as usual. If ``mjUSESINGLE`` is defined, MuJoCo will use single-precision floating point. See :ref:`mjtNum`. Relatedly, fixed various type errors that prevented building with single-precision. -14. Quaternions in ``mjData->qpos`` and ``mjData->mocap_quat`` are no longer normalized in-place by +13. Quaternions in ``mjData->qpos`` and ``mjData->mocap_quat`` are no longer normalized in-place by :ref:`mj_kinematics`. Instead they are normalized when they are used. After the first step, quaternions in ``mjData->qpos`` will be normalized. -15. Mesh loading in the compiler, which is usually the slowest part of the loading process, is now multi-threaded. +14. Mesh loading in the compiler, which is usually the slowest part of the loading process, is now multi-threaded. MJX ~~~ -16. Added support for :ref:`elliptic friction cones`. -17. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. -18. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. +15. Added support for :ref:`elliptic friction cones`. +16. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +17. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. .. youtube:: P83tKA1iz2Y @@ -72,17 +75,17 @@ MJX Simulate ^^^^^^^^ -19. Added improved tutorial video. -20. Improved the Brownian noise generator. -21. Now displaying model load times if they are longer than 0.25 seconds. +18. Added improved tutorial video. +19. Improved the Brownian noise generator. +20. Now displaying model load times if they are longer than 0.25 seconds. Python bindings ^^^^^^^^^^^^^^^ -22. Fixed a memory leak when using ``copy.deepcopy()`` on a ``mujoco.MjData`` instance (:github:issue:`1572`). +21. Fixed a memory leak when using ``copy.deepcopy()`` on a ``mujoco.MjData`` instance (:github:issue:`1572`). Bug fixes ^^^^^^^^^ -23. Fix an issue where ``mj_copyData`` (or ``copy.copy()`` in the Python bindings) was not copying contact information +22. Fix an issue where ``mj_copyData`` (or ``copy.copy()`` in the Python bindings) was not copying contact information correctly (:github:issue:`1710`). Version 3.1.6 (Jun 3, 2024) diff --git a/doc/includes/roles.rst b/doc/includes/roles.rst index 795374cb..6f1cebf7 100644 --- a/doc/includes/roles.rst +++ b/doc/includes/roles.rst @@ -13,3 +13,18 @@ .. Role for attribute value specs in MJCF spec. .. role:: at-val + +.. + Role for pretty-printed inline C/C++ code. +.. role:: cpp(code) + :language: C++ + +.. + Role for pretty-printed inline Python code. +.. role:: python(code) + :language: Python + +.. + Role for pretty-printed inline shell code. +.. role:: shell(code) + :language: bash diff --git a/doc/programming/index.rst b/doc/programming/index.rst index 5d438482..bcf4d99b 100644 --- a/doc/programming/index.rst +++ b/doc/programming/index.rst @@ -110,7 +110,7 @@ To build MuJoCo from source, you will need CMake and a working C++17 compiler in #. Clone the ``mujoco`` repository from GitHub. #. Create a new build directory somewhere, and ``cd`` into it. -#. Run ``cmake $PATH_TO_CLONED_REPO`` to configure the build. +#. Run :shell:`cmake $PATH_TO_CLONED_REPO` to configure the build. #. Run ``cmake --build .`` to build. MuJoCo's build system automatically fetches dependencies from upstream repositories over the Internet using CMake's @@ -123,11 +123,11 @@ section of the documentation. Additionally, the CMake setup also implements an installation phase which will copy and organize the output files to a target directory. -5. Select the directory: ``cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=`` +5. Select the directory: :shell:`cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=` #. After building, install with ``cmake --install .`` When building on Windows, use Visual Studio 2019 or later and make sure Windows SDK version 10.0.22000 or later is -installed (see `here `__ for more details). +installed (see :github:issue:`862` for more details). .. tip:: As a reference, a working build configuration can be found in MuJoCo's @@ -198,7 +198,7 @@ the symbol :ref:`mjVERSION_HEADER ` and the library provides the func .. code-block:: C // recommended version check - if( mjVERSION_HEADER!=mj_version() ) + if (mjVERSION_HEADER!=mj_version()) complain(); Note that only the main header defines this symbol. We assume that the collection of headers released with each software diff --git a/doc/programming/modeledit.rst b/doc/programming/modeledit.rst index 33181d1d..080f07da 100644 --- a/doc/programming/modeledit.rst +++ b/doc/programming/modeledit.rst @@ -17,28 +17,99 @@ use this API when loading a model. Overview ~~~~~~~~ -As summarized in the the :ref:`Overview chapter`, the traditional workflow to create compiled :ref:`mjModel` -instances is: +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`, the traditional workflow is: -1. Create an XML model description file (MJCF or URDF). -2. Call :ref:`mj_loadXML` passing in the XML (and associated assets), obtain an :ref:`mjModel` instance. + 1. Create an XML model description file (MJCF or URDF) and ascociated assets. |br| + 2. Call :ref:`mj_loadXML`, obtain an :ref:`mjModel` instance. -The new workflow looks like: +The new workflow is: -1. Create an :ref:`mjSpec`, either an empty one corresponding to the XML ````, or by loading an existing XML - file. -2. Modify the :ref:`mjSpec` as desired, adding, editing 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` an empty :ref:`mjSpec` or :ref:`parse` 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. .. _meUsage: Usage ~~~~~ +Here we describe the C API for procedural model editing, but it is also exposed in the +:ref:`Python bindings`. +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: -Detailed documentation is still missing. In the meantime, advanced users can refer to -`user_api_test.cc `__ and the MJCF -parser in `xml_native_reader.cc `__, -which is already using this API. +.. code-block:: C + mjSpec* spec = mj_makeSpec(); + spec->opt.timestep = 0.01; + ... + mjModel* model = mj_compile(spec); + +Attributes which have variable length are C++ vectors and strings, :ref:`exposed to C as opaque types`. +In C one uses the provided :ref:`getters` and :ref:`setters`: + +.. code-block:: C + + mjs_setString(model->modelname, "my_model"); + +In C++ one can use these directly: + +.. code-block:: C++ + + std::string modelname = "my_model"; + *spec->modelname = modelname; + +.. _meMjsElements: + +Model elements +^^^^^^^^^^^^^^ + +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 + +.. 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); + +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 `__. + + +.. _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` and :ref:`replicate` meta-elements in MJCF. + + +.. _meKnownIssues: + +Known issues +~~~~~~~~~~~~ + +- Better documentation is still missing and will be added in the future. In the meantime, advanced users can refer + to `user_api_test.cc `__ and the MJCF + parser in `xml_native_reader.cc `__, + which is already using this API. +- One of the central design consideration 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 inceremntal 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. diff --git a/doc/python.rst b/doc/python.rst index 639c73e4..703caaa7 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -465,6 +465,49 @@ Alternatively, if a callback is implemented in a native dynamic library, users c it to ``mujoco.set_mjcb_foo``. The bindings will then retrieve the underlying function pointer and assign it directly to the raw callback pointer, and the GIL will **not** be acquired each time the callback is entered. +.. _PyModelEdit: + +Model editing +============= +The :doc:`Model Editing` 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 `__, 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. + +Below is a simple example of how to use the model editing API. For more examples, please refer to +`specs_test.py `__. + +.. code-block:: python + + import mujoco + spec = mujoco.MjSpec() + body = spec.worldbody.add_body() + body.pos = [1, 2, 3] + body.quat = [0, 1, 0, 0] + geom = body.add_geom() + geom.name = 'my_geom' + geom.type = mujoco.mjtGeom.mjGEOM_SPHERE + geom.size[0] = 1 + geom.rgba = [1, 0, 0, 1] + ... + model = spec.compile() + +.. admonition:: Missing features + :class: attention + + We are aware of multiple missing features in the Python API, including: + + - Convenient constructors like: + |br| :python:`geom = body.add_geom(name='my_geom', size=[1, 1, 1], rgba=[1, 0, 0, 1])` + - 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. + + 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. + + .. _PyBuild: Building from source