Skip to content

Commit 8d98627

Browse files
update README && ngrok instruction
1 parent 24c192e commit 8d98627

8 files changed

+70
-124
lines changed

README.md

+29-19
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,23 @@ This code contains implementation for teleoperation and imitation learning of Op
3434
## Installation
3535

3636
```bash
37-
conda create -n tv python=3.8
38-
conda activate tv
39-
pip install -r requirements.txt
40-
cd act/detr && pip install -e .
37+
conda create -n tv python=3.8
38+
conda activate tv
39+
pip install -r requirements.txt
40+
cd act/detr && pip install -e .
4141
```
4242

43-
If you want to try teleoperation example with an active cam with zed camera (teleop_active_cam.py):
43+
Install ZED sdk: https://www.stereolabs.com/developers/release/
4444

45-
Install zed sdk: https://www.stereolabs.com/developers/release/
45+
Install ZED Python API:
46+
```
47+
cd /usr/local/zed/ && python get_python_api.py
48+
```
4649

4750
If you want to try teleoperation example in a simulated environment (teleop_hand.py):
4851

4952
Install Isaac Gym: https://developer.nvidia.com/isaac-gym/
5053

51-
5254
## Teleoperation Guide
5355

5456
### Local streaming
@@ -57,34 +59,34 @@ Apple does not allow WebXR on non-https connections. To test the application loc
5759
2. check local ip address:
5860

5961
```
60-
ifconfig | grep inet
62+
ifconfig | grep inet
6163
```
6264
Suppose the local ip address of the ubuntu machine is `192.168.8.102`.
6365

6466
3. create certificate:
6567

6668
```
67-
mkcert -install && mkcert -cert-file cert.pem -key-file key.pem 192.168.8.102 localhost 127.0.0.1
69+
mkcert -install && mkcert -cert-file cert.pem -key-file key.pem 192.168.8.102 localhost 127.0.0.1
6870
```
6971

7072
4. open firewall on server
7173
```
72-
sudo iptables -A INPUT -p tcp --dport 8012 -j ACCEPT
73-
sudo iptables-save
74-
sudo iptables -L
74+
sudo iptables -A INPUT -p tcp --dport 8012 -j ACCEPT
75+
sudo iptables-save
76+
sudo iptables -L
7577
```
7678
or can be done with `ufw`:
7779
```
78-
sudo ufw allow 8012
80+
sudo ufw allow 8012
7981
```
8082
5.
81-
```python
82-
self.app = Vuer(host='0.0.0.0', cert="./cert.pem", key="./key.pem")
83+
```
84+
self.app = Vuer(host='0.0.0.0', cert="./cert.pem", key="./key.pem")
8385
```
8486

8587
6. install ca-certificates on VisionPro
8688
```
87-
mkcert -CAROOT
89+
mkcert -CAROOT
8890
```
8991
Copy the rootCA.pem via AirDrop to VisionPro and install it.
9092

@@ -102,14 +104,19 @@ For Meta Quest3, installation of the certificate is not trivial. We need to use
102104
1. Install ngrok: https://ngrok.com/download
103105
2. Run ngrok
104106
```
105-
ngrok http 8012
107+
ngrok http 8012
106108
```
107109
3. Copy the https address and open the browser on Meta Quest3 and go to the address.
108110

111+
ps. When using ngrok for network streaming, remember to call `OpenTeleVision` with:
112+
```
113+
self.tv = OpenTeleVision(self.resolution_cropped, self.shm.name, image_queue, toggle_streaming, ngrok=True)
114+
```
115+
109116
### Simulation Teleoperation Example
110117
1. After setup up streaming with either local or network streaming following the above instructions, you can try teleoperating two robot hands in Issac Gym:
111118
```
112-
cd teleop && python teleop_hand.py
119+
cd teleop && python teleop_hand.py
113120
```
114121
2. Go to your vuer site on VisionPro, click `Enter VR` and ``Allow`` to enter immersive environment.
115122

@@ -136,7 +143,10 @@ cd teleop && python teleop_hand.py
136143
--save_jit --resume_ckpt 25000
137144
```
138145

139-
7. You can visualize the trained policy with inputs from dataset using ``scripts/deploy_sim.py``.
146+
7. You can visualize the trained policy with inputs from dataset using ``scripts/deploy_sim.py``, example usage:
147+
```
148+
python deploy_sim.py --taskid 00 --exptid 01 --resume_ckpt 25000
149+
```
140150

