nepyope commited on
Commit
08c99f6
·
verified ·
1 Parent(s): e154616

Update env.py

Browse files
Files changed (1) hide show
  1. env.py +107 -96
env.py CHANGED
@@ -1,83 +1,55 @@
1
  # env.py
2
 
3
  from pathlib import Path
4
- import yaml
5
- from huggingface_hub import snapshot_download
6
  import os
 
 
7
  import gymnasium as gym
8
  from gymnasium import spaces
9
  import numpy as np
10
 
11
  from sim.simulator_factory import SimulatorFactory, init_channel
12
 
13
- # global state to ensure we only start the simulator once per process
14
- _SIM_STARTED = False
15
- _SIM_INSTANCE = None
16
 
17
 
18
- def _launch_simulator_once(publish_images=True, camera_port=5555, cameras=None):
19
  """
20
- create and start the simulator only once per python process.
21
- subsequent calls are no-ops and just return the same sim.
 
22
  """
23
- global _SIM_STARTED, _SIM_INSTANCE
24
-
25
- if _SIM_STARTED:
26
- return _SIM_INSTANCE
27
-
28
- # load config
29
- config_path = Path(__file__).parent / "config.yaml"
30
- with open(config_path) as f:
31
- config = yaml.safe_load(f)
32
-
33
- # override with defaults from make_env
34
- enable_offscreen = publish_images or config.get("ENABLE_OFFSCREEN", False)
35
-
36
- print("=" * 60)
37
- print("🤖 starting unitree g1 mujoco simulator")
38
- print("=" * 60)
39
- print(f"📁 scene: {config['ROBOT_SCENE']}")
40
- print(f"⏱️ timestep: {config['SIMULATE_DT']}s ({int(1 / config['SIMULATE_DT'])} hz)")
41
- print(f"👁️ visualization: {'ON' if config.get('ENABLE_ONSCREEN', True) else 'OFF'}")
42
-
43
- # configure cameras if requested
44
- camera_configs = {}
45
- if enable_offscreen:
46
- camera_list = cameras or ["head_camera"]
47
- for cam_name in camera_list:
48
- camera_configs[cam_name] = {"height": 480, "width": 640}
49
- print(f"📷 cameras: {', '.join(camera_list)} → zmq port {camera_port}")
50
-
51
- print("=" * 60)
52
-
53
- # init dds channel
54
- init_channel(config=config)
55
-
56
- # create simulator
57
- sim = SimulatorFactory.create_simulator(
58
- config=config,
59
- env_name="default",
60
- onscreen=config.get("ENABLE_ONSCREEN", True),
61
- offscreen=enable_offscreen,
62
- camera_configs=camera_configs,
63
- )
64
-
65
- # start simulator in a background thread so make_env can return
66
- # (if you really want blocking, set as_thread=False and don't return DummyEnv)
67
- print("\nsimulator running (background thread). press ctrl+c in main script to exit.")
68
- if enable_offscreen and publish_images:
69
- print(f"camera images publishing on tcp://localhost:{camera_port}")
70
-
71
- SimulatorFactory.start_simulator(
72
- sim,
73
- as_thread=True,
74
- enable_image_publish=publish_images,
75
- camera_port=camera_port,
76
- )
77
-
78
- _SIM_STARTED = True
79
- _SIM_INSTANCE = sim
80
- return sim
81
 
82
 
