diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..5e8e0504 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,39 @@ +version: 1.0.{build} + +environment: + + global: + # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the + # /E:ON and /V:ON options are not enabled in the batch script intepreter + # See: http://stackoverflow.com/a/13751649/163740 + CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\continuous-integration\\appveyor\\run_with_env.cmd" + +platform: + - x64 + + +install: + - mkdir %userprofile%\.mujoco + - curl %MUJOCO_FOR_WINDOWS% -o %userprofile%\.mujoco\mjpro.tar.gz + - curl https://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.bin.WIN64.zip -o %userprofile%\glfw.zip + - cd %userprofile%\.mujoco\ + - tar -zxvf mjpro.tar.gz + - echo "%PYTHON_VERSION% C:\Miniconda35-x64 %userprofile%" + - set PATH=C:\Miniconda35-x64;C:\Miniconda35-x64\Scripts;%PATH% + - conda config --set always_yes yes --set changeps1 no + - conda update -q conda + - conda info -a + - conda create -q -n test-environment python=3.5 numpy scipy + - activate test-environment + - SET PATH=%userprofile%\.mujoco\mjpro150\bin;%PATH%; + - SET RENDERING_OFF=1 + - cd C:\projects\pymj + - pip install -r requirements.txt + - pip install -r requirements.dev.txt + +build: off + +test_script: + - pytest -s --verbose --durations=10 --instafail + + diff --git a/.dockerignore b/.dockerignore new file mode 100755 index 00000000..ac6a3ef5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,49 @@ +generated_cymj* +_pyxbld* +dist +cache +.idea/* +*~ +.*~ +*#*# +*.o +*.dat +*.prof +*.lprof +*.local +.realsync +.DS_Store +**/*.egg-info +.cache +*.ckpt +*.log +.ipynb_checkpoints +venv/ +.vimrc +*.settings +*.svn +.project +.pydevproject +tags +*sublime-project +*sublime-workspace +# Intermediate outputs +__pycache__ +**/__pycache__ +*.pb.* +*.pyc +*.swp +*.swo +# generated data +*.rdb +*.db +*.avi +# mujoco outputs +MUJOCO_LOG.TXT +model.txt +.window_data +.idea/*.xml +outputfile +tmp* +cymj.c +**/.git diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 94e78d5b..ac6a3ef5 --- a/.gitignore +++ b/.gitignore @@ -1,64 +1,49 @@ +generated_cymj* +_pyxbld* +dist +cache +.idea/* +*~ +.*~ +*#*# +*.o +*.dat +*.prof +*.lprof +*.local +.realsync .DS_Store - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* +**/*.egg-info .cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: +*.ckpt *.log - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - - -gcc-dylibs -MANIFEST -/tmp +.ipynb_checkpoints +venv/ +.vimrc +*.settings +*.svn +.project +.pydevproject +tags +*sublime-project +*sublime-workspace +# Intermediate outputs +__pycache__ +**/__pycache__ +*.pb.* +*.pyc +*.swp +*.swo +# generated data +*.rdb +*.db +*.avi +# mujoco outputs MUJOCO_LOG.TXT +model.txt +.window_data +.idea/*.xml +outputfile +tmp* +cymj.c +**/.git diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..082d4334 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.5.2/envs/pymj diff --git a/.travis.yml b/.travis.yml index 1ffe59b2..3771aa60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,10 @@ -dist: trusty sudo: required -cache: - apt: true -language: python -addons: - apt: - packages: - - python-dev -python: - - "2.7" - - "3.5" -install: pip install tox-travis -script: tox +language: generic +dist: trusty + before_install: - - Xvfb :12 -screen 0 800x600x24 +extension RANDR & - - mkdir -p ~/.mujoco - - curl https://openai-public.s3-us-west-2.amazonaws.com/mujoco/$MUJOCO_KEY_BUNDLE.tar.gz | tar xz -C ~/.mujoco -env: - - DISPLAY=:12 +- curl $MUJOCO_FOR_LINUX | tar -xz ./mjkey.txt +- docker build -t image-under-test . -notifications: - slack: - secure: h/Mxm8K+avH/2W0818zCHmLloRPMFN4NJL01+VShvAkH80/acfjeq/+mMdWXXPL/oOB6kSHDk+GDhwR6+s03ZcPMn5INTFvFYqUc6UWmT+NXtOPxGTN0xda6MdYUkWQUKaMyjFrweZQOMOASFBIzPOq4XeVbM5aB8s4EJhnfAcYZhp/idwKbToVihN4KZgxlvZIFc8iEp1o9uSl5qrsaeYYYXRkb6mauacAwOo4/Chu+cOnoLUOnvhBFE3rV3doDNrbnoalO8XiExtgx5CIAYWrlMni7r2Q+LlzgwdyTH19ZtybPxJTZIIWSBQ2UtcoYdIEDcc36GcUwz1VUGg32mLJJnY2xw80CWR4ixFPpLwwP5Y99WTn8v094B4nmFTWOwNWXp3EkqtTN9XcJoRBqXB5ArucIPqrx57dOCljSKx22gL6WaF2p3stSAxIGFektGyGnisaELrFZG1C63aHoUPicj3gUlijmAoUmYaDRf6P1wnpXqBpKDAWWhAMSatvx1ekmEJgR7OQklQnnfjx9kENDUygNUWS4IQwN2qYieuzHFL3of7/30mTM43+Vt/vWN8GI7j01BXu6FNGGloHxjH1pt3bLP/+uj5BJsT2HWF+Z8XR4VE6cyVuKsQAFgCXwOkoDHALbcwsspONDIt/9ixkesgh1oFt4CzU3UuU5wYs= - on_success: change +script: +- docker run --rm image-under-test diff --git a/Dockerfile b/Dockerfile index 0c793288..1f3e14fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,53 @@ -FROM ubuntu:14.04 +# We need the CUDA base dockerfile to enable GPU rendering +# on hosts with GPUs. +FROM nvidia/cuda:8.0-devel-ubuntu16.04 -RUN apt-get update && apt-get install -y \ - python2.7 \ - python2.7-dev \ - python-pip \ - unzip \ - xorg-dev \ +RUN apt-get update -q \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + curl \ + git \ libgl1-mesa-dev \ - xvfb \ - libxinerama1 \ - libxcursor1 \ - libglu1-mesa \ - cmake \ - gfortran \ - libblas-dev \ - libatlas-dev \ - liblapack-dev \ - libjpeg62 \ - libjpeg62-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -COPY requirements.txt /mujoco-py/requirements.txt -RUN pip install -r /mujoco-py/requirements.txt - -COPY . /mujoco-py - -ENV PYTHONPATH=/mujoco-py -WORKDIR /mujoco-py -ENTRYPOINT ["bin/docker-entrypoint.sh"] -CMD ["nosetests", "tests/"] + libgl1-mesa-glx \ + libosmesa6-dev \ + python3-pip \ + python3-numpy \ + python3-scipy \ + unzip \ + vim \ + wget \ + xpra \ + xserver-xorg-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -o /usr/local/bin/patchelf https://s3-us-west-2.amazonaws.com/openai-sci-artifacts/manual-builds/patchelf_0.9_amd64.elf \ + && chmod +x /usr/local/bin/patchelf + +ENV LANG C.UTF-8 + +RUN mkdir -p /root/.mujoco \ + && wget https://www.roboti.us/download/mjpro150_linux.zip -O mujoco.zip \ + && unzip mujoco.zip -d /root/.mujoco \ + && rm mujoco.zip +COPY ./mjkey.txt /root/.mujoco/ +ENV LD_LIBRARY_PATH /root/.mujoco/mjpro150/bin:$LD_LIBRARY_PATH + +COPY vendor/Xdummy /usr/local/bin/Xdummy +RUN chmod +x /usr/local/bin/Xdummy + +# Workaround for https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-375/+bug/1674677 +COPY ./vendor/10_nvidia.json /usr/share/glvnd/egl_vendor.d/10_nvidia.json + +WORKDIR /mujoco_py +# Copy over just requirements.txt at first. That way, the Docker cache doesn't +# expire until we actually change the requirements. +COPY ./requirements.txt /mujoco_py/ +COPY ./requirements.dev.txt /mujoco_py/ +RUN pip3 install -r requirements.txt +RUN pip3 install -r requirements.dev.txt + +# Delay moving in the entire code until the very end. +ENTRYPOINT ["/mujoco_py/vendor/Xdummy-entrypoint"] +CMD ["pytest"] +COPY . /mujoco_py +RUN python3 setup.py install diff --git a/LICENSE b/LICENSE.md similarity index 70% rename from LICENSE rename to LICENSE.md index 80127810..9214a4a7 100644 --- a/LICENSE +++ b/LICENSE.md @@ -1,6 +1,8 @@ +# mujoco-py + The MIT License -Copyright (c) 2016 OpenAI (http://openai.com) +Copyright (c) 2017 OpenAI (http://openai.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +21,16 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Mujoco models +This work is derived from [MuJuCo models](http://www.mujoco.org/forum/index.php?resources/) used under the following license: +``` +This file is part of MuJoCo. +Copyright 2009-2015 Roboti LLC. +Mujoco :: Advanced physics simulation engine +Source : www.roboti.us +Version : 1.31 +Released : 23Apr16 +Author :: Vikash Kumar +Contacts : kumar@roboti.us +``` diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..9583199a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include requirements.txt +include requirements.dev.txt +recursive-include mujoco_py *.h *.py *.pyx *.pxd *.pxi *.xml *.c *.so +include mujoco_py/gl/*.c +include setup.py diff --git a/Makefile b/Makefile index 7d944e23..7bc74350 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,39 @@ +.PHONY: all clean build test mount_shell shell upload check-env + +MUJOCO_LICENSE_PATH ?= ~/.mujoco/mjkey.txt + +all: test + +clean: + rm -rf mujoco_py.egg-info + rm -rf */__pycache__ + rm -rf */*/__pycache__ + rm -rf mujoco_py/generated/_pyxbld* + rm -rf mujoco_py/generated_cymj* + rm -rf mujoco_py/cythonlock_*.pyc + rm -rf dist + rm -rf build + +build: check-license + cp $(MUJOCO_LICENSE_PATH) mjkey.txt + docker build -t mujoco_py . || rm mjkey.txt && rm mjkey.txt + +test: build + # run it interactive mode so we can abort with CTRL+C + docker run --rm -i mujoco_py pytest + +mount_shell: + docker run --rm -it -v `pwd`:/dev mujoco_py /bin/bash -c "pip uninstall -y mujoco_py; rm -rf /mujoco_py; (cd /dev; /bin/bash)" + +shell: + docker run --rm -it mujoco_py /bin/bash + upload: rm -rf dist python setup.py sdist twine upload dist/* -test: - nose2 +check-license: +ifeq ("","$(wildcard $(MUJOCO_LICENSE_PATH))") + $(error "License key not found at location $(MUJOCO_LICENSE_PATH). Use MUJOCO_LICENSE_PATH to specify its path") +endif diff --git a/README.md b/README.md index 196b52ed..126b30c3 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,77 @@ -# MuJoCo Python Bindings +# mujoco-py [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat)](https://openai.github.io/pymj/build/html/index.html) [![Build Status](https://travis-ci.com/openai/pymj.svg?token=aA2GupFxnzsRTN56psRJ&branch=master)](https://travis-ci.com/openai/pymj) [![Build status](https://ci.appveyor.com/api/projects/status/dsg86qpalv9bi568?svg=true)](https://ci.appveyor.com/project/wojzaremba/pymj) -[![Build Status](https://travis-ci.org/openai/mujoco-py.svg?branch=master)](https://travis-ci.org/openai/mujoco-py) +[MuJoCo](http://mujoco.org/) is a physics engine for detailed, efficient rigid body simulations with contacts. `mujoco-py` allows using MuJoCo from Python. -MuJoCo is a physics engine which can do very detailed efficient -simulations with contacts. This library lets you use MuJoCo from -Python. +## Synopsis -Note that MuJoCo tends to change significantly between versions, so -this library is likely to stay pinned to 1.31 for the near future. +### Install MuJoCo -# Installing this library - -You can install this library using: +1. Obtain a 30-day free trial on the [MuJoCo website](https://www.roboti.us/license.html) + or free license if you are a student. + The license key will arrive in an email with your username and password. +2. Download the MuJoCo version 1.50 binaries for + [Linux](https://www.roboti.us/active/mjpro150_linux.zip), + [OSX](https://www.roboti.us/active/mjpro150_osx.zip), or + [Windows](https://www.roboti.us/active/mjpro150_windows.zip). +3. Unzip the downloaded `mjpro150` directory into `~/.mujoco/mjpro150`, + and place your license key (the `mjkey.txt` file from your email) + at `~/.mujoco/mjkey.txt`. +### Install and use `mujoco-py` +To include `mujoco-py` in another package, add it to your requirements like so: +``` +mujoco-py<1.50.2,>=1.50.1 ``` -pip install mujoco-py +To play with it interactively, follow these steps: ``` +$ pip install -U 'mujoco-py<1.50.2,>=1.50.1' +$ python +import mujoco_py +from os.path import dirname +model = mujoco_py.load_model_from_path(dirname(dirname(mujoco_py.__file__)) +"/xmls/claw.xml") +sim = mujoco_py.MjSim(model) -MuJoCo isn't open-source, so you'll also need to set download the -MuJoCo binaries and obtain license key. +print(sim.data.qpos) +# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] -## Obtaining the binaries and license key +sim.step() +print(sim.data.qpos) +# [ 2.09217903e-06 -1.82329050e-12 -1.16711384e-07 -4.69613872e-11 +# -1.43931860e-05 4.73350204e-10 -3.23749942e-05 -1.19854057e-13 +# -2.39251380e-08 -4.46750545e-07 1.78771599e-09 -1.04232280e-08] +``` -1. Obtain a 30-day free trial on the MuJoCo website: - https://www.roboti.us/license.html. The license key will arrive in - an email with your username and password. -2. Download the MuJoCo version 1.31 binaries for - [Linux](https://www.roboti.us/active/mjpro131_linux.zip), - [OSX](https://www.roboti.us/active/mjpro131_osx.zip), or - [Windows](https://www.roboti.us/active/mjpro131_windows.zip). -3. Download your license key (the `mjkey.txt` file from your email) - and unzip the downloaded mjpro bundle. -4. Place these in `~/.mujoco/mjkey.txt` and `~/.mujoco/mjpro131`. You - can alternatively set the following environment variables: +See the [full documentation](https://openai.github.io/pymj/build/html/index.html) for advanced usage. -``` -export MUJOCO_PY_MJKEY_PATH=/path/to/mjkey.txt -export MUJOCO_PY_MJPRO_PATH=/path/to/mjpro131 -``` +## Usage Examples -## Testing +A number of examples demonstrating some advanced features of `mujoco-py` can be found in [`examples/`](/./examples/). These include: +- [`body_interaction.py`](./examples/body_interaction.py): shows interactions between colliding bodies +- [`disco_fetch.py`](./examples/disco_fetch.py): shows how `TextureModder` can be used to randomize object textures +- [`internal_functions.py`](./examples/internal_functions.py): shows how to call raw mujoco functions like `mjv_room2model` +- [`markers_demo.py`](./examples/markers_demo.py): shows how to add visualization-only geoms to the viewer +- [`serialize_model.py`](./examples/serialize_model.py): shows how to save and restore a model +- [`setting_state.py`](./examples/setting_state.py): shows how to reset the simulation to a given state +- [`simpool.py`](./examples/simpool.py): shows how `MjSimPool` can be used to run a number of simulations in parallel +- [`tosser.py`](./examples/tosser.py): shows a simple actuated object sorting robot application -Run: +See the [full documentation](https://openai.github.io/pymj/build/html/index.html) for advanced usage. + +## Development + +To run the provided unit and integrations tests: ``` make test ``` + +## Credits + +`mujoco-py` is maintained by the OpenAI Robotics team. Contributors include: + +- Alex Ray +- Bob McGrew +- Jonas Schneider +- Jonathan Ho +- Peter Welinder +- Wojciech Zaremba diff --git a/tests/__init__.py b/docs/.nojekyll similarity index 100% rename from tests/__init__.py rename to docs/.nojekyll diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..35ea94ba --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,13 @@ +SPHINXOPTS = +SPHINXBUILD = python -msphinx +SPHINXPROJ = mujoco-py +SOURCEDIR = . +BUILDDIR = build + +.PHONY: all clean + +all: + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +clean: + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 00000000..bc0bf788 Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 00000000..44d21e8c Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/internals.doctree b/docs/build/doctrees/internals.doctree new file mode 100644 index 00000000..981a6e78 Binary files /dev/null and b/docs/build/doctrees/internals.doctree differ diff --git a/docs/build/doctrees/reference.doctree b/docs/build/doctrees/reference.doctree new file mode 100644 index 00000000..4abf290e Binary files /dev/null and b/docs/build/doctrees/reference.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 00000000..847bd706 --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 796e1527cf38c16076806cf6da80d531 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 00000000..bb34b876 --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,223 @@ + + + + + +
+ + + + +
+import distutils
+import imp
+import os
+import shutil
+import subprocess
+import sys
+from distutils.core import Extension
+from distutils.dist import Distribution
+from distutils.sysconfig import customize_compiler
+from os.path import abspath, dirname, exists, join, getmtime
+
+import numpy as np
+from Cython.Build import cythonize
+from Cython.Distutils.old_build_ext import old_build_ext as build_ext
+
+from mujoco_py.utils import discover_mujoco
+
+
+def load_cython_ext(mjpro_path):
+ """
+ Loads the cymj Cython extension. This is safe to be called from
+ multiple processes running on the same machine.
+
+ Cython only gives us back the raw path, regardless of whether
+ it found a cached version or actually compiled. Since we do
+ non-idempotent postprocessing of the DLL, be extra careful
+ to only do that once and then atomically move to the final
+ location.
+ """
+ if ('glfw' in sys.modules and
+ 'mujoco' in abspath(sys.modules["glfw"].__file__)):
+ print('''
+WARNING: Existing glfw python module detected!
+
+MuJoCo comes with its own version of GLFW, so it's preferable to use that one.
+
+The easy solution is to `import mujoco_py` _before_ `import glfw`.
+''')
+
+ if sys.platform == 'darwin':
+ Builder = MacExtensionBuilder
+ elif sys.platform == 'linux':
+ if exists('/usr/local/nvidia/lib64'):
+ Builder = LinuxGPUExtensionBuilder
+ else:
+ Builder = LinuxCPUExtensionBuilder
+ elif sys.platform.startswith("win"):
+ Builder = WindowsExtensionBuilder
+ else:
+ raise RuntimeError("Unsupported platform %s" % sys.platform)
+
+ builder = Builder(mjpro_path)
+
+ cext_so_path = builder.build()
+ mod = imp.load_dynamic("cymj", cext_so_path)
+ return mod
+
+
+class custom_build_ext(build_ext):
+ """
+ Custom build_ext to suppress the "-Wstrict-prototypes" warning.
+ It arises from the fact that we're using C++. This seems to be
+ the cleanest way to get rid of the extra flag.
+
+ See http://stackoverflow.com/a/36293331/248400
+ """
+
+ def build_extensions(self):
+ customize_compiler(self.compiler)
+
+ try:
+ self.compiler.compiler_so.remove("-Wstrict-prototypes")
+ except (AttributeError, ValueError):
+ pass
+ build_ext.build_extensions(self)
+
+
+def fix_shared_library(so_file, name, library_path):
+ ldd_output = subprocess.check_output(
+ ['ldd', so_file]).decode('utf-8')
+
+ if name in ldd_output:
+ subprocess.check_call(['patchelf',
+ '--remove-needed',
+ name,
+ so_file])
+ subprocess.check_call(
+ ['patchelf', '--add-needed',
+ library_path,
+ so_file])
+
+
+class MujocoExtensionBuilder():
+
+ CYMJ_DIR_PATH = abspath(dirname(__file__))
+
+ def __init__(self, mjpro_path):
+ self.mjpro_path = mjpro_path
+ self.extension = Extension(
+ 'mujoco_py.cymj',
+ sources=[join(self.CYMJ_DIR_PATH, "cymj.pyx")],
+ include_dirs=[
+ self.CYMJ_DIR_PATH,
+ join(mjpro_path, 'include'),
+ np.get_include(),
+ ],
+ libraries=['mujoco150'],
+ library_dirs=[join(mjpro_path, 'bin')],
+ extra_compile_args=[
+ '-fopenmp', # needed for OpenMP
+ '-w', # suppress numpy compilation warnings
+ ],
+ extra_link_args=['-fopenmp'],
+ language='c')
+
+ def build(self):
+ dist = Distribution({
+ "script_name": None,
+ "script_args": ["build_ext"]
+ })
+ dist.ext_modules = cythonize([self.extension])
+ dist.include_dirs = []
+ dist.cmdclass = {'build_ext': custom_build_ext}
+ build = dist.get_command_obj('build')
+ # following the convention of cython's pyxbuild and naming
+ # base directory "_pyxbld"
+ build.build_base = join(self.CYMJ_DIR_PATH, 'generated',
+ '_pyxbld_%s' % self.__class__.__name__)
+ dist.parse_command_line()
+ obj_build_ext = dist.get_command_obj("build_ext")
+ dist.run_commands()
+ so_file_path, = obj_build_ext.get_outputs()
+ return so_file_path
+
+
+class WindowsExtensionBuilder(MujocoExtensionBuilder):
+
+ def __init__(self, mjpro_path):
+ super().__init__(mjpro_path)
+ os.environ["PATH"] += ";" + join(mjpro_path, "bin")
+ self.extension.sources.append(self.CYMJ_DIR_PATH + "/gl/dummyshim.c")
+
+
+class LinuxCPUExtensionBuilder(MujocoExtensionBuilder):
+
+ def __init__(self, mjpro_path):
+ super().__init__(mjpro_path)
+
+ self.extension.sources.append(
+ join(self.CYMJ_DIR_PATH, "gl", "osmesashim.c"))
+ self.extension.libraries.extend(['glewosmesa', 'OSMesa'])
+ self.extension.runtime_library_dirs = [join(mjpro_path, 'bin')]
+
+
+class LinuxGPUExtensionBuilder(MujocoExtensionBuilder):
+
+ def __init__(self, mjpro_path):
+ super().__init__(mjpro_path)
+
+ self.extension.sources.append(self.CYMJ_DIR_PATH + "/gl/eglshim.c")
+ self.extension.include_dirs.append(self.CYMJ_DIR_PATH + '/vendor/egl')
+ self.extension.libraries.extend(['glewegl'])
+ self.extension.runtime_library_dirs = [join(mjpro_path, 'bin')]
+
+ def build(self):
+ so_file_path = super().build()
+ nvidia_lib_dir = '/usr/local/nvidia/lib64/'
+ fix_shared_library(so_file_path, 'libOpenGL.so',
+ join(nvidia_lib_dir, 'libOpenGL.so.0'))
+ fix_shared_library(so_file_path, 'libEGL.so',
+ join(nvidia_lib_dir, 'libEGL.so.1'))
+ return so_file_path
+
+
+class MacExtensionBuilder(MujocoExtensionBuilder):
+
+ def __init__(self, mjpro_path):
+ super().__init__(mjpro_path)
+
+ self.extension.sources.append(self.CYMJ_DIR_PATH + "/gl/dummyshim.c")
+ self.extension.libraries.extend(['glfw.3'])
+ self.extension.define_macros = [('ONMAC', None)]
+ self.extension.runtime_library_dirs = [join(mjpro_path, 'bin')]
+
+ def build(self):
+ # Prefer GCC 6 for now since GCC 7 may behave differently.
+ c_compilers = ['/usr/local/bin/gcc-6', '/usr/local/bin/gcc-7']
+ available_c_compiler = None
+ for c_compiler in c_compilers:
+ if distutils.spawn.find_executable(c_compiler) is not None:
+ available_c_compiler = c_compiler
+ break
+ if available_c_compiler is None:
+ raise RuntimeError(
+ 'Could not find GCC 6 or GCC 7 executable.\n\n'
+ 'HINT: On OS X, install GCC 6 with '
+ '`brew install gcc --without-multilib`.')
+ os.environ['CC'] = available_c_compiler
+
+ so_file_path = super().build()
+ del os.environ['CC']
+ return self.manually_link_libraries(so_file_path)
+
+ def manually_link_libraries(self, raw_cext_dll_path):
+ root, ext = os.path.splitext(raw_cext_dll_path)
+ final_cext_dll_path = root + '_final' + ext
+
+ # If someone else already built the final DLL, don't bother
+ # recreating it here, even though this should still be idempotent.
+ if (exists(final_cext_dll_path) and
+ getmtime(final_cext_dll_path) >= getmtime(raw_cext_dll_path)):
+ return final_cext_dll_path
+
+ tmp_final_cext_dll_path = final_cext_dll_path + '~'
+ shutil.copyfile(raw_cext_dll_path, tmp_final_cext_dll_path)
+
+ mj_bin_path = join(self.mjpro_path, 'bin')
+
+ # Fix the rpath of the generated library -- i lost the Stackoverflow
+ # reference here
+ from_mujoco_path = '@executable_path/libmujoco150.dylib'
+ to_mujoco_path = '%s/libmujoco150.dylib' % mj_bin_path
+ subprocess.check_call(['install_name_tool',
+ '-change',
+ from_mujoco_path,
+ to_mujoco_path,
+ tmp_final_cext_dll_path])
+
+ from_glfw_path = 'libglfw.3.dylib'
+ to_glfw_path = os.path.join(mj_bin_path, 'libglfw.3.dylib')
+ subprocess.check_call(['install_name_tool',
+ '-change',
+ from_glfw_path,
+ to_glfw_path,
+ tmp_final_cext_dll_path])
+
+ os.rename(tmp_final_cext_dll_path, final_cext_dll_path)
+ return final_cext_dll_path
+
+
+class MujocoException(Exception):
+ pass
+
+
+def user_warning_raise_exception(warn_bytes):
+ '''
+ User-defined warning callback, which is called by mujoco on warnings.
+ Here we have two primary jobs:
+ - Detect known warnings and suggest fixes (with code)
+ - Decide whether to raise an Exception and raise if needed
+ More cases should be added as we find new failures.
+ '''
+ # TODO: look through test output to see MuJoCo warnings to catch
+ # and recommend. Also fix those tests
+ warn = warn_bytes.decode() # Convert bytes to string
+ if 'Pre-allocated constraint buffer is full' in warn:
+ raise MujocoException(warn + 'Increase njmax in mujoco XML')
+ if 'Pre-allocated contact buffer is full' in warn:
+ raise MujocoException(warn + 'Increase njconmax in mujoco XML')
+ raise MujocoException('Got MuJoCo Warning: {}'.format(warn))
+
+
+def user_warning_ignore_exception(warn_bytes):
+ pass
+
+
+[docs]class ignore_mujoco_warnings:
+ """
+ Class to turn off mujoco warning exceptions within a scope. Useful for
+ large, vectorized rollouts.
+ """
+
+ def __enter__(self):
+ self.prev_user_warning = cymj.get_warning_callback()
+ cymj.set_warning_callback(user_warning_ignore_exception)
+ return self
+
+ def __exit__(self, type, value, traceback):
+ cymj.set_warning_callback(self.prev_user_warning)
+
+
+mjpro_path, key_path = discover_mujoco()
+cymj = load_cython_ext(mjpro_path)
+
+
+# Trick to expose all mj* functions from mujoco in mujoco_py.*
+class dict2(object):
+ pass
+
+
+functions = dict2()
+for func_name in dir(cymj):
+ if func_name.startswith("_mj"):
+ setattr(functions, func_name[1:], getattr(cymj, func_name))
+
+functions.mj_activate(key_path)
+
+# Set user-defined callbacks that raise assertion with message
+cymj.set_warning_callback(user_warning_raise_exception)
+
+from threading import Lock
+import glfw
+from mujoco_py.builder import cymj
+from mujoco_py.generated import const
+import time
+import copy
+from multiprocessing import Process
+from mujoco_py.utils import rec_copy, rec_assign
+import imageio
+
+
+[docs]class MjViewerBasic(cymj.MjRenderContextWindow):
+ """
+ A simple display GUI showing the scene of an :class:`.MjSim` with a mouse-movable camera.
+
+ :class:`.MjViewer` extends this class to provide more sophisticated playback and interaction controls.
+
+ Parameters
+ ----------
+ sim : :class:`.MjSim`
+ The simulator to display.
+ """
+
+ def __init__(self, sim):
+ super().__init__(sim)
+
+ self._gui_lock = Lock()
+ self._button_left_pressed = False
+ self._button_right_pressed = False
+ self._last_mouse_x = 0
+ self._last_mouse_y = 0
+
+ framebuffer_width, _ = glfw.get_framebuffer_size(self.window)
+ window_width, _ = glfw.get_window_size(self.window)
+ self._scale = framebuffer_width * 1.0 / window_width
+
+ glfw.set_cursor_pos_callback(self.window, self._cursor_pos_callback)
+ glfw.set_mouse_button_callback(
+ self.window, self._mouse_button_callback)
+ glfw.set_scroll_callback(self.window, self._scroll_callback)
+ glfw.set_key_callback(self.window, self.key_callback)
+
+ def render(self):
+ """
+ Render the current simulation state to the screen or off-screen buffer.
+ """
+ if self.window is None:
+ return
+ elif glfw.window_should_close(self.window):
+ exit(0)
+
+ with self._gui_lock:
+ super().render()
+
+ glfw.poll_events()
+
+ def key_callback(self, window, key, scancode, action, mods):
+ if action == glfw.RELEASE and key == glfw.KEY_ESCAPE:
+ print("Pressed ESC")
+ print("Quitting.")
+ exit(0)
+
+ def _cursor_pos_callback(self, window, xpos, ypos):
+ if not (self._button_left_pressed or self._button_right_pressed):
+ return
+
+ # Determine whether to move, zoom or rotate view
+ mod_shift = (
+ glfw.get_key(window, glfw.KEY_LEFT_SHIFT) == glfw.PRESS or
+ glfw.get_key(window, glfw.KEY_RIGHT_SHIFT) == glfw.PRESS)
+ if self._button_right_pressed:
+ action = const.MOUSE_MOVE_H if mod_shift else const.MOUSE_MOVE_V
+ elif self._button_left_pressed:
+ action = const.MOUSE_ROTATE_H if mod_shift else const.MOUSE_ROTATE_V
+ else:
+ action = const.MOUSE_ZOOM
+
+ # Determine
+ dx = int(self._scale * xpos) - self._last_mouse_x
+ dy = int(self._scale * ypos) - self._last_mouse_y
+ width, height = glfw.get_framebuffer_size(window)
+
+ with self._gui_lock:
+ self.move_camera(action, dx / height, dy / height)
+
+ self._last_mouse_x = int(self._scale * xpos)
+ self._last_mouse_y = int(self._scale * ypos)
+
+ def _mouse_button_callback(self, window, button, act, mods):
+ self._button_left_pressed = (
+ glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_LEFT) == glfw.PRESS)
+ self._button_right_pressed = (
+ glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_RIGHT) == glfw.PRESS)
+
+ x, y = glfw.get_cursor_pos(window)
+ self._last_mouse_x = int(self._scale * x)
+ self._last_mouse_y = int(self._scale * y)
+
+ def _scroll_callback(self, window, x_offset, y_offset):
+ with self._gui_lock:
+ self.move_camera(const.MOUSE_ZOOM, 0, -0.05 * y_offset)
+
+
+[docs]class MjViewer(MjViewerBasic):
+ """
+ Extends :class:`.MjViewerBasic` to add video recording, interactive time and interaction controls.
+
+ The key bindings are as follows:
+
+ - TAB: Switch between MuJoCo cameras.
+ - H: Toggle hiding all GUI components.
+ - SPACE: Pause/unpause the simulation.
+ - RIGHT: Advance simulation by one step.
+ - V: Start/stop video recording.
+ - T: Capture screenshot.
+ - I: Drop into ``ipdb`` debugger.
+ - S/F: Decrease/Increase simulation playback speed.
+ - C: Toggle visualization of contact forces (off by default).
+ - D: Enable/disable frame skipping when rendering lags behind real time.
+ - R: Toggle transparency of geoms.
+ - M: Toggle display of mocap bodies.
+
+ Parameters
+ ----------
+ sim : :class:`.MjSim`
+ The simulator to display.
+ """
+
+ def __init__(self, sim):
+ super().__init__(sim)
+
+ self._ncam = sim.model.ncam
+ self._paused = False # is viewer paused.
+ # should we advance viewer just by one step.
+ self._advance_by_one_step = False
+
+ # Vars for recording video
+ self._record_video = False
+ self._video_frames = []
+ self._video_idx = 0
+ self._video_path = "/tmp/video_%07d.mp4"
+
+ # vars for capturing screen
+ self._image_idx = 0
+ self._image_path = "/tmp/frame_%07d.png"
+
+ # run_speed = x1, means running real time, x2 means fast-forward times
+ # two.
+ self._run_speed = 1.0
+ self._loop_count = 0
+ self._render_every_frame = False
+
+ self._show_mocap = True # Show / hide mocap bodies.
+ self._transparent = False # Make everything transparent.
+
+ # this variable is estamated as a running average.
+ self._time_per_render = 1 / 60.0
+ self._hide_overlay = False # hide the entire overlay.
+ self._user_overlay = {}
+
+ def render(self):
+ def render_inner_loop(self):
+ render_start = time.time()
+
+ self._overlay.clear()
+ if not self._hide_overlay:
+ for k, v in self._user_overlay.items():
+ self._overlay[k] = v
+ self._create_full_overlay()
+
+ super().render()
+ if self._record_video:
+ frame = self._read_pixels_as_in_window()
+ self._video_frames.append(frame)
+ else:
+ self._time_per_render = 0.9 * self._time_per_render + \
+ 0.1 * (time.time() - render_start)
+
+ self._user_overlay = copy.deepcopy(self._overlay)
+ # Render the same frame if paused.
+ if self._paused:
+ while self._paused:
+ render_inner_loop(self)
+ if self._advance_by_one_step:
+ self._advance_by_one_step = False
+ break
+ else:
+ # inner_loop runs "_loop_count" times in expectation (where "_loop_count" is a float).
+ # Therefore, frames are displayed in the real-time.
+ self._loop_count += self.sim.model.opt.timestep * self.sim.nsubsteps / \
+ (self._time_per_render * self._run_speed)
+ if self._render_every_frame:
+ self._loop_count = 1
+ while self._loop_count > 0:
+ render_inner_loop(self)
+ self._loop_count -= 1
+ # Markers and overlay are regenerated in every pass.
+ self._markers[:] = []
+ self._overlay.clear()
+
+ def _read_pixels_as_in_window(self):
+ # Reads pixels with markers and overlay from the same camera as screen.
+ resolution = glfw.get_framebuffer_size(
+ self.sim._render_context_window.window)
+ if self.sim._render_context_offscreen is None:
+ self.sim.render(*resolution)
+ offscreen_ctx = self.sim._render_context_offscreen
+ window_ctx = self.sim._render_context_window
+ # Save markers and overlay from offscreen.
+ saved = [copy.deepcopy(offscreen_ctx._markers),
+ copy.deepcopy(offscreen_ctx._overlay),
+ rec_copy(offscreen_ctx.cam)]
+ # Copy markers and overlay from window.
+ offscreen_ctx._markers[:] = window_ctx._markers[:]
+ offscreen_ctx._overlay.clear()
+ offscreen_ctx._overlay.update(window_ctx._overlay)
+ rec_assign(offscreen_ctx.cam, rec_copy(window_ctx.cam))
+
+ img = self.sim.render(*resolution)
+ # Restore markers and overlay to offscreen.
+ offscreen_ctx._markers[:] = saved[0][:]
+ offscreen_ctx._overlay.clear()
+ offscreen_ctx._overlay.update(saved[1])
+ rec_assign(offscreen_ctx.cam, saved[2])
+ return img
+
+ def _create_full_overlay(self):
+ if self._render_every_frame:
+ self.add_overlay(const.GRID_TOPLEFT, "", "")
+ else:
+ self.add_overlay(const.GRID_TOPLEFT, "Run speed = %.3f x real time" %
+ self._run_speed, "[S]lower, [F]aster")
+ self.add_overlay(
+ const.GRID_TOPLEFT, "Ren[d]er every frame", "Off" if self._render_every_frame else "On")
+ self.add_overlay(const.GRID_TOPLEFT, "Switch camera (#cams = %d)" % (self._ncam + 1),
+ "[Tab] (camera ID = %d)" % self.cam.fixedcamid)
+ self.add_overlay(const.GRID_TOPLEFT, "[C]ontact forces", "Off" if self.vopt.flags[
+ 10] == 1 else "On")
+ self.add_overlay(
+ const.GRID_TOPLEFT, "Referenc[e] frames", "Off" if self.vopt.frame == 1 else "On")
+ self.add_overlay(const.GRID_TOPLEFT,
+ "T[r]ansparent", "On" if self._transparent else "Off")
+ self.add_overlay(
+ const.GRID_TOPLEFT, "Display [M]ocap bodies", "On" if self._show_mocap else "Off")
+ if self._paused is not None:
+ if not self._paused:
+ self.add_overlay(const.GRID_TOPLEFT, "Stop", "[Space]")
+ else:
+ self.add_overlay(const.GRID_TOPLEFT, "Start", "[Space]")
+ self.add_overlay(const.GRID_TOPLEFT,
+ "Advance simulation by one step", "[right arrow]")
+ self.add_overlay(const.GRID_TOPLEFT, "[H]ide Menu", "")
+ if self._record_video:
+ ndots = int(7 * (time.time() % 1))
+ dots = ("." * ndots) + (" " * (6 - ndots))
+ self.add_overlay(const.GRID_TOPLEFT,
+ "Record [V]ideo (On) " + dots, "")
+ else:
+ self.add_overlay(const.GRID_TOPLEFT, "Record [V]ideo (Off) ", "")
+ if self._video_idx > 0:
+ fname = self._video_path % (self._video_idx - 1)
+ self.add_overlay(const.GRID_TOPLEFT, " saved as %s" % fname, "")
+
+ self.add_overlay(const.GRID_TOPLEFT, "Cap[t]ure frame", "")
+ if self._image_idx > 0:
+ fname = self._image_path % (self._image_idx - 1)
+ self.add_overlay(const.GRID_TOPLEFT, " saved as %s" % fname, "")
+ self.add_overlay(const.GRID_TOPLEFT, "Start [i]pdb", "")
+ if self._record_video:
+ extra = " (while video is not recorded)"
+ else:
+ extra = ""
+ self.add_overlay(const.GRID_BOTTOMLEFT, "FPS", "%d%s" %
+ (1 / self._time_per_render, extra))
+ self.add_overlay(const.GRID_BOTTOMLEFT, "Solver iterations", str(
+ self.sim.data.solver_iter + 1))
+
+ def key_callback(self, window, key, scancode, action, mods):
+ if action != glfw.RELEASE:
+ return
+ elif key == glfw.KEY_TAB: # Switches cameras.
+ self.cam.fixedcamid += 1
+ self.cam.type = const.CAMERA_FIXED
+ if self.cam.fixedcamid >= self._ncam:
+ self.cam.fixedcamid = -1
+ self.cam.type = const.CAMERA_FREE
+ elif key == glfw.KEY_H: # hides all overlay.
+ self._hide_overlay = not self._hide_overlay
+ elif key == glfw.KEY_SPACE and self._paused is not None: # stops simulation.
+ self._paused = not self._paused
+ # Advances simulation by one step.
+ elif key == glfw.KEY_RIGHT and self._paused is not None:
+ self._advance_by_one_step = True
+ self._paused = True
+ elif key == glfw.KEY_V or \
+ (key == glfw.KEY_ESCAPE and self._record_video): # Records video. Trigers with V or if in progress by ESC.
+ self._record_video = not self._record_video
+ if not self._record_video and len(self._video_frames) > 0:
+ # This include captures console, if in the top declaration.
+ frames = [f for f in self._video_frames]
+ fps = (1 / self._time_per_render)
+ process = Process(target=save_video,
+ args=(frames, self._video_path % self._video_idx, fps))
+ process.start()
+ self._video_frames = []
+ self._video_idx += 1
+ elif key == glfw.KEY_T: # capture screenshot
+ img = self._read_pixels_as_in_window()
+ imageio.imwrite(self._image_path % self._image_idx, img)
+ self._image_idx += 1
+ elif key == glfw.KEY_I: # drops in debugger.
+ print('You can access the simulator by self.sim')
+ import ipdb
+ ipdb.set_trace()
+ elif key == glfw.KEY_S: # Slows down simulation.
+ self._run_speed /= 2.0
+ elif key == glfw.KEY_F: # Speeds up simulation.
+ self._run_speed *= 2.0
+ elif key == glfw.KEY_C: # Displays contact forces.
+ vopt = self.vopt
+ vopt.flags[10] = vopt.flags[11] = not vopt.flags[10]
+ elif key == glfw.KEY_D: # turn off / turn on rendering every frame.
+ self._render_every_frame = not self._render_every_frame
+ elif key == glfw.KEY_E:
+ vopt = self.vopt
+ vopt.frame = 1 - vopt.frame
+ elif key == glfw.KEY_R: # makes everything little bit transparent.
+ self._transparent = not self._transparent
+ if self._transparent:
+ self.sim.model.geom_rgba[:, 3] /= 5.0
+ else:
+ self.sim.model.geom_rgba[:, 3] *= 5.0
+ elif key == glfw.KEY_M: # Shows / hides mocap bodies
+ self._show_mocap = not self._show_mocap
+ for body_idx1, val in enumerate(self.sim.model.body_mocapid):
+ if val != -1:
+ for geom_idx, body_idx2 in enumerate(self.sim.model.geom_bodyid):
+ if body_idx1 == body_idx2:
+ if not self._show_mocap:
+ # Store transparency for later to show it.
+ self.sim.extras[
+ geom_idx] = self.sim.model.geom_rgba[geom_idx, 3]
+ self.sim.model.geom_rgba[geom_idx, 3] = 0
+ else:
+ self.sim.model.geom_rgba[
+ geom_idx, 3] = self.sim.extras[geom_idx]
+ super().key_callback(window, key, scancode, action, mods)
+
+# Separate Process to save video. This way visualization is
+# less slowed down.
+
+
+def save_video(frames, filename, fps):
+ writer = imageio.get_writer(filename, fps=fps)
+ for f in frames:
+ writer.append_data(f)
+ writer.close()
+
mujoco_py.
PyMjData
¶Attributes
+ +.. attribute:: act +.. attribute:: act_dot +.. attribute:: active_contacts_efc_pos +.. attribute:: actuator_force +.. attribute:: actuator_length +.. attribute:: actuator_moment +.. attribute:: actuator_velocity +.. attribute:: body_jacp +.. attribute:: body_jacr +.. attribute:: body_xmat +.. attribute:: body_xpos +.. attribute:: body_xquat +.. attribute:: body_xvelp +.. attribute:: body_xvelr +.. attribute:: cacc +.. attribute:: cam_xmat +.. attribute:: cam_xpos +.. attribute:: cdof +.. attribute:: cdof_dot +.. attribute:: cfrc_ext +.. attribute:: cfrc_int +.. attribute:: cinert +.. attribute:: contact +.. attribute:: crb +.. attribute:: ctrl +.. attribute:: cvel +.. attribute:: efc_AR +.. attribute:: efc_AR_colind +.. attribute:: efc_AR_rowadr +.. attribute:: efc_AR_rownnz +.. attribute:: efc_D +.. attribute:: efc_J +.. attribute:: efc_JT +.. attribute:: efc_JT_colind +.. attribute:: efc_JT_rowadr +.. attribute:: efc_JT_rownnz +.. attribute:: efc_J_colind +.. attribute:: efc_J_rowadr +.. attribute:: efc_J_rownnz +.. attribute:: efc_R +.. attribute:: efc_aref +.. attribute:: efc_b +.. attribute:: efc_diagApprox +.. attribute:: efc_force +.. attribute:: efc_frictionloss +.. attribute:: efc_id +.. attribute:: efc_margin +.. attribute:: efc_solimp +.. attribute:: efc_solref +.. attribute:: efc_state +.. attribute:: efc_type +.. attribute:: efc_vel +.. attribute:: energy +.. attribute:: geom_jacp +.. attribute:: geom_jacr +.. attribute:: geom_xmat +.. attribute:: geom_xpos +.. attribute:: geom_xvelp +.. attribute:: geom_xvelr +.. attribute:: light_xdir +.. attribute:: light_xpos +.. attribute:: maxuse_con +.. attribute:: maxuse_efc +.. attribute:: maxuse_stack +.. attribute:: mocap_pos +.. attribute:: mocap_quat +.. attribute:: nbuffer +.. attribute:: ncon +.. attribute:: ne +.. attribute:: nefc +.. attribute:: nf +.. attribute:: nstack +.. attribute:: pstack +.. attribute:: qLD +.. attribute:: qLDiagInv +.. attribute:: qLDiagSqrtInv +.. attribute:: qM +.. attribute:: qacc +.. attribute:: qacc_unc +.. attribute:: qacc_warmstart +.. attribute:: qfrc_actuator +.. attribute:: qfrc_applied +.. attribute:: qfrc_bias +.. attribute:: qfrc_constraint +.. attribute:: qfrc_inverse +.. attribute:: qfrc_passive +.. attribute:: qfrc_unc +.. attribute:: qpos +.. attribute:: qvel +.. attribute:: sensordata +.. attribute:: set_joint_qpos +.. attribute:: set_joint_qvel +.. attribute:: set_mocap_pos +.. attribute:: set_mocap_quat +.. attribute:: site_jacp +.. attribute:: site_jacr +.. attribute:: site_xmat +.. attribute:: site_xpos +.. attribute:: site_xvelp +.. attribute:: site_xvelr +.. attribute:: solver +.. attribute:: solver_fwdinv +.. attribute:: solver_iter +.. attribute:: solver_nnz +.. attribute:: subtree_angmom +.. attribute:: subtree_com +.. attribute:: subtree_linvel +.. attribute:: ten_length +.. attribute:: ten_moment +.. attribute:: ten_velocity +.. attribute:: ten_wrapadr +.. attribute:: ten_wrapnum +.. attribute:: time +.. attribute:: timer +.. attribute:: userdata +.. attribute:: warning +.. attribute:: wrap_obj +.. attribute:: wrap_xpos +.. attribute:: xanchor +.. attribute:: xaxis +.. attribute:: xfrc_applied +.. attribute:: ximat +.. attribute:: xipos + +.. raw:: html + +Methods
+ + +.. method:: get_body_jacp(name) + + Get the entry in ``jacp`` corresponding to the body with the given `name` + +.. method:: get_body_jacr(name) + + Get the entry in ``jacr`` corresponding to the body with the given `name` + +.. method:: get_body_ximat(name) + + Get the entry in ``ximat`` corresponding to the body with the given `name` + +.. method:: get_body_xipos(name) + + Get the entry in ``xipos`` corresponding to the body with the given `name` + +.. method:: get_body_xmat(name) + + Get the entry in ``xmat`` corresponding to the body with the given `name` + +.. method:: get_body_xpos(name) + + Get the entry in ``xpos`` corresponding to the body with the given `name` + +.. method:: get_body_xquat(name) + + Get the entry in ``xquat`` corresponding to the body with the given `name` + +.. method:: get_body_xvelp(name) + + Get the entry in ``xvelp`` corresponding to the body with the given `name` + +.. method:: get_body_xvelr(name) + + Get the entry in ``xvelr`` corresponding to the body with the given `name` + +.. method:: get_cam_xmat(name) + + Get the entry in ``xmat`` corresponding to the cam with the given `name` + +.. method:: get_cam_xpos(name) + + Get the entry in ``xpos`` corresponding to the cam with the given `name` + +.. method:: get_camera_xmat(name) + + Get the entry in ``xmat`` corresponding to the camera with the given `name` + +.. method:: get_camera_xpos(name) + + Get the entry in ``xpos`` corresponding to the camera with the given `name` + +.. method:: get_geom_jacp(name) + + Get the entry in ``jacp`` corresponding to the geom with the given `name` + +.. method:: get_geom_jacr(name) + + Get the entry in ``jacr`` corresponding to the geom with the given `name` + +.. method:: get_geom_xmat(name) + + Get the entry in ``xmat`` corresponding to the geom with the given `name` + +.. method:: get_geom_xpos(name) + + Get the entry in ``xpos`` corresponding to the geom with the given `name` + +.. method:: get_geom_xvelp(name) + + Get the entry in ``xvelp`` corresponding to the geom with the given `name` + +.. method:: get_geom_xvelr(name) + + Get the entry in ``xvelr`` corresponding to the geom with the given `name` + +.. method:: get_joint_qpos(name) + + Get the entry in ``qpos`` corresponding to the joint with the given `name` + +.. method:: get_joint_qvel(name) + + Get the entry in ``qvel`` corresponding to the joint with the given `name` + +.. method:: get_joint_xanchor(name) + + Get the entry in ``xanchor`` corresponding to the joint with the given `name` + +.. method:: get_joint_xaxis(name) + + Get the entry in ``xaxis`` corresponding to the joint with the given `name` + +.. method:: get_light_xdir(name) + + Get the entry in ``xdir`` corresponding to the light with the given `name` + +.. method:: get_light_xpos(name) + + Get the entry in ``xpos`` corresponding to the light with the given `name` + +.. method:: get_mocap_pos(name) + + Get the entry in ``pos`` corresponding to the mocap with the given `name` + +.. method:: get_mocap_quat(name) + + Get the entry in ``quat`` corresponding to the mocap with the given `name` + +.. method:: get_site_jacp(name) + + Get the entry in ``jacp`` corresponding to the site with the given `name` + +.. method:: get_site_jacr(name) + + Get the entry in ``jacr`` corresponding to the site with the given `name` + +.. method:: get_site_xmat(name) + + Get the entry in ``xmat`` corresponding to the site with the given `name` + +.. method:: get_site_xpos(name) + + Get the entry in ``xpos`` corresponding to the site with the given `name` + +.. method:: get_site_xvelp(name) + + Get the entry in ``xvelp`` corresponding to the site with the given `name` + +.. method:: get_site_xvelr(name) + + Get the entry in ``xvelr`` corresponding to the site with the given `name` + +.. raw:: html + +