141151
## Citation
142152
```

act/utils.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
from pathlib import Path
1010

1111
class EpisodicDataset(torch.utils.data.Dataset):
12-
def __init__(self, episode_ids, dataset_dir, camera_names, norm_stats, episode_len, control_mode, history_stack=0):
12+
def __init__(self, episode_ids, dataset_dir, camera_names, norm_stats, episode_len, history_stack=0):
1313
super(EpisodicDataset).__init__()
1414
self.episode_ids = episode_ids
1515
self.dataset_dir = dataset_dir
1616
self.camera_names = camera_names
1717
self.norm_stats = norm_stats
1818
self.is_sim = None
1919
self.max_pad_len = 200
20-
action_str = 'qpos_action' if control_mode == 'qpos' else 'ee_action'
20+
action_str = 'qpos_action'
2121

2222
self.history_stack = history_stack
2323

@@ -122,8 +122,8 @@ def __getitem__(self, ts_index):
122122
return image_data, qpos_data, action_data, is_pad
123123

124124

125-
def get_norm_stats(dataset_dir, num_episodes, control_mode):
126-
action_str = 'qpos_action' if control_mode == 'qpos' else 'ee_action'
125+
def get_norm_stats(dataset_dir, num_episodes):
126+
action_str = 'qpos_action'
127127
all_qpos_data = []
128128
all_action_data = []
129129
all_episode_len = []
@@ -171,7 +171,6 @@ def BatchSampler(batch_size, episode_len_l, sample_weights=None):
171171
yield batch
172172

173173
def load_data(dataset_dir, camera_names, batch_size_train, batch_size_val):
174-
control_mode = "qpos"
175174
print(f'\nData from: {dataset_dir}\n')
176175

177176
all_eps = find_all_processed_episodes(dataset_dir)
@@ -184,16 +183,16 @@ def load_data(dataset_dir, camera_names, batch_size_train, batch_size_val):
184183
val_indices = shuffled_indices[int(train_ratio * num_episodes):]
185184
print(f"Train episodes: {len(train_indices)}, Val episodes: {len(val_indices)}")
186185
# obtain normalization stats for qpos and action
187-
norm_stats, all_episode_len = get_norm_stats(dataset_dir, num_episodes, control_mode)
186+
norm_stats, all_episode_len = get_norm_stats(dataset_dir, num_episodes)
188187

189188
train_episode_len_l = [all_episode_len[i] for i in train_indices]
190189
val_episode_len_l = [all_episode_len[i] for i in val_indices]
191190
batch_sampler_train = BatchSampler(batch_size_train, train_episode_len_l)
192191
batch_sampler_val = BatchSampler(batch_size_val, val_episode_len_l, None)
193192

194193
# construct dataset and dataloader
195-
train_dataset = EpisodicDataset(train_indices, dataset_dir, camera_names, norm_stats, train_episode_len_l, control_mode)
196-
val_dataset = EpisodicDataset(val_indices, dataset_dir, camera_names, norm_stats, val_episode_len_l, control_mode)
194+
train_dataset = EpisodicDataset(train_indices, dataset_dir, camera_names, norm_stats, train_episode_len_l)
195+
val_dataset = EpisodicDataset(val_indices, dataset_dir, camera_names, norm_stats, val_episode_len_l)
197196
train_dataloader = DataLoader(train_dataset, batch_sampler=batch_sampler_train, pin_memory=True, num_workers=24, prefetch_factor=2)
198197
val_dataloader = DataLoader(val_dataset, batch_sampler=batch_sampler_val, pin_memory=True, num_workers=16, prefetch_factor=2)
199198

requirements.txt

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ aiohttp==3.9.5
22
aiohttp_cors==0.7.0
33
aiortc==1.8.0
44
av==11.0.0
5-
core==1.0.1
65
dex_retargeting==0.1.1
76
dynamixel_sdk==3.7.31
87
einops==0.8.0
@@ -17,9 +16,8 @@ pandas==2.0.3
1716
params_proto==2.12.1
1817
pytransform3d==3.5.0
1918
PyYAML==6.0.1
20-
pyzed==4.1
2119
scikit_learn==1.3.2
22-
scipy==1.14.0
20+
scipy==1.10.1
2321
seaborn==0.13.2
2422
setuptools==69.5.1
2523
torch==2.3.0

scripts/deploy_sim.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,7 @@ def merge_act(actions_for_curr_step, k = 0.01):
8686
policy_path = Path(exp_path) / f"traced_jit_{args['resume_ckpt']}.pt"
8787

8888
temporal_agg = True
89-
control_mode = "qpos"
90-
if control_mode == "ee":
91-
action_dim = 26
92-
elif control_mode == "qpos":
93-
action_dim = 28
94-
else:
95-
raise ValueError("Invalid control mode")
89+
action_dim = 28
9690

9791
chunk_size = 60
9892
device = "cuda"
@@ -150,7 +144,7 @@ def merge_act(actions_for_curr_step, k = 0.01):
150144
if history_stack > 0:
151145
last_action_queue.append(act)
152146
act = act * norm_stats["action_std"] + norm_stats["action_mean"]
153-
player.step(act, left_imgs[t], right_imgs[t], control_mode)
147+
player.step(act, left_imgs[t], right_imgs[t])
154148
except KeyboardInterrupt:
155149
player.end()
156150
exit()

scripts/plot_action.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
episode_path = Path(root) / exp_name / "processed" / episode_name
1919

2020
data = h5py.File(str(episode_path), 'r')
21-
actions = np.array(data['action'])
21+
actions = np.array(data['qpos_action'])
2222
data.close()
2323
timestamps = actions.shape[0]
2424
action_dim = actions.shape[1]

scripts/post_process.py

-25
Original file line numberDiff line numberDiff line change
@@ -111,30 +111,6 @@ def process_episode(file_name, ep):
111111
timesteps = len(closest_indices)
112112
qpos_actions = actions[closest_indices]
113113
cmds = cmds[closest_indices]
114-
115-
# compose new actions
116-
# left wrist relative pose(6) + left hand qpos(6) + right wrist relative pose(6) + right hand qpos(6) + head yp(2)
117-
ee_actions = np.zeros((timesteps, 6+6+6+6+2))
118-
for t in range(timesteps):
119-
120-
# compute left wrist absolute pose
121-
left_ee = cmds[t, 16:32].reshape((4, 4))
122-
ee_actions[t, 0:3] = left_ee[0:3, 3]
123-
ee_actions[t, 3:6] = rotations.intrinsic_euler_zyx_from_active_matrix(left_ee[0:3, 0:3])
124-
125-
# left hand qpos
126-
ee_actions[t, 6:12] = qpos_actions[t, 7:13]
127-
128-
# compute right wrist absolute pose
129-
right_ee = cmds[t, 32:48].reshape((4, 4))
130-
ee_actions[t, 12:15] = right_ee[0:3, 3]
131-
ee_actions[t, 15:18] = rotations.intrinsic_euler_zyx_from_active_matrix(right_ee[0:3, 0:3])
132-
133-
# right hand qpos
134-
ee_actions[t, 18:24] = qpos_actions[t, 20:26]
135-
136-
# head yp
137-
ee_actions[t, 24:26] = qpos_actions[t, 26:28]
138114

139115
# save_video(left_imgs, file_name + ".mp4")
140116
path = os.path.dirname(file_name)
@@ -145,7 +121,6 @@ def process_episode(file_name, ep):
145121
start = time.time()
146122
hf.create_dataset('observation.image.left', data=left_imgs)
147123
hf.create_dataset('observation.image.right', data=right_imgs)
148-
hf.create_dataset('ee_action', data=ee_actions.astype(np.float32))
149124
hf.create_dataset('cmds', data=cmds.astype(np.float32))
150125
hf.create_dataset('observation.state', data=states[closest_indices].astype(np.float32))
151126
hf.create_dataset('qpos_action', data=qpos_actions.astype(np.float32))

scripts/replay_demo.py

+24-58
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import matplotlib.pyplot as plt
77

88
from pytransform3d import rotations
9-
from core.RobotController import RobotController
109

1110
from pathlib import Path
1211
import h5py
@@ -20,7 +19,6 @@
2019
class Player:
2120
def __init__(self, dt=1/60):
2221
self.dt = dt
23-
self.controller = None
2422
self.head_mat = None
2523
self.left_wrist_mat = None
2624
self.right_wrist_mat = None
@@ -91,14 +89,11 @@ def __init__(self, dt=1/60):
9189
cam_target = gymapi.Vec3(0, 0, 1)
9290
self.gym.viewer_camera_look_at(self.viewer, None, cam_pos, cam_target)
9391

94-
self.controller = RobotController()
95-
self.controller.load_config('h1_inspire.yml')
96-
9792
plt.figure(figsize=(12, 6))
9893
plt.ion()
9994

100-
def step(self, action, left_img, right_img, control_mode='qpos'):
101-
qpos = self.convert_h1_qpos(action, control_mode)
95+
def step(self, action, left_img, right_img):
96+
qpos = self.convert_h1_qpos(action)
10297
states = np.zeros(qpos.shape, dtype=gymapi.DofState.dtype)
10398
states['pos'] = qpos
10499
self.gym.set_actor_dof_states(self.env, self.robot_handle, states, gymapi.STATE_POS)
@@ -124,63 +119,34 @@ def end(self):
124119
self.gym.destroy_sim(self.sim)
125120
plt.close()
126121

127-
def convert_h1_qpos(self, action, control_mode):
122+
def convert_h1_qpos(self, action):
128123
'''
129124
left_arm_indices = [13, 14, 15, 16, 17, 18, 19]
130125
right_arm_indices = [32, 33, 34, 35, 36, 37, 38]
131126
left_hand_indices = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
132127
right_hand_indices = [39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
133128
'''
134-
if control_mode == 'ee':
135-
left_wrist_mat = np.eye(4)
136-
left_wrist_mat[0:3, 3] = action[0:3]
137-
left_wrist_mat[0:3, 0:3] = rotations.active_matrix_from_intrinsic_euler_zyx(action[3:6])
138-
139-
right_wrist_mat = np.eye(4)
140-
right_wrist_mat[0:3, 3] = action[12:15]
141-
right_wrist_mat[0:3, 0:3] = rotations.active_matrix_from_intrinsic_euler_zyx(action[15:18])
142-
143-
self.controller.update(np.eye(4), left_wrist_mat, right_wrist_mat, np.zeros((25,3)), np.zeros((25,3)))
144-
qpos = self.controller.qpos
145-
146-
# left hand actions
147-
qpos[20:22] = action[6]
148-
qpos[22:24] = action[7]
149-
qpos[24:26] = action[8]
150-
qpos[26:28] = action[9]
151-
qpos[28] = action[10]
152-
qpos[29:32] = action[11] * np.array([1, 1.6, 2.4])
153-
154-
# right hand actions
155-
qpos[39:41] = action[18]
156-
qpos[41:43] = action[19]
157-
qpos[43:45] = action[20]
158-
qpos[45:47] = action[21]
159-
qpos[47] = action[22]
160-
qpos[48:51] = action[23] * np.array([1, 1.6, 2.4])
161-
elif control_mode == 'qpos':
162-
qpos = np.zeros(51)
163-
qpos[13:20] = action[0:7]
164-
165-
# left hand actions
166-
qpos[20:22] = action[7]
167-
qpos[22:24] = action[8]
168-
qpos[24:26] = action[9]
169-
qpos[26:28] = action[10]
170-
qpos[28] = action[11]
171-
qpos[29:32] = action[12] * np.array([1, 1.6, 2.4])
172-
173-
qpos[32:39] = action[13:20]
174-
175-
# right hand actions
176-
qpos[39:41] = action[20]
177-
qpos[41:43] = action[21]
178-
qpos[43:45] = action[22]
179-
qpos[45:47] = action[23]
180-
qpos[47] = action[24]
181-
qpos[48:51] = action[25] * np.array([1, 1.6, 2.4])
182-
else:
183-
raise NotImplementedError('Invalid control mode')
129+
qpos = np.zeros(51)
130+
qpos[13:20] = action[0:7]
131+
132+
# left hand actions
133+
qpos[20:22] = action[7]
134+
qpos[22:24] = action[8]
135+
qpos[24:26] = action[9]
136+
qpos[26:28] = action[10]
137+
qpos[28] = action[11]
138+
qpos[29:32] = action[12] * np.array([1, 1.6, 2.4])
139+
140+
qpos[32:39] = action[13:20]
141+
142+
# right hand actions
143+
qpos[39:41] = action[20]
144+
qpos[41:43] = action[21]
145+
qpos[43:45] = action[22]
146+
qpos[45:47] = action[23]
147+
qpos[47] = action[24]
148+
qpos[48:51] = action[25] * np.array([1, 1.6, 2.4])
149+
184150
return qpos
185151

186152
if __name__ == '__main__':

teleop/TeleVision.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
from webrtc.zed_server import *
99

1010
class OpenTeleVision:
11-
def __init__(self, img_shape, shm_name, queue, toggle_streaming, stream_mode="image", cert_file="./cert.pem", key_file="./key.pem"):
11+
def __init__(self, img_shape, shm_name, queue, toggle_streaming, stream_mode="image", cert_file="./cert.pem", key_file="./key.pem", ngrok=False):
1212
# self.app=Vuer()
1313
self.img_shape = (img_shape[0], 2*img_shape[1], 3)
1414
self.img_height, self.img_width = img_shape[:2]
1515

16-
self.app = Vuer(host='0.0.0.0', cert=cert_file, key=key_file, queries=dict(grid=False))
16+
if ngrok:
17+
self.app = Vuer(host='0.0.0.0', queries=dict(grid=False), queue_len=3)
18+
else:
19+
self.app = Vuer(host='0.0.0.0', cert=cert_file, key=key_file, queries=dict(grid=False), queue_len=3)
20+
1721
self.app.add_handler("HAND_MOVE")(self.on_hand_move)
1822
self.app.add_handler("CAMERA_MOVE")(self.on_cam_move)
1923
if stream_mode == "image":

0 commit comments

Comments
 (0)