INITIAL
This commit is contained in:
commit
034bd64990
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
*.pyc
|
||||
**/__pycache__/
|
||||
**/.pytest_cache/
|
||||
**/dist/
|
||||
**/build/
|
||||
**/*.egg-info/
|
||||
**/.mypy_cache/
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Yuji Kanagawa
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# mujoco-maze
|
||||
|
||||
Some maze environments for reinforcement learning(RL) using [mujoco-py] and
|
||||
[openai gym][gym].
|
||||
|
||||
Thankfully, this project is based on the code from [tensorflow/models][models], [rllab]
|
||||
and [deep-skill-chaining][dsc].
|
||||
|
||||
## License
|
||||
This project is licensed under Apache License, Version 2.0
|
||||
([LICENSE-APACHE](LICENSE) or http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
[dsc]: https://github.com/deep-skill-chaining/deep-skill-chaining
|
||||
[gym]: https://github.com/openai/gym
|
||||
[models]: https://github.com/tensorflow/models/tree/master/research/efficient-hrl
|
||||
[mujoco-py]: https://github.com/openai/mujoco-py
|
||||
[rllab]: https://github.com/rll/rllab
|
1
mujoco_maze/__init__.py
Normal file
1
mujoco_maze/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
156
mujoco_maze/ant.py
Normal file
156
mujoco_maze/ant.py
Normal file
@ -0,0 +1,156 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""Wrapper for creating the ant environment in gym_mujoco."""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
import mujoco_py
|
||||
from gym import utils
|
||||
from gym.envs.mujoco import mujoco_env
|
||||
|
||||
|
||||
def q_inv(a):
|
||||
return [a[0], -a[1], -a[2], -a[3]]
|
||||
|
||||
|
||||
def q_mult(a, b): # multiply two quaternion
|
||||
w = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]
|
||||
i = a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2]
|
||||
j = a[0] * b[2] - a[1] * b[3] + a[2] * b[0] + a[3] * b[1]
|
||||
k = a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0]
|
||||
return [w, i, j, k]
|
||||
|
||||
|
||||
class AntEnv(mujoco_env.MujocoEnv, utils.EzPickle):
|
||||
FILE = "ant.xml"
|
||||
ORI_IND = 3
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file_path=None,
|
||||
expose_all_qpos=True,
|
||||
expose_body_coms=None,
|
||||
expose_body_comvels=None,
|
||||
):
|
||||
self._expose_all_qpos = expose_all_qpos
|
||||
self._expose_body_coms = expose_body_coms
|
||||
self._expose_body_comvels = expose_body_comvels
|
||||
self._body_com_indices = {}
|
||||
self._body_comvel_indices = {}
|
||||
|
||||
mujoco_env.MujocoEnv.__init__(self, file_path, 5)
|
||||
utils.EzPickle.__init__(self)
|
||||
|
||||
@property
|
||||
def physics(self):
|
||||
# check mujoco version is greater than version 1.50 to call correct physics
|
||||
# model containing PyMjData object for getting and setting position/velocity
|
||||
# check https://github.com/openai/mujoco-py/issues/80 for updates to api
|
||||
if mujoco_py.get_version() >= "1.50":
|
||||
return self.sim
|
||||
else:
|
||||
return self.model
|
||||
|
||||
def _step(self, a):
|
||||
return self.step(a)
|
||||
|
||||
def step(self, a):
|
||||
xposbefore = self.get_body_com("torso")[0]
|
||||
self.do_simulation(a, self.frame_skip)
|
||||
xposafter = self.get_body_com("torso")[0]
|
||||
forward_reward = (xposafter - xposbefore) / self.dt
|
||||
ctrl_cost = 0.5 * np.square(a).sum()
|
||||
survive_reward = 1.0
|
||||
reward = forward_reward - ctrl_cost + survive_reward
|
||||
_ = self.state_vector()
|
||||
done = False
|
||||
ob = self._get_obs()
|
||||
return (
|
||||
ob,
|
||||
reward,
|
||||
done,
|
||||
dict(
|
||||
reward_forward=forward_reward,
|
||||
reward_ctrl=-ctrl_cost,
|
||||
reward_survive=survive_reward,
|
||||
),
|
||||
)
|
||||
|
||||
def _get_obs(self):
|
||||
# No cfrc observation
|
||||
if self._expose_all_qpos:
|
||||
obs = np.concatenate(
|
||||
[
|
||||
self.physics.data.qpos.flat[:15], # Ensures only ant obs.
|
||||
self.physics.data.qvel.flat[:14],
|
||||
]
|
||||
)
|
||||
else:
|
||||
obs = np.concatenate(
|
||||
[self.physics.data.qpos.flat[2:15], self.physics.data.qvel.flat[:14],]
|
||||
)
|
||||
|
||||
if self._expose_body_coms is not None:
|
||||
for name in self._expose_body_coms:
|
||||
com = self.get_body_com(name)
|
||||
if name not in self._body_com_indices:
|
||||
indices = range(len(obs), len(obs) + len(com))
|
||||
self._body_com_indices[name] = indices
|
||||
obs = np.concatenate([obs, com])
|
||||
|
||||
if self._expose_body_comvels is not None:
|
||||
for name in self._expose_body_comvels:
|
||||
comvel = self.get_body_comvel(name)
|
||||
if name not in self._body_comvel_indices:
|
||||
indices = range(len(obs), len(obs) + len(comvel))
|
||||
self._body_comvel_indices[name] = indices
|
||||
obs = np.concatenate([obs, comvel])
|
||||
return obs
|
||||
|
||||
def reset_model(self):
|
||||
qpos = self.init_qpos + self.np_random.uniform(
|
||||
size=self.model.nq, low=-0.1, high=0.1
|
||||
)
|
||||
qvel = self.init_qvel + self.np_random.randn(self.model.nv) * 0.1
|
||||
|
||||
# Set everything other than ant to original position and 0 velocity.
|
||||
qpos[15:] = self.init_qpos[15:]
|
||||
qvel[14:] = 0.0
|
||||
self.set_state(qpos, qvel)
|
||||
return self._get_obs()
|
||||
|
||||
def viewer_setup(self):
|
||||
self.viewer.cam.distance = self.model.stat.extent * 0.5
|
||||
|
||||
def get_ori(self):
|
||||
ori = [0, 1, 0, 0]
|
||||
rot = self.physics.data.qpos[
|
||||
self.__class__.ORI_IND : self.__class__.ORI_IND + 4
|
||||
] # take the quaternion
|
||||
ori = q_mult(q_mult(rot, ori), q_inv(rot))[1:3] # project onto x-y plane
|
||||
ori = math.atan2(ori[1], ori[0])
|
||||
return ori
|
||||
|
||||
def set_xy(self, xy):
|
||||
qpos = np.copy(self.physics.data.qpos)
|
||||
qpos[0] = xy[0]
|
||||
qpos[1] = xy[1]
|
||||
|
||||
qvel = self.physics.data.qvel
|
||||
self.set_state(qpos, qvel)
|
||||
|
||||
def get_xy(self):
|
||||
return self.physics.data.qpos[:2]
|
21
mujoco_maze/ant_maze_env.py
Normal file
21
mujoco_maze/ant_maze_env.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
from environments.maze_env import MazeEnv
|
||||
from environments.ant import AntEnv
|
||||
|
||||
|
||||
class AntMazeEnv(MazeEnv):
|
||||
MODEL_CLASS = AntEnv
|
81
mujoco_maze/assets/ant.xml
Executable file
81
mujoco_maze/assets/ant.xml
Executable file
@ -0,0 +1,81 @@
|
||||
<mujoco model="ant">
|
||||
<compiler inertiafromgeom="true" angle="degree" coordinate="local" />
|
||||
<option timestep="0.02" integrator="RK4" />
|
||||
<custom>
|
||||
<numeric name="init_qpos" data="0.0 0.0 0.55 1.0 0.0 0.0 0.0 0.0 1.0 0.0 -1.0 0.0 -1.0 0.0 1.0" />
|
||||
</custom>
|
||||
<default>
|
||||
<joint limited="true" armature="1" damping="1" />
|
||||
<geom condim="3" conaffinity="0" margin="0.01" friction="1 0.5 0.5" solref=".02 1" solimp=".8 .8 .01" rgba="0.8 0.6 0.4 1" density="5.0" />
|
||||
</default>
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" width="100" height="100" rgb1="1 1 1" rgb2="0 0 0" />
|
||||
<texture name="texgeom" type="cube" builtin="flat" mark="cross" width="127" height="1278" rgb1="0.8 0.6 0.4" rgb2="0.8 0.6 0.4" markrgb="1 1 1" random="0.01" />
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1="0 0 0" rgb2="0.8 0.8 0.8" width="100" height="100" />
|
||||
<material name='MatPlane' texture="texplane" shininess="1" texrepeat="60 60" specular="1" reflectance="0.5" />
|
||||
<material name='geom' texture="texgeom" texuniform="true" />
|
||||
</asset>
|
||||
<worldbody>
|
||||
<light directional="true" cutoff="100" exponent="1" diffuse="1 1 1" specular=".1 .1 .1" pos="0 0 1.3" dir="-0 0 -1.3" />
|
||||
<geom name='floor' pos='0 0 0' size='40 40 40' type='plane' conaffinity='1' rgba='0.8 0.9 0.8 1' condim='3' />
|
||||
<body name="torso" pos="0 0 0.75">
|
||||
<geom name="torso_geom" type="sphere" size="0.25" pos="0 0 0" />
|
||||
<joint name="root" type="free" limited="false" pos="0 0 0" axis="0 0 1" margin="0.01" armature="0" damping="0" />
|
||||
<body name="front_left_leg" pos="0 0 0">
|
||||
<geom name="aux_1_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.2 0.2 0.0" />
|
||||
<body name="aux_1" pos="0.2 0.2 0">
|
||||
<joint name="hip_1" type="hinge" pos="0.0 0.0 0.0" axis="0 0 1" range="-30 30" />
|
||||
<geom name="left_leg_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.2 0.2 0.0" />
|
||||
<body pos="0.2 0.2 0">
|
||||
<joint name="ankle_1" type="hinge" pos="0.0 0.0 0.0" axis="-1 1 0" range="30 70" />
|
||||
<geom name="left_ankle_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.4 0.4 0.0" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="front_right_leg" pos="0 0 0">
|
||||
<geom name="aux_2_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.2 0.2 0.0" />
|
||||
<body name="aux_2" pos="-0.2 0.2 0">
|
||||
<joint name="hip_2" type="hinge" pos="0.0 0.0 0.0" axis="0 0 1" range="-30 30" />
|
||||
<geom name="right_leg_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.2 0.2 0.0" />
|
||||
<body pos="-0.2 0.2 0">
|
||||
<joint name="ankle_2" type="hinge" pos="0.0 0.0 0.0" axis="1 1 0" range="-70 -30" />
|
||||
<geom name="right_ankle_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.4 0.4 0.0" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="back_leg" pos="0 0 0">
|
||||
<geom name="aux_3_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.2 -0.2 0.0" />
|
||||
<body name="aux_3" pos="-0.2 -0.2 0">
|
||||
<joint name="hip_3" type="hinge" pos="0.0 0.0 0.0" axis="0 0 1" range="-30 30" />
|
||||
<geom name="back_leg_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.2 -0.2 0.0" />
|
||||
<body pos="-0.2 -0.2 0">
|
||||
<joint name="ankle_3" type="hinge" pos="0.0 0.0 0.0" axis="-1 1 0" range="-70 -30" />
|
||||
<geom name="third_ankle_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 -0.4 -0.4 0.0" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="right_back_leg" pos="0 0 0">
|
||||
<geom name="aux_4_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.2 -0.2 0.0" />
|
||||
<body name="aux_4" pos="0.2 -0.2 0">
|
||||
<joint name="hip_4" type="hinge" pos="0.0 0.0 0.0" axis="0 0 1" range="-30 30" />
|
||||
<geom name="rightback_leg_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.2 -0.2 0.0" />
|
||||
<body pos="0.2 -0.2 0">
|
||||
<joint name="ankle_4" type="hinge" pos="0.0 0.0 0.0" axis="1 1 0" range="30 70" />
|
||||
<geom name="fourth_ankle_geom" type="capsule" size="0.08" fromto="0.0 0.0 0.0 0.4 -0.4 0.0" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<motor joint="hip_4" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="ankle_4" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="hip_1" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="ankle_1" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="hip_2" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="ankle_2" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="hip_3" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
<motor joint="ankle_3" ctrlrange="-30.0 30.0" ctrllimited="true" />
|
||||
</actuator>
|
||||
</mujoco>
|
550
mujoco_maze/maze_env.py
Normal file
550
mujoco_maze/maze_env.py
Normal file
@ -0,0 +1,550 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""Adapted from rllab maze_env.py."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
import math
|
||||
import numpy as np
|
||||
import gym
|
||||
|
||||
from environments import maze_env_utils
|
||||
|
||||
# Directory that contains mujoco xml files.
|
||||
MODEL_DIR = "environments/assets"
|
||||
|
||||
|
||||
class MazeEnv(gym.Env):
|
||||
MODEL_CLASS = None
|
||||
|
||||
MAZE_HEIGHT = None
|
||||
MAZE_SIZE_SCALING = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
maze_id=None,
|
||||
maze_height=0.5,
|
||||
maze_size_scaling=8,
|
||||
n_bins=0,
|
||||
sensor_range=3.0,
|
||||
sensor_span=2 * math.pi,
|
||||
observe_blocks=False,
|
||||
put_spin_near_agent=False,
|
||||
top_down_view=False,
|
||||
manual_collision=False,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
self._maze_id = maze_id
|
||||
|
||||
model_cls = self.__class__.MODEL_CLASS
|
||||
if model_cls is None:
|
||||
raise "MODEL_CLASS unspecified!"
|
||||
xml_path = os.path.join(MODEL_DIR, model_cls.FILE)
|
||||
tree = ET.parse(xml_path)
|
||||
worldbody = tree.find(".//worldbody")
|
||||
|
||||
self.MAZE_HEIGHT = height = maze_height
|
||||
self.MAZE_SIZE_SCALING = size_scaling = maze_size_scaling
|
||||
self._n_bins = n_bins
|
||||
self._sensor_range = sensor_range * size_scaling
|
||||
self._sensor_span = sensor_span
|
||||
self._observe_blocks = observe_blocks
|
||||
self._put_spin_near_agent = put_spin_near_agent
|
||||
self._top_down_view = top_down_view
|
||||
self._manual_collision = manual_collision
|
||||
|
||||
self.MAZE_STRUCTURE = structure = maze_env_utils.construct_maze(
|
||||
maze_id=self._maze_id
|
||||
)
|
||||
self.elevated = any(
|
||||
-1 in row for row in structure
|
||||
) # Elevate the maze to allow for falling.
|
||||
self.blocks = any(
|
||||
any(maze_env_utils.can_move(r) for r in row) for row in structure
|
||||
) # Are there any movable blocks?
|
||||
|
||||
torso_x, torso_y = self._find_robot()
|
||||
self._init_torso_x = torso_x
|
||||
self._init_torso_y = torso_y
|
||||
self._init_positions = [
|
||||
(x - torso_x, y - torso_y) for x, y in self._find_all_robots()
|
||||
]
|
||||
|
||||
self._xy_to_rowcol = lambda x, y: (
|
||||
2 + (y + size_scaling / 2) / size_scaling,
|
||||
2 + (x + size_scaling / 2) / size_scaling,
|
||||
)
|
||||
self._view = np.zeros(
|
||||
[5, 5, 3]
|
||||
) # walls (immovable), chasms (fall), movable blocks
|
||||
|
||||
height_offset = 0.0
|
||||
if self.elevated:
|
||||
# Increase initial z-pos of ant.
|
||||
height_offset = height * size_scaling
|
||||
torso = tree.find(".//body[@name='torso']")
|
||||
torso.set("pos", "0 0 %.2f" % (0.75 + height_offset))
|
||||
if self.blocks:
|
||||
# If there are movable blocks, change simulation settings to perform
|
||||
# better contact detection.
|
||||
default = tree.find(".//default")
|
||||
default.find(".//geom").set("solimp", ".995 .995 .01")
|
||||
|
||||
self.movable_blocks = []
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
struct = structure[i][j]
|
||||
if struct == "r" and self._put_spin_near_agent:
|
||||
struct = maze_env_utils.Move.SpinXY
|
||||
if self.elevated and struct not in [-1]:
|
||||
# Create elevated platform.
|
||||
ET.SubElement(
|
||||
worldbody,
|
||||
"geom",
|
||||
name="elevated_%d_%d" % (i, j),
|
||||
pos="%f %f %f"
|
||||
% (
|
||||
j * size_scaling - torso_x,
|
||||
i * size_scaling - torso_y,
|
||||
height / 2 * size_scaling,
|
||||
),
|
||||
size="%f %f %f"
|
||||
% (
|
||||
0.5 * size_scaling,
|
||||
0.5 * size_scaling,
|
||||
height / 2 * size_scaling,
|
||||
),
|
||||
type="box",
|
||||
material="",
|
||||
contype="1",
|
||||
conaffinity="1",
|
||||
rgba="0.9 0.9 0.9 1",
|
||||
)
|
||||
if struct == 1: # Unmovable block.
|
||||
# Offset all coordinates so that robot starts at the origin.
|
||||
ET.SubElement(
|
||||
worldbody,
|
||||
"geom",
|
||||
name="block_%d_%d" % (i, j),
|
||||
pos="%f %f %f"
|
||||
% (
|
||||
j * size_scaling - torso_x,
|
||||
i * size_scaling - torso_y,
|
||||
height_offset + height / 2 * size_scaling,
|
||||
),
|
||||
size="%f %f %f"
|
||||
% (
|
||||
0.5 * size_scaling,
|
||||
0.5 * size_scaling,
|
||||
height / 2 * size_scaling,
|
||||
),
|
||||
type="box",
|
||||
material="",
|
||||
contype="1",
|
||||
conaffinity="1",
|
||||
rgba="0.4 0.4 0.4 1",
|
||||
)
|
||||
elif maze_env_utils.can_move(struct): # Movable block.
|
||||
# The "falling" blocks are shrunk slightly and increased in mass to
|
||||
# ensure that it can fall easily through a gap in the platform blocks.
|
||||
name = "movable_%d_%d" % (i, j)
|
||||
self.movable_blocks.append((name, struct))
|
||||
falling = maze_env_utils.can_move_z(struct)
|
||||
spinning = maze_env_utils.can_spin(struct)
|
||||
x_offset = 0.25 * size_scaling if spinning else 0.0
|
||||
y_offset = 0.0
|
||||
shrink = 0.1 if spinning else 0.99 if falling else 1.0
|
||||
height_shrink = 0.1 if spinning else 1.0
|
||||
movable_body = ET.SubElement(
|
||||
worldbody,
|
||||
"body",
|
||||
name=name,
|
||||
pos="%f %f %f"
|
||||
% (
|
||||
j * size_scaling - torso_x + x_offset,
|
||||
i * size_scaling - torso_y + y_offset,
|
||||
height_offset + height / 2 * size_scaling * height_shrink,
|
||||
),
|
||||
)
|
||||
ET.SubElement(
|
||||
movable_body,
|
||||
"geom",
|
||||
name="block_%d_%d" % (i, j),
|
||||
pos="0 0 0",
|
||||
size="%f %f %f"
|
||||
% (
|
||||
0.5 * size_scaling * shrink,
|
||||
0.5 * size_scaling * shrink,
|
||||
height / 2 * size_scaling * height_shrink,
|
||||
),
|
||||
type="box",
|
||||
material="",
|
||||
mass="0.001" if falling else "0.0002",
|
||||
contype="1",
|
||||
conaffinity="1",
|
||||
rgba="0.9 0.1 0.1 1",
|
||||
)
|
||||
if maze_env_utils.can_move_x(struct):
|
||||
ET.SubElement(
|
||||
movable_body,
|
||||
"joint",
|
||||
armature="0",
|
||||
axis="1 0 0",
|
||||
damping="0.0",
|
||||
limited="true" if falling else "false",
|
||||
range="%f %f" % (-size_scaling, size_scaling),
|
||||
margin="0.01",
|
||||
name="movable_x_%d_%d" % (i, j),
|
||||
pos="0 0 0",
|
||||
type="slide",
|
||||
)
|
||||
if maze_env_utils.can_move_y(struct):
|
||||
ET.SubElement(
|
||||
movable_body,
|
||||
"joint",
|
||||
armature="0",
|
||||
axis="0 1 0",
|
||||
damping="0.0",
|
||||
limited="true" if falling else "false",
|
||||
range="%f %f" % (-size_scaling, size_scaling),
|
||||
margin="0.01",
|
||||
name="movable_y_%d_%d" % (i, j),
|
||||
pos="0 0 0",
|
||||
type="slide",
|
||||
)
|
||||
if maze_env_utils.can_move_z(struct):
|
||||
ET.SubElement(
|
||||
movable_body,
|
||||
"joint",
|
||||
armature="0",
|
||||
axis="0 0 1",
|
||||
damping="0.0",
|
||||
limited="true",
|
||||
range="%f 0" % (-height_offset),
|
||||
margin="0.01",
|
||||
name="movable_z_%d_%d" % (i, j),
|
||||
pos="0 0 0",
|
||||
type="slide",
|
||||
)
|
||||
if maze_env_utils.can_spin(struct):
|
||||
ET.SubElement(
|
||||
movable_body,
|
||||
"joint",
|
||||
armature="0",
|
||||
axis="0 0 1",
|
||||
damping="0.0",
|
||||
limited="false",
|
||||
name="spinable_%d_%d" % (i, j),
|
||||
pos="0 0 0",
|
||||
type="ball",
|
||||
)
|
||||
|
||||
torso = tree.find(".//body[@name='torso']")
|
||||
geoms = torso.findall(".//geom")
|
||||
for geom in geoms:
|
||||
if "name" not in geom.attrib:
|
||||
raise Exception("Every geom of the torso must have a name " "defined")
|
||||
|
||||
_, file_path = tempfile.mkstemp(text=True, suffix=".xml")
|
||||
tree.write(file_path)
|
||||
|
||||
self.wrapped_env = model_cls(*args, file_path=file_path, **kwargs)
|
||||
|
||||
def get_ori(self):
|
||||
return self.wrapped_env.get_ori()
|
||||
|
||||
def get_top_down_view(self):
|
||||
self._view = np.zeros_like(self._view)
|
||||
|
||||
def valid(row, col):
|
||||
return self._view.shape[0] > row >= 0 and self._view.shape[1] > col >= 0
|
||||
|
||||
def update_view(x, y, d, row=None, col=None):
|
||||
if row is None or col is None:
|
||||
x = x - self._robot_x
|
||||
y = y - self._robot_y
|
||||
th = self._robot_ori
|
||||
|
||||
row, col = self._xy_to_rowcol(x, y)
|
||||
update_view(x, y, d, row=row, col=col)
|
||||
return
|
||||
|
||||
row, row_frac, col, col_frac = int(row), row % 1, int(col), col % 1
|
||||
if row_frac < 0:
|
||||
row_frac += 1
|
||||
if col_frac < 0:
|
||||
col_frac += 1
|
||||
|
||||
if valid(row, col):
|
||||
self._view[row, col, d] += (
|
||||
min(1.0, row_frac + 0.5) - max(0.0, row_frac - 0.5)
|
||||
) * (min(1.0, col_frac + 0.5) - max(0.0, col_frac - 0.5))
|
||||
if valid(row - 1, col):
|
||||
self._view[row - 1, col, d] += (max(0.0, 0.5 - row_frac)) * (
|
||||
min(1.0, col_frac + 0.5) - max(0.0, col_frac - 0.5)
|
||||
)
|
||||
if valid(row + 1, col):
|
||||
self._view[row + 1, col, d] += (max(0.0, row_frac - 0.5)) * (
|
||||
min(1.0, col_frac + 0.5) - max(0.0, col_frac - 0.5)
|
||||
)
|
||||
if valid(row, col - 1):
|
||||
self._view[row, col - 1, d] += (
|
||||
min(1.0, row_frac + 0.5) - max(0.0, row_frac - 0.5)
|
||||
) * (max(0.0, 0.5 - col_frac))
|
||||
if valid(row, col + 1):
|
||||
self._view[row, col + 1, d] += (
|
||||
min(1.0, row_frac + 0.5) - max(0.0, row_frac - 0.5)
|
||||
) * (max(0.0, col_frac - 0.5))
|
||||
if valid(row - 1, col - 1):
|
||||
self._view[row - 1, col - 1, d] += (max(0.0, 0.5 - row_frac)) * max(
|
||||
0.0, 0.5 - col_frac
|
||||
)
|
||||
if valid(row - 1, col + 1):
|
||||
self._view[row - 1, col + 1, d] += (max(0.0, 0.5 - row_frac)) * max(
|
||||
0.0, col_frac - 0.5
|
||||
)
|
||||
if valid(row + 1, col + 1):
|
||||
self._view[row + 1, col + 1, d] += (max(0.0, row_frac - 0.5)) * max(
|
||||
0.0, col_frac - 0.5
|
||||
)
|
||||
if valid(row + 1, col - 1):
|
||||
self._view[row + 1, col - 1, d] += (max(0.0, row_frac - 0.5)) * max(
|
||||
0.0, 0.5 - col_frac
|
||||
)
|
||||
|
||||
# Draw ant.
|
||||
robot_x, robot_y = self.wrapped_env.get_body_com("torso")[:2]
|
||||
self._robot_x = robot_x
|
||||
self._robot_y = robot_y
|
||||
self._robot_ori = self.get_ori()
|
||||
|
||||
structure = self.MAZE_STRUCTURE
|
||||
size_scaling = self.MAZE_SIZE_SCALING
|
||||
height = self.MAZE_HEIGHT
|
||||
|
||||
# Draw immovable blocks and chasms.
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
if structure[i][j] == 1: # Wall.
|
||||
update_view(
|
||||
j * size_scaling - self._init_torso_x,
|
||||
i * size_scaling - self._init_torso_y,
|
||||
0,
|
||||
)
|
||||
if structure[i][j] == -1: # Chasm.
|
||||
update_view(
|
||||
j * size_scaling - self._init_torso_x,
|
||||
i * size_scaling - self._init_torso_y,
|
||||
1,
|
||||
)
|
||||
|
||||
# Draw movable blocks.
|
||||
for block_name, block_type in self.movable_blocks:
|
||||
block_x, block_y = self.wrapped_env.get_body_com(block_name)[:2]
|
||||
update_view(block_x, block_y, 2)
|
||||
|
||||
return self._view
|
||||
|
||||
def get_range_sensor_obs(self):
|
||||
"""Returns egocentric range sensor observations of maze."""
|
||||
robot_x, robot_y, robot_z = self.wrapped_env.get_body_com("torso")[:3]
|
||||
ori = self.get_ori()
|
||||
|
||||
structure = self.MAZE_STRUCTURE
|
||||
size_scaling = self.MAZE_SIZE_SCALING
|
||||
height = self.MAZE_HEIGHT
|
||||
|
||||
segments = []
|
||||
# Get line segments (corresponding to outer boundary) of each immovable
|
||||
# block or drop-off.
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
if structure[i][j] in [1, -1]: # There's a wall or drop-off.
|
||||
cx = j * size_scaling - self._init_torso_x
|
||||
cy = i * size_scaling - self._init_torso_y
|
||||
x1 = cx - 0.5 * size_scaling
|
||||
x2 = cx + 0.5 * size_scaling
|
||||
y1 = cy - 0.5 * size_scaling
|
||||
y2 = cy + 0.5 * size_scaling
|
||||
struct_segments = [
|
||||
((x1, y1), (x2, y1)),
|
||||
((x2, y1), (x2, y2)),
|
||||
((x2, y2), (x1, y2)),
|
||||
((x1, y2), (x1, y1)),
|
||||
]
|
||||
for seg in struct_segments:
|
||||
segments.append(dict(segment=seg, type=structure[i][j],))
|
||||
# Get line segments (corresponding to outer boundary) of each movable
|
||||
# block within the agent's z-view.
|
||||
for block_name, block_type in self.movable_blocks:
|
||||
block_x, block_y, block_z = self.wrapped_env.get_body_com(block_name)[:3]
|
||||
if (
|
||||
block_z + height * size_scaling / 2 >= robot_z
|
||||
and robot_z >= block_z - height * size_scaling / 2
|
||||
): # Block in view.
|
||||
x1 = block_x - 0.5 * size_scaling
|
||||
x2 = block_x + 0.5 * size_scaling
|
||||
y1 = block_y - 0.5 * size_scaling
|
||||
y2 = block_y + 0.5 * size_scaling
|
||||
struct_segments = [
|
||||
((x1, y1), (x2, y1)),
|
||||
((x2, y1), (x2, y2)),
|
||||
((x2, y2), (x1, y2)),
|
||||
((x1, y2), (x1, y1)),
|
||||
]
|
||||
for seg in struct_segments:
|
||||
segments.append(dict(segment=seg, type=block_type,))
|
||||
|
||||
sensor_readings = np.zeros((self._n_bins, 3)) # 3 for wall, drop-off, block
|
||||
for ray_idx in range(self._n_bins):
|
||||
ray_ori = (
|
||||
ori
|
||||
- self._sensor_span * 0.5
|
||||
+ (2 * ray_idx + 1.0) / (2 * self._n_bins) * self._sensor_span
|
||||
)
|
||||
ray_segments = []
|
||||
# Get all segments that intersect with ray.
|
||||
for seg in segments:
|
||||
p = maze_env_utils.ray_segment_intersect(
|
||||
ray=((robot_x, robot_y), ray_ori), segment=seg["segment"]
|
||||
)
|
||||
if p is not None:
|
||||
ray_segments.append(
|
||||
dict(
|
||||
segment=seg["segment"],
|
||||
type=seg["type"],
|
||||
ray_ori=ray_ori,
|
||||
distance=maze_env_utils.point_distance(
|
||||
p, (robot_x, robot_y)
|
||||
),
|
||||
)
|
||||
)
|
||||
if len(ray_segments) > 0:
|
||||
# Find out which segment is intersected first.
|
||||
first_seg = sorted(ray_segments, key=lambda x: x["distance"])[0]
|
||||
seg_type = first_seg["type"]
|
||||
idx = (
|
||||
0
|
||||
if seg_type == 1
|
||||
else 1 # Wall.
|
||||
if seg_type == -1
|
||||
else 2 # Drop-off.
|
||||
if maze_env_utils.can_move(seg_type)
|
||||
else None # Block.
|
||||
)
|
||||
if first_seg["distance"] <= self._sensor_range:
|
||||
sensor_readings[ray_idx][idx] = (
|
||||
self._sensor_range - first_seg["distance"]
|
||||
) / self._sensor_range
|
||||
|
||||
return sensor_readings
|
||||
|
||||
def _get_obs(self):
|
||||
wrapped_obs = self.wrapped_env._get_obs()
|
||||
if self._top_down_view:
|
||||
view = [self.get_top_down_view().flat]
|
||||
else:
|
||||
view = []
|
||||
|
||||
if self._observe_blocks:
|
||||
additional_obs = []
|
||||
for block_name, block_type in self.movable_blocks:
|
||||
additional_obs.append(self.wrapped_env.get_body_com(block_name))
|
||||
wrapped_obs = np.concatenate(
|
||||
[wrapped_obs[:3]] + additional_obs + [wrapped_obs[3:]]
|
||||
)
|
||||
|
||||
range_sensor_obs = self.get_range_sensor_obs()
|
||||
return np.concatenate(
|
||||
[wrapped_obs, range_sensor_obs.flat] + view + [[self.t * 0.001]]
|
||||
)
|
||||
|
||||
def reset(self):
|
||||
self.t = 0
|
||||
self.trajectory = []
|
||||
self.wrapped_env.reset()
|
||||
if len(self._init_positions) > 1:
|
||||
xy = random.choice(self._init_positions)
|
||||
self.wrapped_env.set_xy(xy)
|
||||
return self._get_obs()
|
||||
|
||||
@property
|
||||
def viewer(self):
|
||||
return self.wrapped_env.viewer
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
return self.wrapped_env.render(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def observation_space(self):
|
||||
shape = self._get_obs().shape
|
||||
high = np.inf * np.ones(shape)
|
||||
low = -high
|
||||
return gym.spaces.Box(low, high)
|
||||
|
||||
@property
|
||||
def action_space(self):
|
||||
return self.wrapped_env.action_space
|
||||
|
||||
def _find_robot(self):
|
||||
structure = self.MAZE_STRUCTURE
|
||||
size_scaling = self.MAZE_SIZE_SCALING
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
if structure[i][j] == "r":
|
||||
return j * size_scaling, i * size_scaling
|
||||
assert False, "No robot in maze specification."
|
||||
|
||||
def _find_all_robots(self):
|
||||
structure = self.MAZE_STRUCTURE
|
||||
size_scaling = self.MAZE_SIZE_SCALING
|
||||
coords = []
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
if structure[i][j] == "r":
|
||||
coords.append((j * size_scaling, i * size_scaling))
|
||||
return coords
|
||||
|
||||
def _is_in_collision(self, pos):
|
||||
x, y = pos
|
||||
structure = self.MAZE_STRUCTURE
|
||||
size_scaling = self.MAZE_SIZE_SCALING
|
||||
for i in range(len(structure)):
|
||||
for j in range(len(structure[0])):
|
||||
if structure[i][j] == 1:
|
||||
minx = j * size_scaling - size_scaling * 0.5 - self._init_torso_x
|
||||
maxx = j * size_scaling + size_scaling * 0.5 - self._init_torso_x
|
||||
miny = i * size_scaling - size_scaling * 0.5 - self._init_torso_y
|
||||
maxy = i * size_scaling + size_scaling * 0.5 - self._init_torso_y
|
||||
if minx <= x <= maxx and miny <= y <= maxy:
|
||||
return True
|
||||
return False
|
||||
|
||||
def step(self, action):
|
||||
self.t += 1
|
||||
if self._manual_collision:
|
||||
old_pos = self.wrapped_env.get_xy()
|
||||
inner_next_obs, inner_reward, done, info = self.wrapped_env.step(action)
|
||||
new_pos = self.wrapped_env.get_xy()
|
||||
if self._is_in_collision(new_pos):
|
||||
self.wrapped_env.set_xy(old_pos)
|
||||
else:
|
||||
inner_next_obs, inner_reward, done, info = self.wrapped_env.step(action)
|
||||
next_obs = self._get_obs()
|
||||
done = False
|
||||
return next_obs, inner_reward, done, info
|
162
mujoco_maze/maze_env_utils.py
Normal file
162
mujoco_maze/maze_env_utils.py
Normal file
@ -0,0 +1,162 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""Adapted from rllab maze_env_utils.py."""
|
||||
import math
|
||||
|
||||
|
||||
class Move(object):
|
||||
X = 11
|
||||
Y = 12
|
||||
Z = 13
|
||||
XY = 14
|
||||
XZ = 15
|
||||
YZ = 16
|
||||
XYZ = 17
|
||||
SpinXY = 18
|
||||
|
||||
|
||||
def can_move_x(movable):
|
||||
return movable in [Move.X, Move.XY, Move.XZ, Move.XYZ, Move.SpinXY]
|
||||
|
||||
|
||||
def can_move_y(movable):
|
||||
return movable in [Move.Y, Move.XY, Move.YZ, Move.XYZ, Move.SpinXY]
|
||||
|
||||
|
||||
def can_move_z(movable):
|
||||
return movable in [Move.Z, Move.XZ, Move.YZ, Move.XYZ]
|
||||
|
||||
|
||||
def can_spin(movable):
|
||||
return movable in [Move.SpinXY]
|
||||
|
||||
|
||||
def can_move(movable):
|
||||
return can_move_x(movable) or can_move_y(movable) or can_move_z(movable)
|
||||
|
||||
|
||||
def construct_maze(maze_id="Maze"):
|
||||
if maze_id == "Maze":
|
||||
structure = [
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, "r", 0, 0, 1],
|
||||
[1, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
]
|
||||
elif maze_id == "Push":
|
||||
structure = [
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, 0, "r", 1, 1],
|
||||
[1, 0, Move.XY, 0, 1],
|
||||
[1, 1, 0, 1, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
]
|
||||
elif maze_id == "Fall":
|
||||
structure = [
|
||||
[1, 1, 1, 1],
|
||||
[1, "r", 0, 1],
|
||||
[1, 0, Move.YZ, 1],
|
||||
[1, -1, -1, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 1, 1],
|
||||
]
|
||||
elif maze_id == "Block":
|
||||
O = "r"
|
||||
structure = [
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, O, 0, 0, 1],
|
||||
[1, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
]
|
||||
elif maze_id == "BlockMaze":
|
||||
O = "r"
|
||||
structure = [
|
||||
[1, 1, 1, 1],
|
||||
[1, O, 0, 1],
|
||||
[1, 1, 0, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 1, 1],
|
||||
]
|
||||
else:
|
||||
raise NotImplementedError("The provided MazeId %s is not recognized" % maze_id)
|
||||
|
||||
return structure
|
||||
|
||||
|
||||
def line_intersect(pt1, pt2, ptA, ptB):
|
||||
"""
|
||||
Taken from https://www.cs.hmc.edu/ACM/lectures/intersections.html
|
||||
|
||||
this returns the intersection of Line(pt1,pt2) and Line(ptA,ptB)
|
||||
"""
|
||||
|
||||
DET_TOLERANCE = 0.00000001
|
||||
|
||||
# the first line is pt1 + r*(pt2-pt1)
|
||||
# in component form:
|
||||
x1, y1 = pt1
|
||||
x2, y2 = pt2
|
||||
dx1 = x2 - x1
|
||||
dy1 = y2 - y1
|
||||
|
||||
# the second line is ptA + s*(ptB-ptA)
|
||||
x, y = ptA
|
||||
xB, yB = ptB
|
||||
dx = xB - x
|
||||
dy = yB - y
|
||||
|
||||
DET = -dx1 * dy + dy1 * dx
|
||||
|
||||
if math.fabs(DET) < DET_TOLERANCE:
|
||||
return (0, 0, 0, 0, 0)
|
||||
|
||||
# now, the determinant should be OK
|
||||
DETinv = 1.0 / DET
|
||||
|
||||
# find the scalar amount along the "self" segment
|
||||
r = DETinv * (-dy * (x - x1) + dx * (y - y1))
|
||||
|
||||
# find the scalar amount along the input line
|
||||
s = DETinv * (-dy1 * (x - x1) + dx1 * (y - y1))
|
||||
|
||||
# return the average of the two descriptions
|
||||
xi = (x1 + r * dx1 + x + s * dx) / 2.0
|
||||
yi = (y1 + r * dy1 + y + s * dy) / 2.0
|
||||
return (xi, yi, 1, r, s)
|
||||
|
||||
|
||||
def ray_segment_intersect(ray, segment):
|
||||
"""
|
||||
Check if the ray originated from (x, y) with direction theta intersects the line segment (x1, y1) -- (x2, y2),
|
||||
and return the intersection point if there is one
|
||||
"""
|
||||
(x, y), theta = ray
|
||||
# (x1, y1), (x2, y2) = segment
|
||||
pt1 = (x, y)
|
||||
len = 1
|
||||
pt2 = (x + len * math.cos(theta), y + len * math.sin(theta))
|
||||
xo, yo, valid, r, s = line_intersect(pt1, pt2, *segment)
|
||||
if valid and r >= 0 and 0 <= s <= 1:
|
||||
return (xo, yo)
|
||||
return None
|
||||
|
||||
|
||||
def point_distance(p1, p2):
|
||||
x1, y1 = p1
|
||||
x2, y2 = p2
|
||||
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
|
101
mujoco_maze/point.py
Normal file
101
mujoco_maze/point.py
Normal file
@ -0,0 +1,101 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""Wrapper for creating the ant environment in gym_mujoco."""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
import mujoco_py
|
||||
from gym import utils
|
||||
from gym.envs.mujoco import mujoco_env
|
||||
|
||||
|
||||
class PointEnv(mujoco_env.MujocoEnv, utils.EzPickle):
|
||||
FILE = "point.xml"
|
||||
ORI_IND = 2
|
||||
|
||||
def __init__(self, file_path=None, expose_all_qpos=True):
|
||||
self._expose_all_qpos = expose_all_qpos
|
||||
|
||||
mujoco_env.MujocoEnv.__init__(self, file_path, 1)
|
||||
utils.EzPickle.__init__(self)
|
||||
|
||||
@property
|
||||
def physics(self):
|
||||
# check mujoco version is greater than version 1.50 to call correct physics
|
||||
# model containing PyMjData object for getting and setting position/velocity
|
||||
# check https://github.com/openai/mujoco-py/issues/80 for updates to api
|
||||
if mujoco_py.get_version() >= "1.50":
|
||||
return self.sim
|
||||
else:
|
||||
return self.model
|
||||
|
||||
def _step(self, a):
|
||||
return self.step(a)
|
||||
|
||||
def step(self, action):
|
||||
action[0] = 0.2 * action[0]
|
||||
qpos = np.copy(self.physics.data.qpos)
|
||||
qpos[2] += action[1]
|
||||
ori = qpos[2]
|
||||
# compute increment in each direction
|
||||
dx = math.cos(ori) * action[0]
|
||||
dy = math.sin(ori) * action[0]
|
||||
# ensure that the robot is within reasonable range
|
||||
qpos[0] = np.clip(qpos[0] + dx, -100, 100)
|
||||
qpos[1] = np.clip(qpos[1] + dy, -100, 100)
|
||||
qvel = self.physics.data.qvel
|
||||
self.set_state(qpos, qvel)
|
||||
for _ in range(0, self.frame_skip):
|
||||
self.physics.step()
|
||||
next_obs = self._get_obs()
|
||||
reward = 0
|
||||
done = False
|
||||
info = {}
|
||||
return next_obs, reward, done, info
|
||||
|
||||
def _get_obs(self):
|
||||
if self._expose_all_qpos:
|
||||
return np.concatenate(
|
||||
[
|
||||
self.physics.data.qpos.flat[:3], # Only point-relevant coords.
|
||||
self.physics.data.qvel.flat[:3],
|
||||
]
|
||||
)
|
||||
return np.concatenate(
|
||||
[self.physics.data.qpos.flat[2:3], self.physics.data.qvel.flat[:3]]
|
||||
)
|
||||
|
||||
def reset_model(self):
|
||||
qpos = self.init_qpos + self.np_random.uniform(
|
||||
size=self.physics.model.nq, low=-0.1, high=0.1
|
||||
)
|
||||
qvel = self.init_qvel + self.np_random.randn(self.physics.model.nv) * 0.1
|
||||
|
||||
# Set everything other than point to original position and 0 velocity.
|
||||
qpos[3:] = self.init_qpos[3:]
|
||||
qvel[3:] = 0.0
|
||||
self.set_state(qpos, qvel)
|
||||
return self._get_obs()
|
||||
|
||||
def get_ori(self):
|
||||
return self.physics.data.qpos[self.__class__.ORI_IND]
|
||||
|
||||
def set_xy(self, xy):
|
||||
qpos = np.copy(self.physics.data.qpos)
|
||||
qpos[0] = xy[0]
|
||||
qpos[1] = xy[1]
|
||||
|
||||
qvel = self.physics.data.qvel
|
21
mujoco_maze/point_maze_env.py
Normal file
21
mujoco_maze/point_maze_env.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2018 The TensorFlow Authors All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
from environments.maze_env import MazeEnv
|
||||
from environments.point import PointEnv
|
||||
|
||||
|
||||
class PointMazeEnv(MazeEnv):
|
||||
MODEL_CLASS = PointEnv
|
18
pyproject.toml
Normal file
18
pyproject.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
target-version = ['py36']
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
|
||||
(
|
||||
/(
|
||||
\.eggs
|
||||
| \.git
|
||||
| \.mypy_cache
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
)/
|
||||
)
|
||||
'''
|
18
setup.cfg
Normal file
18
setup.cfg
Normal file
@ -0,0 +1,18 @@
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
ignore = W391, W503, F821, E203, E231
|
||||
|
||||
[mypy]
|
||||
python_version = 3.6
|
||||
ignore_missing_imports = True
|
||||
warn_unused_configs = True
|
||||
|
||||
[isort]
|
||||
multi_line_output=3
|
||||
include_trailing_comma=True
|
||||
force_grid_wrap=0
|
||||
use_parentheses=True
|
||||
line_length=88
|
||||
lines_between_types=0
|
||||
combine_as_imports=True
|
||||
known_first_party=rainy, tests
|
Loading…
Reference in New Issue
Block a user