├── .dockerignore ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE.md ├── MANIFEST.in ├── Makefile ├── README.md ├── docs ├── .nojekyll ├── Makefile ├── _static │ └── .gitkeep ├── _templates │ └── .gitkeep ├── build │ ├── doctrees │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── internals.doctree │ │ └── reference.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _modules │ │ ├── index.html │ │ └── mujoco_py │ │ │ ├── builder.html │ │ │ └── mjviewer.html │ │ ├── _sources │ │ ├── index.rst.txt │ │ ├── internals.rst.txt │ │ └── reference.rst.txt │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── css │ │ │ ├── badge_only.css │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── fonts │ │ │ ├── Inconsolata-Bold.ttf │ │ │ ├── Inconsolata-Regular.ttf │ │ │ ├── Lato-Bold.ttf │ │ │ ├── Lato-Regular.ttf │ │ │ ├── RobotoSlab-Bold.ttf │ │ │ ├── RobotoSlab-Regular.ttf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── jquery-3.1.0.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── modernizr.min.js │ │ │ └── theme.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.3.1.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── genindex.html │ │ ├── index.html │ │ ├── internals.html │ │ ├── objects.inv │ │ ├── reference.html │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── index.html ├── index.rst ├── internals.rst └── reference.rst ├── examples ├── body_interaction.py ├── disco_fetch.py ├── internal_functions.py ├── markers_demo.py ├── mjvive.py ├── multigpu_rendering.py ├── render_callback.py ├── serialize_model.py ├── setting_state.py ├── substep_callback.py └── tosser.py ├── mujoco_py ├── __init__.py ├── builder.py ├── cymj.pyx ├── generated │ ├── README.md │ ├── __init__.py │ ├── const.py │ └── wrappers.pxi ├── gl │ ├── __init__.py │ ├── dummyshim.c │ ├── egl.h │ ├── eglext.h │ ├── eglplatform.h │ ├── eglshim.c │ ├── glshim.h │ ├── khrplatform.h │ └── osmesashim.c ├── mjbatchrenderer.pyx ├── mjpid.pyx ├── mjrendercontext.pyx ├── mjrenderpool.py ├── mjsim.pyx ├── mjsimstate.pyx ├── mjviewer.py ├── modder.py ├── opengl_context.pyx ├── pxd │ ├── __init__.py │ ├── mjdata.pxd │ ├── mjmodel.pxd │ ├── mjrender.pxd │ ├── mjui.pxd │ ├── mjvisualize.pxd │ └── mujoco.pxd ├── test_imgs │ ├── test_glfw_context.png │ ├── test_materials.premod.png │ ├── test_materials.props.png │ ├── test_materials.rand_all.png │ ├── test_multiple_sims.loop0_0.png │ ├── test_multiple_sims.loop0_1.png │ ├── test_multiple_sims.loop1_0.png │ ├── test_multiple_sims.loop1_1.png │ ├── test_multiple_sims.loop2_0.png │ ├── test_multiple_sims.loop2_1.png │ ├── test_render_pool.mp_test_cameras.0.png │ ├── test_render_pool.mp_test_cameras.1.png │ ├── test_render_pool.mp_test_rendering.0.png │ ├── test_render_pool.mp_test_states.1.png │ ├── test_render_pool.mp_test_states.2.png │ ├── test_render_pool.mp_test_states.3.png │ ├── test_render_pool.mp_test_states.4.png │ ├── test_render_pool.mp_test_states.5.png │ ├── test_rendering.camera1.narrow.png │ ├── test_rendering.camera1.png │ ├── test_rendering.freecam.depth-darwin.png │ ├── test_rendering.freecam.depth.png │ ├── test_rendering.freecam.png │ ├── test_rendering_markers.camera1.png │ ├── test_resetting.loop0_0.png │ ├── test_resetting.loop0_1.png │ ├── test_resetting.loop1_0.png │ ├── test_resetting.loop1_1.png │ ├── test_resetting.loop2_0.png │ ├── test_resetting.loop2_1.png │ ├── test_textures.premod.png │ ├── test_textures.rand_all.png │ ├── test_textures.rand_specific.png │ ├── test_textures.rand_texrepeat.png │ ├── test_textures.rgb.png │ └── test_textures.variety.png ├── tests │ ├── __init__.py │ ├── include.xml │ ├── test.xml │ ├── test_composite.py │ ├── test_cymj.py │ ├── test_examples.py │ ├── test_gen_wrappers.py │ ├── test_modder.py │ ├── test_opengl_context.py │ ├── test_pid.py │ ├── test_render_pool.py │ ├── test_substep.py │ ├── test_vfs.py │ ├── test_viewer.py │ └── utils.py ├── utils.py └── version.py ├── pyproject.toml ├── requirements.dev.txt ├── requirements.txt ├── scripts └── gen_wrappers.py ├── setup.py ├── vendor ├── 10_nvidia.json ├── Xdummy └── Xdummy-entrypoint └── xmls ├── claw.xml ├── door.xml ├── fetch ├── base_link_collision.stl ├── bellows_link_collision.stl ├── elbow_flex_link_collision.stl ├── estop_link.stl ├── forearm_roll_link_collision.stl ├── gripper_link.stl ├── head_pan_link_collision.stl ├── head_tilt_link_collision.stl ├── l_wheel_link_collision.stl ├── laser_link.stl ├── main.xml ├── r_wheel_link_collision.stl ├── shoulder_lift_link_collision.stl ├── shoulder_pan_link_collision.stl ├── torso_fixed_link.stl ├── torso_lift_link_collision.stl ├── upperarm_roll_link_collision.stl ├── wrist_flex_link_collision.stl └── wrist_roll_link_collision.stl ├── juggler.xml ├── key.xml ├── shelf.xml ├── slider.xml └── tosser.xml /.dockerignore: -------------------------------------------------------------------------------- 1 | generated_cymj* 2 | _pyxbld* 3 | dist 4 | cache 5 | .idea/* 6 | *~ 7 | .*~ 8 | *#*# 9 | *.o 10 | *.dat 11 | *.prof 12 | *.lprof 13 | *.local 14 | .realsync 15 | .DS_Store 16 | **/*.egg-info 17 | .cache 18 | *.ckpt 19 | *.log 20 | .ipynb_checkpoints 21 | venv/ 22 | .vimrc 23 | *.settings 24 | *.svn 25 | .project 26 | .pydevproject 27 | tags 28 | *sublime-project 29 | *sublime-workspace 30 | # Intermediate outputs 31 | __pycache__ 32 | **/__pycache__ 33 | *.pb.* 34 | *.pyc 35 | *.swp 36 | *.swo 37 | # generated data 38 | *.rdb 39 | *.db 40 | *.avi 41 | # mujoco outputs 42 | MUJOCO_LOG.TXT 43 | model.txt 44 | .window_data 45 | .idea/*.xml 46 | outputfile 47 | tmp* 48 | cymj.c 49 | **/.git 50 | **/*.so 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior. 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Error Messages** 17 | Including more/longer error messages gives us more information to work with. 18 | 19 | **Desktop (please complete the following information):** 20 | - OS: [e.g. macOS 10.13.6] 21 | - Python Version [e.g. 3.6.6] 22 | - Mujoco Version [e.g. 1.50] 23 | - mujoco-py version [e.g. 1.50.1.59] 24 | 25 | **Environment** 26 | - output of: `echo $LD_LIBRARY_PATH` 27 | - output of: `echo $HOME` 28 | - output of: `echo $USER` 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mujoco-py-** 2 | mjkey.txt 3 | mujoco_py/generated/cymj* 4 | _pyxbld* 5 | dist 6 | cache 7 | .idea/* 8 | *~ 9 | .*~ 10 | *#*# 11 | *.o 12 | *.dat 13 | *.prof 14 | *.lprof 15 | *.local 16 | .realsync 17 | .DS_Store 18 | **/*.egg-info 19 | .cache 20 | *.ckpt 21 | *.log 22 | .ipynb_checkpoints 23 | venv/ 24 | .vimrc 25 | *.settings 26 | *.svn 27 | .project 28 | .pydevproject 29 | tags 30 | *sublime-project 31 | *sublime-workspace 32 | # Intermediate outputs 33 | __pycache__ 34 | **/__pycache__ 35 | *.pb.* 36 | *.pyc 37 | *.swp 38 | *.swo 39 | # generated data 40 | *.rdb 41 | *.db 42 | *.avi 43 | # mujoco outputs 44 | MUJOCO_LOG.TXT 45 | model.txt 46 | .window_data 47 | .idea/*.xml 48 | outputfile 49 | tmp* 50 | cymj.c 51 | **/.git 52 | .eggs/ 53 | *.so 54 | .python-version 55 | /build 56 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | 3 | before_install: 4 | - curl $MUJOCO_FOR_LINUX | tar -xz ./mjkey.txt 5 | - docker build -t image-under-test . 6 | 7 | script: 8 | - docker run --rm image-under-test 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # We need the CUDA base dockerfile to enable GPU rendering 2 | # on hosts with GPUs. 3 | # The image below is a pinned version of nvidia/cuda:9.1-cudnn7-devel-ubuntu16.04 (from Jan 2018) 4 | # If updating the base image, be sure to test on GPU since it has broken in the past. 5 | FROM nvidia/cuda@sha256:4df157f2afde1cb6077a191104ab134ed4b2fd62927f27b69d788e8e79a45fa1 6 | 7 | RUN apt-get update -q \ 8 | && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 9 | curl \ 10 | git \ 11 | libgl1-mesa-dev \ 12 | libgl1-mesa-glx \ 13 | libglew-dev \ 14 | libosmesa6-dev \ 15 | software-properties-common \ 16 | net-tools \ 17 | vim \ 18 | virtualenv \ 19 | wget \ 20 | xpra \ 21 | xserver-xorg-dev \ 22 | && apt-get clean \ 23 | && rm -rf /var/lib/apt/lists/* 24 | 25 | RUN DEBIAN_FRONTEND=noninteractive add-apt-repository --yes ppa:deadsnakes/ppa && apt-get update 26 | RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes python3.6-dev python3.6 python3-pip 27 | RUN virtualenv --python=python3.6 env 28 | 29 | RUN rm /usr/bin/python 30 | RUN ln -s /env/bin/python3.6 /usr/bin/python 31 | RUN ln -s /env/bin/pip3.6 /usr/bin/pip 32 | RUN ln -s /env/bin/pytest /usr/bin/pytest 33 | 34 | RUN curl -o /usr/local/bin/patchelf https://s3-us-west-2.amazonaws.com/openai-sci-artifacts/manual-builds/patchelf_0.9_amd64.elf \ 35 | && chmod +x /usr/local/bin/patchelf 36 | 37 | ENV LANG C.UTF-8 38 | 39 | RUN mkdir -p /root/.mujoco \ 40 | && wget https://mujoco.org/download/mujoco210-linux-x86_64.tar.gz -O mujoco.tar.gz \ 41 | && tar -xf mujoco.tar.gz -C /root/.mujoco \ 42 | && rm mujoco.tar.gz 43 | 44 | ENV LD_LIBRARY_PATH /root/.mujoco/mujoco210/bin:${LD_LIBRARY_PATH} 45 | ENV LD_LIBRARY_PATH /usr/local/nvidia/lib64:${LD_LIBRARY_PATH} 46 | 47 | COPY vendor/Xdummy /usr/local/bin/Xdummy 48 | RUN chmod +x /usr/local/bin/Xdummy 49 | 50 | # Workaround for https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-375/+bug/1674677 51 | COPY ./vendor/10_nvidia.json /usr/share/glvnd/egl_vendor.d/10_nvidia.json 52 | 53 | WORKDIR /mujoco_py 54 | # Copy over just requirements.txt at first. That way, the Docker cache doesn't 55 | # expire until we actually change the requirements. 56 | COPY ./requirements.txt /mujoco_py/ 57 | COPY ./requirements.dev.txt /mujoco_py/ 58 | RUN pip install --no-cache-dir -r requirements.txt 59 | RUN pip install --no-cache-dir -r requirements.dev.txt 60 | 61 | # Delay moving in the entire code until the very end. 62 | ENTRYPOINT ["/mujoco_py/vendor/Xdummy-entrypoint"] 63 | CMD ["pytest"] 64 | COPY . /mujoco_py 65 | RUN python setup.py install 66 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # mujoco-py 2 | 3 | The MIT License 4 | 5 | Copyright (c) 2017 OpenAI (http://openai.com) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | # Mujoco models 26 | This work is derived from [MuJuCo models](http://www.mujoco.org/forum/index.php?resources/) used under the following license: 27 | ``` 28 | This file is part of MuJoCo. 29 | Copyright 2009-2015 Roboti LLC. 30 | Mujoco :: Advanced physics simulation engine 31 | Source : www.roboti.us 32 | Version : 1.31 33 | Released : 23Apr16 34 | Author :: Vikash Kumar 35 | Contacts : kumar@roboti.us 36 | ``` 37 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml 2 | include requirements.txt 3 | include requirements.dev.txt 4 | recursive-include mujoco_py *.h *.py *.pyx *.pxd *.pxi *.xml *.c 5 | include mujoco_py/gl/*.c 6 | include setup.py 7 | include mujoco_py/generated/cymj_*_linuxcpuextensionbuilder.so 8 | include mujoco_py/generated/cymj_*_linuxgpuextensionbuilder.so 9 | include xmls/* 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | .PHONY: all clean build test mount_shell shell upload check-env 3 | 4 | VERSION := `cd mujoco_py; python -c "from version import get_version;print(get_version())"; cd ..` 5 | PYTHON_VERSION := "36" 6 | DOCKER_NAME := quay.io/openai/mujoco_py:$(USER)_$(VERSION) 7 | DOCKER := $(shell type -p nvidia-docker || echo docker) 8 | UUID := $(shell uuidgen) 9 | 10 | all: test 11 | 12 | clean: 13 | rm -rf mujoco_py.egg-info 14 | rm -rf */__pycache__ 15 | rm -rf */*/__pycache__ 16 | rm -rf mujoco_py/generated/_pyxbld* 17 | rm -rf mujoco_py/generated/*.so 18 | rm -rf mujoco_py/generated/*.dll 19 | rm -rf mujoco_py/generated_cymj* 20 | rm -rf mujoco_py/cythonlock_*.pyc 21 | rm -rf mujoco_py/cymj.c 22 | rm -rf mujoco_py/__pycache__ 23 | rm -rf dist 24 | rm -rf build 25 | 26 | build: 27 | docker build -t $(DOCKER_NAME) . 28 | 29 | push: 30 | docker push $(DOCKER_NAME) 31 | 32 | pull: 33 | docker pull $(DOCKER_NAME) 34 | 35 | test: build 36 | # run it interactive mode so we can abort with CTRL+C 37 | $(DOCKER) run --rm -i $(DOCKER_NAME) pytest 38 | 39 | test_gpu: build push 40 | $(eval NODE="$(shell cirrascale-cli reserve -g 1080ti -t 10m)") 41 | $(eval GPUS="$(shell echo $(NODE)| grep -oE '[^:]+f' | cut -c1-1 )") 42 | $(eval NODE="$(shell echo $(NODE)| grep -oE '[^=]+:' | sed 's/://')") 43 | ssh -t -o StrictHostKeyChecking=no $(NODE) 'docker pull $(DOCKER_NAME) && export GPUS=$(GPUS) && nvidia-docker run --rm -e GPUS -i $(DOCKER_NAME) pytest -m "not requires_glfw"' 44 | 45 | shell: 46 | $(DOCKER) run --rm -it $(DOCKER_NAME) /bin/bash 47 | 48 | mount: 49 | $(DOCKER) run -e GPUS -it -v `pwd`:/mujoco_py-dev $(DOCKER_NAME) /bin/bash -c "pip uninstall -y mujoco-py; rm -rf /mujoco_py; (cd /mujoco_py-dev; export PYTHONPATH='/mujoco_py-dev'; /bin/bash)" 50 | 51 | cirra: 52 | $(eval NODE="$(shell cirrascale-cli reserve)") 53 | $(eval GPUS="$(shell echo $(NODE)| grep -oE '[^:]+f' | cut -c1-1 )") 54 | $(eval NODE="$(shell echo $(NODE)| grep -oE '[^=]+:' | sed 's/://')") 55 | tmux new-session "while :; do rsync -e 'ssh -o StrictHostKeyChecking=no' --delete -rzai --out-format='%t %f %b' --chmod=Du+rwx --exclude='*extensionbuilder.so' --exclude='dist' --exclude='cymj.c' --exclude='_pyxbld_*' --exclude='__pycache__' --exclude='*.egg-info' --exclude='.cache' --exclude='.git' --exclude='*.pyc' --exclude='*.swp' --exclude='.idea' . $(NODE):~/mujoco_py/ ; sleep 1; done" \; \ 56 | split-window "ssh -t -o StrictHostKeyChecking=no $(NODE) 'mkdir -p ~/mujoco_py && cd ~/mujoco_py && export GPUS=$(GPUS) && /bin/bash'; " \; select-layout 9ce0,204x51,0,0[204x4,0,0,32,204x46,0,5,33] 57 | 58 | # Requires to generate all *.so files. Call make generate_mac_so on mac; 59 | # Call make generate_cpu_so on mac or linux. 60 | # 61 | # Call make generate_gpu_so on linux with nvidia-docker. The easiest way is to get to cirrascale, and call: make cirra. 62 | # Then you have to copy back generated cymj_linuxgpuextensionbuilder.so file. 63 | # 64 | # Gather all *.so files. 65 | upload: 66 | # Commented out for now, binary distributions seem not to work very well 67 | # test -f ./mujoco_py/generated/cymj_$(VERSION)_$(PYTHON_VERSION)_linuxcpuextensionbuilder.so || exit -1 68 | # test -f ./mujoco_py/generated/cymj_$(VERSION)_$(PYTHON_VERSION)_linuxgpuextensionbuilder.so || exit -1 69 | rm -rf dist 70 | python setup.py sdist 71 | twine upload dist/* 72 | 73 | generate_gpu_so: 74 | rm -f ./mujoco_py/generated/cymj_*_linuxgpuextensionbuilder.so 75 | nvidia-docker run -it --name $(UUID) $(DOCKER_NAME) bash -c "make clean;python -c 'import mujoco_py'" 76 | nvidia-docker cp $(UUID):/mujoco_py/mujoco_py/generated/cymj_$(VERSION)_$(PYTHON_VERSION)_linuxgpuextensionbuilder.so mujoco_py/generated/ 77 | 78 | generate_cpu_so: 79 | rm -f ./mujoco_py/generated/cymj_*_linuxcpuextensionbuilder.so 80 | docker run -it --name $(UUID) $(DOCKER_NAME) bash -c "make clean;python -c 'import mujoco_py'" 81 | docker cp $(UUID):/mujoco_py/mujoco_py/generated/cymj_$(VERSION)_$(PYTHON_VERSION)_linuxcpuextensionbuilder.so mujoco_py/generated/ 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Status**: Deprecated 2 | 3 | ### `mujoco-py` does not support versions of MuJoCo after 2.1.0. 4 | 5 | ### New users should depend on the [official MuJoCo Python bindings](https://github.com/deepmind/mujoco/blob/main/python/README.md). 6 | 7 | # mujoco-py [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat)](https://openai.github.io/mujoco-py/build/html/index.html) [![Build Status](https://travis-ci.org/openai/mujoco-py.svg?branch=master)](https://travis-ci.org/openai/mujoco-py) 8 | 9 | [MuJoCo](http://mujoco.org/) is a physics engine for detailed, efficient rigid body simulations with contacts. 10 | `mujoco-py` allows using MuJoCo from Python 3. 11 | 12 | This library has been updated to be compatible with MuJoCo version 2.1 released on 2021-10-18. 13 | 14 | 15 | ## Synopsis 16 | 17 | ### Requirements 18 | 19 | The following platforms are currently supported: 20 | 21 | - Linux with Python 3.6+. See [the `Dockerfile`](Dockerfile) for the canonical list of system dependencies. 22 | - OS X with Python 3.6+. 23 | 24 | The following platforms are DEPRECATED and unsupported: 25 | 26 | - Windows support has been DEPRECATED and removed in [2.0.2.0](https://github.com/openai/mujoco-py/releases/tag/v2.0.2.0a1). One known good past version is [1.50.1.68](https://github.com/openai/mujoco-py/blob/9ea9bb000d6b8551b99f9aa440862e0c7f7b4191/README.md#requirements). 27 | - Python 2 has been DEPRECATED and removed in [1.50.1.0](https://github.com/openai/mujoco-py/releases/tag/1.50.1.0). Python 2 users can stay on the [`0.5` branch](https://github.com/openai/mujoco-py/tree/0.5). The latest release there is [`0.5.7`](https://github.com/openai/mujoco-py/releases/tag/0.5.7) which can be installed with `pip install mujoco-py==0.5.7`. 28 | 29 | ### Install MuJoCo 30 | 31 | 1. Download the MuJoCo version 2.1 binaries for 32 | [Linux](https://mujoco.org/download/mujoco210-linux-x86_64.tar.gz) or 33 | [OSX](https://mujoco.org/download/mujoco210-macos-x86_64.tar.gz). 34 | 1. Extract the downloaded `mujoco210` directory into `~/.mujoco/mujoco210`. 35 | 36 | If you want to specify a nonstandard location for the package, 37 | use the env variable `MUJOCO_PY_MUJOCO_PATH`. 38 | 39 | ### Install and use `mujoco-py` 40 | To include `mujoco-py` in your own package, add it to your requirements like so: 41 | ``` 42 | mujoco-py<2.2,>=2.1 43 | ``` 44 | To play with `mujoco-py` interactively, follow these steps: 45 | ``` 46 | $ pip3 install -U 'mujoco-py<2.2,>=2.1' 47 | $ python3 48 | import mujoco_py 49 | import os 50 | mj_path = mujoco_py.utils.discover_mujoco() 51 | xml_path = os.path.join(mj_path, 'model', 'humanoid.xml') 52 | model = mujoco_py.load_model_from_path(xml_path) 53 | sim = mujoco_py.MjSim(model) 54 | 55 | print(sim.data.qpos) 56 | # [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] 57 | 58 | sim.step() 59 | print(sim.data.qpos) 60 | # [-2.09531783e-19 2.72130735e-05 6.14480786e-22 -3.45474715e-06 61 | # 7.42993721e-06 -1.40711141e-04 -3.04253586e-04 -2.07559344e-04 62 | # 8.50646247e-05 -3.45474715e-06 7.42993721e-06 -1.40711141e-04 63 | # -3.04253586e-04 -2.07559344e-04 -8.50646247e-05 1.11317030e-04 64 | # -7.03465386e-05 -2.22862221e-05 -1.11317030e-04 7.03465386e-05 65 | # -2.22862221e-05] 66 | ``` 67 | 68 | See the [full documentation](https://openai.github.io/mujoco-py/build/html/index.html) for advanced usage. 69 | 70 | ## Troubleshooting 71 | 72 | ### You're on MacOS and you see `clang: error: unsupported option '-fopenmp'` 73 | 74 | If this happend during installation or just running `python -c "import mujoco_py"` then the issue seems to be related to [this](https://github.com/velocyto-team/velocyto.R/issues/2#issuecomment-341165967) and the TL;DR is that for macOS the default compiler Apple clang LLVM does not support openmp. So you can try to install another clang/llvm installation. For example (requires [brew](https://brew.sh/)): 75 | 76 | ```bash 77 | brew install llvm 78 | brew install boost 79 | brew install hdf5 80 | 81 | # Add this to your .bashrc/.zshrc: 82 | export PATH="/usr/local/opt/llvm/bin:$PATH" 83 | 84 | export CC="/usr/local/opt/llvm/bin/clang" 85 | export CXX="/usr/local/opt/llvm/bin/clang++" 86 | export CXX11="/usr/local/opt/llvm/bin/clang++" 87 | export CXX14="/usr/local/opt/llvm/bin/clang++" 88 | export CXX17="/usr/local/opt/llvm/bin/clang++" 89 | export CXX1X="/usr/local/opt/llvm/bin/clang++" 90 | 91 | export LDFLAGS="-L/usr/local/opt/llvm/lib" 92 | export CPPFLAGS="-I/usr/local/opt/llvm/include" 93 | ``` 94 | 95 | **Note:** Don't forget to source your `.bashrc/.zshrc` after editing it and try to install `mujoco-py` again: 96 | 97 | ```bash 98 | # Make sure your python environment is activated 99 | pip install -U 'mujoco-py<2.2,>=2.1' 100 | ``` 101 | 102 | ### Missing GLFW 103 | 104 | A common error when installing is: 105 | 106 | raise ImportError("Failed to load GLFW3 shared library.") 107 | 108 | Which happens when the `glfw` python package fails to find a GLFW dynamic library. 109 | 110 | MuJoCo ships with its own copy of this library, which can be used during installation. 111 | 112 | Add the path to the mujoco bin directory to your dynamic loader: 113 | 114 | LD_LIBRARY_PATH=$HOME/.mujoco/mujoco210/bin pip install mujoco-py 115 | 116 | This is particularly useful on Ubuntu 14.04, which does not have a GLFW package. 117 | 118 | 119 | ### Ubuntu installtion troubleshooting 120 | 121 | Because `mujoco_py` has compiled native code that needs to be linked to a supplied MuJoCo binary, it's installation 122 | on linux can be more challenging than pure Python source packages. 123 | 124 | To install mujoco-py on Ubuntu, make sure you have the following libraries installed: 125 | 126 | sudo apt install libosmesa6-dev libgl1-mesa-glx libglfw3 127 | 128 | If you installed above libraries and you still see an error that `-lGL` cannot be found, most likely you need 129 | to create the symbolic link directly: 130 | 131 | sudo ln -s /usr/lib/x86_64-linux-gnu/libGL.so.1 /usr/lib/x86_64-linux-gnu/libGL.so 132 | 133 | 134 | ## Usage Examples 135 | 136 | A number of examples demonstrating some advanced features of `mujoco-py` can be found in [`examples/`](/./examples/). These include: 137 | - [`body_interaction.py`](./examples/body_interaction.py): shows interactions between colliding bodies 138 | - [`disco_fetch.py`](./examples/disco_fetch.py): shows how `TextureModder` can be used to randomize object textures 139 | - [`internal_functions.py`](./examples/internal_functions.py): shows how to call raw mujoco functions like `mjv_room2model` 140 | - [`markers_demo.py`](./examples/markers_demo.py): shows how to add visualization-only geoms to the viewer 141 | - [`serialize_model.py`](./examples/serialize_model.py): shows how to save and restore a model 142 | - [`setting_state.py`](./examples/setting_state.py): shows how to reset the simulation to a given state 143 | - [`tosser.py`](./examples/tosser.py): shows a simple actuated object sorting robot application 144 | 145 | See the [full documentation](https://openai.github.io/mujoco-py/build/html/index.html) for advanced usage. 146 | 147 | ## Development 148 | 149 | To run the provided unit and integrations tests: 150 | 151 | ``` 152 | make test 153 | ``` 154 | 155 | To test GPU-backed rendering, run: 156 | 157 | ``` 158 | make test_gpu 159 | ``` 160 | 161 | This is somewhat dependent on internal OpenAI infrastructure at the moment, but it should run if you change the `Makefile` parameters for your own setup. 162 | 163 | ## Changelog 164 | 165 | - 03/08/2018: We removed MjSimPool, because most of benefit one can get with multiple processes having single simulation. 166 | 167 | ## Credits 168 | 169 | `mujoco-py` is maintained by the OpenAI Robotics team. Contributors include: 170 | 171 | - Alex Ray 172 | - Bob McGrew 173 | - Jonas Schneider 174 | - Jonathan Ho 175 | - Peter Welinder 176 | - Wojciech Zaremba 177 | - Jerry Tworek 178 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/.nojekyll -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | SPHINXOPTS = 2 | SPHINXBUILD = python -msphinx 3 | SPHINXPROJ = mujoco-py 4 | SOURCEDIR = . 5 | BUILDDIR = build 6 | 7 | .PHONY: all clean 8 | 9 | all: 10 | @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 11 | 12 | clean: 13 | @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | -------------------------------------------------------------------------------- /docs/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/_static/.gitkeep -------------------------------------------------------------------------------- /docs/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/_templates/.gitkeep -------------------------------------------------------------------------------- /docs/build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/internals.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/doctrees/internals.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/reference.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/doctrees/reference.doctree -------------------------------------------------------------------------------- /docs/build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: ed7df2c1f6fc81f1edc97343eaa55651 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/build/html/_modules/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Overview: module code — mujoco-py 1.50.1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 51 | 101 | 102 |
103 | 104 | 105 | 111 | 112 | 113 | 114 |
115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
133 | 134 |
    135 | 136 |
  • Docs »
  • 137 | 138 |
  • Overview: module code
  • 139 | 140 | 141 |
  • 142 | 143 | 144 | 145 |
  • 146 | 147 |
