nepyope commited on
Commit
32fd889
·
verified ·
1 Parent(s): 541ddf1

Update env.py

Browse files
Files changed (1) hide show
  1. env.py +116 -41
env.py CHANGED
@@ -1,59 +1,134 @@
1
  from pathlib import Path
2
- import subprocess
3
  import sys
4
  import gymnasium as gym
5
  from gymnasium import spaces
6
  import numpy as np
7
  from huggingface_hub import snapshot_download
8
- import os
9
- import signal
10
- snapshot_download("lerobot/unitree-g1-mujoco")
11
 
 
 
12
 
13
- def make_env(n_envs=1, use_async_envs=False, **kwargs):
14
-
15
- # define run_sim FIRST
16
- repo_dir = Path(__file__).parent
17
- run_sim = repo_dir / "run_sim.py"
18
 
19
- print("=" * 60)
20
- print("launching run_sim.py as subprocess")
21
- print("path:", run_sim)
22
- print("=" * 60)
23
 
24
- # now you can launch it
25
- proc = subprocess.Popen([sys.executable, str(run_sim)])
26
- print(f"simulator started as pid={proc.pid}")
27
-
28
- class DummyEnv(gym.Env):
29
- metadata = {"render_modes": []}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- def __init__(self, process):
32
  super().__init__()
33
- self.process = process
34
- self.action_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
35
- self.observation_space = spaces.Box(-1, 1, shape=(1,), dtype=np.float32)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  def reset(self, seed=None, options=None):
 
38
  super().reset(seed=seed, options=options)
39
- obs = np.zeros(1, dtype=np.float32)
40
- info = {}
41
- return obs, info
 
42
 