83
  def make_env(
@@ -89,39 +61,78 @@ def make_env(
89
  **kwargs,
90
  ):
91
  """
92
- factory used by your runner:
93
- from env import make_env
94
- env = make_env()
95
- calling this multiple times in the same process will NOT start multiple sims.
 
 
 
 
 
96
  """
97
-
98
- # force defaults if you want
99
  publish_images = True
100
  camera_port = 5555
101
  cameras = None
102
 
103
- # ensure simulator is running (only once)
104
- _launch_simulator_once(
105
- publish_images=publish_images,
106
- camera_port=camera_port,
107
- cameras=cameras,
108
- )
109
-
110
- class DummyEnv(gym.Env):
111
- def __init__(self):
112
- super().__init__()
113
- self.action_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
114
- self.observation_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
115
-
116
- def reset(self, seed=None, options=None):
117
- super().reset(seed=seed)
118
- return np.zeros(1, dtype=np.float32), {}
119
-
120
- def step(self, action):
121
- return np.zeros(1, dtype=np.float32), 0.0, False, False, {}
122
-
123
- def run(self):
124
- # optional: expose sim control here later if you want
125
- pass
126
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  return DummyEnv()
 
1
  # env.py
2
 
3
  from pathlib import Path
 
 
4
  import os
5
+ import errno
6
+ import yaml
7
  import gymnasium as gym
8
  from gymnasium import spaces
9
  import numpy as np
10
 
11
  from sim.simulator_factory import SimulatorFactory, init_channel
12
 
13
+ # lock file shared across processes
14
+ _LOCK_PATH = Path.home() / ".unitree_g1_mujoco_sim.lock"
 
15
 
16
 
17
+ def _acquire_lock():
18
  """
19
+ try to create a lock file.
20
+ returns True if this process acquired the lock (i.e. we should start the sim),
21
+ False if some other process already did.
22
  """
23
+ try:
24
+ fd = os.open(str(_LOCK_PATH), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
25
+ os.close(fd)
26
+ return True
27
+ except OSError as e:
28
+ if e.errno == errno.EEXIST:
29
+ # lock already exists -> another process started the sim
30
+ return False
31
+ raise
32
+
33
+
34
+ def _release_lock():
35
+ try:
36
+ _LOCK_PATH.unlink()
37
+ except FileNotFoundError:
38
+ pass
39
+
40
+
41
+ class DummyEnv(gym.Env):
42
+ def __init__(self):
43
+ super().__init__()
44
+ self.action_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
45
+ self.observation_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
46
+
47
+ def reset(self, seed=None, options=None):
48
+ super().reset(seed=seed)
49
+ return np.zeros(1, dtype=np.float32), {}
50
+
51
+ def step(self, action):
52
+ return np.zeros(1, dtype=np.float32), 0.0, False, False, {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
 
55
  def make_env(
 
61
  **kwargs,
62
  ):
63
  """
64
+ first caller (in any process) behaves exactly like run_sim.main():
65
+ - loads config
66
+ - prints banner
67
+ - init_channel
68
+ - creates simulator
69
+ - starts simulator (blocking, as_thread=False)
70
+
71
+ other callers in other processes see the lock and **do not** start the sim again,
72
+ they just return DummyEnv().
73
  """
74
+ # hard-override to the same defaults as run_sim.py
 
75
  publish_images = True
76
  camera_port = 5555
77
  cameras = None
78
 
79
+ # only the process that acquires the lock starts the simulator
80
+ is_owner = _acquire_lock()
81
+ if is_owner:
82
+ try:
83
+ # --- run_sim.py: main() body ---
84
+
85
+ config_path = Path(__file__).parent / "config.yaml"
86
+ with open(config_path) as f:
87
+ config = yaml.safe_load(f)
88
+
89
+ enable_offscreen = publish_images or config.get("ENABLE_OFFSCREEN", False)
90
+
91
+ print("=" * 60)
92
+ print("🤖 Starting Unitree G1 MuJoCo Simulator")
93
+ print("=" * 60)
94
+ print(f"📁 Scene: {config['ROBOT_SCENE']}")
95
+ print(f"⏱️ Timestep: {config['SIMULATE_DT']}s ({int(1/config['SIMULATE_DT'])} Hz)")
96
+ print(f"👁️ Visualization: {'ON' if config.get('ENABLE_ONSCREEN', True) else 'OFF'}")
97
+
98
+ camera_configs = {}
99
+ if enable_offscreen:
100
+ camera_list = cameras or ["head_camera"]
101
+ for cam_name in camera_list:
102
+ camera_configs[cam_name] = {"height": 480, "width": 640}
103
+ print(f"📷 Cameras: {', '.join(camera_list)} → ZMQ port {camera_port}")
104
+
105
+ print("=" * 60)
106
+
107
+ init_channel(config=config)
108
+
109
+ sim = SimulatorFactory.create_simulator(
110
+ config=config,
111
+ env_name="default",
112
+ onscreen=config.get("ENABLE_ONSCREEN", True),
113
+ offscreen=enable_offscreen,
114
+ camera_configs=camera_configs,
115
+ )
116
+
117
+ print("\nSimulator running. Press Ctrl+C to exit.")
118
+ if enable_offscreen and publish_images:
119
+ print(f"Camera images publishing on tcp://localhost:{camera_port}")
120
+
121
+ try:
122
+ SimulatorFactory.start_simulator(
123
+ sim,
124
+ as_thread=False, # blocking, identical to run_sim.py
125
+ enable_image_publish=publish_images,
126
+ camera_port=camera_port,
127
+ )
128
+ except KeyboardInterrupt:
129
+ print("\nShutting down simulator...")
130
+ sim.close()
131
+ finally:
132
+ _release_lock()
133
+ else:
134
+ # another process already started the simulator; do nothing here
135
+ pass
136
+
137
+ # after (or while) the sim is running/exited, return a dummy env
138
  return DummyEnv()