148 | 149 | 150 |
151 |
152 |
153 |
154 | 155 |

All modules for which code is available

156 | 160 | 161 |
162 |
163 | 164 |
165 |
166 | 180 | 181 |
182 |
183 | 184 |
185 | 186 |
187 | 188 | 189 | 190 | 191 | 192 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | ======================= 2 | mujoco-py Documentation 3 | ======================= 4 | 5 | .. toctree:: 6 | reference 7 | internals 8 | 9 | `MuJoCo `_ is a physics engine for detailed, efficient rigid body simulations with contacts. ``mujoco-py`` allows using MuJoCo from Python 3. 10 | 11 | See the `README `_ for installation instructions and example usage. 12 | 13 | ``mujoco-py`` allows access to MuJoCo on a number of different levels of abstraction: 14 | 15 | - Directly from Cython (low-level): `Raw Cython declarations `_ are provided for using the MuJoCo C structs and functions directly in your own Cython code. 16 | 17 | - Using :ref:`pymjdata` (medium-level): These wrappers are lightweight Cython ``cdef`` classes that expose MuJuCo data to Python space. The data in the MuJoCo structs is exposed as NumPy arrays bound to Mujoco-allocated memory, so there is no copying overhead when accessing or modifying MuJoCo state from Python. For more information on how this works internally, see [this document](./doc/cython_wrappers.md). 18 | 19 | - Using :class:`mujoco_py.MjSim` (high-level): :class:`mujoco_py.MjSim` manages a stateful simulation similar to the `MujocoEnv `_ class found in `Gym `_ 20 | . 21 | -------------------------------------------------------------------------------- /docs/build/html/_sources/internals.rst.txt: -------------------------------------------------------------------------------- 1 | Internals 2 | ========= 3 | 4 | .. _genwrapper: 5 | 6 | Autogenerated wrappers 7 | ---------------------- 8 | 9 | The Cython struct wrappers are generated by ``scripts/gen_wrappers.py``. To illustrate how the wrappers work, let's consider a toy C structure and the corresponding generated Cython cdef class. Here's a stripped down version of ``mjData`` with a scalar member and a pointer (array) member:: 10 | 11 | typedef struct _mjData { 12 | int ne; 13 | mjtNum* qpos; // (nq x 1) 14 | } mjData; 15 | 16 | Here's the corresponding generated Cython wrapper code:: 17 | 18 | cdef class PyMjData(object): 19 | cdef mjData* ptr 20 | cdef mjModel* _model 21 | cdef np.ndarray _qpos 22 | 23 | cdef void _set(self, mjData* p, mjModel* model): 24 | self.ptr = p 25 | self._model = model 26 | self._qpos = _wrap_mjtNum_1d(p.qpos, model.nq) 27 | 28 | @property 29 | def ne(self): return self.ptr.ne 30 | 31 | @ne.setter 32 | def ne(self, int x): self.ptr.ne = x 33 | 34 | @property 35 | def qpos(self): return self._qpos 36 | 37 | cdef PyMjData WrapMjData(mjData* p, mjModel* model): 38 | cdef PyMjData o = PyMjData() 39 | o._set(p, model) 40 | return o 41 | 42 | ``PyMjData`` is the wrapper class for exposing the underlying Mujoco structure to Python; it doesn't perform any memory mangement. A user writing Cython code can create this wrapper using ``WrapMjData``. A ``mjModel`` pointer must be passed because the shape of a ``mjData`` member, namely ``qpos``, depends on ``model->nq``. 43 | 44 | Each field of ``mjData`` corresponds to some generated piece of code in ``PyMjData`` that depends on the type of that field. For example, ``ne`` is a scalar integer, so it gets exposed as a pair of getter and setter methods in ``PyMjData``. ``qpos`` is an array represented as a pointer to its first element, so it's wrapped with a NumPy array by ``_wrap_mjtNum_1d`` and is exposed with a getter for that NumPy array. 45 | 46 | The function ``_wrap_mjtNum_1d`` creates a Cython memoryview from the data pointer and converts it to a NumPy array pointing to the same memory:: 47 | 48 | cdef inline np.ndarray _wrap_mjtNum_1d(mjtNum* a, int shape0): 49 | if shape0 == 0: return None 50 | cdef mjtNum[:] b = a 51 | return np.asarray(b) 52 | 53 | Similar functions for other types are also generated as required. 54 | 55 | Keep in mind that the only reason to use these autogenerated wrappers is to allow Python users of the Cython code to easily access Mujoco data (for instance the ``MjSim`` Cython class, found in ``cymj/cymj.pyx`). If you're writing Cython code and you don't need the user to access Mujoco data from Python, then there is no reason to use these wrappers. 56 | -------------------------------------------------------------------------------- /docs/build/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/build/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/build/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/comment-close.png -------------------------------------------------------------------------------- /docs/build/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/comment.png -------------------------------------------------------------------------------- /docs/build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} 2 | /*# sourceMappingURL=badge_only.css.map */ 3 | -------------------------------------------------------------------------------- /docs/build/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/build/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/down.png -------------------------------------------------------------------------------- /docs/build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/file.png -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Inconsolata-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/Inconsolata-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/RobotoSlab-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/RobotoSlab-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/RobotoSlab-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/RobotoSlab-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"); 80 | 81 | // Add expand links to all parents of nested ul 82 | $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { 83 | var link = $(this); 84 | expand = $(''); 85 | expand.on('click', function (ev) { 86 | self.toggleCurrent(link); 87 | ev.stopPropagation(); 88 | return false; 89 | }); 90 | link.prepend(expand); 91 | }); 92 | }; 93 | 94 | nav.reset = function () { 95 | // Get anchor from URL and open up nested nav 96 | var anchor = encodeURI(window.location.hash); 97 | if (anchor) { 98 | try { 99 | var link = $('.wy-menu-vertical') 100 | .find('[href="' + anchor + '"]'); 101 | // If we didn't find a link, it may be because we clicked on 102 | // something that is not in the sidebar (eg: when using 103 | // sphinxcontrib.httpdomain it generates headerlinks but those 104 | // aren't picked up and placed in the toctree). So let's find 105 | // the closest header in the document and try with that one. 106 | if (link.length === 0) { 107 | var doc_link = $('.document a[href="' + anchor + '"]'); 108 | var closest_section = doc_link.closest('div.section'); 109 | // Try again with the closest section entry. 110 | link = $('.wy-menu-vertical') 111 | .find('[href="#' + closest_section.attr("id") + '"]'); 112 | 113 | } 114 | $('.wy-menu-vertical li.toctree-l1 li.current') 115 | .removeClass('current'); 116 | link.closest('li.toctree-l2').addClass('current'); 117 | link.closest('li.toctree-l3').addClass('current'); 118 | link.closest('li.toctree-l4').addClass('current'); 119 | } 120 | catch (err) { 121 | console.log("Error expanding nav for anchor", err); 122 | } 123 | } 124 | }; 125 | 126 | nav.onScroll = function () { 127 | this.winScroll = false; 128 | var newWinPosition = this.win.scrollTop(), 129 | winBottom = newWinPosition + this.winHeight, 130 | navPosition = this.navBar.scrollTop(), 131 | newNavPosition = navPosition + (newWinPosition - this.winPosition); 132 | if (newWinPosition < 0 || winBottom > this.docHeight) { 133 | return; 134 | } 135 | this.navBar.scrollTop(newNavPosition); 136 | this.winPosition = newWinPosition; 137 | }; 138 | 139 | nav.onResize = function () { 140 | this.winResize = false; 141 | this.winHeight = this.win.height(); 142 | this.docHeight = $(document).height(); 143 | }; 144 | 145 | nav.hashChange = function () { 146 | this.linkScroll = true; 147 | this.win.one('hashchange', function () { 148 | this.linkScroll = false; 149 | }); 150 | }; 151 | 152 | nav.toggleCurrent = function (elem) { 153 | var parent_li = elem.closest('li'); 154 | parent_li.siblings('li.current').removeClass('current'); 155 | parent_li.siblings().find('li.current').removeClass('current'); 156 | parent_li.find('> ul li.current').removeClass('current'); 157 | parent_li.toggleClass('current'); 158 | } 159 | 160 | return nav; 161 | }; 162 | 163 | module.exports.ThemeNav = ThemeNav(); 164 | 165 | if (typeof(window) != 'undefined') { 166 | window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav }; 167 | } 168 | 169 | },{"jquery":"jquery"}]},{},["sphinx-rtd-theme"]); 170 | -------------------------------------------------------------------------------- /docs/build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/build/html/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/_static/up.png -------------------------------------------------------------------------------- /docs/build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | mujoco-py Documentation — mujoco-py 1.50.1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | 51 | 52 | 102 | 103 |
104 | 105 | 106 | 112 | 113 | 114 | 115 |
116 |
117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
134 | 135 |
    136 | 137 |
  • Docs »
  • 138 | 139 |
  • mujoco-py Documentation
  • 140 | 141 | 142 |
  • 143 | 144 | 145 | View page source 146 | 147 | 148 |
  • 149 | 150 |
151 | 152 | 153 |
154 |
155 |
156 |
157 | 158 |
159 |

mujoco-py Documentation

160 | 175 |

MuJoCo is a physics engine for detailed, efficient rigid body simulations with contacts. mujoco-py allows using MuJoCo from Python 3.

176 |

See the README for installation instructions and example usage.

177 |

mujoco-py allows access to MuJoCo on a number of different levels of abstraction:

178 |
    179 |
  • Directly from Cython (low-level): Raw Cython declarations are provided for using the MuJoCo C structs and functions directly in your own Cython code.
  • 180 |
  • Using PyMjData: Time-dependent data (medium-level): These wrappers are lightweight Cython cdef classes that expose MuJuCo data to Python space. The data in the MuJoCo structs is exposed as NumPy arrays bound to Mujoco-allocated memory, so there is no copying overhead when accessing or modifying MuJoCo state from Python. For more information on how this works internally, see [this document](./doc/cython_wrappers.md).
  • 181 |
  • Using mujoco_py.MjSim (high-level): mujoco_py.MjSim manages a stateful simulation similar to the MujocoEnv class found in Gym
  • 182 |
183 |

.

184 |
185 | 186 | 187 |
188 |
189 | 190 |
191 |
192 |
193 | 194 | 200 | 201 | 202 |
203 | 204 |
205 |

206 | © Copyright 2017, OpenAI. 207 | 208 |

209 |
210 | Built with Sphinx using a theme provided by Read the Docs. 211 | 212 |
213 | 214 |
215 |
216 | 217 |
218 | 219 |
220 | 221 | 222 | 223 | 224 | 225 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search — mujoco-py 1.50.1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 51 | 101 | 102 |
103 | 104 | 105 | 111 | 112 | 113 | 114 |
115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
133 | 134 |
    135 | 136 |
  • Docs »
  • 137 | 138 |
  • Search
  • 139 | 140 | 141 |
  • 142 | 143 |
  • 144 | 145 |
146 | 147 | 148 |
149 |
150 |
151 |
152 | 153 | 161 | 162 | 163 |
164 | 165 |
166 | 167 |
168 |
169 | 170 |
171 |
172 |
173 | 174 | 175 |
176 | 177 |
178 |

179 | © Copyright 2017, OpenAI. 180 | 181 |

182 |
183 | Built with Sphinx using a theme provided by Read the Docs. 184 | 185 |
186 | 187 |
188 |
189 | 190 |
191 | 192 |
193 | 194 | 195 | 196 | 197 | 198 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 227 | 228 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # mujoco-py documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Jun 27 15:21:11 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # import os 21 | # import sys 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = ['sphinx.ext.autodoc', 35 | 'sphinx.ext.viewcode', 36 | 'numpydoc'] 37 | 38 | # Add any paths that contain templates here, relative to this directory. 39 | templates_path = ['_templates'] 40 | 41 | # The suffix(es) of source filenames. 42 | # You can specify multiple suffix as a list of string: 43 | # 44 | # source_suffix = ['.rst', '.md'] 45 | source_suffix = '.rst' 46 | 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # General information about the project. 51 | project = 'mujoco-py' 52 | copyright = '2017, OpenAI' 53 | author = 'OpenAI' 54 | 55 | # The version info for the project you're documenting, acts as replacement for 56 | # |version| and |release|, also used in various other places throughout the 57 | # built documents. 58 | # 59 | # The short X.Y version. 60 | import mujoco_py 61 | version = mujoco_py.__version__ 62 | # The full version, including alpha/beta/rc tags. 63 | release = mujoco_py.__version__ 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This patterns also effect to html_static_path and html_extra_path 75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | # If true, `todo` and `todoList` produce output, else they produce nothing. 81 | todo_include_todos = False 82 | 83 | 84 | # -- Options for HTML output ---------------------------------------------- 85 | 86 | # The theme to use for HTML and HTML Help pages. See the documentation for 87 | # a list of builtin themes. 88 | # 89 | html_theme = "sphinx_rtd_theme" 90 | 91 | # Theme options are theme-specific and customize the look and feel of a theme 92 | # further. For a list of options available for each theme, see the 93 | # documentation. 94 | # 95 | # html_theme_options = {} 96 | 97 | # Add any paths that contain custom static files (such as style sheets) here, 98 | # relative to this directory. They are copied after the builtin static files, 99 | # so a file named "default.css" will overwrite the builtin "default.css". 100 | html_static_path = ['_static'] 101 | 102 | 103 | # -- Options for HTMLHelp output ------------------------------------------ 104 | 105 | # Output file base name for HTML help builder. 106 | htmlhelp_basename = 'mujoco-pydoc' 107 | 108 | 109 | # -- Options for LaTeX output --------------------------------------------- 110 | 111 | latex_elements = { 112 | # The paper size ('letterpaper' or 'a4paper'). 113 | # 114 | # 'papersize': 'letterpaper', 115 | 116 | # The font size ('10pt', '11pt' or '12pt'). 117 | # 118 | # 'pointsize': '10pt', 119 | 120 | # Additional stuff for the LaTeX preamble. 121 | # 122 | # 'preamble': '', 123 | 124 | # Latex figure (float) alignment 125 | # 126 | # 'figure_align': 'htbp', 127 | } 128 | 129 | # Grouping the document tree into LaTeX files. List of tuples 130 | # (source start file, target name, title, 131 | # author, documentclass [howto, manual, or own class]). 132 | latex_documents = [ 133 | (master_doc, 'mujoco-py.tex', 'mujoco-py Documentation', 134 | 'OpenAI', 'manual'), 135 | ] 136 | 137 | 138 | # -- Options for manual page output --------------------------------------- 139 | 140 | # One entry per manual page. List of tuples 141 | # (source start file, name, description, authors, manual section). 142 | man_pages = [ 143 | (master_doc, 'mujoco-py', 'mujoco-py Documentation', 144 | [author], 1) 145 | ] 146 | 147 | 148 | # -- Options for Texinfo output ------------------------------------------- 149 | 150 | # Grouping the document tree into Texinfo files. List of tuples 151 | # (source start file, target name, title, author, 152 | # dir menu entry, description, category) 153 | texinfo_documents = [ 154 | (master_doc, 'mujoco-py', 'mujoco-py Documentation', 155 | author, 'mujoco-py', 'One line description of project.', 156 | 'Miscellaneous'), 157 | ] 158 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | mujoco-py Documentation 3 | ======================= 4 | 5 | .. toctree:: 6 | reference 7 | internals 8 | 9 | `MuJoCo `_ is a physics engine for detailed, efficient rigid body simulations with contacts. ``mujoco-py`` allows using MuJoCo from Python 3. 10 | 11 | See the `README `_ for installation instructions and example usage. 12 | 13 | ``mujoco-py`` allows access to MuJoCo on a number of different levels of abstraction: 14 | 15 | - Directly from Cython (low-level): `Raw Cython declarations `_ are provided for using the MuJoCo C structs and functions directly in your own Cython code. 16 | 17 | - Using :ref:`pymjdata` (medium-level): These wrappers are lightweight Cython ``cdef`` classes that expose MuJuCo data to Python space. The data in the MuJoCo structs is exposed as NumPy arrays bound to Mujoco-allocated memory, so there is no copying overhead when accessing or modifying MuJoCo state from Python. For more information on how this works internally, see [this document](./doc/cython_wrappers.md). 18 | 19 | - Using :class:`mujoco_py.MjSim` (high-level): :class:`mujoco_py.MjSim` manages a stateful simulation similar to the `MujocoEnv `_ class found in `Gym `_ 20 | . 21 | -------------------------------------------------------------------------------- /docs/internals.rst: -------------------------------------------------------------------------------- 1 | Internals 2 | ========= 3 | 4 | .. _genwrapper: 5 | 6 | Autogenerated wrappers 7 | ---------------------- 8 | 9 | The Cython struct wrappers are generated by ``scripts/gen_wrappers.py``. To illustrate how the wrappers work, let's consider a toy C structure and the corresponding generated Cython cdef class. Here's a stripped down version of ``mjData`` with a scalar member and a pointer (array) member:: 10 | 11 | typedef struct _mjData { 12 | int ne; 13 | mjtNum* qpos; // (nq x 1) 14 | } mjData; 15 | 16 | Here's the corresponding generated Cython wrapper code:: 17 | 18 | cdef class PyMjData(object): 19 | cdef mjData* ptr 20 | cdef mjModel* _model 21 | cdef np.ndarray _qpos 22 | 23 | cdef void _set(self, mjData* p, mjModel* model): 24 | self.ptr = p 25 | self._model = model 26 | self._qpos = _wrap_mjtNum_1d(p.qpos, model.nq) 27 | 28 | @property 29 | def ne(self): return self.ptr.ne 30 | 31 | @ne.setter 32 | def ne(self, int x): self.ptr.ne = x 33 | 34 | @property 35 | def qpos(self): return self._qpos 36 | 37 | cdef PyMjData WrapMjData(mjData* p, mjModel* model): 38 | cdef PyMjData o = PyMjData() 39 | o._set(p, model) 40 | return o 41 | 42 | ``PyMjData`` is the wrapper class for exposing the underlying Mujoco structure to Python; it doesn't perform any memory mangement. A user writing Cython code can create this wrapper using ``WrapMjData``. A ``mjModel`` pointer must be passed because the shape of a ``mjData`` member, namely ``qpos``, depends on ``model->nq``. 43 | 44 | Each field of ``mjData`` corresponds to some generated piece of code in ``PyMjData`` that depends on the type of that field. For example, ``ne`` is a scalar integer, so it gets exposed as a pair of getter and setter methods in ``PyMjData``. ``qpos`` is an array represented as a pointer to its first element, so it's wrapped with a NumPy array by ``_wrap_mjtNum_1d`` and is exposed with a getter for that NumPy array. 45 | 46 | The function ``_wrap_mjtNum_1d`` creates a Cython memoryview from the data pointer and converts it to a NumPy array pointing to the same memory:: 47 | 48 | cdef inline np.ndarray _wrap_mjtNum_1d(mjtNum* a, int shape0): 49 | if shape0 == 0: return None 50 | cdef mjtNum[:] b = a 51 | return np.asarray(b) 52 | 53 | Similar functions for other types are also generated as required. 54 | 55 | Keep in mind that the only reason to use these autogenerated wrappers is to allow Python users of the Cython code to easily access Mujoco data (for instance the ``MjSim`` Cython class, found in ``cymj/cymj.pyx`). If you're writing Cython code and you don't need the user to access Mujoco data from Python, then there is no reason to use these wrappers. 56 | -------------------------------------------------------------------------------- /examples/body_interaction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Example of how bodies interact with each other. For a body to be able to 4 | move it needs to have joints. In this example, the "robot" is a red ball 5 | with X and Y slide joints (and a Z slide joint that isn't controlled). 6 | On the floor, there's a cylinder with X and Y slide joints, so it can 7 | be pushed around with the robot. There's also a box without joints. Since 8 | the box doesn't have joints, it's fixed and can't be pushed around. 9 | """ 10 | from mujoco_py import load_model_from_xml, MjSim, MjViewer 11 | import math 12 | import os 13 | 14 | MODEL_XML = """ 15 | 16 | 17 | 47 | """ 48 | 49 | model = load_model_from_xml(MODEL_XML) 50 | sim = MjSim(model) 51 | viewer = MjViewer(sim) 52 | t = 0 53 | while True: 54 | sim.data.ctrl[0] = math.cos(t / 10.) * 0.01 55 | sim.data.ctrl[1] = math.sin(t / 10.) * 0.01 56 | t += 1 57 | sim.step() 58 | viewer.render() 59 | if t > 100 and os.getenv('TESTING') is not None: 60 | break 61 | -------------------------------------------------------------------------------- /examples/disco_fetch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Displays robot fetch at a disco party. 4 | """ 5 | from mujoco_py import load_model_from_path, MjSim, MjViewer 6 | from mujoco_py.modder import TextureModder 7 | import os 8 | 9 | model = load_model_from_path("xmls/fetch/main.xml") 10 | sim = MjSim(model) 11 | 12 | viewer = MjViewer(sim) 13 | modder = TextureModder(sim) 14 | 15 | t = 0 16 | 17 | while True: 18 | for name in sim.model.geom_names: 19 | modder.rand_all(name) 20 | 21 | viewer.render() 22 | t += 1 23 | if t > 100 and os.getenv('TESTING') is not None: 24 | break 25 | -------------------------------------------------------------------------------- /examples/internal_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Shows how to access internal functions from MuJoCo. 4 | """ 5 | from mujoco_py import load_model_from_xml, MjSim, functions 6 | import numpy as np 7 | 8 | MODEL_XML = """ 9 | 10 | 11 | 19 | """ 20 | 21 | model = load_model_from_xml(MODEL_XML) 22 | sim = MjSim(model) 23 | 24 | print("Nicely exposed function:\n") 25 | print(sim.model.get_xml()) 26 | 27 | print("\nversus MuJoCo internals:\n\n") 28 | 29 | functions.mj_saveLastXML("/tmp/saved.xml", model, "", 0) 30 | with open("/tmp/saved.xml", "r") as f: 31 | print(f.read()) 32 | 33 | sim.render(100, 100) 34 | 35 | modelpos = np.zeros(3) 36 | modelquat = np.zeros(4) 37 | roompos = np.ones(3) 38 | roomquat = np.array([1., 0., 1., 0.]) 39 | 40 | functions.mjv_room2model(modelpos, modelquat, roompos, 41 | roomquat, sim.render_contexts[0].scn) 42 | 43 | print("\n\nAnother internal function, mjv_room2model:") 44 | print("modelpos = %s, modelquat = %s" % (str(modelpos), str(modelquat))) 45 | 46 | 47 | res = np.zeros(9) 48 | functions.mju_quat2Mat(res, roomquat) 49 | print("\n\nAnother internal function, mju_quat2Mat:\n%s" % res) 50 | -------------------------------------------------------------------------------- /examples/markers_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # demonstration of markers (visual-only geoms) 3 | 4 | import math 5 | import time 6 | import os 7 | import numpy as np 8 | from mujoco_py import load_model_from_xml, MjSim, MjViewer 9 | 10 | MODEL_XML = """ 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | """ 25 | 26 | model = load_model_from_xml(MODEL_XML) 27 | sim = MjSim(model) 28 | viewer = MjViewer(sim) 29 | step = 0 30 | while True: 31 | t = time.time() 32 | x, y = math.cos(t), math.sin(t) 33 | viewer.add_marker(pos=np.array([x, y, 1]), 34 | label=str(t)) 35 | viewer.render() 36 | 37 | step += 1 38 | if step > 100 and os.getenv('TESTING') is not None: 39 | break 40 | -------------------------------------------------------------------------------- /examples/mjvive.py: -------------------------------------------------------------------------------- 1 | ''' 2 | EXPERIMENTAL: Displays MuJoCo model in VR. 3 | 4 | Based on http://www.mujoco.org/book/source/mjvive.cpp 5 | No controller integration currently 6 | 7 | This example is a demonstration of a near-direct translation of an existing 8 | MuJoCo example (mjvive.cpp), showing how to use lower-level functionality 9 | in conjunction with Python. 10 | 11 | Known occasional issues with SteamVR (all-black textures seen), 12 | fixed by closing and re-opening. 13 | 14 | Requires HTC Vive, Windows, and OpenVR. 15 | 16 | Install openvr python module with: 17 | pip install openvr 18 | ''' 19 | 20 | 21 | import math 22 | import os 23 | import glfw 24 | import openvr 25 | import numpy as np 26 | import OpenGL.GL as gl 27 | from mujoco_py import functions 28 | from mujoco_py.builder import mujoco_path 29 | from mujoco_py.cymj import (MjRenderContext, MjSim, load_model_from_xml, 30 | PyMjrRect, PyMjvCamera) 31 | from mujoco_py.generated.const import (CAT_ALL, FB_OFFSCREEN, FONT_BIG, 32 | GRID_BOTTOMLEFT, STEREO_SIDEBYSIDE) 33 | 34 | 35 | # Normally global variables aren't used like this in python, 36 | # but we want to be as close as possible to the original file. 37 | class HMD(): # anonymous object we can set fields on 38 | pass 39 | 40 | 41 | window = None 42 | sim = None 43 | ctx = None 44 | hmd = HMD() 45 | 46 | 47 | def initMuJoCo(filename, width2, height): 48 | ''' load model, init simulation and rendering ''' 49 | global window, sim, ctx 50 | assert glfw.init(), 'Could not initialize GLFW' 51 | glfw.window_hint(glfw.SAMPLES, 0) 52 | glfw.window_hint(glfw.DOUBLEBUFFER, True) 53 | glfw.window_hint(glfw.RESIZABLE, 0) 54 | window = glfw.create_window(width2 // 4, height // 2, "mjvive.py", None, None) 55 | assert window, "Could not create GLFW window" 56 | glfw.make_context_current(window) 57 | glfw.swap_interval(0) 58 | # GLEW init required in C++, not in Python 59 | sim = MjSim(load_model_from_xml(open(filename).read())) 60 | sim.forward() 61 | sim.model.vis.global_.offwidth = width2 62 | sim.model.vis.global_.offheight = height 63 | sim.model.vis.quality.offsamples = 8 64 | ctx = MjRenderContext(sim) 65 | ctx.scn.enabletransform = 1 66 | ctx.scn.translate[1:3] = -0.5 67 | ctx.scn.rotate[0:2] = math.cos(-0.25 * math.pi), math.sin(-0.25 * math.pi) 68 | ctx.scn.scale = 1 69 | ctx.scn.stereo = STEREO_SIDEBYSIDE 70 | 71 | 72 | def v_initPre(): 73 | ''' init vr before MuJoCo init ''' 74 | global hmd 75 | hmd.system = openvr.init(openvr.VRApplication_Scene) 76 | hmd.roompos = np.zeros(3) 77 | hmd.roommat = np.eye(3) 78 | hmd.eyeoffset = np.zeros((2, 3)) 79 | openvr.VRCompositor().setTrackingSpace(openvr.TrackingUniverseStanding) 80 | hmd.width, hmd.height = hmd.system.getRecommendedRenderTargetSize() 81 | for n in range(2): 82 | hmd.eyeoffset[n] = np.array(hmd.system.getEyeToHeadTransform(n).m)[0:3, 3] 83 | 84 | 85 | def v_initPost(): 86 | ''' init vr after MuJoCo init ''' 87 | global hmd 88 | for n in range(2): 89 | znear, zfar = 0.05, 50.0 90 | left, right, top, bottom = hmd.system.getProjectionRaw(n) 91 | ctx.scn.camera[n].frustum_bottom = -bottom * znear 92 | ctx.scn.camera[n].frustum_top = -top * znear 93 | ctx.scn.camera[n].frustum_center = 0.5 * (left + right) * znear 94 | ctx.scn.camera[n].frustum_near = znear 95 | ctx.scn.camera[n].frustum_far = zfar 96 | gl.glActiveTexture(gl.GL_TEXTURE2) 97 | hmd.idtex = gl.glGenTextures(1) 98 | gl.glBindTexture(gl.GL_TEXTURE_2D, hmd.idtex) 99 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST) 100 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST) 101 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE) 102 | gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE) 103 | gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA8, 2 * hmd.width, hmd.height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None) 104 | hmd.poses = (openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount)() 105 | hmd.boundLeft = openvr.VRTextureBounds_t(0., 0., 0.5, 1.) 106 | hmd.boundRight = openvr.VRTextureBounds_t(0.5, 0., 1., 1.) 107 | hmd.vTex = openvr.Texture_t(hmd.idtex, openvr.TextureType_OpenGL, openvr.ColorSpace_Gamma) 108 | 109 | 110 | def v_update(): 111 | ''' update vr poses and controller states ''' 112 | global ctx, hmd 113 | openvr.VRCompositor().waitGetPoses(hmd.poses, openvr.k_unMaxTrackedDeviceCount, None, 0) 114 | m = np.array(hmd.poses[openvr.k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking.m) 115 | hmd.roompos, hmd.roommat = m[0:3, 3], m[0:3, 0:3] 116 | for n in range(2): 117 | ctx.scn.camera[n].pos[:] = hmd.roompos + np.matmul(hmd.roommat, hmd.eyeoffset[n]) 118 | ctx.scn.camera[n].forward[0:3] = -hmd.roommat[:, 2] 119 | ctx.scn.camera[n].up[0:3] = hmd.roommat[:, 1] 120 | 121 | 122 | def v_render(): 123 | ''' render to vr and window ''' 124 | global hmd, ctx, window 125 | # resolve multi-sample offscreen buffer 126 | gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, ctx.con.offFBO) 127 | gl.glReadBuffer(gl.GL_COLOR_ATTACHMENT0) 128 | gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, ctx.con.offFBO_r) 129 | gl.glDrawBuffer(gl.GL_COLOR_ATTACHMENT0) 130 | gl.glBlitFramebuffer(0, 0, 2 * hmd.width, hmd.height, 131 | 0, 0, 2 * hmd.width, hmd.height, 132 | gl.GL_COLOR_BUFFER_BIT, gl.GL_NEAREST) 133 | # blit to window, left only, window is half-size 134 | gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, ctx.con.offFBO_r) 135 | gl.glReadBuffer(gl.GL_COLOR_ATTACHMENT0) 136 | gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, 0) 137 | gl.glDrawBuffer(gl.GL_BACK if ctx.con.windowDoublebuffer else gl.GL_FRONT) 138 | gl.glBlitFramebuffer(0, 0, hmd.width, hmd.height, 139 | 0, 0, hmd.width // 2, hmd.height // 2, 140 | gl.GL_COLOR_BUFFER_BIT, gl.GL_NEAREST) 141 | # blit to vr texture 142 | gl.glActiveTexture(gl.GL_TEXTURE2) 143 | gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, ctx.con.offFBO_r) 144 | gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT1, gl.GL_TEXTURE_2D, hmd.idtex, 0) 145 | gl.glDrawBuffer(gl.GL_COLOR_ATTACHMENT1) 146 | gl.glBlitFramebuffer(0, 0, 2 * hmd.width, hmd.height, 147 | 0, 0, 2 * hmd.width, hmd.height, 148 | gl.GL_COLOR_BUFFER_BIT, gl.GL_NEAREST) 149 | gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT1, gl.GL_TEXTURE_2D, 0, 0) 150 | gl.glDrawBuffer(gl.GL_COLOR_ATTACHMENT0) 151 | openvr.VRCompositor().submit(openvr.Eye_Left, hmd.vTex, hmd.boundLeft) 152 | openvr.VRCompositor().submit(openvr.Eye_Right, hmd.vTex, hmd.boundRight) 153 | # swap if window is double-buffered, flush just in case 154 | if ctx.con.windowDoublebuffer: 155 | glfw.swap_buffers(window) 156 | gl.glFlush() 157 | 158 | 159 | if __name__ == '__main__': 160 | filename = os.path.join(mujoco_path, 'model', 'humanoid.xml') 161 | v_initPre() 162 | initMuJoCo(filename, hmd.width * 2, hmd.height) 163 | v_initPost() 164 | 165 | FPS = 90.0 166 | lasttm = glfw.get_time() 167 | frametime = sim.data.time 168 | viewFull = PyMjrRect() 169 | viewFull.width, viewFull.height = 2 * hmd.width, hmd.height 170 | nullCam = PyMjvCamera() 171 | 172 | while not glfw.window_should_close(window): 173 | if sim.data.time - frametime > 1 / FPS or sim.data.time < frametime: 174 | functions.mjv_updateScene(sim.model, sim.data, ctx.vopt, ctx.pert, nullCam, CAT_ALL, ctx.scn) 175 | v_update() 176 | functions.mjr_setBuffer(FB_OFFSCREEN, ctx.con) 177 | functions.mjr_render(viewFull, ctx.scn, ctx.con) 178 | FPS = .9 * FPS + .1 / (glfw.get_time() - lasttm) 179 | lasttm = glfw.get_time() 180 | functions.mjr_overlay(FONT_BIG, GRID_BOTTOMLEFT, viewFull, 'FPS %.1f' % FPS, '', ctx.con) 181 | v_render() 182 | frametime = sim.data.time 183 | sim.step() 184 | glfw.poll_events() 185 | # close 186 | openvr.shutdown() 187 | gl.glDeleteTextures(1, hmd.idtex) 188 | del sim 189 | del ctx 190 | glfw.terminate() 191 | -------------------------------------------------------------------------------- /examples/multigpu_rendering.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an example for rendering on multiple GPUs in parallel, 3 | using the multiprocessing module. 4 | """ 5 | from multiprocessing import set_start_method 6 | from time import perf_counter 7 | 8 | from mujoco_py import load_model_from_path, MjRenderPool 9 | 10 | 11 | def main(): 12 | # Image size for rendering 13 | IMAGE_WIDTH = 255 14 | IMAGE_HEIGHT = 255 15 | # Number of frames to render per sim 16 | N_FRAMES = 100 17 | # Number of sims to run in parallel (assumes one per GPU), 18 | # so N_SIMS=2 assumes there are 2 GPUs available. 19 | N_SIMS = 2 20 | 21 | pool = MjRenderPool(load_model_from_path("xmls/tosser.xml"), device_ids=N_SIMS) 22 | 23 | print("main(): start benchmarking", flush=True) 24 | start_t = perf_counter() 25 | 26 | for _ in range(N_FRAMES): 27 | pool.render(IMAGE_WIDTH, IMAGE_HEIGHT) 28 | 29 | t = perf_counter() - start_t 30 | print("Completed in %.1fs: %.3fms, %.1f FPS" % ( 31 | t, t / (N_FRAMES * N_SIMS) * 1000, (N_FRAMES * N_SIMS) / t), 32 | flush=True) 33 | 34 | print("main(): finished", flush=True) 35 | 36 | 37 | if __name__ == "__main__": 38 | set_start_method('spawn') 39 | main() 40 | -------------------------------------------------------------------------------- /examples/render_callback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Shows how to use render callback. 4 | """ 5 | from mujoco_py import load_model_from_path, MjSim, MjViewer 6 | from mujoco_py.modder import TextureModder 7 | import os 8 | 9 | modder = None 10 | def render_callback(sim, viewer): 11 | global modder 12 | if modder is None: 13 | modder = TextureModder(sim) 14 | for name in sim.model.geom_names: 15 | modder.rand_all(name) 16 | 17 | model = load_model_from_path("xmls/fetch/main.xml") 18 | sim = MjSim(model, render_callback=render_callback) 19 | 20 | viewer = MjViewer(sim) 21 | 22 | t = 0 23 | 24 | while True: 25 | viewer.render() 26 | t += 1 27 | if t > 100 and os.getenv('TESTING') is not None: 28 | break -------------------------------------------------------------------------------- /examples/serialize_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Serialization/Deserialization of Models 3 | 4 | Sometimes its useful to send a mujoco model over the network, or save it 5 | to a file with all assets embedded. 6 | """ 7 | import mujoco_py 8 | 9 | # The binary MJB format is preferable, since it includes assets like 10 | # textures and meshes. 11 | model = mujoco_py.load_model_from_path("xmls/claw.xml") 12 | mjb_bytestring = model.get_mjb() 13 | model_from_binary = mujoco_py.load_model_from_mjb(mjb_bytestring) 14 | assert model.nbody == model_from_binary.nbody 15 | 16 | # XML is preferable to MJB when readability and backward compatibility are 17 | # important. 18 | xml_string = model.get_xml() 19 | model_from_xml = mujoco_py.load_model_from_xml(xml_string) 20 | assert model.nbody == model_from_xml.nbody 21 | -------------------------------------------------------------------------------- /examples/setting_state.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Example for how to modifying the MuJoCo qpos during execution. 4 | """ 5 | 6 | import os 7 | from mujoco_py import load_model_from_xml, MjSim, MjViewer 8 | 9 | MODEL_XML = """ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | """ 24 | 25 | 26 | def print_box_xpos(sim): 27 | print("box xpos:", sim.data.get_body_xpos("box")) 28 | 29 | 30 | model = load_model_from_xml(MODEL_XML) 31 | sim = MjSim(model) 32 | viewer = MjViewer(sim) 33 | 34 | states = [{'box:x': +0.8, 'box:y': +0.8}, 35 | {'box:x': -0.8, 'box:y': +0.8}, 36 | {'box:x': -0.8, 'box:y': -0.8}, 37 | {'box:x': +0.8, 'box:y': -0.8}, 38 | {'box:x': +0.0, 'box:y': +0.0}] 39 | 40 | # MjModel.joint_name2id returns the index of a joint in 41 | # MjData.qpos. 42 | x_joint_i = sim.model.get_joint_qpos_addr("box:x") 43 | y_joint_i = sim.model.get_joint_qpos_addr("box:y") 44 | 45 | print_box_xpos(sim) 46 | 47 | while True: 48 | for state in states: 49 | sim_state = sim.get_state() 50 | sim_state.qpos[x_joint_i] = state["box:x"] 51 | sim_state.qpos[y_joint_i] = state["box:y"] 52 | sim.set_state(sim_state) 53 | sim.forward() 54 | print("updated state to", state) 55 | print_box_xpos(sim) 56 | viewer.render() 57 | 58 | if os.getenv('TESTING') is not None: 59 | break 60 | -------------------------------------------------------------------------------- /examples/substep_callback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Example how substep_callback can be used to detect contacts/penetrations that 4 | are not visible in between steps. 5 | 6 | In this example a sphere pushes a cylinder, which then slides away. 7 | The entirety of the contact and separation happens in substeps. 8 | 9 | In between steps there is always zero contacts, so the push is not observed. 10 | 11 | We use a substep_callback to get a sum of the external forces on the cylinder. 12 | """ 13 | from mujoco_py import load_model_from_xml, MjSim 14 | 15 | MODEL_XML = """ 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | """ 33 | 34 | fn = ''' 35 | #define SQUARE(a) (a * a) 36 | void fun(const mjModel* m, mjData* d) { 37 | for (int i = d->ne; i < d->nefc; i++) { 38 | pos_sum += SQUARE(d->efc_pos[i]); 39 | } 40 | } 41 | ''' 42 | 43 | sim = MjSim(load_model_from_xml(MODEL_XML), nsubsteps=50, 44 | substep_callback=fn, userdata_names=['pos_sum']) 45 | t = 0 46 | while t < 10: 47 | t += 1 48 | sim.data.ctrl[0] = .2 49 | print('t', t) 50 | sim.step() 51 | # verify that there are no contacts visible between steps 52 | assert sim.data.ncon == 0, 'No contacts should be detected here' 53 | # verify that contacts (and penetrations) are visible to substep_callback 54 | if t > 1: 55 | assert sim.data.get_userdata('pos_sum') > 0 # detected collision 56 | -------------------------------------------------------------------------------- /examples/tosser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Shows how to toss a capsule to a container. 4 | """ 5 | from mujoco_py import load_model_from_path, MjSim, MjViewer 6 | import os 7 | 8 | model = load_model_from_path("xmls/tosser.xml") 9 | sim = MjSim(model) 10 | 11 | viewer = MjViewer(sim) 12 | 13 | 14 | sim_state = sim.get_state() 15 | 16 | while True: 17 | sim.set_state(sim_state) 18 | 19 | for i in range(1000): 20 | if i < 150: 21 | sim.data.ctrl[:] = 0.0 22 | else: 23 | sim.data.ctrl[:] = -1.0 24 | sim.step() 25 | viewer.render() 26 | 27 | if os.getenv('TESTING') is not None: 28 | break 29 | -------------------------------------------------------------------------------- /mujoco_py/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mujoco_py.builder import cymj, ignore_mujoco_warnings, functions, MujocoException 3 | from mujoco_py.generated import const 4 | from mujoco_py.mjrenderpool import MjRenderPool 5 | from mujoco_py.mjviewer import MjViewer, MjViewerBasic 6 | from mujoco_py.version import __version__, get_version 7 | 8 | load_model_from_path = cymj.load_model_from_path 9 | load_model_from_xml = cymj.load_model_from_xml 10 | load_model_from_mjb = cymj.load_model_from_mjb 11 | MjSim = cymj.MjSim 12 | MjSimState = cymj.MjSimState 13 | MjRenderContext = cymj.MjRenderContext 14 | MjRenderContextOffscreen = cymj.MjRenderContextOffscreen 15 | MjRenderContextWindow = cymj.MjRenderContextWindow 16 | MjBatchRenderer = cymj.MjBatchRenderer 17 | GlfwContext = cymj.GlfwContext 18 | 19 | 20 | # Public API: 21 | __all__ = ['MjSim', 'MjSimState', 22 | 'MjRenderContextOffscreen', 'MjRenderContextWindow', 23 | 'MjRenderContext', 'MjViewer', 'MjViewerBasic', 24 | 'MujocoException', 'MjRenderPool', 'MjBatchRenderer', 'GlfwContext', 25 | 'load_model_from_path', 'load_model_from_xml', 26 | 'load_model_from_mjb', 27 | 'ignore_mujoco_warnings', 'const', "functions", 28 | "__version__", "get_version"] 29 | 30 | -------------------------------------------------------------------------------- /mujoco_py/cymj.pyx: -------------------------------------------------------------------------------- 1 | # cython: language_level=3 2 | import copy 3 | import logging 4 | import os 5 | import platform 6 | import tempfile 7 | import sys 8 | from collections import namedtuple 9 | from libc.stdlib cimport malloc, free 10 | from libc.string cimport strncpy 11 | from numbers import Number 12 | from tempfile import TemporaryDirectory 13 | 14 | import numpy as np 15 | from cython cimport view 16 | from cython.parallel import parallel, prange 17 | from mujoco_py.generated import const 18 | 19 | include "generated/wrappers.pxi" 20 | include "opengl_context.pyx" 21 | include "mjsim.pyx" 22 | include "mjsimstate.pyx" 23 | include "mjrendercontext.pyx" 24 | include "mjbatchrenderer.pyx" 25 | include "mjpid.pyx" 26 | 27 | cdef extern from "gl/glshim.h": 28 | 29 | cdef int usingEGL() 30 | cdef int initOpenGL(int device_id) 31 | cdef void closeOpenGL() 32 | cdef int makeOpenGLContextCurrent(int device_id) 33 | cdef int setOpenGLBufferSize(int device_id, int width, int height) 34 | 35 | cdef unsigned int createPBO(int width, int height, int batchSize, int use_short) 36 | cdef void freePBO(unsigned int pixelBuffer) 37 | cdef void copyFBOToPBO(mjrContext* con, 38 | unsigned int pbo_rgb, unsigned int pbo_depth, 39 | mjrRect viewport, int bufferOffset) 40 | cdef void readPBO(unsigned char *buffer_rgb, unsigned short *buffer_depth, 41 | unsigned int pbo_rgb, unsigned int pbo_depth, 42 | int width, int height, int batchSize) 43 | 44 | 45 | 46 | # TODO: make this function or class so these comments turn into doc strings: 47 | 48 | # Python warning callback function, which is set 49 | # MuJoCo has a user-settable callback function for warnings: mju_user_warning() 50 | # We want to supply a python function, and be able to raise exceptions. 51 | # To do this we have to wrap two things: 52 | 53 | # This is the python callback function. We save it in the global() context 54 | # so we can access it from a C wrapper function (c_warning_callback) 55 | cdef object py_warning_callback 56 | cdef object py_error_callback 57 | # This is the saved exception. Because the C callback can not propagate 58 | # exceptions, this must be set to None before calling into MuJoCo, and then 59 | # inspected afterwards. 60 | # These are combined in a simple class which handles both: 61 | # with wrap_mujoco_warning(): 62 | # mj_somefunc() 63 | cdef object py_warning_exception = None 64 | cdef object py_error_exception = None 65 | 66 | 67 | cdef void c_warning_callback(const char *msg) with gil: 68 | ''' 69 | Wraps the warning callback so we can raise exceptions. 70 | Because callbacks can't propagate exceptions, we set a global that has 71 | to be inspected later. 72 | Use wrap_mujoco_warning() to check for that saved exception and 73 | re-raise it back to the caller. 74 | ''' 75 | global py_warning_callback 76 | try: 77 | ( py_warning_callback)(msg) 78 | except Exception as e: 79 | global py_warning_exception 80 | py_warning_exception = e 81 | 82 | 83 | def set_warning_callback(warn): 84 | ''' 85 | Set a user-defined warning callback. It should take in a string message 86 | (the warning string) and raise an Exception. 87 | See c_warning_callback, which is the C wrapper to the user defined function 88 | ''' 89 | global py_warning_callback 90 | global mju_user_warning 91 | py_warning_callback = warn 92 | mju_user_warning = c_warning_callback 93 | 94 | 95 | def get_warning_callback(): 96 | ''' 97 | Returns the user-defined warning callback, for use in e.g. a context 98 | manager. 99 | ''' 100 | global py_warning_callback 101 | return py_warning_callback 102 | 103 | 104 | cdef void c_error_callback(const char *msg) with gil: 105 | ''' 106 | Wraps the error callback so that we can pass a python function to the callback. 107 | MuJoCo error handlers are expected to terminate the program and never return. 108 | ''' 109 | global py_error_callback 110 | 111 | try: 112 | ( py_error_callback)(msg) 113 | except Exception as e: 114 | global py_error_exception 115 | py_error_exception = e 116 | 117 | 118 | def set_error_callback(err_callback): 119 | ''' 120 | Set a user-defined error callback. It should take in a string message 121 | (the warning string) and terminate the program. 122 | See c_warning_callback, which is the C wrapper to the user defined function 123 | ''' 124 | global py_error_callback 125 | global mju_user_error 126 | py_error_callback = err_callback 127 | mju_user_error = c_error_callback 128 | 129 | 130 | def get_error_callback(): 131 | ''' 132 | Returns the user-defined warning callback, for use in e.g. a context 133 | manager. 134 | ''' 135 | global py_error_callback 136 | return py_error_callback 137 | 138 | 139 | class wrap_mujoco_warning(object): 140 | ''' 141 | Class to wrap capturing exceptions raised during warning callbacks. 142 | Use this to capture warnings in mujoco calls. Example: 143 | with wrap_mujoco_warning(): 144 | mj_somefunc() 145 | ''' 146 | def __enter__(self): 147 | global py_warning_exception 148 | py_warning_exception = None 149 | global py_error_exception 150 | py_error_exception = None 151 | def __exit__(self, type, value, traceback): 152 | global py_warning_exception 153 | global py_error_exception 154 | 155 | if py_warning_exception is not None: 156 | raise py_warning_exception 157 | 158 | if py_error_exception is not None: 159 | raise py_error_exception 160 | 161 | 162 | def load_model_from_path(str path): 163 | """Loads model from path.""" 164 | cdef char errstr[300] 165 | cdef mjModel *model 166 | with wrap_mujoco_warning(): 167 | if (path.endswith(".mjb")): 168 | model = mj_loadModel(path.encode(), NULL) 169 | elif (path.endswith(".xml")): 170 | model = mj_loadXML(path.encode(), NULL, errstr, 300) 171 | else: 172 | raise RuntimeError("Unrecognized extension for %s. Expected .xml or .mjb" % path) 173 | 174 | if model == NULL: 175 | raise Exception('Failed to load XML file: %s. mj_loadXML error: %s' % (path, errstr,)) 176 | return WrapMjModel(model) 177 | 178 | def load_model_from_xml(str xml_str): 179 | """ 180 | Loads and returns a PyMjModel model from a string containing XML markup. 181 | Saves the XML string used to create the returned model in `model.xml`. 182 | """ 183 | cdef char errstr[300] 184 | cdef mjModel *model 185 | with wrap_mujoco_warning(): 186 | with tempfile.NamedTemporaryFile(suffix=".xml", delete=True) as fp: 187 | fp.write(xml_str.encode()) 188 | fp.flush() 189 | model = mj_loadXML(fp.name.encode(), NULL, errstr, 300) 190 | if model == NULL: 191 | raise Exception('%s\nFailed to load XML from string. mj_loadXML error: %s' % (xml_str, errstr,)) 192 | return WrapMjModel(model) 193 | 194 | 195 | def load_model_from_mjb(bytes mjb_bytes): 196 | """ 197 | Loads and returns a PyMjModel model from bytes encoded MJB. 198 | MJB is a MuJoCo-custom format that includes assets like meshes/textures. 199 | """ 200 | cdef mjModel *model 201 | with wrap_mujoco_warning(): 202 | with TemporaryDirectory() as td: 203 | filename = os.path.join(td, 'model.mjb') 204 | with open(filename, 'wb') as f: 205 | f.write(mjb_bytes) 206 | model = mj_loadModel(filename.encode(), NULL) 207 | if model == NULL: 208 | raise Exception('%s\nFailed to load MJB') 209 | return WrapMjModel(model) 210 | -------------------------------------------------------------------------------- /mujoco_py/generated/README.md: -------------------------------------------------------------------------------- 1 | This directory contains files which are automatically generated. 2 | Don't modify them directly. -------------------------------------------------------------------------------- /mujoco_py/generated/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/generated/__init__.py -------------------------------------------------------------------------------- /mujoco_py/gl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/gl/__init__.py -------------------------------------------------------------------------------- /mujoco_py/gl/dummyshim.c: -------------------------------------------------------------------------------- 1 | #include "glshim.h" 2 | 3 | int usingEGL() { 4 | return 0; 5 | } 6 | 7 | int initOpenGL(int device_id) { 8 | return 1; 9 | } 10 | 11 | int setOpenGLBufferSize(int device_id, int width, int height) { 12 | return 1; 13 | } 14 | 15 | int makeOpenGLContextCurrent(int device_id) { 16 | return 1; 17 | } 18 | 19 | void closeOpenGL() { 20 | } 21 | 22 | unsigned int createPBO(int width, int height, int batchSize, int use_short) { 23 | return 0; 24 | } 25 | 26 | void freePBO(unsigned int pixelBuffer) { 27 | } 28 | 29 | void copyFBOToPBO(mjrContext* con, 30 | unsigned int pbo_rgb, unsigned int pbo_depth, 31 | mjrRect viewport, int bufferOffset) { 32 | } 33 | 34 | void readPBO(unsigned char *buffer_rgb, unsigned short *buffer_depth, 35 | unsigned int pbo_rgb, unsigned int pbo_depth, 36 | int width, int height, int batchSize) { 37 | } 38 | -------------------------------------------------------------------------------- /mujoco_py/gl/eglplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __eglplatform_h_ 2 | #define __eglplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2007-2013 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Platform-specific types and definitions for egl.h 28 | * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $ 29 | * 30 | * Adopters may modify khrplatform.h and this file to suit their platform. 31 | * You are encouraged to submit all modifications to the Khronos group so that 32 | * they can be included in future versions of this file. Please submit changes 33 | * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) 34 | * by filing a bug against product "EGL" component "Registry". 35 | */ 36 | 37 | #include "khrplatform.h" 38 | 39 | /* Macros used in EGL function prototype declarations. 40 | * 41 | * EGL functions should be prototyped as: 42 | * 43 | * EGLAPI return-type EGLAPIENTRY eglFunction(arguments); 44 | * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments); 45 | * 46 | * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h 47 | */ 48 | 49 | #ifndef EGLAPI 50 | #define EGLAPI KHRONOS_APICALL 51 | #endif 52 | 53 | #ifndef EGLAPIENTRY 54 | #define EGLAPIENTRY KHRONOS_APIENTRY 55 | #endif 56 | #define EGLAPIENTRYP EGLAPIENTRY* 57 | 58 | /* The types NativeDisplayType, NativeWindowType, and NativePixmapType 59 | * are aliases of window-system-dependent types, such as X Display * or 60 | * Windows Device Context. They must be defined in platform-specific 61 | * code below. The EGL-prefixed versions of Native*Type are the same 62 | * types, renamed in EGL 1.3 so all types in the API start with "EGL". 63 | * 64 | * Khronos STRONGLY RECOMMENDS that you use the default definitions 65 | * provided below, since these changes affect both binary and source 66 | * portability of applications using EGL running on different EGL 67 | * implementations. 68 | */ 69 | 70 | #if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ 71 | #ifndef WIN32_LEAN_AND_MEAN 72 | #define WIN32_LEAN_AND_MEAN 1 73 | #endif 74 | #include 75 | 76 | typedef HDC EGLNativeDisplayType; 77 | typedef HBITMAP EGLNativePixmapType; 78 | typedef HWND EGLNativeWindowType; 79 | 80 | #elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ 81 | 82 | typedef int EGLNativeDisplayType; 83 | typedef void *EGLNativeWindowType; 84 | typedef void *EGLNativePixmapType; 85 | 86 | #elif defined(__ANDROID__) || defined(ANDROID) 87 | 88 | #include 89 | 90 | struct egl_native_pixmap_t; 91 | 92 | typedef struct ANativeWindow* EGLNativeWindowType; 93 | typedef struct egl_native_pixmap_t* EGLNativePixmapType; 94 | typedef void* EGLNativeDisplayType; 95 | 96 | #elif defined(__unix__) 97 | 98 | /* X11 (tentative) */ 99 | #include 100 | #include 101 | 102 | typedef Display *EGLNativeDisplayType; 103 | typedef Pixmap EGLNativePixmapType; 104 | typedef Window EGLNativeWindowType; 105 | 106 | #else 107 | #error "Platform not recognized" 108 | #endif 109 | 110 | /* EGL 1.2 types, renamed for consistency in EGL 1.3 */ 111 | typedef EGLNativeDisplayType NativeDisplayType; 112 | typedef EGLNativePixmapType NativePixmapType; 113 | typedef EGLNativeWindowType NativeWindowType; 114 | 115 | 116 | /* Define EGLint. This must be a signed integral type large enough to contain 117 | * all legal attribute names and values passed into and out of EGL, whether 118 | * their type is boolean, bitmask, enumerant (symbolic constant), integer, 119 | * handle, or other. While in general a 32-bit integer will suffice, if 120 | * handles are 64 bit types, then EGLint should be defined as a signed 64-bit 121 | * integer type. 122 | */ 123 | typedef khronos_int32_t EGLint; 124 | 125 | #endif /* __eglplatform_h */ 126 | -------------------------------------------------------------------------------- /mujoco_py/gl/glshim.h: -------------------------------------------------------------------------------- 1 | #ifndef __GLSHIM_H__ 2 | #define __GLSHIM_H__ 3 | 4 | #include "mujoco.h" 5 | #include "mjrender.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | int usingEGL(); 12 | int initOpenGL(int device_id); 13 | void closeOpenGL(); 14 | int makeOpenGLContextCurrent(int device_id); 15 | int setOpenGLBufferSize(int device_id, int width, int height); 16 | 17 | unsigned int createPBO(int width, int height, int batchSize, int use_short); 18 | void freePBO(unsigned int pixelBuffer); 19 | void copyFBOToPBO(mjrContext* con, 20 | unsigned int pbo_rgb, unsigned int pbo_depth, 21 | mjrRect viewport, int bufferOffset); 22 | void readPBO(unsigned char *buffer_rgb, unsigned short *buffer_depth, 23 | unsigned int pbo_rgb, unsigned int pbo_depth, 24 | int width, int height, int batchSize); 25 | 26 | #ifdef __cplusplus 27 | } // extern "C" 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /mujoco_py/gl/osmesashim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "glshim.h" 3 | 4 | OSMesaContext ctx; 5 | 6 | // this size was picked pretty arbitrarily 7 | int BUFFER_WIDTH = 1024; 8 | int BUFFER_HEIGHT = 1024; 9 | // 4 channels for RGBA 10 | unsigned char buffer[1024 * 1024 * 4]; 11 | 12 | int is_initialized = 0; 13 | 14 | int usingEGL() { 15 | return 0; 16 | } 17 | 18 | int initOpenGL(int device_id) { 19 | if (is_initialized) 20 | return 1; 21 | 22 | // note: device id not used here 23 | ctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0); 24 | if( !ctx ) { 25 | printf("OSMesa context creation failed\n"); 26 | return -1; 27 | } 28 | 29 | if( !OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, BUFFER_WIDTH, BUFFER_HEIGHT) ) { 30 | printf("OSMesa make current failed\n"); 31 | return -1; 32 | } 33 | 34 | is_initialized = 1; 35 | 36 | return 1; 37 | } 38 | 39 | int makeOpenGLContextCurrent(int device_id) { 40 | // Don't need to make context current here, causes issues with large tests 41 | return 1; 42 | } 43 | 44 | int setOpenGLBufferSize(int device_id, int width, int height) { 45 | if (width > BUFFER_WIDTH || height > BUFFER_HEIGHT) { 46 | printf("Buffer size too big\n"); 47 | return -1; 48 | } 49 | // Noop since we don't support changing the actual buffer 50 | return 1; 51 | } 52 | 53 | void closeOpenGL() { 54 | if (is_initialized) { 55 | OSMesaDestroyContext(ctx); 56 | is_initialized = 0; 57 | } 58 | } 59 | 60 | unsigned int createPBO(int width, int height, int batchSize, int use_short) { 61 | return 0; 62 | } 63 | 64 | void freePBO(unsigned int pixelBuffer) { 65 | } 66 | 67 | void copyFBOToPBO(mjrContext* con, 68 | unsigned int pbo_rgb, unsigned int pbo_depth, 69 | mjrRect viewport, int bufferOffset) { 70 | } 71 | 72 | void readPBO(unsigned char *buffer_rgb, unsigned short *buffer_depth, 73 | unsigned int pbo_rgb, unsigned int pbo_depth, 74 | int width, int height, int batchSize) { 75 | } 76 | -------------------------------------------------------------------------------- /mujoco_py/mjsimstate.pyx: -------------------------------------------------------------------------------- 1 | 2 | class MjSimState(namedtuple('SimStateBase', 'time qpos qvel act udd_state')): 3 | """Represents a snapshot of the simulator's state. 4 | 5 | This includes time, qpos, qvel, act, and udd_state. 6 | """ 7 | __slots__ = () 8 | 9 | # need to implement this because numpy doesn't support == on arrays 10 | def __eq__(self, other): 11 | if not isinstance(other, self.__class__): 12 | return NotImplemented 13 | 14 | if set(self.udd_state.keys()) != set(other.udd_state.keys()): 15 | return False 16 | 17 | for k in self.udd_state.keys(): 18 | if isinstance(self.udd_state[k], Number) and self.udd_state[k] != other.udd_state[k]: 19 | return False 20 | elif not np.array_equal(self.udd_state[k], other.udd_state[k]): 21 | return False 22 | 23 | return (self.time == other.time and 24 | np.array_equal(self.qpos, other.qpos) and 25 | np.array_equal(self.qvel, other.qvel) and 26 | np.array_equal(self.act, other.act)) 27 | 28 | def __ne__(self, other): 29 | return not self.__eq__(other) 30 | 31 | def flatten(self): 32 | """ Flattens a state into a numpy array of numbers.""" 33 | if self.act is None: 34 | act = np.empty(0) 35 | else: 36 | act = self.act 37 | state_tuple = ([self.time], self.qpos, self.qvel, act, 38 | MjSimState._flatten_dict(self.udd_state)) 39 | return np.concatenate(state_tuple) 40 | 41 | @staticmethod 42 | def _flatten_dict(d): 43 | a = [] 44 | for k in sorted(d.keys()): 45 | v = d[k] 46 | if isinstance(v, Number): 47 | a.extend([v]) 48 | else: 49 | a.extend(v.ravel()) 50 | 51 | return np.array(a) 52 | 53 | @staticmethod 54 | def from_flattened(array, sim): 55 | idx_time = 0 56 | idx_qpos = idx_time + 1 57 | idx_qvel = idx_qpos + sim.model.nq 58 | idx_act = idx_qvel + sim.model.nv 59 | idx_udd = idx_act + sim.model.na 60 | 61 | time = array[idx_time] 62 | qpos = array[idx_qpos:idx_qpos + sim.model.nq] 63 | qvel = array[idx_qvel:idx_qvel + sim.model.nv] 64 | if sim.model.na == 0: 65 | act = None 66 | else: 67 | act = array[idx_act:idx_act + sim.model.na] 68 | flat_udd_state = array[idx_udd:] 69 | udd_state = MjSimState._unflatten_dict(flat_udd_state, sim.udd_state) 70 | 71 | return MjSimState(time, qpos, qvel, act, udd_state) 72 | 73 | @staticmethod 74 | def _unflatten_dict(a, schema_example): 75 | d = {} 76 | idx = 0 77 | for k in sorted(schema_example.keys()): 78 | schema_val = schema_example[k] 79 | if isinstance(schema_val, Number): 80 | val = a[idx] 81 | idx += 1 82 | d[k] = val 83 | else: 84 | assert isinstance(schema_val, np.ndarray) 85 | val_array = a[idx:idx+schema_val.size] 86 | idx += schema_val.size 87 | val = np.array(val_array).reshape(schema_val.shape) 88 | d[k] = val 89 | return d 90 | -------------------------------------------------------------------------------- /mujoco_py/opengl_context.pyx: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from abc import ABCMeta, abstractmethod 4 | from mujoco_py.utils import discover_mujoco 5 | 6 | 7 | def _add_mujoco_bin_to_dyld_library_path(): 8 | mujoco_path = discover_mujoco() 9 | bin_path = os.path.join(mujoco_path, "bin") 10 | old_dyld_library_path = os.getenv("DYLD_LIBRARY_PATH", "") 11 | os.environ["DYLD_LIBRARY_PATH"] = "{}:{}".format( 12 | bin_path, old_dyld_library_path) 13 | 14 | 15 | try: 16 | _add_mujoco_bin_to_dyld_library_path() 17 | import glfw 18 | except ImportError: 19 | pass 20 | 21 | 22 | class OpenGLContext(metaclass=ABCMeta): 23 | 24 | @abstractmethod 25 | def make_context_current(self): 26 | raise NotImplementedError() 27 | 28 | @abstractmethod 29 | def set_buffer_size(self, width, height): 30 | raise NotImplementedError() 31 | 32 | 33 | class GlfwError(RuntimeError): 34 | pass 35 | 36 | 37 | class GlfwContext(OpenGLContext): 38 | 39 | _INIT_WIDTH = 1000 40 | _INIT_HEIGHT = 1000 41 | _GLFW_IS_INITIALIZED = False 42 | 43 | def __init__(self, offscreen=False, quiet=False): 44 | GlfwContext._init_glfw() 45 | 46 | self._width = self._INIT_WIDTH 47 | self._height = self._INIT_HEIGHT 48 | self.window = self._create_window(offscreen, quiet=quiet) 49 | self._set_window_size(self._width, self._height) 50 | 51 | @staticmethod 52 | def _init_glfw(): 53 | if GlfwContext._GLFW_IS_INITIALIZED: 54 | return 55 | 56 | if 'glfw' not in globals(): 57 | raise GlfwError("GLFW not installed") 58 | 59 | glfw.set_error_callback(GlfwContext._glfw_error_callback) 60 | 61 | # HAX: sometimes first init() fails, while second works fine. 62 | glfw.init() 63 | if not glfw.init(): 64 | raise GlfwError("Failed to initialize GLFW") 65 | 66 | GlfwContext._GLFW_IS_INITIALIZED = True 67 | 68 | def make_context_current(self): 69 | glfw.make_context_current(self.window) 70 | 71 | def set_buffer_size(self, width, height): 72 | self._set_window_size(width, height) 73 | self._width = width 74 | self._height = height 75 | 76 | def _create_window(self, offscreen, quiet=False): 77 | if offscreen: 78 | if not quiet: 79 | print("Creating offscreen glfw") 80 | glfw.window_hint(glfw.VISIBLE, 0) 81 | glfw.window_hint(glfw.DOUBLEBUFFER, 0) 82 | init_width, init_height = self._INIT_WIDTH, self._INIT_HEIGHT 83 | else: 84 | if not quiet: 85 | print("Creating window glfw") 86 | glfw.window_hint(glfw.SAMPLES, 4) 87 | glfw.window_hint(glfw.VISIBLE, 1) 88 | glfw.window_hint(glfw.DOUBLEBUFFER, 1) 89 | resolution, _, refresh_rate = glfw.get_video_mode( 90 | glfw.get_primary_monitor()) 91 | init_width, init_height = resolution 92 | 93 | self._width = init_width 94 | self._height = init_height 95 | window = glfw.create_window( 96 | self._width, self._height, "mujoco_py", None, None) 97 | 98 | if not window: 99 | raise GlfwError("Failed to create GLFW window") 100 | 101 | return window 102 | 103 | def get_buffer_size(self): 104 | return glfw.get_framebuffer_size(self.window) 105 | 106 | def _set_window_size(self, target_width, target_height): 107 | self.make_context_current() 108 | if target_width != self._width or target_height != self._height: 109 | self._width = target_width 110 | self._height = target_height 111 | glfw.set_window_size(self.window, target_width, target_height) 112 | 113 | # HAX: When running on a Mac with retina screen, the size 114 | # sometimes doubles 115 | width, height = glfw.get_framebuffer_size(self.window) 116 | if target_width != width and "darwin" in sys.platform.lower(): 117 | glfw.set_window_size(self.window, target_width // 2, target_height // 2) 118 | 119 | @staticmethod 120 | def _glfw_error_callback(error_code, description): 121 | print("GLFW error (code %d): %s", error_code, description) 122 | 123 | 124 | class OffscreenOpenGLContext(): 125 | 126 | def __init__(self, device_id): 127 | self.device_id = device_id 128 | res = initOpenGL(device_id) 129 | if res != 1: 130 | raise RuntimeError("Failed to initialize OpenGL") 131 | 132 | def close(self): 133 | # TODO: properly close OpenGL in our contexts 134 | closeOpenGL() 135 | 136 | def make_context_current(self): 137 | makeOpenGLContextCurrent(self.device_id) 138 | 139 | def set_buffer_size(self, int width, int height): 140 | res = setOpenGLBufferSize(self.device_id, width, height) 141 | if res != 1: 142 | raise RuntimeError("Failed to set buffer size") 143 | -------------------------------------------------------------------------------- /mujoco_py/pxd/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/pxd/__init__.py -------------------------------------------------------------------------------- /mujoco_py/pxd/mjrender.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from "mjrender.h" nogil: 2 | # Global constants 3 | enum: mjNAUX 4 | enum: mjMAXTEXTURE 5 | 6 | ctypedef enum mjtGridPos: # grid position for overlay 7 | mjGRID_TOPLEFT = 0, # top left 8 | mjGRID_TOPRIGHT, # top right 9 | mjGRID_BOTTOMLEFT, # bottom left 10 | mjGRID_BOTTOMRIGHT # bottom right 11 | 12 | 13 | ctypedef enum mjtFramebuffer: # OpenGL framebuffer option 14 | mjFB_WINDOW = 0, # default/window buffer 15 | mjFB_OFFSCREEN # offscreen buffer 16 | 17 | 18 | ctypedef enum mjtFontScale: # font scale, used at context creation 19 | mjFONTSCALE_100 = 100, # normal scale, suitable in the absence of DPI scaling 20 | mjFONTSCALE_150 = 150, # 150% scale 21 | mjFONTSCALE_200 = 200 # 200% scale 22 | 23 | 24 | ctypedef enum mjtFont: # font type, used at each text operation 25 | mjFONT_NORMAL = 0, # normal font 26 | mjFONT_SHADOW, # normal font with shadow (for higher contrast) 27 | mjFONT_BIG # big font (for user alerts) 28 | 29 | ctypedef struct mjrRect: # OpenGL rectangle 30 | int left # left (usually 0) 31 | int bottom # bottom (usually 0) 32 | int width # width (usually buffer width) 33 | int height # height (usually buffer height) 34 | 35 | 36 | ctypedef struct mjrContext: # custom OpenGL context 37 | # parameters copied from mjVisual 38 | float lineWidth # line width for wireframe rendering 39 | float shadowClip # clipping radius for directional lights 40 | float shadowScale # fraction of light cutoff for spot lights 41 | float fogStart # fog start = stat.extent * vis.map.fogstart 42 | float fogEnd # fog end = stat.extent * vis.map.fogend 43 | float fogRGBA[4] # fog rgba 44 | int shadowSize # size of shadow map texture 45 | int offWidth # width of offscreen buffer 46 | int offHeight # height of offscreen buffer 47 | int offSamples # number of offscreen buffer multisamples 48 | 49 | # parameters specified at creation 50 | int fontScale; # font scale 51 | int auxWidth[mjNAUX] # auxiliary buffer width 52 | int auxHeight[mjNAUX] # auxiliary buffer height 53 | int auxSamples[mjNAUX] # auxiliary buffer multisamples 54 | 55 | # offscreen rendering objects 56 | unsigned int offFBO # offscreen framebuffer object 57 | unsigned int offFBO_r # offscreen framebuffer for resolving multisamples 58 | unsigned int offColor # offscreen color buffer 59 | unsigned int offColor_r # offscreen color buffer for resolving multisamples 60 | unsigned int offDepthStencil # offscreen depth and stencil buffer 61 | unsigned int offDepthStencil_r # offscreen depth and stencil buffer for resolving multisamples 62 | 63 | # shadow rendering objects 64 | unsigned int shadowFBO # shadow map framebuffer object 65 | unsigned int shadowTex # shadow map texture 66 | 67 | # auxiliary buffers 68 | unsigned int auxFBO[mjNAUX] # auxiliary framebuffer object 69 | unsigned int auxFBO_r[mjNAUX] # auxiliary framebuffer object for resolving 70 | unsigned int auxColor[mjNAUX] # auxiliary color buffer 71 | unsigned int auxColor_r[mjNAUX] # auxiliary color buffer for resolving 72 | 73 | # texture objects and info 74 | int ntexture # number of allocated textures 75 | int textureType[100] # type of texture (mjtTexture) 76 | unsigned int texture[100] # texture names 77 | 78 | # displaylist starting positions 79 | unsigned int basePlane # all planes from model 80 | unsigned int baseMesh # all meshes from model 81 | unsigned int baseHField # all hfields from model 82 | unsigned int baseBuiltin # all buildin geoms, with quality from model 83 | unsigned int baseFontNormal # normal font 84 | unsigned int baseFontShadow # shadow font 85 | unsigned int baseFontBig # big font 86 | 87 | # displaylist ranges 88 | int rangePlane # all planes from model 89 | int rangeMesh # all meshes from model 90 | int rangeHField # all hfields from model 91 | int rangeBuiltin # all builtin geoms, with quality from model 92 | int rangeFont # all characters in font 93 | 94 | # skin VBOs 95 | int nskin # number of skins 96 | unsigned int* skinvertVBO # skin vertex position VBOs 97 | unsigned int* skinnormalVBO # skin vertex normal VBOs 98 | unsigned int* skintexcoordVBO # skin vertex texture coordinate VBOs 99 | unsigned int* skinfaceVBO # skin face index VBOs 100 | 101 | # character info 102 | int charWidth[127] # character widths: normal and shadow 103 | int charWidthBig[127] # chacarter widths: big 104 | int charHeight # character heights: normal and shadow 105 | int charHeightBig # character heights: big 106 | 107 | # capabilities 108 | int glewInitialized # is glew initialized 109 | int windowAvailable # is default/window framebuffer available 110 | int windowSamples # number of samples for default/window framebuffer 111 | int windowStereo # is stereo available for default/window framebuffer 112 | int windowDoublebuffer # is default/window framebuffer double buffered 113 | 114 | # only field that changes after mjr_makeContext 115 | int currentBuffer # currently active framebuffer: mjFB_WINDOW or mjFB_OFFSCREEN 116 | -------------------------------------------------------------------------------- /mujoco_py/pxd/mjui.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from "mjui.h" nogil: 2 | # Global constants 3 | enum: mjMAXUISECT 4 | enum: mjMAXUIITEM 5 | enum: mjMAXUITEXT 6 | enum: mjMAXUINAME 7 | enum: mjMAXUIMULTI 8 | enum: mjMAXUIEDIT 9 | enum: mjMAXUIRECT 10 | enum: mjSEPCLOSED 11 | 12 | 13 | # predicate function: set enable/disable based on item category 14 | ctypedef int (*mjfItemEnable)(int category, void* data); 15 | 16 | ctypedef struct mjuiState: # mouse and keyboard state 17 | # constants set by user 18 | int nrect # number of rectangles used 19 | mjrRect rect[mjMAXUIRECT] # rectangles (index 0: entire window) 20 | void* userdata # pointer to user data (for callbacks) 21 | 22 | # event type 23 | int type # (type mjtEvent) 24 | 25 | # mouse buttons 26 | int left # is left button down 27 | int right # is right button down 28 | int middle # is middle button down 29 | int doubleclick # is last press a double click 30 | int button # which button was pressed (mjtButton) 31 | double buttontime # time of last button press 32 | 33 | # mouse position 34 | double x # x position 35 | double y # y position 36 | double dx # x displacement 37 | double dy # y displacement 38 | double sx # x scroll 39 | double sy # y scroll 40 | 41 | # keyboard 42 | int control # is control down 43 | int shift # is shift down 44 | int alt # is alt down 45 | int key # which key was pressed 46 | double keytime # time of last key press 47 | 48 | # rectangle ownership and dragging 49 | int mouserect # which rectangle contains mouse 50 | int dragrect # which rectangle is dragged with mouse 51 | int dragbutton # which button started drag (mjtButton) 52 | 53 | ctypedef struct mjuiThemeSpacing: # UI visualization theme spacing 54 | int total # total width 55 | int scroll # scrollbar width 56 | int label # label width 57 | int section # section gap 58 | int itemside # item side gap 59 | int itemmid # item middle gap 60 | int itemver # item vertical gap 61 | int texthor # text horizontal gap 62 | int textver # text vertical gap 63 | int linescroll # number of pixels to scroll 64 | int samples # number of multisamples 65 | 66 | 67 | ctypedef struct mjuiThemeColor: # UI visualization theme color 68 | float master[3] # master background 69 | float thumb[3] # scrollbar thumb 70 | float secttitle[3] # section title 71 | float sectfont[3] # section font 72 | float sectsymbol[3] # section symbol 73 | float sectpane[3] # section pane 74 | float shortcut[3] # shortcut background 75 | float fontactive[3] # font active 76 | float fontinactive[3] # font inactive 77 | float decorinactive[3] # decor inactive 78 | float decorinactive2[3] # inactive slider color 2 79 | float button[3] # button 80 | float check[3] # check 81 | float radio[3] # radio 82 | float select[3] # select 83 | float select2[3] # select pane 84 | float slider[3] # slider 85 | float slider2[3] # slider color 2 86 | float edit[3] # edit 87 | float edit2[3] # edit invalid 88 | float cursor[3] # edit cursor 89 | 90 | 91 | ctypedef struct mjuiItem: # UI item 92 | # common properties 93 | int type # type (mjtItem) 94 | char name[mjMAXUINAME] # name 95 | int state # 0: disable, 1: enable, 2+: use predicate 96 | void *pdata # data pointer (type-specific) 97 | int sectionid # id of section containing item 98 | int itemid # id of item within section 99 | 100 | # internal 101 | mjrRect rect # rectangle occupied by item 102 | 103 | 104 | ctypedef struct mjuiSection: # UI section 105 | # properties 106 | char name[mjMAXUINAME] # name 107 | int state # 0: closed, 1: open 108 | int modifier # 0: none, 1: control, 2: shift; 4: alt 109 | int shortcut # shortcut key; 0: undefined 110 | int nitem # number of items in use 111 | mjuiItem item[mjMAXUIITEM] # preallocated array of items 112 | # internal 113 | mjrRect rtitle # rectangle occupied by title 114 | mjrRect rcontent # rectangle occupied by content 115 | 116 | 117 | ctypedef struct mjUI: # entire UI 118 | # constants set by user 119 | mjuiThemeSpacing spacing # UI theme spacing 120 | mjuiThemeColor color # UI theme color 121 | mjfItemEnable predicate # callback to set item state programmatically 122 | void* userdata # pointer to user data (passed to predicate) 123 | int rectid # index of this ui rectangle in mjuiState 124 | int auxid # aux buffer index of this ui 125 | int radiocol # number of radio columns (0 defaults to 2) 126 | 127 | # UI sizes (framebuffer units) 128 | int width # width 129 | int height # current heigth 130 | int maxheight # height when all sections open 131 | int scroll # scroll from top of UI 132 | 133 | # mouse focus 134 | int mousesect # 0: none, -1: scroll, otherwise 1+section 135 | int mouseitem # item within section 136 | int mousehelp # help button down: print shortcuts 137 | 138 | # keyboard focus and edit 139 | int editsect # 0: none, otherwise 1+section 140 | int edititem # item within section 141 | int editcursor # cursor position 142 | int editscroll # horizontal scroll 143 | char edittext[mjMAXUITEXT] # current text 144 | mjuiItem* editchanged # pointer to changed edit in last mjui_event 145 | 146 | # sections 147 | int nsect # number of sections in use 148 | mjuiSection sect[mjMAXUISECT] # preallocated array of sections 149 | 150 | 151 | ctypedef struct mjuiDef: 152 | int type # type (mjtItem); -1: section 153 | char name[mjMAXUINAME] # name 154 | int state # state 155 | void* pdata # pointer to data 156 | char other[mjMAXUITEXT] # string with type-specific properties 157 | -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_glfw_context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_glfw_context.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_materials.premod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_materials.premod.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_materials.props.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_materials.props.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_materials.rand_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_materials.rand_all.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop0_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop0_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop0_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop1_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop1_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop2_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop2_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_multiple_sims.loop2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_multiple_sims.loop2_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_cameras.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_cameras.0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_cameras.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_cameras.1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_rendering.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_rendering.0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_states.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_states.1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_states.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_states.2.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_states.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_states.3.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_states.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_states.4.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_render_pool.mp_test_states.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_render_pool.mp_test_states.5.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering.camera1.narrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering.camera1.narrow.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering.camera1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering.camera1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering.freecam.depth-darwin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering.freecam.depth-darwin.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering.freecam.depth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering.freecam.depth.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering.freecam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering.freecam.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_rendering_markers.camera1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_rendering_markers.camera1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop0_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop0_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop0_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop1_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop1_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop2_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop2_0.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_resetting.loop2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_resetting.loop2_1.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.premod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.premod.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.rand_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.rand_all.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.rand_specific.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.rand_specific.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.rand_texrepeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.rand_texrepeat.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.rgb.png -------------------------------------------------------------------------------- /mujoco_py/test_imgs/test_textures.variety.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/test_imgs/test_textures.variety.png -------------------------------------------------------------------------------- /mujoco_py/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/mujoco_py/tests/__init__.py -------------------------------------------------------------------------------- /mujoco_py/tests/include.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mujoco_py/tests/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_composite.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test whether library can parse composite object definitions - new in MuJoCo 2.0 3 | """ 4 | 5 | from mujoco_py import load_model_from_xml 6 | 7 | 8 | PARTICLE_XML = """ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | """ 17 | 18 | 19 | def test_load_particle(): 20 | load_model_from_xml(PARTICLE_XML) 21 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_examples.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import glob 3 | import os.path 4 | import mujoco_py 5 | import sys 6 | import pytest 7 | mujoco_py_root = os.path.dirname(os.path.dirname(mujoco_py.__file__)) 8 | 9 | 10 | @pytest.mark.requires_rendering 11 | @pytest.mark.requires_glfw 12 | def test_examples(): 13 | scripts = glob.glob("%s/examples/*.py" % mujoco_py_root) 14 | env = os.environ.update({'TESTING': 'true'}) 15 | assert len(scripts) > 0, 'No example scripts found!' 16 | for tutorial_script in scripts: 17 | if tutorial_script.find("mjvive") > -1: 18 | continue 19 | print("Executing %s" % tutorial_script) 20 | 21 | subprocess.check_call([sys.executable, tutorial_script], env=env) 22 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_gen_wrappers.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | import os 4 | import pytest 5 | import mujoco_py 6 | 7 | 8 | @pytest.mark.skipif(sys.platform.startswith("win"), reason="This test fails on windows.") 9 | def test_gen_wrappers(): 10 | # Verifies that gen_wrappers can be executed. 11 | fname = "/tmp/generated_wrappers.pxi" 12 | subprocess.check_call([sys.executable, 13 | os.path.join("scripts", "gen_wrappers.py"), fname]) 14 | return fname 15 | 16 | 17 | @pytest.mark.skipif(sys.platform.startswith("win"), reason="This test fails on windows.") 18 | def test_deterministic(): 19 | # Verifies that gen_wrappers is deterministic 20 | fname = test_gen_wrappers() 21 | with open(fname) as f: 22 | s = f.read() 23 | fname = test_gen_wrappers() 24 | with open(fname) as f: 25 | assert f.read() == s, 'Generate wrappers returned different result' 26 | 27 | 28 | @pytest.mark.skipif(sys.platform.startswith("win"), reason="This test fails on windows.") 29 | def test_generated(): 30 | # Verifies generated wrappers match checked-in wrappers 31 | fname = test_gen_wrappers() 32 | with open(fname) as f: 33 | generated_str = f.read() 34 | gname = os.path.join(os.path.dirname(mujoco_py.cymj.__file__), 'wrappers.pxi') 35 | with open(gname) as f: 36 | checkedin_str = f.read() 37 | assert generated_str == checkedin_str, 'Generated wrappers do not match' 38 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_modder.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from mujoco_py import MjSim, load_model_from_xml 3 | from mujoco_py.modder import MaterialModder, TextureModder 4 | from mujoco_py.tests.utils import compare_imgs 5 | import numpy as np 6 | 7 | BASIC_MODEL_XML = """ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | """ 34 | 35 | @pytest.mark.requires_rendering 36 | def test_textures(): 37 | model = load_model_from_xml(BASIC_MODEL_XML) 38 | sim = MjSim(model) 39 | sim.forward() 40 | 41 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 42 | 'test_textures.premod.png') 43 | 44 | random_state = np.random.RandomState(0) 45 | modder = TextureModder(sim, random_state=random_state) 46 | modder.whiten_materials() 47 | modder.whiten_materials(['g1', 'g2']) 48 | 49 | modder.set_rgb('g1', (255, 0, 0)) 50 | modder.set_rgb('g2', (0, 255, 0)) 51 | modder.set_rgb('g3', (0, 0, 255)) 52 | modder.set_rgb('g4', (255, 0, 255)) 53 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 54 | 'test_textures.rgb.png') 55 | 56 | modder.set_checker('g1', (255, 0, 0), (0, 255, 0)) 57 | modder.set_gradient('g2', (0, 255, 0), (0, 0, 255), vertical=True) 58 | modder.set_gradient('g3', (255, 255, 0), (0, 0, 255), vertical=False) 59 | modder.set_noise('g4', (0, 0, 255), (255, 0, 0), 0.1) 60 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 61 | 'test_textures.variety.png') 62 | 63 | modder.rand_checker('g1') 64 | modder.rand_gradient('g2') 65 | modder.rand_noise('g3') 66 | modder.rand_rgb('g4') 67 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 68 | 'test_textures.rand_specific.png') 69 | 70 | modder.rand_all('g1') 71 | modder.rand_all('g2') 72 | modder.rand_all('g3') 73 | modder.rand_all('g4') 74 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 75 | 'test_textures.rand_all.png') 76 | 77 | modder.rand_checker('g1') 78 | modder.rand_checker('g2') 79 | modder.rand_checker('g3') 80 | modder.rand_checker('g4') 81 | mat_modder = MaterialModder(sim, random_state=random_state) 82 | mat_modder.rand_texrepeat('g1') 83 | mat_modder.rand_texrepeat('g2') 84 | mat_modder.rand_texrepeat('g3') 85 | mat_modder.rand_texrepeat('g4') 86 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 87 | 'test_textures.rand_texrepeat.png') 88 | 89 | @pytest.mark.requires_rendering 90 | def test_materials(): 91 | model = load_model_from_xml(BASIC_MODEL_XML) 92 | sim = MjSim(model) 93 | sim.forward() 94 | 95 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 96 | 'test_materials.premod.png') 97 | 98 | random_state = np.random.RandomState(0) 99 | modder = MaterialModder(sim, random_state=random_state) 100 | 101 | modder.set_specularity('g1', 1.0) 102 | modder.set_reflectance('g2', 1.0) 103 | modder.set_shininess('g3', 1.0) 104 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 105 | 'test_materials.props.png') 106 | 107 | modder.rand_all('g4') 108 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 109 | 'test_materials.rand_all.png') 110 | 111 | @pytest.mark.requires_rendering 112 | def test_multiple_sims(): 113 | # Ensure that creating new simulators still produces good renderings. 114 | xml = """ 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | """ 127 | 128 | model = load_model_from_xml(xml) 129 | random_state = np.random.RandomState(0) 130 | 131 | for i in range(3): 132 | sim = MjSim(model) 133 | sim.forward() 134 | modder = TextureModder(sim, random_state=random_state) 135 | for j in range(2): 136 | modder.rand_checker('g1') 137 | compare_imgs( 138 | sim.render(201, 205, camera_name="topcam"), 139 | 'test_multiple_sims.loop%d_%d.png' % (i, j)) 140 | 141 | 142 | @pytest.mark.requires_rendering 143 | def test_resetting(): 144 | # Ensure that resetting environment and creating new simulators 145 | # still produces good renderings. 146 | xml = """ 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | """ 159 | 160 | def get_sim(seed): 161 | geom_type = ["box", "sphere"][seed % 2] 162 | model = load_model_from_xml(xml.format(geom_type=geom_type)) 163 | return MjSim(model) 164 | 165 | random_state = np.random.RandomState(0) 166 | 167 | for i in range(3): 168 | sim = get_sim(i - 1) 169 | sim.forward() 170 | modder = TextureModder(sim, random_state=random_state) 171 | for j in range(2): 172 | modder.rand_checker('g1') 173 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 174 | 'test_resetting.loop%d_%d.png' % (i, j)) 175 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_opengl_context.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import pytest 3 | import numpy as np 4 | from mujoco_py import MjSim, load_model_from_xml, MjRenderContext, GlfwContext 5 | from mujoco_py.tests.utils import compare_imgs 6 | 7 | BASIC_MODEL_XML = """ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | """ 34 | 35 | @pytest.mark.requires_rendering 36 | def test_glfw_context(): 37 | model = load_model_from_xml(BASIC_MODEL_XML) 38 | sim = MjSim(model) 39 | sim.forward() 40 | 41 | render_context = MjRenderContext(sim, offscreen=True, opengl_backend='glfw') 42 | assert len(sim.render_contexts) == 1 43 | assert sim.render_contexts[0] is render_context 44 | assert isinstance(render_context.opengl_context, GlfwContext) 45 | 46 | compare_imgs(sim.render(201, 205, camera_name="topcam"), 'test_glfw_context.png') 47 | assert len(sim.render_contexts) == 1 48 | assert sim.render_contexts[0] is render_context 49 | 50 | 51 | @pytest.mark.requires_rendering 52 | def test_read_depth_buffer(): 53 | model = load_model_from_xml(BASIC_MODEL_XML) 54 | sim = MjSim(model) 55 | sim.forward() 56 | ctx = MjRenderContext(sim, offscreen=True, opengl_backend='glfw') 57 | 58 | buf = np.zeros((11, 100), dtype=np.float32) 59 | assert buf.sum() == 0, f'{buf.sum()}' 60 | 61 | ctx.render(buf.shape[1], buf.shape[0], 0) 62 | ctx.read_pixels_depth(buf) 63 | assert buf.sum() != 0, f'{buf.sum()} {buf.max()}' 64 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_render_pool.py: -------------------------------------------------------------------------------- 1 | import multiprocessing as mp 2 | import os 3 | import subprocess 4 | import sys 5 | from os.path import abspath, dirname, join 6 | 7 | import numpy as np 8 | import pytest 9 | 10 | from mujoco_py import MjSim, MjRenderPool, load_model_from_xml 11 | from mujoco_py.modder import TextureModder 12 | from mujoco_py.tests.utils import compare_imgs 13 | 14 | BASIC_MODEL_XML = """ 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | """ 43 | 44 | 45 | @pytest.mark.requires_rendering 46 | def test_spawn(): 47 | # pytest and nose both use 'fork' by default, so we 48 | # expect a runtime error 49 | model = load_model_from_xml(BASIC_MODEL_XML) 50 | with pytest.raises(RuntimeError): 51 | MjRenderPool(model, n_workers=3) 52 | 53 | 54 | @pytest.mark.requires_rendering 55 | def test_multiprocessing(): 56 | # pytest doesn't work well with multiprocessing, so just 57 | # run the multiprocessing tests manually by running this 58 | # script as a subprocess 59 | env = os.environ 60 | env['MUJOCO_PY_TEST_ASSET_DIR_PATH'] = abspath( 61 | join(dirname(__file__), '..', 'test_imgs')) 62 | subprocess.check_call([sys.executable, __file__], env=env, shell=True) 63 | 64 | 65 | def mp_test_create_destroy(): 66 | model = load_model_from_xml(BASIC_MODEL_XML) 67 | pool = MjRenderPool(model, n_workers=2) 68 | del pool 69 | 70 | # closing before deleting should be fine too 71 | pool = MjRenderPool(model, n_workers=2) 72 | pool.close() 73 | del pool 74 | 75 | 76 | def mp_test_rendering(): 77 | model = load_model_from_xml(BASIC_MODEL_XML) 78 | pool = MjRenderPool(model, n_workers=3) 79 | 80 | images = pool.render(100, 100) 81 | assert images.shape == (3, 100, 100, 3) 82 | compare_imgs(images[0], 'test_render_pool.mp_test_rendering.0.png') 83 | assert np.all(images[0] == images[1]) 84 | 85 | images, depth = pool.render(101, 103, depth=True) 86 | assert images.shape == (3, 103, 101, 3) 87 | assert depth.shape == (3, 103, 101) 88 | assert np.all(images[0] == images[1]) 89 | assert np.all(images[1] == images[2]) 90 | 91 | 92 | def mp_test_cameras(): 93 | model = load_model_from_xml(BASIC_MODEL_XML) 94 | pool = MjRenderPool(model, n_workers=1) 95 | 96 | image = pool.render(100, 100) 97 | assert image.shape == (1, 100, 100, 3) 98 | compare_imgs(image[0], 'test_render_pool.mp_test_cameras.0.png') 99 | 100 | image = pool.render(100, 100, camera_name='camera1') 101 | assert image.shape == (1, 100, 100, 3) 102 | compare_imgs(image[0], 'test_render_pool.mp_test_cameras.1.png') 103 | 104 | 105 | def mp_test_modder(): 106 | model = load_model_from_xml(BASIC_MODEL_XML) 107 | pool = MjRenderPool(model, n_workers=2, modder=TextureModder) 108 | 109 | images = pool.render(100, 100, randomize=True) 110 | assert images.shape == (2, 100, 100, 3) 111 | 112 | # the order of the images aren't guaranteed to be consistent 113 | # between the render runs 114 | images1 = pool.render(100, 100, randomize=False) 115 | assert images1.shape == (2, 100, 100, 3) 116 | 117 | if np.all(images[0] == images1[0]) and np.all(images[1] == images1[1]): 118 | images_same = True 119 | elif np.all(images[0] == images1[1]) and np.all(images[1] == images1[0]): 120 | images_same = True 121 | else: 122 | images_same = False 123 | assert images_same 124 | 125 | images2 = pool.render(100, 100, randomize=True) 126 | assert images2.shape == (2, 100, 100, 3) 127 | 128 | if np.all(images[0] == images2[0]) and np.all(images[1] == images2[1]): 129 | images_same = True 130 | elif np.all(images[0] == images2[1]) and np.all(images[1] == images2[0]): 131 | images_same = True 132 | else: 133 | images_same = False 134 | assert not images_same 135 | 136 | 137 | def mp_test_states(): 138 | sim = MjSim(load_model_from_xml(BASIC_MODEL_XML)) 139 | 140 | states = [] 141 | for val in range(3): 142 | sim.data.qpos[:3] = val * 0.1 143 | states.append(sim.get_state()) 144 | 145 | pool = MjRenderPool(sim.model, n_workers=3) 146 | 147 | images = pool.render(100, 100, states=states[:2]) 148 | assert images.shape == (2, 100, 100, 3) 149 | compare_imgs(images[0], 'test_render_pool.mp_test_states.1.png') 150 | compare_imgs(images[1], 'test_render_pool.mp_test_states.2.png') 151 | 152 | states = list(reversed(states)) 153 | images = pool.render(100, 100, states=states) 154 | assert images.shape == (3, 100, 100, 3) 155 | compare_imgs(images[0], 'test_render_pool.mp_test_states.3.png') 156 | compare_imgs(images[1], 'test_render_pool.mp_test_states.4.png') 157 | compare_imgs(images[2], 'test_render_pool.mp_test_states.5.png') 158 | 159 | 160 | if __name__ == '__main__': 161 | mp.freeze_support() 162 | mp.set_start_method('spawn') 163 | 164 | mp_test_create_destroy() 165 | mp_test_rendering() 166 | mp_test_states() 167 | mp_test_cameras() 168 | mp_test_modder() 169 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_substep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import unittest 3 | import numpy as np 4 | from mujoco_py import load_model_from_xml, MjSim, functions 5 | 6 | 7 | XML = ''' 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ''' 26 | 27 | INCREMENT_FN = ''' 28 | void fun(const mjModel* m, mjData* d) { 29 | d->userdata[0] += 1; 30 | } 31 | ''' 32 | 33 | 34 | class TestSubstep(unittest.TestCase): 35 | def test_hello(self): 36 | fn = ''' 37 | #include 38 | void fun(const mjModel* m, mjData* d) { 39 | printf("hello"); 40 | } 41 | ''' 42 | sim = MjSim(load_model_from_xml(XML.format(nuserdata=0)), 43 | substep_callback=fn) 44 | sim.step() # should print 'hello' 45 | 46 | def test_increment(self): 47 | sim = MjSim(load_model_from_xml(XML.format(nuserdata=1)), 48 | substep_callback=INCREMENT_FN) 49 | self.assertEqual(sim.data.userdata[0], 0) 50 | sim.step() # should increment userdata[0] 51 | self.assertEqual(sim.data.userdata[0], 1) 52 | # Test again with different nsubsteps, reuse model 53 | sim = MjSim(sim.model, nsubsteps=7, substep_callback=INCREMENT_FN) 54 | self.assertEqual(sim.data.userdata[0], 0) 55 | sim.step() # should increment userdata[0] 7 times 56 | self.assertEqual(sim.data.userdata[0], 7) 57 | 58 | def test_sum_ctrl(self): 59 | fn = ''' 60 | void fun(const mjModel* m, mjData* d) { 61 | for (int i = 0; i < m->nu; i++) { 62 | my_sum += d->ctrl[i]; 63 | } 64 | } 65 | ''' 66 | sim = MjSim(load_model_from_xml(XML.format(nuserdata=1)), 67 | substep_callback=fn, 68 | userdata_names=['my_sum']) 69 | self.assertEqual(sim.data.userdata[0], 0) 70 | sim.step() # should set userdata[0] to sum of controls 71 | self.assertEqual(sim.data.userdata[0], 0) 72 | sim.data.ctrl[0] = 12. 73 | sim.data.ctrl[1] = .34 74 | sim.step() 75 | self.assertEqual(sim.data.userdata[0], 12.34) 76 | sim.step() 77 | self.assertEqual(sim.data.userdata[0], 24.68) 78 | 79 | def test_penetrations(self): 80 | # Test that we capture the max penetration of a falling sphere 81 | # Given a single step with many substeps 82 | xml = ''' 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ''' 94 | # NOTE: penetration is negative distance in contacts 95 | fn = ''' 96 | #define MIN(a, b) (a < b ? a : b) 97 | void fun(const mjModel* m, mjData* d) { 98 | for (int i = 0; i < d->ncon; i++) { 99 | min_contact_dist = MIN(min_contact_dist, d->contact[i].dist); 100 | } 101 | } 102 | ''' 103 | sim = MjSim(load_model_from_xml(xml), nsubsteps=1000, 104 | substep_callback=fn, userdata_names=['min_contact_dist']) 105 | self.assertEqual(sim.data.userdata[0], 0) 106 | sim.step() 107 | self.assertEqual(sim.data.ncon, 1) # Assert we have a contact 108 | self.assertLess(sim.data.contact[0].dist, 0) # assert penetration 109 | # Assert that min penetration is much less than current penetration 110 | self.assertLess(sim.data.userdata[0], 10 * sim.data.contact[0].dist) 111 | 112 | def test_reuse(self): 113 | ''' Test that we can re-use a substep_callback between sims ''' 114 | fn = ''' 115 | #define MAX(a, b) (a > b ? a : b) 116 | void fun(const mjModel* m, mjData* d) { 117 | for (int i = 0; i < m->nu; i++) { 118 | ctrl_max = MAX(ctrl_max, d->ctrl[i]); 119 | } 120 | } 121 | ''' 122 | sim = MjSim(load_model_from_xml(XML.format(nuserdata=1)), 123 | substep_callback=fn, userdata_names=['ctrl_max']) 124 | sim.step() 125 | self.assertEqual(sim.data.userdata[0], 0) 126 | sim.data.ctrl[:] = [1, 2] 127 | sim.step() 128 | self.assertEqual(sim.data.userdata[0], 2) 129 | sim2 = MjSim(sim.model, substep_callback=sim.substep_callback_ptr) 130 | sim2.step() 131 | self.assertEqual(sim2.data.userdata[0], 0) 132 | sim2.data.ctrl[:] = [.1, .2] 133 | sim2.step() 134 | self.assertEqual(sim2.data.userdata[0], .2) 135 | 136 | def test_mjfunctions(self): 137 | # Test calling mujoco function from within callback 138 | fn = ''' 139 | void fun(const mjModel *m, mjData *d) { 140 | // set userdata to rotation matrix for body_xquat[2] 141 | mju_quat2Mat(d->userdata, &(d->xquat[4 * 2])); 142 | } 143 | ''' 144 | sim = MjSim(load_model_from_xml(XML.format(nuserdata=9)), 145 | substep_callback=fn) 146 | sim.data.ctrl[:] = [9, 13] 147 | for _ in range(30): 148 | sim.step() 149 | sim.substep_callback() 150 | mat = np.zeros(9, dtype=np.float64) 151 | functions.mju_quat2Mat(mat, sim.data.body_xquat[2]) 152 | np.testing.assert_array_equal(sim.data.userdata, mat) 153 | 154 | 155 | if __name__ == '__main__': 156 | unittest.main() 157 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_vfs.py: -------------------------------------------------------------------------------- 1 | from tempfile import NamedTemporaryFile 2 | from mujoco_py import functions 3 | from mujoco_py.cymj import PyMjVFS 4 | 5 | 6 | def test_vfs(): 7 | ''' Test basic VFS functionality ''' 8 | vfs = PyMjVFS() 9 | functions.mj_defaultVFS(vfs) 10 | functions.mj_deleteVFS(vfs) 11 | 12 | 13 | def test_files(): 14 | ''' Testing handling VFS files ''' 15 | vfs = PyMjVFS() 16 | functions.mj_defaultVFS(vfs) 17 | f = NamedTemporaryFile(delete=False) 18 | name = f.name 19 | # Try to find file before added -> missing 20 | assert functions.mj_findFileVFS(vfs, name) == -1 21 | # Add file -> success 22 | assert functions.mj_addFileVFS(vfs, '', name) == 0 23 | # Add file again -> failure, duplicate 24 | assert functions.mj_addFileVFS(vfs, '', name) == 2 25 | # Find file -> success (index 0) 26 | assert functions.mj_findFileVFS(vfs, name) == 0 27 | # Delete file -> success 28 | assert functions.mj_deleteFileVFS(vfs, name) == 0 29 | # Delete file again -> failure, missing 30 | assert functions.mj_deleteFileVFS(vfs, name) == -1 31 | # Add a file which does not exist -> failure, missing 32 | assert functions.mj_addFileVFS(vfs, 'bla', name) == -1 33 | -------------------------------------------------------------------------------- /mujoco_py/tests/test_viewer.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from mujoco_py import load_model_from_path, MjSim 3 | from mujoco_py.mjviewer import MjViewer 4 | 5 | 6 | @pytest.mark.requires_rendering 7 | @pytest.mark.requires_glfw 8 | def test_viewer(): 9 | model = load_model_from_path("mujoco_py/tests/test.xml") 10 | sim = MjSim(model) 11 | viewer = MjViewer(sim) 12 | for _ in range(100): 13 | sim.step() 14 | viewer.render() 15 | -------------------------------------------------------------------------------- /mujoco_py/tests/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from PIL import Image 3 | from os.path import exists, join, dirname, abspath 4 | from shutil import move 5 | import numpy as np 6 | from os.path import splitext 7 | import imagehash 8 | import pytest 9 | 10 | if os.getenv('MUJOCO_PY_TEST_ASSET_DIR_PATH'): 11 | TEST_ASSET_DIR_PATH = os.getenv('MUJOCO_PY_TEST_ASSET_DIR_PATH') 12 | else: 13 | TEST_ASSET_DIR_PATH = abspath(join(dirname(__file__), '..', 'test_imgs')) 14 | 15 | 16 | def save_test_image(filename, array): 17 | Image.fromarray(array).save(filename) 18 | 19 | 20 | def compare_imgs(img, truth_filename, do_assert=True): 21 | """ 22 | PROTIP: run the following to re-generate the test images: 23 | 24 | REGENERATE_TEST_IMAGES=1 pytest mujoco_py/tests/test_modder.py 25 | 26 | Note: do this in Docker so that images will work for testing. 27 | """ 28 | assert isinstance(truth_filename, str) 29 | truth_filename = join(TEST_ASSET_DIR_PATH, truth_filename) 30 | if os.getenv('REGENERATE_TEST_IMAGES'): 31 | if exists(truth_filename): 32 | pre_path, ext = splitext(truth_filename) 33 | backup_path = "%s_old%s" % (pre_path, ext) 34 | move(truth_filename, backup_path) 35 | save_test_image(truth_filename, img) 36 | return 0 37 | true_img = np.asarray(Image.open(truth_filename)) 38 | assert img.shape == true_img.shape 39 | hash0 = imagehash.dhash(Image.fromarray(img)) 40 | hash1 = imagehash.dhash(Image.fromarray(true_img)) 41 | diff = np.sum(hash0.hash != hash1.hash) 42 | if diff != 0: 43 | # If the assert fails, the best way to investigate is to run 44 | # pytest for the particular test. For example, 45 | # 46 | # pytest -k test_something_something path/to/test.py 47 | save_test_image("/tmp/img.png", img) 48 | save_test_image("/tmp/true_img.png", true_img) 49 | save_test_image("/tmp/diff_img.png", img - true_img) 50 | if do_assert: 51 | assert diff <= 1 52 | return diff 53 | -------------------------------------------------------------------------------- /mujoco_py/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import copy 4 | from os.path import join, expanduser, exists 5 | 6 | import numpy as np 7 | 8 | MISSING_MUJOCO_MESSAGE = ''' 9 | You appear to be missing MuJoCo. We expected to find the file here: {} 10 | 11 | This package only provides python bindings, the library must be installed separately. 12 | 13 | Please follow the instructions on the README to install MuJoCo 14 | 15 | https://github.com/openai/mujoco-py#install-mujoco 16 | 17 | Which can be downloaded from the website 18 | 19 | https://www.roboti.us/index.html 20 | ''' 21 | 22 | 23 | def remove_empty_lines(string): 24 | lines = [] 25 | for line in string.splitlines(): 26 | if line.strip(): 27 | lines.append(line) 28 | return "\n".join(lines) 29 | 30 | 31 | def rec_assign(node, assign): 32 | # Assigns values to node recursively. 33 | # This is neccessary to avoid overriding pointers in MuJoCo. 34 | for field in dir(node): 35 | if field.find("__") == -1 and field != 'uintptr': 36 | val = getattr(node, field) 37 | if isinstance(val, (int, bool, float, None.__class__, str)): 38 | setattr(node, field, assign[field]) 39 | elif isinstance(val, np.ndarray): 40 | val[:] = assign[field][:] 41 | elif not hasattr(val, "__call__"): 42 | rec_assign(val, assign[field]) 43 | 44 | 45 | def rec_copy(node): 46 | # Recursively copies object to dictionary. 47 | # Applying directly copy.deepcopy causes seg fault. 48 | ret = {} 49 | for field in dir(node): 50 | if field.find("__") == -1: 51 | val = getattr(node, field) 52 | if isinstance(val, (int, bool, float, None.__class__, str)): 53 | ret[field] = val 54 | elif isinstance(val, np.ndarray): 55 | ret[field] = copy.deepcopy(val) 56 | elif not hasattr(val, "__call__"): 57 | ret[field] = rec_copy(val) 58 | return ret 59 | 60 | 61 | def discover_mujoco(): 62 | """ 63 | Discovers where MuJoCo is located in the file system. 64 | Currently assumes path is in ~/.mujoco 65 | 66 | Returns: 67 | - mujoco_path (str): Path to MuJoCo 2.1 directory. 68 | """ 69 | mujoco_path = os.getenv('MUJOCO_PY_MUJOCO_PATH') 70 | if not mujoco_path: 71 | mujoco_path = join(expanduser('~'), '.mujoco', 'mujoco210') 72 | 73 | # We get lots of github issues that seem to be missing these 74 | # so check that mujoco is really there and raise errors if not. 75 | if not exists(mujoco_path): 76 | message = MISSING_MUJOCO_MESSAGE.format(mujoco_path) 77 | print(message, file=sys.stderr) 78 | raise Exception(message) 79 | 80 | return mujoco_path 81 | -------------------------------------------------------------------------------- /mujoco_py/version.py: -------------------------------------------------------------------------------- 1 | __all__ = ['__version__', 'get_version'] 2 | 3 | version_info = (2, 1, 2, 14) 4 | # format: 5 | # ('mujoco_major', 'mujoco_minor', 'mujoco_py_major', 'mujoco_py_minor') 6 | 7 | 8 | def get_version(): 9 | """ Returns the version as a human-format string. """ 10 | return '%d.%d.%d.%d' % version_info 11 | 12 | 13 | __version__ = get_version() 14 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools", 4 | "glfw>=1.4.0", 5 | "numpy>=1.11", 6 | "Cython>=0.27.2", 7 | "imageio>=2.1.2", 8 | "cffi>=1.10", 9 | "fasteners~=0.15" 10 | ] 11 | -------------------------------------------------------------------------------- /requirements.dev.txt: -------------------------------------------------------------------------------- 1 | imagehash>=3.4 2 | ipdb 3 | Pillow>=4.0.0 4 | pycparser>=2.17.0 5 | pytest>=3.0.5 6 | pytest-instafail==0.3.0 7 | sphinx 8 | sphinx_rtd_theme 9 | numpydoc 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # When updating these, you may need to also update pyproject.toml 2 | glfw>=1.4.0 3 | numpy>=1.11 4 | Cython>=0.27.2 5 | imageio>=2.1.2 6 | cffi>=1.10 7 | fasteners~=0.15 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | from os.path import join, dirname, realpath 5 | from setuptools import find_packages, setup 6 | from distutils.command.build import build as DistutilsBuild 7 | 8 | 9 | with open(join("mujoco_py", "version.py")) as version_file: 10 | exec(version_file.read()) 11 | 12 | 13 | def read_requirements_file(filename): 14 | req_file_path = '%s/%s' % (dirname(realpath(__file__)), filename) 15 | with open(req_file_path) as f: 16 | return [line.strip() for line in f] 17 | 18 | 19 | packages = find_packages() 20 | # Ensure that we don't pollute the global namespace. 21 | for p in packages: 22 | assert p == 'mujoco_py' or p.startswith('mujoco_py.') 23 | 24 | 25 | class Build(DistutilsBuild): 26 | def run(self): 27 | os.environ['MUJOCO_PY_FORCE_REBUILD'] = 'True' 28 | os.environ['MUJOCO_PY_SKIP_ACTIVATE'] = 'True' 29 | import mujoco_py # noqa: force build 30 | DistutilsBuild.run(self) 31 | 32 | 33 | setup( 34 | name='mujoco-py', 35 | version=__version__, # noqa 36 | author='OpenAI Robotics Team', 37 | author_email='robotics@openai.com', 38 | url='https://github.com/openai/mujoco-py', 39 | packages=packages, 40 | include_package_data=True, 41 | cmdclass={'build': Build}, 42 | package_dir={'mujoco_py': 'mujoco_py'}, 43 | package_data={'mujoco_py': ['generated/*.so']}, 44 | install_requires=read_requirements_file('requirements.txt'), 45 | tests_require=read_requirements_file('requirements.dev.txt'), 46 | python_requires='>=3.6', 47 | classifiers=[ 48 | 'Programming Language :: Python :: 3', 49 | 'Programming Language :: Python :: 3.6', 50 | 'Programming Language :: Python :: 3.7', 51 | 'Programming Language :: Python :: 3 :: Only', 52 | ], 53 | ) 54 | -------------------------------------------------------------------------------- /vendor/10_nvidia.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "ICD" : { 4 | "library_path" : "libEGL_nvidia.so.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /vendor/Xdummy-entrypoint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import argparse 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | parser = argparse.ArgumentParser() 8 | args, extra_args = parser.parse_known_args() 9 | subprocess.Popen(["nohup", "Xdummy"], stdout=open('/dev/null', 'w'), stderr=open('/dev/null', 'w')) 10 | os.environ['DISPLAY'] = ':0' 11 | if not extra_args: 12 | sys.argv = ['/bin/bash'] 13 | else: 14 | sys.argv = extra_args 15 | # Explicitly flush right before the exec since otherwise things might get 16 | # lost in Python's buffers around stdout/stderr (!). 17 | sys.stdout.flush() 18 | sys.stderr.flush() 19 | os.execvpe(sys.argv[0], sys.argv, os.environ) 20 | 21 | -------------------------------------------------------------------------------- /xmls/claw.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /xmls/door.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /xmls/fetch/base_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/base_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/bellows_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/bellows_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/elbow_flex_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/elbow_flex_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/estop_link.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/estop_link.stl -------------------------------------------------------------------------------- /xmls/fetch/forearm_roll_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/forearm_roll_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/gripper_link.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/gripper_link.stl -------------------------------------------------------------------------------- /xmls/fetch/head_pan_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/head_pan_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/head_tilt_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/head_tilt_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/l_wheel_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/l_wheel_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/laser_link.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/laser_link.stl -------------------------------------------------------------------------------- /xmls/fetch/r_wheel_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/r_wheel_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/shoulder_lift_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/shoulder_lift_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/shoulder_pan_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/shoulder_pan_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/torso_fixed_link.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/torso_fixed_link.stl -------------------------------------------------------------------------------- /xmls/fetch/torso_lift_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/torso_lift_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/upperarm_roll_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/upperarm_roll_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/wrist_flex_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/wrist_flex_link_collision.stl -------------------------------------------------------------------------------- /xmls/fetch/wrist_roll_link_collision.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mujoco-py/a13903b82f1ab316815e63b3575526005b3b2ae1/xmls/fetch/wrist_roll_link_collision.stl -------------------------------------------------------------------------------- /xmls/juggler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /xmls/key.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /xmls/shelf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /xmls/slider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /xmls/tosser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | --------------------------------------------------------------------------------