tinysim 0.0.4__py3-none-any.whl → 0.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. tinysim/__init__.py +7 -2
  2. tinysim/_tk_base.py +20 -2
  3. tinysim/_widget_base.py +41 -0
  4. tinysim/flappy/__init__.py +5 -3
  5. tinysim/flappy/tk.py +10 -34
  6. tinysim/flappy/widget.py +9 -38
  7. tinysim/frogger/__init__.py +21 -4
  8. tinysim/frogger/tk.py +12 -28
  9. tinysim/frogger/widget.py +8 -37
  10. tinysim/mountain_car/__init__.py +17 -12
  11. tinysim/mountain_car/tk.py +9 -27
  12. tinysim/mountain_car/widget.py +8 -37
  13. tinysim/tinyspace.py +68 -0
  14. tinysim/topdown_driving/__init__.py +16 -18
  15. tinysim/topdown_driving/tk.py +13 -38
  16. tinysim/topdown_driving/track_0.json +1 -753
  17. tinysim/topdown_driving/widget.py +8 -42
  18. {tinysim-0.0.4.dist-info → tinysim-0.0.5.dist-info}/METADATA +5 -6
  19. {tinysim-0.0.4.dist-info → tinysim-0.0.5.dist-info}/RECORD +35 -35
  20. {tinysim-0.0.4.dist-info → tinysim-0.0.5.dist-info}/WHEEL +1 -1
  21. tinysim_mujoco/gl_viewer.py +36 -11
  22. tinysim_mujoco/manipulation/__init__.py +19 -5
  23. tinysim_mujoco/manipulation/push_env.py +268 -0
  24. tinysim_mujoco/manipulation/push_env_cam.py +274 -0
  25. tinysim_mujoco/manipulation/xmls/panda.xml +59 -16
  26. tinysim_mujoco/manipulation/xmls/scene.xml +0 -3
  27. tinysim_mujoco/manipulation/xmls/table.xml +17 -13
  28. tinysim_mujoco/notebook_viewer.py +3 -6
  29. tinysim_mujoco/unitree_a1/__init__.py +7 -10
  30. tinysim_warp/__init__.py +108 -0
  31. tinysim_warp/cart_pole/__init__.py +44 -200
  32. tinysim_warp/quadruped/__init__.py +23 -160
  33. tinysim_warp/simple_quadruped/__init__.py +20 -91
  34. tinysim_warp/simple_quadruped/simple_quadruped.urdf +0 -9
  35. tinysim/simple_amr/__init__.py +0 -0
  36. tinysim/simple_amr/example_maps.py +0 -121
  37. tinysim/simple_amr/sim.js +0 -430
  38. tinysim/simple_amr/styles.css +0 -54
  39. tinysim/simple_amr/widget.py +0 -73
  40. {tinysim-0.0.4.dist-info → tinysim-0.0.5.dist-info}/top_level.txt +0 -0
@@ -1,59 +1,25 @@
1
1
  import pathlib
2
- import anywidget
2
+
3
3
  import traitlets
4
- import asyncio
5
- import numpy as np
6
- from IPython.display import display
7
- from jupyter_ui_poll import ui_events
8
4
 
9
- from . import TopDownDrivingEnv, LOCAL_WALLS
5
+ from .._widget_base import BaseWidget
6
+ from . import LOCAL_WALLS, TopDownDrivingEnv
10
7
 
11
8
 
12
- class TopDownDrivingWidget(anywidget.AnyWidget):
9
+ class TopDownDrivingWidget(BaseWidget):
13
10
  _esm = pathlib.Path(__file__).parent / "sim.js"
14
11
 
15
12
  sim_state = traitlets.Dict(default_value={}).tag(sync=True)
16
13
  wall_positions = traitlets.List(default_value=LOCAL_WALLS).tag(sync=True)
14
+ _viewport_size = traitlets.Tuple(default_value=(800, 600)).tag(sync=True)
17
15
 
18
- _viewport_size = traitlets.Tuple(
19
- traitlets.Int(), traitlets.Int(), default_value=(800, 600)
20
- ).tag(sync=True)
21
- _view_ready = traitlets.Bool(default_value=False).tag(sync=True)
22
-
23
- def __init__(self, viewport_size=(800, 600), sim_env=None):
16
+ def __init__(self, viewport_size=(800, 600), sim_env=TopDownDrivingEnv()):
17
+ super().__init__(sim_env)
24
18
  self._viewport_size = viewport_size
25
- super().__init__()
26
- if sim_env is None:
27
- sim_env = TopDownDrivingEnv()
28
-
29
- self.sim_env = sim_env
30
- self.copy_py_state(self.sim_env.reset())
31
-
32
- def render(self):
33
- display(self)
34
19
 
35
- try:
36
- with ui_events() as ui_poll:
37
- while not self._view_ready:
38
- ui_poll(100)
39
- except Exception:
40
- pass
41
-
42
- def copy_py_state(self, sim_state: dict):
20
+ def _update_props(self, sim_state):
43
21
  self.sim_state = {
44
22
  "x": sim_state["x"].tolist(),
45
23
  "y": sim_state["y"].tolist(),
46
24
  "angle": sim_state["angle"].tolist(),
47
25
  }
