Download code/datasets/yor_temporal.py from szcharlesji/yor-patch-policy-checkpoints: direct link, hf CLI and curl.
- Browser
- Download file 6.03 kB
-
https://huggingface.co/szcharlesji/yor-patch-policy-checkpoints/resolve/main/code/datasets/yor_temporal.py
- Command line
-
hf download hf://szcharlesji/yor-patch-policy-checkpoints/code/datasets/yor_temporal.py
-
curl -L -o yor_temporal.py https://huggingface.co/szcharlesji/yor-patch-policy-checkpoints/resolve/main/code/datasets/yor_temporal.py
6.03 kB
| """YOR-only waypoint filtering and lossless interleaved temporal subsampling.""" | |
| import numpy as np | |
| from datasets.core import TrajectoryDataset | |
| DEFAULT_THRESHOLDS = dict(left_xyz_m=.005,right_xyz_m=.005,left_rotation_deg=3., | |
| right_rotation_deg=3.,left_hand_rad=.08,right_hand_rad=.08,lift_m=.005, | |
| base_xy_m=.01,base_yaw_rad=.03) | |
| GROUPS = dict(left_xyz_m=slice(4,7),right_xyz_m=slice(11,14), | |
| left_hand_rad=slice(14,30),right_hand_rad=slice(34,50),lift_m=slice(54,55), | |
| base_xy_m=slice(55,57),base_yaw_rad=slice(57,58)) | |
| def validate_thresholds(thresholds): | |
| unknown=set(thresholds)-set(DEFAULT_THRESHOLDS) | |
| if unknown: raise ValueError(f'Unknown motion thresholds: {unknown}') | |
| for key,t in thresholds.items(): | |
| if t is None: continue | |
| t=np.asarray(t,float) | |
| width=GROUPS[key].stop-GROUPS[key].start if key in GROUPS else 1 | |
| if t.ndim>1 or t.size not in (1,width) or not np.isfinite(t).all() or (t<=0).any(): | |
| raise ValueError(f'{key}: use null to disable, or positive scalar/{width}-vector') | |
| def motion_scores(actions,reference,thresholds): | |
| """Largest per-axis normalized change. Quaternion change is sign-invariant.""" | |
| a=np.asarray(actions,float);b=np.asarray(reference,float);scores=[] | |
| for key,t in thresholds.items(): | |
| if t is None: continue | |
| if key in GROUPS: | |
| d=np.abs(a[...,GROUPS[key]]-b[GROUPS[key]]) | |
| if key=='base_yaw_rad':d=np.abs((a[...,57:58]-b[57:58]+np.pi)%(2*np.pi)-np.pi) | |
| scores.append(np.max(d/np.asarray(t),axis=-1)) | |
| else: | |
| sl=slice(0,4) if key=='left_rotation_deg' else slice(7,11) | |
| q=a[...,sl];r=b[sl];den=np.linalg.norm(q,axis=-1)*np.linalg.norm(r) | |
| if (den<1e-9).any():raise ValueError('Invalid quaternion in motion selection') | |
| angle=np.degrees(2*np.arccos(np.clip(np.abs(np.sum(q*r,axis=-1))/den,0,1))) | |
| scores.append(angle/float(np.asarray(t).reshape(-1)[0])) | |
| return np.max(np.stack(scores),axis=0) if scores else np.zeros(a.shape[:-1]) | |
| def select_waypoints(actions,enabled=False,thresholds=None,max_gap_frames=30): | |
| """Keep first/last, threshold crossings vs last kept, and optional hold anchors. | |
| max_gap_frames=0 removes the hold-anchor rule. It does not enforce minimum | |
| motion on anchors or endpoints. Source data and source indices are unchanged. | |
| """ | |
| a=np.asarray(actions);n=len(a) | |
| if a.ndim!=2 or a.shape[1]!=58 or not np.isfinite(a).all():raise ValueError('Expected finite [T,58] actions') | |
| if int(max_gap_frames)!=max_gap_frames or max_gap_frames<0:raise ValueError('max_gap_frames must be a nonnegative integer') | |
| t=dict(DEFAULT_THRESHOLDS if thresholds is None else thresholds);validate_thresholds(t) | |
| if not enabled or n<2:return np.arange(n,dtype=np.int64) | |
| kept=[0] | |
| for i in range(1,n): | |
| if motion_scores(a[i],a[kept[-1]],t)>=1 or (max_gap_frames and i-kept[-1]>=max_gap_frames):kept.append(i) | |
| if kept[-1]!=n-1:kept.append(n-1) | |
| return np.asarray(kept,dtype=np.int64) | |
| def phase_indices(indices,frame_skip): | |
| if int(frame_skip)!=frame_skip or frame_skip<1:raise ValueError('frame_skip must be a positive integer') | |
| return [np.asarray(indices)[p::int(frame_skip)] for p in range(min(int(frame_skip),len(indices)))] | |
| class YorTemporalDataset(TrajectoryDataset): | |
| """Wrap AFTER episode splitting and embedding; phases cannot leak across splits.""" | |
| def __init__(self,dataset,frame_skip=1,motion_filter=None): | |
| self.dataset=dataset;self.frame_skip=frame_skip;self.motion_filter=dict(motion_filter or {}) | |
| self.sequences=[] | |
| for i in range(len(dataset)): | |
| a=dataset.get_frames(i,range(dataset.get_seq_length(i)))[1] | |
| if hasattr(a,'detach'):a=a.detach().cpu().numpy() | |
| ids=select_waypoints(a,**self.motion_filter) | |
| self.sequences.extend((i,phase,frames) for phase,frames in enumerate(phase_indices(ids,frame_skip))) | |
| def __len__(self):return len(self.sequences) | |
| def get_seq_length(self,idx):return len(self.sequences[idx][2]) | |
| def get_frames(self,idx,frames): | |
| i,phase,ids=self.sequences[idx] | |
| return self.dataset.get_frames(i,ids[np.asarray(list(frames),dtype=int)].tolist()) | |
| def __getitem__(self,idx):return self.get_frames(idx,range(self.get_seq_length(idx))) | |
| def get_all_actions(self): | |
| import torch | |
| return torch.cat([self[i][1] for i in range(len(self))]) | |
| from datasets.core import TrajectorySlicerDataset | |
| class YorTemporalSlicer(TrajectorySlicerDataset): | |
| """Pad short phases and optionally predict actions after each observation. | |
| action_offset is measured in sampled sequence rows. With offset 1 the | |
| current observation at t predicts t+1 onward. Exclude anchors that have no | |
| next target, but repeat the last target when a chunk reaches the end. | |
| """ | |
| def __init__(self,dataset,action_offset=0,**kwargs): | |
| if int(action_offset)!=action_offset or action_offset<0: | |
| raise ValueError('action_offset must be a nonnegative integer') | |
| self.action_offset=int(action_offset) | |
| super().__init__(dataset,**kwargs) | |
| self.slices=[(i,t) for i in range(len(dataset)) | |
| for t in range(max(0,dataset.get_seq_length(i)-self.action_offset)) | |
| if self.pad_seq_length or t+self.action_offset+self.action_window<=dataset.get_seq_length(i)] | |
| def source_indices(self,idx): | |
| i,t=self.slices[idx];T=self.dataset.get_seq_length(i) | |
| obs=np.clip(np.arange(t-self.window+1,t+1),0,T-1) | |
| start=t if self.vqbet_get_future_action_chunk else t-self.window+1 | |
| act=np.clip(np.arange(start,t+self.action_window)+self.action_offset,self.action_offset,T-1) | |
| return i,obs,act | |
| def __getitem__(self,idx): | |
| i,oi,ai=self.source_indices(idx) | |
| obs,act,*others=self.dataset[i] | |
| values=(obs[oi],act[ai],*[other[oi] for other in others]) | |
| return self.transform(values) if self.transform else values | |