├── gym_panda ├── envs │ ├── __init__.py │ └── panda_env.py └── __init__.py ├── .travis.yml ├── tests └── basic_import_test.py ├── setup.py ├── LICENSE ├── .gitignore └── README.md /gym_panda/envs/__init__.py: -------------------------------------------------------------------------------- 1 | from gym_panda.envs.panda_env import PandaEnv 2 | -------------------------------------------------------------------------------- /gym_panda/__init__.py: -------------------------------------------------------------------------------- 1 | from gym.envs.registration import register 2 | 3 | register( 4 | id='panda-v0', 5 | entry_point='gym_panda.envs:PandaEnv', 6 | ) 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '3.6' 4 | cache: 5 | pip: true 6 | # directories: 7 | # - $HOME/data 8 | install: 9 | - pip install . 10 | script: 11 | - pytest 12 | -------------------------------------------------------------------------------- /tests/basic_import_test.py: -------------------------------------------------------------------------------- 1 | # Basic package test 2 | import os 3 | 4 | import gym 5 | import gym_panda 6 | 7 | def main(): 8 | """ 9 | Tests importing of gym envs 10 | """ 11 | spec = gym.spec('panda-v0') 12 | 13 | print("Test complete.") 14 | return True 15 | 16 | 17 | def test_import(): 18 | assert main() is True 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | from pathlib import Path 3 | 4 | setuptools.setup( 5 | name='gym_panda', 6 | author="Mahyar Abdeetedal", 7 | author_email="mahyar.etedal@icloud.com", 8 | version='0.0.6', 9 | description="An OpenAI Gym Env for Panda", 10 | long_description=Path("README.md").read_text(), 11 | long_description_content_type="text/markdown", 12 | url="https://github.com/mahyaret/gym-panda", 13 | packages=setuptools.find_packages(include="gym_panda*"), 14 | install_requires=['gym', 'pybullet', 'numpy'], # And any other dependencies foo needs 15 | classifiers=[ 16 | "Programming Language :: Python :: 3", 17 | "License :: OSI Approved :: MIT License", 18 | "Operating System :: OS Independent", 19 | ], 20 | python_requires='>=3.6' 21 | ) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mahyar Abdeetedal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gym-panda 2 | 3 | [![Build Status](https://travis-ci.org/mahyaret/gym-panda.svg?branch=master)](https://travis-ci.org/mahyaret/gym-panda) 4 | [![Downloads](https://pepy.tech/badge/gym-panda)](https://pepy.tech/project/gym-panda) 5 | [![PyPI version](https://badge.fury.io/py/gym-panda.svg)](https://badge.fury.io/py/gym-panda) 6 | 7 | 8 | OpenaAI Gym Franka Emika Panda robot grasping environment implemented with PyBullet 9 | 10 | 11 | ![](https://www.etedal.net/img/twenty/panda.gif) 12 | 13 | ## Links 14 | 15 | - GitHub: [https://github.com/mahyaret/gym-panda](https://github.com/mahyaret/gym-panda) 16 | - PyPI: [https://pypi.org/project/gym-panda/](https://pypi.org/project/gym-panda/) 17 | - Documentation: [https://www.etedal.net/2020/04/pybullet-panda_2.html](https://www.etedal.net/2020/04/pybullet-panda_2.html) 18 | - Issue Tracker:[https://github.com/mahyaret/gym-panda/issues](https://github.com/mahyaret/gym-panda/issues) 19 | - Download: [https://pypi.org/project/gym-panda/#files](https://pypi.org/project/gym-panda/#files) 20 | 21 | 22 | ## Install 23 | 24 | Install with `pip`: 25 | 26 | pip install gym-panda 27 | 28 | Or, install from source: 29 | 30 | git clone https://github.com/mahyaret/gym-panda.git 31 | cd gym-panda 32 | pip install . 33 | 34 | ## Basic Usage 35 | 36 | Running an environment: 37 | 38 | ```python 39 | import gym 40 | import gym_panda 41 | env = gym.make('panda-v0') 42 | env.reset() 43 | for _ in range(100): 44 | env.render() 45 | obs, reward, done, info = env.step( 46 | env.action_space.sample()) # take a random action 47 | env.close() 48 | ``` 49 | 50 | Running a PD control HACK! 51 | 52 | ```python 53 | import gym 54 | import gym_panda 55 | 56 | env = gym.make('panda-v0') 57 | observation = env.reset() 58 | done = False 59 | error = 0.01 60 | fingers = 1 61 | object_position = [0.7, 0, 0.1] 62 | 63 | k_p = 10 64 | k_d = 1 65 | dt = 1./240. # the default timestep in pybullet is 240 Hz 66 | t = 0 67 | 68 | for i_episode in range(20): 69 | observation = env.reset() 70 | fingers = 1 71 | for t in range(100): 72 | env.render() 73 | print(observation) 74 | dx = object_position[0]-observation[0] 75 | dy = object_position[1]-observation[1] 76 | target_z = object_position[2] 77 | if abs(dx) < error and abs(dy) < error and abs(dz) < error: 78 | fingers = 0 79 | if (observation[3]+observation[4])0.45: 52 | reward = 1 53 | done = True 54 | else: 55 | reward = 0 56 | done = False 57 | 58 | self.step_counter += 1 59 | 60 | if self.step_counter > MAX_EPISODE_LEN: 61 | reward = 0 62 | done = True 63 | 64 | info = {'object_position': state_object} 65 | self.observation = state_robot + state_fingers 66 | return np.array(self.observation).astype(np.float32), reward, done, info 67 | 68 | def reset(self): 69 | self.step_counter = 0 70 | p.resetSimulation() 71 | p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,0) # we will enable rendering after we loaded everything 72 | urdfRootPath=pybullet_data.getDataPath() 73 | p.setGravity(0,0,-10) 74 | 75 | planeUid = p.loadURDF(os.path.join(urdfRootPath,"plane.urdf"), basePosition=[0,0,-0.65]) 76 | 77 | rest_poses = [0,-0.215,0,-2.57,0,2.356,2.356,0.08,0.08] 78 | self.pandaUid = p.loadURDF(os.path.join(urdfRootPath, "franka_panda/panda.urdf"),useFixedBase=True) 79 | for i in range(7): 80 | p.resetJointState(self.pandaUid,i, rest_poses[i]) 81 | p.resetJointState(self.pandaUid, 9, 0.08) 82 | p.resetJointState(self.pandaUid,10, 0.08) 83 | tableUid = p.loadURDF(os.path.join(urdfRootPath, "table/table.urdf"),basePosition=[0.5,0,-0.65]) 84 | 85 | trayUid = p.loadURDF(os.path.join(urdfRootPath, "tray/traybox.urdf"),basePosition=[0.65,0,0]) 86 | 87 | state_object= [random.uniform(0.5,0.8),random.uniform(-0.2,0.2),0.05] 88 | self.objectUid = p.loadURDF(os.path.join(urdfRootPath, "random_urdfs/000/000.urdf"), basePosition=state_object) 89 | state_robot = p.getLinkState(self.pandaUid, 11)[0] 90 | state_fingers = (p.getJointState(self.pandaUid,9)[0], p.getJointState(self.pandaUid, 10)[0]) 91 | self.observation = state_robot + state_fingers 92 | p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,1) 93 | return np.array(self.observation).astype(np.float32) 94 | 95 | def render(self, mode='human'): 96 | view_matrix = p.computeViewMatrixFromYawPitchRoll(cameraTargetPosition=[0.7,0,0.05], 97 | distance=.7, 98 | yaw=90, 99 | pitch=-70, 100 | roll=0, 101 | upAxisIndex=2) 102 | proj_matrix = p.computeProjectionMatrixFOV(fov=60, 103 | aspect=float(960) /720, 104 | nearVal=0.1, 105 | farVal=100.0) 106 | (_, _, px, _, _) = p.getCameraImage(width=960, 107 | height=720, 108 | viewMatrix=view_matrix, 109 | projectionMatrix=proj_matrix, 110 | renderer=p.ER_BULLET_HARDWARE_OPENGL) 111 | 112 | rgb_array = np.array(px, dtype=np.uint8) 113 | rgb_array = np.reshape(rgb_array, (720,960, 4)) 114 | 115 | rgb_array = rgb_array[:, :, :3] 116 | return rgb_array 117 | 118 | def _get_state(self): 119 | return self.observation 120 | 121 | def close(self): 122 | p.disconnect() 123 | --------------------------------------------------------------------------------