xspadex commited on
Commit
0de4c0d
·
verified ·
1 Parent(s): 89e5b81

Upload lerobot_ee6d_piper_real.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. lerobot_ee6d_piper_real.py +139 -0
lerobot_ee6d_piper_real.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import numpy as np, torch, random
3
+ from mmengine import fileio
4
+ from scipy.interpolate import interp1d
5
+ from ..utils import read_video_to_frames, read_parquet, quat_to_rotate6d, euler_to_rotate6d
6
+ from PIL import Image
7
+ from .base import DomainHandler
8
+ import pandas as pd
9
+ import json
10
+ from typing import Iterable, Any
11
+ from tqdm import tqdm
12
+ import pickle
13
+
14
+ DEBUG_TRIGGER = True
15
+
16
+ class PiperLeRobotEE6DHandler(DomainHandler):
17
+
18
+ def __init__(self, meta: dict, num_views: int):
19
+ super().__init__(meta, num_views)
20
+ stats_path = self.meta.get("action_stats_path", None)
21
+ self.stats_path = stats_path
22
+ if stats_path is not None and fileio.exists(stats_path):
23
+ with open(stats_path, 'rb') as f:
24
+ self.action_stats = pickle.load(f)
25
+ self.mean = self.action_stats['mean']
26
+ self.std = self.action_stats['std']
27
+ self.min = self.action_stats['min']
28
+ self.max = self.action_stats['max']
29
+
30
+
31
+ def index_candidates(self, T_left: int, training: bool) -> Iterable[int]:
32
+ return range(0, max(0, T_left - 10))
33
+
34
+
35
+ def iter_episode(self, traj_idx: int, *, num_actions: int, training: bool,
36
+ image_aug, lang_aug_map: dict | None, **kwargs):
37
+ datapath = self.meta["datalist"][traj_idx]
38
+ video_base_path = self.meta["video_base_path"]
39
+ tasks_path = self.meta.get("tasks_path", None)
40
+ with open(tasks_path, 'r') as f:
41
+ tasks = f.readlines()
42
+
43
+ df = pd.read_parquet(datapath)
44
+ task_index = df['task_index'].iloc[0]
45
+ cur_task = json.loads(tasks[task_index])["task"]
46
+ raw_action = np.stack(df['action_eepose'])
47
+
48
+ # 2. 提取左臂 (Arm 1) 的组件并转换
49
+ # 假设格式: [pos(3), euler(3), grip(1), pos(3), euler(3), grip(1)]
50
+ pos1 = raw_action[:, :3]
51
+ rot1_6d = euler_to_rotate6d(raw_action[:, 3:6], 'xyz') # 只传欧拉角部分 (N, 3)
52
+ grip1 = raw_action[:, 6:7]
53
+
54
+ # 3. 提取右臂 (Arm 2) 的组件并转换
55
+ pos2 = raw_action[:, 7:10]
56
+ rot2_6d = euler_to_rotate6d(raw_action[:, 10:13], 'xyz') # 只传欧拉角部分 (N, 3)
57
+ grip2 = raw_action[:, 13:14]
58
+
59
+ # 4. 重新拼接成 20 维的向量 (3+6+1 + 3+6+1)
60
+ action_ee6d = np.concatenate([pos1, rot1_6d, grip1, pos2, rot2_6d, grip2], axis=-1)
61
+ image_observations = ['observation.images.cam_high', 'observation.images.cam_left_wrist', 'observation.images.cam_right_wrist']
62
+ vid_paths = []
63
+ for image_type in image_observations:
64
+ image_type_base_path = video_base_path + image_type + "/"
65
+ vid_path = image_type_base_path + f"episode_{traj_idx:06d}.mp4"
66
+ vid_paths.append(vid_path)
67
+
68
+ images = [read_video_to_frames(p) for p in vid_paths]
69
+ image_mask = torch.ones(self.num_views, dtype=torch.bool)
70
+ image_mask[:len(images)] = True
71
+
72
+ # stack 之后 shape 应该是 [T, 20]
73
+ all_actions = np.stack(action_ee6d)
74
+
75
+ # --- 切分左右臂 ---
76
+ dim_per_arm = 10
77
+ left = all_actions[:, :dim_per_arm]
78
+ right = all_actions[:, dim_per_arm:]
79
+
80
+ # 4. 时间插值 (处理帧率对齐)
81
+ freq = 30.0 # 请确认你的录制频率
82
+ qdur = 1.0 # 预测未来 1 秒
83
+ t = np.arange(left.shape[0], dtype=np.float64) / freq
84
+
85
+ lt = np.arange(left.shape[0], dtype=np.float64) / float(freq)
86
+ rt = np.arange(right.shape[0], dtype=np.float64) / float(freq)
87
+
88
+ # Candidate indices (optionally shuffled)
89
+ idxs = list(self.index_candidates(left.shape[0], training))
90
+ if training: random.shuffle(idxs)
91
+
92
+ # Interpolators; clamp to endpoints
93
+ L = interp1d(lt, left, axis=0, bounds_error=False, fill_value=(left[0], left[-1]))
94
+ R = interp1d(rt, right, axis=0, bounds_error=False, fill_value=(right[0], right[-1]))
95
+ ref = (lt + rt) / 2.0
96
+
97
+ V = min(self.num_views, len(images))
98
+ for idx in idxs:
99
+ imgs = []
100
+ for v in range(min(self.num_views, len(images))):
101
+ imgs.append(image_aug(Image.fromarray(images[v][idx])))
102
+ while len(imgs) < self.num_views: imgs.append(torch.zeros_like(imgs[0]))
103
+ image_input = torch.stack(imgs, 0)
104
+ cur = t[idx]
105
+ q = np.linspace(cur, min(cur + qdur, float(t.max())), num_actions + 1, dtype=np.float32)
106
+ lseq, rseq = torch.tensor(L(q)), torch.tensor(R(q))
107
+ if (lseq[1]-lseq[0]).abs().max() < 1e-5 and (rseq[1]-rseq[0]).abs().max() < 1e-5:continue
108
+ if lang_aug_map is not None and ins in lang_aug_map: ins = random.choice(lang_aug_map[ins])
109
+
110
+ trajectory = torch.cat([lseq, rseq], -1).float()
111
+ # if self.stats_path is not None and hasattr(self, 'min') and hasattr(self, 'max'):
112
+ # min_val = torch.tensor(self.min)
113
+ # max_val = torch.tensor(self.max)
114
+ # # 归一化到 [-1, 1]
115
+ # norm_action = 2 * (trajectory - min_val) / (max_val - min_val + 1e-8) - 1
116
+ # trajectory = norm_action
117
+
118
+ # if training:
119
+ # # 注入微量高斯噪声 (例如 0.001 级别)
120
+ # # 注意不要给最后的 gripper 维度加噪声(如果是 0/1)
121
+ # noise = torch.randn_like(trajectory) * 0.001
122
+ # # 第10维是左臂 gripper,第20维是右臂 gripper
123
+ # noise[:, 9] = 0.0
124
+ # noise[:, 19] = 0.0
125
+ # trajectory += noise
126
+
127
+ yield {
128
+ "language_instruction": cur_task,
129
+ "image_input": image_input,
130
+ "image_mask": image_mask,
131
+ "abs_trajectory": trajectory
132
+ }
133
+
134
+ # yield {
135
+ # "language_instruction": cur_task,
136
+ # "image_input": image_input,
137
+ # "image_mask": image_mask,
138
+ # "abs_trajectory": torch.cat([lseq, rseq], -1).float()
139
+ # }