48
-
49
- async def step(self, action: int, dt: float = 0.01) -> dict:
50
- sim_state = self.sim_env.step(action)
51
- self.copy_py_state(sim_state)
52
-
53
- await asyncio.sleep(dt)
54
- return sim_state
55
-
56
- async def reset(self) -> dict:
57
- sim_state = self.sim_env.reset()
58
- self.copy_py_state(sim_state)
59
- return sim_state
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinysim
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: small modular simulation environments
5
5
  Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/MatthewAndreTaylor/TinySim
@@ -30,7 +30,7 @@ Requires-Dist: mujoco; extra == "mujoco"
30
30
  # TinySim
31
31
 
32
32
  [![PyPI](https://img.shields.io/pypi/v/tinysim.svg)](https://pypi.org/project/tinysim)
33
- [![Jupyterlite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://matthewandretaylor.github.io/TinySim/lab?path=example.ipynb)
33
+ [![Jupyterlite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://matthewandretaylor.github.io/TinySim)
34
34
 
35
35
  The goal of this project is to create more minimal simulation environments for robotics and machine learning.
36
36
 
@@ -73,8 +73,7 @@ To use the [mujoco](https://github.com/google-deepmind/mujoco) simulation enviro
73
73
  ```bash
74
74
  pip install tinysim[mujoco]
75
75
  ```
76
- | [UnitreeA1WalkEnv](https://github.com/MatthewAndreTaylor/TinySim/tree/main/tinysim_mujoco/unitree_a1) |
77
- |:-------------------------:|
78
- | <img width="312" alt="mujoco_sim" src="https://github.com/user-attachments/assets/1109488e-0563-4694-8926-682edd0c8278" /> |
79
-
76
+ | [UnitreeA1WalkEnv](https://github.com/MatthewAndreTaylor/TinySim/tree/main/tinysim_mujoco/unitree_a1) | [ManipulationEnvV0](https://github.com/MatthewAndreTaylor/TinySim/tree/main/tinysim_mujoco/manipulation) |
77
+ |:-------------------------:|:-------------------------:|
78
+ | <img width="312" alt="mujoco_sim" src="https://github.com/user-attachments/assets/1109488e-0563-4694-8926-682edd0c8278" /> | <img width="312" alt="mujoco_sim" src="https://github.com/user-attachments/assets/07b406b5-15fc-4cfd-83c4-cf6b855d773a" /> |
80
79
 
@@ -1,35 +1,34 @@
1
- tinysim/__init__.py,sha256=ptkxqwWun-Tw-L2-zUTm2fqunNgdLBkdU3DHF1wAMBI,197
2
- tinysim/_tk_base.py,sha256=kzF-41H56A5FWNxtMhlvEbl3iH8Wp04zTqAxTcjRPDM,1279
3
- tinysim/flappy/__init__.py,sha256=lUHGteFeYRb2vQ3-x9WxnKWM7sfsDwIHoVps732JIDY,3482
1
+ tinysim/__init__.py,sha256=971PtvBBFPJ9zBfRRLdfuGRE2oEX739127n_Y6MNJIg,263
2
+ tinysim/_tk_base.py,sha256=vUJEx0NXfo8TLZycQNOhZgbx_2ybrBjvaJC_KQ6jvBo,1790
3
+ tinysim/_widget_base.py,sha256=_L6xSOH4LOpXIvCFCczDkcorS8xH2E6UjdTH3L6B6tM,1084
4
+ tinysim/tinyspace.py,sha256=x4MKrou4-9aKmNQ0V7RSxYhHk3akzruF56Sn14wnO7s,1950
5
+ tinysim/flappy/__init__.py,sha256=_0OBPUyymQCVkwCRiG7Km_oIwlrhWP808r4e1COSZ3A,3551
4
6
  tinysim/flappy/sim.js,sha256=g8ycKdJGopu0Z5CeBgtF8Rr2Pe64dTFcSiYh7hzYPzU,3190
5
- tinysim/flappy/tk.py,sha256=DUJqy0uPn-AvNE4tuA3a4Dh6xaj0twsUOP8leqGgBX4,2458
6
- tinysim/flappy/widget.py,sha256=zyaoW-rWgAkmbCY7yrSYyAZDUQaAPnuQEzldSawkOnI,1597
7
- tinysim/frogger/__init__.py,sha256=gA2jvG_WDZRpZY0Zg_aJmWrdsk2siIL49wjFTRfOSxo,5227
7
+ tinysim/flappy/tk.py,sha256=HR3AlHzGLxLWw2qdtY8pbyFxEELb3sItGWNeOqqjHDA,2111
8
+ tinysim/flappy/widget.py,sha256=BSrEvAmUKbaqF9zZq7rZ4WxgIDjt1NTmUIb0gg0gt4M,748
9
+ tinysim/frogger/__init__.py,sha256=YHumyU2tR8oRZoJACLPQ3Ncyw7b3MbR_W2LJSmtdv9g,5712
8
10
  tinysim/frogger/sim.js,sha256=b1HfEtFZFbbsrZNtltuWO4h-QM4Fw3XjMRydwm5l2NU,3081
9
- tinysim/frogger/tk.py,sha256=gb2V8bVCVuVazRrfTbAWq46bK20d5ugHxau_rlNABXE,2992
10
- tinysim/frogger/widget.py,sha256=Bvb6aUiVqJTHTdd6EzpCp0FqpRa0bl9RNcK12_weiPM,1810
11
- tinysim/mountain_car/__init__.py,sha256=V3Qxj_ydWAqOJiQUqmIRegBe9GNjU82BfbG2XvV0OQg,1986
11
+ tinysim/frogger/tk.py,sha256=gXxp_9sbcFM__-JdX6Jhymf9ZzoIt7N1GmK9DPTW724,2518
12
+ tinysim/frogger/widget.py,sha256=mYlc7zToV7C1Gy_OdvFjCk2xygAdO2ggGupNxXruckk,952
13
+ tinysim/mountain_car/__init__.py,sha256=zR-WaZGF3PPz4ajBlhOqWDxaKSa68bYuKJNaR_PwKjI,2207
12
14
  tinysim/mountain_car/sim.js,sha256=z7kxl7LbkRglfzXxmrBbwz2vMYdQV84QP3WhE0hoPVQ,4581
13
15
  tinysim/mountain_car/styles.css,sha256=a8Bc64RdRNB1uWsBdpMEQA9STZO8mVAB2vvIPhhYuik,63
14
- tinysim/mountain_car/tk.py,sha256=EbnLirA0Q5egJaDUzu_WkGjbofbclwYUcHIvvifNt0Y,4256
15
- tinysim/mountain_car/widget.py,sha256=bUIXsofy5moBXMDBdIOVvRi59NFbLRRQ1WZcwm_mYG8,1726
16
- tinysim/simple_amr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- tinysim/simple_amr/example_maps.py,sha256=38duuHbd6HvhQBIIcg9PWokNMjkasPSsKPjHnBhv9qQ,3307
18
- tinysim/simple_amr/sim.js,sha256=2YW2YWEEe-QWgJGLAdpt1aZi__sLL5ML84fv1s90poY,11575
19
- tinysim/simple_amr/styles.css,sha256=t85kXf0xyrq5ldL-j_pV0FEekWoorK63T_ZorWEgBOw,1087
20
- tinysim/simple_amr/widget.py,sha256=dmNcftB7-Z9Kf3hQ0MBaJvo97eyjCA6R74oEkHASMas,2557
21
- tinysim/topdown_driving/__init__.py,sha256=HiRpxQNeu4IIOPFraGHLTO2F0VNISAk6bUiK1rsbZTE,6228
16
+ tinysim/mountain_car/tk.py,sha256=WMlktnnSEWXJ7ppWb-WKg2wm_jtWN-XwIrFVGk64GIo,3753
17
+ tinysim/mountain_car/widget.py,sha256=3EasBKyA0_MsjqlG97OLBNRlGyK8qzT2zQijWnVf9SM,860
18
+ tinysim/topdown_driving/__init__.py,sha256=ZA4DcQHRs9zeraTYE7Id8ityGZksYtGPN8GiSq2RsJI,5965
22
19
  tinysim/topdown_driving/sim.js,sha256=5jmyQk4zp4KFhaG-uTsW_Cq1gHoIkR9d2Cv_ezDKarM,3076
23
- tinysim/topdown_driving/tk.py,sha256=8UB95ZjIJiUQeXC5w3pXkjukOJXfisLKrfIn0bVFzjU,4751
24
- tinysim/topdown_driving/track_0.json,sha256=3zPfPX72s5zxs4znoO8rXBqGNciai10Jbl2rFCaACow,15338
25
- tinysim/topdown_driving/widget.py,sha256=Eh7tvtmMbgmzFEvQZ10D6npfF2s1k7QD5AHLYX5Jaqw,1718
20
+ tinysim/topdown_driving/tk.py,sha256=WfiFq1zub7x9MGi5aQ4JVQB8qVGKwBT47e3n-_Mju48,4272
21
+ tinysim/topdown_driving/track_0.json,sha256=tZVZtftQCdsB11Gwqj6OECOyOa52RBFTE3vk_e4n8_k,9515
22
+ tinysim/topdown_driving/widget.py,sha256=KVn8BCHicVql07BqdcO8x3i1H67woDiTbu60ypJPZc4,802
26
23
  tinysim_mujoco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- tinysim_mujoco/gl_viewer.py,sha256=s0x4HuYwspsIOl7CBywz7cs0g37p22e7fWGtcLXuq6g,2754
28
- tinysim_mujoco/notebook_viewer.py,sha256=4hbQKcJLC8jAOd5xQyz5yjia5N0_CaKe0s4LgP0VnmM,1035
29
- tinysim_mujoco/manipulation/__init__.py,sha256=7JB29iraYN64MXAuWNoB6vY38Qr_KvZB66BxPmhZw9U,1129
30
- tinysim_mujoco/manipulation/xmls/panda.xml,sha256=y5tZFUX6zbj8nb32jux4WuT4nMQ4u3i-ziOEnfoFtFA,14803
31
- tinysim_mujoco/manipulation/xmls/scene.xml,sha256=cM_FJCnt-aK4b4L2kTVNSd7P-rL6l4aQodOcd-5zB-s,921
32
- tinysim_mujoco/manipulation/xmls/table.xml,sha256=TFG7lbTr-93_V94sDpJMveuGgtNVPrip6g1YFeE8eXs,1068
24
+ tinysim_mujoco/gl_viewer.py,sha256=0cPmjk3GDhtca9cMiV3px_w8McjXF577NNNGJIdAWxQ,3401
25
+ tinysim_mujoco/notebook_viewer.py,sha256=TwLa4VCKovE3y8NkcdSOkUhoQ3JHvq7Pqni5VpANhQ4,996
26
+ tinysim_mujoco/manipulation/__init__.py,sha256=8sEszaf47LjRaVlSO6k81cwK-UT9mRRHB2mXZyfCl-s,1675
27
+ tinysim_mujoco/manipulation/push_env.py,sha256=IEQi4dR8DVwWxwvmzZhzYLBzVTkZ_62tKsy2yL2PDYw,10456
28
+ tinysim_mujoco/manipulation/push_env_cam.py,sha256=B-GSB9WHew2iq4iUDWc-X78OqwHBl1jtpTGvSgVaOyY,10707
29
+ tinysim_mujoco/manipulation/xmls/panda.xml,sha256=5uTQ473BIGDIO1aASEeLIxw2aaMBjistu_PYMjaN6BU,17868
30
+ tinysim_mujoco/manipulation/xmls/scene.xml,sha256=_6nYdaFPi_Of-GTJPBUg2nCLKab_uz5SM-eFWfvz6Gc,918
31
+ tinysim_mujoco/manipulation/xmls/table.xml,sha256=Gm0-ejM7mmrJQXGkZjEI2JpyOElQsJNbh3srGSWTuUI,1637
33
32
  tinysim_mujoco/manipulation/xmls/assets/d405.stl,sha256=Zhfz-yK2BKZsJ-FtI5fzBfzcqKeBJIq9urv2nJ_mo8U,50084
34
33
  tinysim_mujoco/manipulation/xmls/assets/finger_0.obj,sha256=8UwcM_XWXKrdyKWg0-Hwg-GjPsqqe-HpHLgdTyHE4vM,23013
35
34
  tinysim_mujoco/manipulation/xmls/assets/finger_1.obj,sha256=tLZPSqpntdM74TQDqHT-r0-ov3swyLJr15JCgpx-QIg,16772
@@ -98,7 +97,7 @@ tinysim_mujoco/manipulation/xmls/assets/link7_4.obj,sha256=SZSny1BhpxKpgq5AYX1Hy
98
97
  tinysim_mujoco/manipulation/xmls/assets/link7_5.obj,sha256=MsT-PFfY9bpGdtVULacqRtZKBSKmZ15Lf6J_tChFlkE,61442
99
98
  tinysim_mujoco/manipulation/xmls/assets/link7_6.obj,sha256=1avsmFwLFqymiEQN7-usXrwQcYDJAhoiZBgs1RXnqNw,27403
100
99
  tinysim_mujoco/manipulation/xmls/assets/link7_7.obj,sha256=s56R21mHTno3skIqRb9lnEoS5Qw8G6N74E6lulVmdgo,221627
101
- tinysim_mujoco/unitree_a1/__init__.py,sha256=VH71dmkOeBWBdNVWyYJ5MMfBx-wpPfZ-GuFJY9kbYfA,10632
100
+ tinysim_mujoco/unitree_a1/__init__.py,sha256=tcWaG1yL8tnF-e6EdQcKNVhQYUsiFsoqmkUD8qZ7D1I,10542
102
101
  tinysim_mujoco/unitree_a1/unitree_a1/CHANGELOG.md,sha256=FIdhX4_Q21xamWMS3-2677HhlETlCnfaAzGemqs4Lpo,143
103
102
  tinysim_mujoco/unitree_a1/unitree_a1/LICENSE,sha256=K3lKzx4lD1VFrlpH2B5WhepUiM4HfRCH7Yvl-B6bpcA,1559
104
103
  tinysim_mujoco/unitree_a1/unitree_a1/README.md,sha256=tHCjy_A1jcGD7bkFy9Qw9z249Kb6HTDQQBlCk8buqlM,1606
@@ -109,11 +108,12 @@ tinysim_mujoco/unitree_a1/unitree_a1/assets/hip.obj,sha256=fVW_Gc8Csumvrb-z-4Ztd
109
108
  tinysim_mujoco/unitree_a1/unitree_a1/assets/thigh.obj,sha256=CdXaRgx4N_02RSH8R6h5k3jyvbd8df04YM8qlP-En1c,979501
110
109
  tinysim_mujoco/unitree_a1/unitree_a1/assets/thigh_mirror.obj,sha256=2LL2hKx8de-Ivj7lM5jkqCEOHTm_AMj78bVyqh99orw,1010010
111
110
  tinysim_mujoco/unitree_a1/unitree_a1/assets/trunk.obj,sha256=8DX8cdkBSlOq4ay-KVhXghmTTjqJwR0qqj9O_5DAt3c,2014440
112
- tinysim_warp/cart_pole/__init__.py,sha256=ltL0cgySOSbeqBvks-UqV1OpT-K-kxo3GylNdvfCB1w,8344
113
- tinysim_warp/quadruped/__init__.py,sha256=aIOlF9vO9GIYn1y4dth4q-fpUdvB9KZq2rf25YcOcqc,6393
114
- tinysim_warp/simple_quadruped/__init__.py,sha256=sXL7EOtlB4qm_l4NsYTOFKvpt438voq7OaPgzciPOU0,5329
115
- tinysim_warp/simple_quadruped/simple_quadruped.urdf,sha256=UfyGROUD5Qlf_t5ISipE2L7uQPBl-hwFnmRsjNL9yH4,8311
116
- tinysim-0.0.4.dist-info/METADATA,sha256=iCf6ezMgRK8qjZt1Q_Sk0dqEWYyMlgao-XtDni0MqYw,3941
117
- tinysim-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
118
- tinysim-0.0.4.dist-info/top_level.txt,sha256=v-9NmTLfZukVjPwJwEdtCU7yY4TdLsBf1H8s25tR3V4,36
119
- tinysim-0.0.4.dist-info/RECORD,,
111
+ tinysim_warp/__init__.py,sha256=i8lHo6ZtR-XGD-isd9zifnp3F1DEs24HOcOKeF9mCjg,3525
112
+ tinysim_warp/cart_pole/__init__.py,sha256=ED-xfKpTlrRftjfBHDyIQ8eDuE3HxuniMAeRWNVAJXg,3636
113
+ tinysim_warp/quadruped/__init__.py,sha256=3fJBrG-mBdim2lT_ON5LlFEYPCfFCs608zNvbB7Q6gs,2247
114
+ tinysim_warp/simple_quadruped/__init__.py,sha256=QoIXplgKUoARiZn4N9Xqu6o2OZn0XLOSk61Ki-5xiz8,3085
115
+ tinysim_warp/simple_quadruped/simple_quadruped.urdf,sha256=60eXZG8nH-ABbtJzYuzdGqdLq-gFKSaDM7brNYt5PN4,8177
116
+ tinysim-0.0.5.dist-info/METADATA,sha256=-EqR_V8RIjVImxDFldhYHM3legWynSl2cM9XIMTv4ko,4177
117
+ tinysim-0.0.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
118
+ tinysim-0.0.5.dist-info/top_level.txt,sha256=v-9NmTLfZukVjPwJwEdtCU7yY4TdLsBf1H8s25tR3V4,36
119
+ tinysim-0.0.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,10 +1,35 @@
1
- import numpy as np
2
- import mujoco
3
1
  import glfw
2
+ import mujoco
3
+ import numpy as np
4
+
5
+
6
+ class OffScreenRenderer:
7
+ def __init__(self, model, data, cam_id):
8
+ self.model = model
9
+ self.data = data
10
+ self.cam_id = cam_id
11
+ self.renderer = mujoco.Renderer(self.model)
12
+ self.renderer.update_scene(self.data, camera=self.cam_id)
13
+
14
+ def render(self):
15
+ self.renderer.update_scene(self.data, camera=self.cam_id)
16
+
17
+ def capture_frame(self):
18
+ return self.renderer.render()
19
+
20
+
21
+ def default_camera():
22
+ cam = mujoco.MjvCamera()
23
+ cam.azimuth = 90.0
24
+ cam.elevation = -25.0
25
+ cam.distance = 4.0
26
+ cam.lookat[:] = np.array([0.0, 0.0, 0.0])
27
+ cam.type = mujoco.mjtCamera.mjCAMERA_FREE
28
+ return cam
4
29
 
5
30
 
6
31
  class GLViewer:
7
- def __init__(self, model, data, width=1200, height=900):
32
+ def __init__(self, model, data, cam_id=-1, width=1200, height=900):
8
33
  self.width = width
9
34
  self.height = height
10
35
  self.model = model
@@ -15,6 +40,13 @@ class GLViewer:
15
40
  if not glfw.init():
16
41
  raise RuntimeError("Failed to initialize GLFW")
17
42
 
43
+ if cam_id != -1:
44
+ self.cam = mujoco.MjvCamera()
45
+ self.cam.fixedcamid = cam_id
46
+ self.cam.type = mujoco.mjtCamera.mjCAMERA_FIXED
47
+ else:
48
+ self.cam = default_camera()
49
+
18
50
  self._create_window()
19
51
 
20
52
  def _create_window(self):
@@ -27,19 +59,12 @@ class GLViewer:
27
59
  glfw.swap_interval(1)
28
60
  glfw.set_window_close_callback(self.window, self._on_close)
29
61
 
30
- self.cam = mujoco.MjvCamera()
31
62
  self.opt = mujoco.MjvOption()
32
63
  self.scene = mujoco.MjvScene(self.model, maxgeom=500)
33
64
  self.context = mujoco.MjrContext(
34
65
  self.model, mujoco.mjtFontScale.mjFONTSCALE_150
35
66
  )
36
67
 
37
- self.cam.azimuth = 90.0
38
- self.cam.elevation = -25.0
39
- self.cam.distance = 4.0
40
- self.cam.lookat[:] = np.array([0.0, 0.0, 0.0])
41
- self.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
42
-
43
68
  def render(self):
44
69
  if self.window is None:
45
70
  return
@@ -85,4 +110,4 @@ class GLViewer:
85
110
 
86
111
  # OpenGL framebuffer origin is bottom-left, flip vertically
87
112
  rgb_buffer = np.flip(rgb_buffer, axis=0)
88
- return rgb_buffer
113
+ return rgb_buffer
@@ -1,10 +1,9 @@
1
1
  import pathlib
2
2
 
3
- import numpy as np
4
-
5
3
  try:
6
4
  import mujoco
7
- from ..gl_viewer import GLViewer
5
+
6
+ from ..gl_viewer import GLViewer, OffScreenRenderer
8
7
  from ..notebook_viewer import NotebookViewer
9
8
  except ImportError:
10
9
  raise ImportError(
@@ -13,8 +12,9 @@ except ImportError:
13
12
 
14
13
 
15
14
  class ManipulationBaseEnv:
16
-
17
- def __init__(self, headless=False, **kwargs):
15
+ def __init__(
16
+ self, headless=False, use_d405_camera=False, show_cam_renders=False, **kwargs
17
+ ):
18
18
  model_path = str(pathlib.Path(__file__).parent / "xmls/scene.xml")
19
19
  self.model = mujoco.MjModel.from_xml_path(model_path)
20
20
  self.data = mujoco.MjData(self.model)
@@ -28,11 +28,25 @@ class ManipulationBaseEnv:
28
28
  else:
29
29
  self.viewer = None
30
30
 
31
+ self.d405_viewer = None
32
+
33
+ if use_d405_camera:
34
+ self.cam_id = mujoco.mj_name2id(
35
+ self.model, mujoco.mjtObj.mjOBJ_CAMERA, "camera_d405"
36
+ )
37
+ if show_cam_renders:
38
+ self.d405_viewer = GLViewer(self.model, self.data, cam_id=self.cam_id)
39
+ else:
40
+ self.d405_viewer = OffScreenRenderer(self.model, self.data, self.cam_id)
41
+
31
42
  def step(self, action=None, n_frames=20):
32
43
  self.render()
33
44
  mujoco.mj_step(self.model, self.data, nstep=n_frames)
34
45
  mujoco.mj_rnePostConstraint(self.model, self.data)
35
46
 
47
+ if self.d405_viewer:
48
+ self.d405_viewer.render()
49
+
36
50
  def render(self):
37
51
  if self.viewer:
38
52
  self.viewer.render()
@@ -0,0 +1,268 @@
1
+ import numpy as np
2
+
3
+ try:
4
+ import mujoco
5
+
6
+ from . import ManipulationBaseEnv
7
+ except ImportError:
8
+ raise ImportError(
9
+ "Mujoco is not properly installed. Install using `pip install tinysim[mujoco]`"
10
+ )
11
+
12
+ _FLOAT_EPS = np.finfo(np.float64).eps
13
+ _EPS4 = _FLOAT_EPS * 4.0
14
+
15
+
16
+ class ManipulationEnvV0:
17
+
18
+ def __init__(self, **kwargs):
19
+ self.env = ManipulationBaseEnv(**kwargs)
20
+ self._model = self.env.model
21
+ self._data = self.env.data
22
+ self.frame_skip = 25
23
+ self.dt = self._model.opt.timestep * self.frame_skip
24
+
25
+ self._body_name2id = {}
26
+ for body_id in range(self._model.nbody):
27
+ name = mujoco.mj_id2name(self._model, mujoco.mjtObj.mjOBJ_BODY, body_id)
28
+ if name is not None:
29
+ self._body_name2id[name] = body_id
30
+
31
+ self.joint_names = []
32
+ for j in range(self._model.njnt):
33
+ name = mujoco.mj_id2name(self._model, mujoco.mjtObj.mjOBJ_JOINT, j)
34
+ if name is not None:
35
+ self.joint_names.append(name)
36
+
37
+ distance_threshold = 0.05
38
+ self.neutral_joint_values = np.array(
39
+ [0.00, 0.41, 0.00, -1.85, 0.00, 2.26, 0.79]
40
+ )
41
+
42
+ self.current_step = 0
43
+ self.maximum_episode_steps = 128
44
+ self.distance_threshold = distance_threshold
45
+
46
+ self.goal = self.get_site_xpos(self._model, self._data, "goal_site").copy()
47
+ self.initial_object_pos = self.get_site_xpos(
48
+ self._model, self._data, "obj_site"
49
+ ).copy()
50
+ free_joint_index = self.joint_names.index("obj_joint")
51
+ self.arm_joint_names = self.joint_names[:free_joint_index][0:7]
52
+ # self.gripper_joint_names = self.joint_names[:free_joint_index][7:9]
53
+ self.set_joint_neutral()
54
+ self.reset_mocap_welds(self._model, self._data)
55
+ mujoco.mj_forward(self._model, self._data)
56
+
57
+ def reset_mocap_welds(self, model, data):
58
+ if model.nmocap > 0 and model.eq_data is not None:
59
+ for i in range(model.eq_data.shape[0]):
60
+ if model.eq_type[i] == mujoco.mjtEq.mjEQ_WELD:
61
+ model.eq_data[i, :7] = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
62
+ mujoco.mj_forward(model, data)
63
+
64
+ def reset_mocap2body_xpos(self, model, data):
65
+ if model.eq_type is None or model.eq_obj1id is None or model.eq_obj2id is None:
66
+ return
67
+ for eq_type, obj1_id, obj2_id in zip(
68
+ model.eq_type, model.eq_obj1id, model.eq_obj2id
69
+ ):
70
+ if eq_type != mujoco.mjtEq.mjEQ_WELD:
71
+ continue
72
+
73
+ mocap_id = model.body_mocapid[obj1_id]
74
+ if mocap_id != -1:
75
+ # obj1 is the mocap, obj2 is the welded body
76
+ body_idx = obj2_id
77
+ else:
78
+ # obj2 is the mocap, obj1 is the welded body
79
+ mocap_id = model.body_mocapid[obj2_id]
80
+ body_idx = obj1_id
81
+
82
+ assert mocap_id != -1
83
+ data.mocap_pos[mocap_id][:] = data.xpos[body_idx]
84
+ data.mocap_quat[mocap_id][:] = data.xquat[body_idx]
85
+
86
+ def mocap_set_action(self, model, data, action):
87
+ if model.nmocap > 0:
88
+ action, _ = np.split(action, (model.nmocap * 7,))
89
+ action = action.reshape(model.nmocap, 7)
90
+ pos_delta = action[:, :3]
91
+ quat_delta = action[:, 3:]
92
+ self.reset_mocap2body_xpos(model, data)
93
+ data.mocap_pos[:] = data.mocap_pos + pos_delta
94
+ data.mocap_quat[:] = data.mocap_quat + quat_delta
95
+
96
+ def ctrl_set_action(self, model, data, action):
97
+ if model.nmocap > 0:
98
+ _, action = np.split(action, (model.nmocap * 7,))
99
+
100
+ if len(data.ctrl) > 0:
101
+ for i in range(action.shape[0]):
102
+ if model.actuator_biastype[i] == 0:
103
+ data.ctrl[i] = action[i]
104
+ else:
105
+ idx = model.jnt_qposadr[model.actuator_trnid[i, 0]]
106
+ data.ctrl[i] = data.qpos[idx] + action[i]
107
+
108
+ def step(self, action):
109
+ self.current_step += 1
110
+ # just move the end effector in x, y, z
111
+ pos_ctrl = action.copy() * 0.05 # scale action
112
+ rot_ctrl = [
113
+ 1.0,
114
+ 0.0,
115
+ 0.0,
116
+ 0.0,
117
+ ]
118
+ action = np.concatenate([pos_ctrl, rot_ctrl])
119
+
120
+ self.ctrl_set_action(self._model, self._data, action)
121
+ self.mocap_set_action(self._model, self._data, action)
122
+
123
+ self.env.step(n_frames=self.frame_skip)
124
+ obs = self._get_obs()
125
+ terminated = self._is_success(obs["achieved_goal"], self.goal)
126
+ truncated = self.current_step >= self.maximum_episode_steps
127
+ reward = self.compute_reward(obs["achieved_goal"], self.goal)
128
+ return obs, reward, terminated, truncated, {}
129
+
130
+ def mat2euler(self, mat):
131
+ mat = np.asarray(mat, dtype=np.float64)
132
+ cy = np.sqrt(mat[..., 2, 2] * mat[..., 2, 2] + mat[..., 1, 2] * mat[..., 1, 2])
133
+ condition = cy > _EPS4
134
+ euler = np.empty(mat.shape[:-1], dtype=np.float64)
135
+ euler[..., 2] = np.where(
136
+ condition,
137
+ -np.arctan2(mat[..., 0, 1], mat[..., 0, 0]),
138
+ -np.arctan2(-mat[..., 1, 0], mat[..., 1, 1]),
139
+ )
140
+ euler[..., 1] = np.where(
141
+ condition,
142
+ -np.arctan2(-mat[..., 0, 2], cy),
143
+ -np.arctan2(-mat[..., 0, 2], cy),
144
+ )
145
+ euler[..., 0] = np.where(
146
+ condition, -np.arctan2(mat[..., 1, 2], mat[..., 2, 2]), 0.0
147
+ )
148
+ return euler
149
+
150
+ def get_site_xpos(self, model, data, name):
151
+ site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, name)
152
+ return data.site_xpos[site_id]
153
+
154
+ def get_site_xvelp(self, model, data, name):
155
+ site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, name)
156
+ jacp = self.get_site_jacp(model, data, site_id)
157
+ xvelp = jacp @ data.qvel
158
+ return xvelp
159
+
160
+ def get_site_jacp(self, model, data, site_id):
161
+ jacp = np.zeros((3, model.nv))
162
+ mujoco.mj_jacSite(model, data, jacp, None, site_id)
163
+ return jacp
164
+
165
+ def _get_obs(self) -> dict:
166
+ ee_position = self.get_site_xpos(self._model, self._data, "tip").copy()
167
+ ee_velocity = (
168
+ self.get_site_xvelp(self._model, self._data, "tip").copy() * self.dt
169
+ )
170
+ object_position = self.get_site_xpos(self._model, self._data, "obj_site").copy()
171
+ object_rotation = self.mat2euler(
172
+ self.get_site_xmat(self._model, self._data, "obj_site")
173
+ )
174
+
175
+ return {
176
+ "observation": np.concatenate(
177
+ [
178
+ ee_position,
179
+ ee_velocity,
180
+ object_position,
181
+ object_rotation,
182
+ ]
183
+ ).copy(),
184
+ "achieved_goal": object_position.copy(),
185
+ "desired_goal": self.goal.copy(),
186
+ }
187
+
188
+ def set_joint_qpos(self, model, data, name, value):
189
+ """Set the joint positions (qpos) of the model."""
190
+ joint_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, name)
191
+ joint_addr = model.jnt_qposadr[joint_id]
192
+ # All joints are assumed to be of type mjJNT_HINGE in this environment
193
+ data.qpos[joint_addr] = value
194
+
195
+ def set_object_qpos(self, model, data):
196
+ joint_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, "obj_joint")
197
+ joint_addr = model.jnt_qposadr[joint_id]
198
+ data.qpos[joint_addr : joint_addr + 3] = np.array(self.initial_object_pos)
199
+ # reset orientation
200
+ data.qpos[joint_addr + 3 : joint_addr + 7] = np.array([1.0, 0.0, 0.0, 0.0])
201
+
202
+ def _sample_goal(self):
203
+ goal = np.array([0.3, 0.0, 0.0])
204
+ return goal
205
+
206
+ def goal_distance(self, goal_a, goal_b):
207
+ assert goal_a.shape == goal_b.shape
208
+ return np.linalg.norm(goal_a - goal_b, axis=-1)
209
+
210
+ def compute_reward(self, achieved_goal, desired_goal, info=None):
211
+ d = self.goal_distance(achieved_goal, desired_goal)
212
+ # give reward for the end effector being close to the object
213
+ ee_position = self.get_site_xpos(self._model, self._data, "tip")
214
+ object_position = self.get_site_xpos(self._model, self._data, "obj_site")
215
+ ee_object_distance = np.linalg.norm(ee_position - object_position, axis=-1)
216
+
217
+ # the arm tries to cheat by moving the object with the side of the gripper
218
+ # negative ward if the arm contacts the ground with
219
+ effector_distance_reward = -0.1 * (ee_object_distance > 0.05).astype(np.float32)
220
+
221
+ # todo: check correctness
222
+ # give some reward if the cube is moving towards the goal
223
+ object_velocity = self.get_site_xvelp(self._model, self._data, "obj_site")
224
+ goal_direction = desired_goal[0] - object_position
225
+ goal_direction /= np.linalg.norm(goal_direction) + 1e-8
226
+ velocity_towards_goal = np.dot(object_velocity, goal_direction)
227
+ velocity_reward = max(0.0, velocity_towards_goal)
228
+
229
+ reward = -d + effector_distance_reward + velocity_reward
230
+ return reward
231
+
232
+ # sparse
233
+ # return -(d > self.distance_threshold).astype(np.float32)
234
+
235
+ def _is_success(self, achieved_goal, desired_goal):
236
+ d = self.goal_distance(achieved_goal, desired_goal)
237
+ return (d < self.distance_threshold).astype(np.float32)
238
+
239
+ def set_joint_neutral(self):
240
+ for name, value in zip(self.arm_joint_names, self.neutral_joint_values):
241
+ self.set_joint_qpos(self._model, self._data, name, value)
242
+
243
+ def _reset_sim(self) -> bool:
244
+ self.set_joint_neutral()
245
+ self.set_object_qpos(self._model, self._data)
246
+ mujoco.mj_forward(self._model, self._data)
247
+
248
+ def reset(self, **kwargs):
249
+ self.current_step = 0
250
+ self._reset_sim()
251
+ obs = self._get_obs()
252
+
253
+ # self.env.step(n_frames=self.frame_skip)
254
+ # frame = self.env.viewer.capture_frame()
255
+ # import matplotlib.pyplot as plt
256
+ # plt.imsave("debug_frame.png", frame)
257
+ # raise Exception("Debug")
258
+ return obs, {}
259
+
260
+ def get_site_xmat(self, model, data, name: str):
261
+ site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, name)
262
+ return data.site_xmat[site_id].reshape(3, 3)
263
+
264
+ def get_body_state(self, name):
265
+ body_id = self._body_name2id[name]
266
+ body_xpos = self._data.xpos[body_id]
267
+ body_xquat = self._data.xquat[body_id]
268
+ return np.concatenate([body_xpos, body_xquat])