43
- def step(self, action):
44
- return np.zeros(1, dtype=np.float32), 0.0, False, False, {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  def close(self):
47
- pass
48
-
49
- def kill_sim(self):
50
- if self.process.poll() is None:
51
- print("killing simulator subprocess...")
52
- self.process.terminate()
53
- try:
54
- self.process.wait(timeout=2)
55
- except subprocess.TimeoutExpired:
56
- print("force killing simulator subprocess...")
57
- self.process.kill()
58
-
59
- return DummyEnv(proc)
 
1
  from pathlib import Path
 
2
  import sys
3
  import gymnasium as gym
4
  from gymnasium import spaces
5
  import numpy as np
6
  from huggingface_hub import snapshot_download
7
+ import yaml
 
 
8
 
9
+ # Ensure sim module is importable
10
+ sys.path.insert(0, str(Path(__file__).parent))
11
 
12
+ from sim.simulator_factory import SimulatorFactory, init_channel
 
 
 
 
13
 
 
 
 
 
14
 
15
+ def make_env(n_envs=1, use_async_envs=False, **kwargs):
16
+ """Create a UnitreeG1 simulation environment.
17
+
18
+ Args:
19
+ n_envs: Number of environments (currently only 1 supported)
20
+ use_async_envs: Whether to use async envs (not implemented)
21
+ **kwargs: Additional arguments passed to simulator
22
+ - publish_images: bool, whether to publish camera images via ZMQ
23
+ - camera_port: int, ZMQ port for camera images
24
+ - cameras: list of camera names
25
+ """
26
+ repo_dir = Path(__file__).parent
27
+
28
+ # Load config
29
+ config_path = repo_dir / "config.yaml"
30
+ with open(config_path) as f:
31
+ config = yaml.safe_load(f)
32
+
33
+ # Configure cameras if requested
34
+ publish_images = kwargs.get("publish_images", True)
35
+ camera_port = kwargs.get("camera_port", 5555)
36
+ cameras = kwargs.get("cameras", ["head_camera"])
37
+
38
+ enable_offscreen = publish_images or config.get("ENABLE_OFFSCREEN", False)
39
+ camera_configs = {}
40
+ if enable_offscreen:
41
+ for cam_name in cameras:
42
+ camera_configs[cam_name] = {"height": 480, "width": 640}
43
+
44
+ # Initialize DDS channel
45
+ init_channel(config=config)
46
+
47
+ # Create simulator (runs in same process)
48
+ simulator = SimulatorFactory.create_simulator(
49
+ config=config,
50
+ env_name="default",
51
+ onscreen=config.get("ENABLE_ONSCREEN", True),
52
+ offscreen=enable_offscreen,
53
+ camera_configs=camera_configs,
54
+ )
55
+
56
+ class UnitreeG1Env(gym.Env):
57
+ """Gymnasium environment wrapper for Unitree G1 MuJoCo simulation."""
58
+
59
+ metadata = {"render_modes": ["human"]}
60
 
61
+ def __init__(self, sim, cam_configs, pub_images, cam_port):
62
  super().__init__()
63
+ self.simulator = sim
64
+ self.sim_env = sim.sim_env
65
+ self.step_count = 0
66
+ self.camera_configs = cam_configs
67
+
68
+ # Get timing from config
69
+ self.sim_dt = config["SIMULATE_DT"]
70
+ self.viewer_dt = config.get("VIEWER_DT", 0.02)
71
+ self.image_dt = config.get("IMAGE_DT", 0.033333)
72
+
73
+ # Start image publishing subprocess if requested
74
+ if pub_images and len(cam_configs) > 0:
75
+ sim.start_image_publish_subprocess(
76
+ start_method=config.get("MP_START_METHOD", "spawn"),
77
+ camera_port=cam_port
78
+ )
79
+ print(f"Camera images publishing on tcp://localhost:{cam_port}")
80
+
81
+ # Define spaces
82
+ num_joints = config.get("NUM_MOTORS", 29)
83
+ self.action_space = spaces.Box(-np.pi, np.pi, shape=(num_joints,), dtype=np.float32)
84
+ self.observation_space = spaces.Box(-np.inf, np.inf, shape=(num_joints * 3 + 10,), dtype=np.float32)
85
 
86
  def reset(self, seed=None, options=None):
87
+ """Reset the simulation."""
88
  super().reset(seed=seed, options=options)
89
+ self.simulator.reset()
90
+ self.step_count = 0
91
+ obs = self._get_obs()
92
+ return obs, {}
93
 
94
+ def step(self, action=None):
95
+ """Execute one simulation step. Caller handles timing."""
96
+ # Run physics step
97
+ self.sim_env.sim_step()
98
+
99
+ # Update viewer at viewer rate
100
+ if self.step_count % int(self.viewer_dt / self.sim_dt) == 0:
101
+ self.sim_env.update_viewer()
102
+
103
+ # Update render caches at image rate (for ZMQ publishing)
104
+ if self.step_count % int(self.image_dt / self.sim_dt) == 0:
105
+ self.sim_env.update_render_caches()
106
+
107
+ self.step_count += 1
108
+
109
+ obs = self._get_obs()
110
+ reward = 0.0
111
+ terminated = False
112
+ truncated = False
113
+ info = {}
114
+ return obs, reward, terminated, truncated, info
115
+
116
+ def _get_obs(self):
117
+ """Get current observation from simulation."""
118
+ obs_dict = self.sim_env.prepare_obs()
119
+ obs = np.concatenate([
120
+ obs_dict.get("body_q", np.zeros(29)),
121
+ obs_dict.get("body_dq", np.zeros(29)),
122
+ obs_dict.get("body_tau_est", np.zeros(29)),
123
+ obs_dict.get("floating_base_pose", np.zeros(7))[:4],
124
+ obs_dict.get("floating_base_vel", np.zeros(6))[:3],
125
+ obs_dict.get("floating_base_acc", np.zeros(3)),
126
+ ]).astype(np.float32)
127
+ return obs
128
 
129
  def close(self):
130
+ """Close the simulation."""
131
+ print("++++closing simulator ++++")
132
+ self.simulator.close()
133
+
134
+ return UnitreeG1Env(simulator, camera_configs, publish_images, camera_port)