7e4ef6f98b
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
390 lines
9.2 KiB
Markdown
390 lines
9.2 KiB
Markdown
# Data Structure
|
|
|
|
SPIDER uses a structured file organization to handle multiple datasets, robots, and tasks efficiently.
|
|
|
|
## Directory Layout
|
|
|
|
```
|
|
example_datasets/
|
|
├── raw/ # Raw human motion data
|
|
│ └── {dataset_name}/
|
|
│ ├── {data_id}.pkl
|
|
│ └── meshes/
|
|
│
|
|
└── processed/ # Processed robot trajectories
|
|
└── {dataset_name}/
|
|
├── dataset_summary.json # High-level metadata
|
|
├── assets/ # Shared assets (avoid duplication)
|
|
│ ├── objects/
|
|
│ └── robots/
|
|
├── mano/ # Extracted human hand data
|
|
│ └── {embodiment_type}/
|
|
│ └── {task}/
|
|
│ └── {data_id}/
|
|
│ ├── trajectory_keypoint.npz
|
|
│ └── info.json
|
|
└── {robot_type}/ # Robot-specific data
|
|
└── {embodiment_type}/
|
|
└── {task}/
|
|
└── {data_id}/
|
|
├── scene.xml
|
|
├── trajectory_kinematic.npz
|
|
├── trajectory_mjwp.npz
|
|
├── visualization_mjwp.mp4
|
|
└── metrics.json
|
|
```
|
|
|
|
## Path Resolution
|
|
|
|
SPIDER automatically resolves paths based on task parameters:
|
|
|
|
```python
|
|
from spider.io import get_processed_data_dir
|
|
|
|
# Resolve processed directory
|
|
processed_dir = get_processed_data_dir(
|
|
dataset_dir="/path/to/datasets",
|
|
dataset_name="oakink",
|
|
robot_type="allegro",
|
|
embodiment_type="bimanual",
|
|
task="pick_spoon_bowl",
|
|
data_id=0
|
|
)
|
|
# Returns: /path/to/datasets/processed/oakink/allegro/bimanual/pick_spoon_bowl/0/
|
|
```
|
|
|
|
## File Formats
|
|
|
|
### Raw Data (`raw/{dataset_name}/{data_id}.pkl`)
|
|
|
|
Raw human motion data from dataset processing:
|
|
|
|
```python
|
|
{
|
|
'qpos_wrist_right': np.ndarray, # [T, 7] position + quaternion
|
|
'qpos_finger_right': np.ndarray, # [T, n_joints] joint angles
|
|
'qpos_wrist_left': np.ndarray, # [T, 7]
|
|
'qpos_finger_left': np.ndarray, # [T, n_joints]
|
|
'qpos_obj_right': np.ndarray, # [T, 7] object pose
|
|
'qpos_obj_left': np.ndarray, # [T, 7]
|
|
'contact': np.ndarray, # [T, ncon] contact indicators
|
|
'contact_pos': np.ndarray # [T, ncon, 3] contact positions
|
|
}
|
|
```
|
|
|
|
### Trajectory Files (`.npz`)
|
|
|
|
#### `trajectory_keypoint.npz`
|
|
|
|
Human hand keypoints (from dataset processor):
|
|
|
|
```python
|
|
{
|
|
'qpos_wrist_right': [T, 7],
|
|
'qpos_finger_right': [T, 15],
|
|
'qpos_obj_right': [T, 7],
|
|
# ... left hand equivalents
|
|
}
|
|
```
|
|
|
|
#### `trajectory_kinematic.npz`
|
|
|
|
Kinematic retargeting result (from IK):
|
|
|
|
```python
|
|
{
|
|
'qpos': [T, nq], # Full robot configuration
|
|
'qvel': [T, nv], # Velocities
|
|
'ctrl': [T, nu], # Control targets
|
|
'contact': [T, ncon], # Contact indicators
|
|
'contact_pos': [T, ncon, 3] # Contact positions
|
|
}
|
|
```
|
|
|
|
#### `trajectory_mjwp.npz`
|
|
|
|
Physics-optimized trajectory (from MJWP):
|
|
|
|
```python
|
|
{
|
|
'qpos': [N_steps, ctrl_steps, nq], # Per-step configs
|
|
'qvel': [N_steps, ctrl_steps, nv], # Velocities
|
|
'ctrl': [N_steps, ctrl_steps, nu], # Control commands
|
|
'time': [N_steps, ctrl_steps], # Timestamps
|
|
'opt_steps': [N_steps], # Iterations per step
|
|
'improvement': [N_steps], # Convergence metric
|
|
'rewards': [N_steps, num_samples], # Per-sample rewards
|
|
}
|
|
```
|
|
|
|
### Scene Files
|
|
|
|
#### `scene.xml`
|
|
|
|
MuJoCo scene definition:
|
|
|
|
```xml
|
|
<mujoco model="scene">
|
|
<!-- Solver settings -->
|
|
<option timestep="0.01" iterations="20" ...>
|
|
|
|
<!-- Robot model -->
|
|
<include file="../../assets/robots/allegro/bimanual.xml"/>
|
|
|
|
<!-- Object geometries -->
|
|
<body name="object_right" pos="0 0 0">
|
|
<freejoint/>
|
|
<geom type="mesh" mesh="obj_visual"/>
|
|
<geom type="mesh" mesh="obj_collision_0" .../>
|
|
...
|
|
</body>
|
|
|
|
<!-- Contact pairs -->
|
|
<contact>
|
|
<pair geom1="finger_tip" geom2="obj_collision_0"/>
|
|
...
|
|
</contact>
|
|
</mujoco>
|
|
```
|
|
|
|
#### `scene_eq.xml`
|
|
|
|
Scene with equality constraints (for `num_dyn > 1`):
|
|
|
|
Includes additional `<equality>` elements for virtual contact constraints used in annealing.
|
|
|
|
### Metadata Files
|
|
|
|
#### `task_info.json`
|
|
|
|
Task-level metadata:
|
|
|
|
```json
|
|
{
|
|
"dataset_name": "oakink",
|
|
"task": "pick_spoon_bowl",
|
|
"embodiment_type": "bimanual",
|
|
"ref_dt": 0.02,
|
|
"object_names": ["spoon", "bowl"],
|
|
"num_frames": 250
|
|
}
|
|
```
|
|
|
|
#### `info.json`
|
|
|
|
Trial-level metadata:
|
|
|
|
```json
|
|
{
|
|
"robot_type": "allegro",
|
|
"data_id": 0,
|
|
"timestamp": "2025-01-15T10:30:00",
|
|
"object_asset_paths": {
|
|
"spoon": "../../../../assets/objects/spoon/visual.obj",
|
|
"bowl": "../../../../assets/objects/bowl/visual.obj"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `metrics.json`
|
|
|
|
Performance metrics:
|
|
|
|
```json
|
|
{
|
|
"success_rate": 0.95,
|
|
"avg_position_error": 0.012,
|
|
"avg_rotation_error": 0.08,
|
|
"avg_optimization_time": 0.234,
|
|
"total_simulation_steps": 300
|
|
}
|
|
```
|
|
|
|
## Asset Organization
|
|
|
|
### Object Assets
|
|
|
|
Shared object meshes to avoid duplication:
|
|
|
|
```
|
|
assets/objects/{object_name}/
|
|
├── visual.obj # High-res visual mesh
|
|
└── convex/ # Convex decomposition
|
|
├── 0.obj
|
|
├── 1.obj
|
|
├── 2.obj
|
|
└── ...
|
|
```
|
|
|
|
### Robot Assets
|
|
|
|
Robot MJCF files:
|
|
|
|
```
|
|
assets/robots/{robot_type}/
|
|
├── left.xml # Left hand only
|
|
├── right.xml # Right hand only
|
|
└── bimanual.xml # Both hands
|
|
```
|
|
|
|
## Loading Data
|
|
|
|
### In Python
|
|
|
|
```python
|
|
import numpy as np
|
|
from spider.io import load_data
|
|
from spider.config import Config, process_config
|
|
|
|
# Create and process config
|
|
config = Config(
|
|
dataset_name="oakink",
|
|
robot_type="allegro",
|
|
embodiment_type="bimanual",
|
|
task="pick_spoon_bowl",
|
|
data_id=0
|
|
)
|
|
config = process_config(config)
|
|
|
|
# Load trajectory
|
|
qpos, qvel, ctrl, contact, contact_pos = load_data(
|
|
config,
|
|
config.data_path
|
|
)
|
|
|
|
print(f"Trajectory length: {qpos.shape[0]}")
|
|
print(f"DOF: {qpos.shape[1]}")
|
|
```
|
|
|
|
### Manual Loading
|
|
|
|
```python
|
|
import numpy as np
|
|
|
|
# Load trajectory
|
|
data = np.load('trajectory_mjwp.npz')
|
|
|
|
# Access arrays
|
|
qpos = data['qpos'] # [T, nq]
|
|
qvel = data['qvel'] # [T, nv]
|
|
ctrl = data['ctrl'] # [T, nu]
|
|
|
|
# Reshape if needed (MJWP saves per-step)
|
|
if qpos.ndim == 3:
|
|
qpos = qpos.reshape(-1, qpos.shape[-1])
|
|
```
|
|
|
|
## Data Flow Through Pipeline
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Raw Data<br/>raw/dataset/data.pkl] --> B[Dataset Processor]
|
|
B --> C[Keypoints<br/>mano/.../trajectory_keypoint.npz]
|
|
C --> D[Preprocessing Pipeline]
|
|
D --> E[Object Decomposition<br/>assets/objects/*/convex/*.obj]
|
|
D --> F[Scene XML<br/>robot/.../scene.xml]
|
|
D --> G[Kinematic Trajectory<br/>robot/.../trajectory_kinematic.npz]
|
|
G --> H[Physics Optimization]
|
|
H --> I[Optimized Trajectory<br/>robot/.../trajectory_mjwp.npz]
|
|
H --> J[Video<br/>robot/.../visualization_mjwp.mp4]
|
|
|
|
style C fill:#e1f5ff
|
|
style G fill:#fff4e1
|
|
style I fill:#ffe1e1
|
|
```
|
|
|
|
## Configuration Paths
|
|
|
|
Paths in `Config` are automatically resolved:
|
|
|
|
```python
|
|
@dataclass
|
|
class Config:
|
|
dataset_dir: str = "example_datasets" # Base directory
|
|
dataset_name: str = "oakink"
|
|
robot_type: str = "allegro"
|
|
embodiment_type: str = "bimanual"
|
|
task: str = "pick_spoon_bowl"
|
|
data_id: int = 0
|
|
|
|
# Auto-set by process_config():
|
|
model_path: str = "" # Path to scene.xml
|
|
data_path: str = "" # Path to trajectory_kinematic.npz
|
|
output_dir: str = "" # Where to save results
|
|
```
|
|
|
|
After `process_config()`:
|
|
|
|
```python
|
|
config.model_path = "{dataset_dir}/processed/{dataset_name}/{embodiment_type}/{task}/{data_id}/scene.xml"
|
|
config.data_path = "{...}/{robot_type}/{embodiment_type}/{task}/{data_id}/trajectory_kinematic.npz"
|
|
config.output_dir = "{...}/{robot_type}/{embodiment_type}/{task}/{data_id}/"
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Organization
|
|
|
|
1. **Keep raw data separate**: Never modify `raw/` directory
|
|
2. **Share assets**: Use `assets/` for common objects/robots
|
|
3. **One trial per directory**: Each `{data_id}` is self-contained
|
|
4. **Relative paths in JSON**: Use relative paths for portability
|
|
|
|
### Naming Conventions
|
|
|
|
- **Tasks**: Use descriptive names (e.g., `pick_cup`, `open_jar`)
|
|
- **Objects**: Match names across datasets
|
|
- **Robots**: Use official model names (e.g., `allegro`, `inspire`)
|
|
|
|
### Cleanup
|
|
|
|
Remove intermediate files after successful optimization:
|
|
|
|
```bash
|
|
# Keep only final results
|
|
rm trajectory_kinematic.npz
|
|
rm scene.xml # If not needed for replay
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Path Not Found
|
|
|
|
Check that all path components match:
|
|
|
|
```bash
|
|
# Verify structure
|
|
ls example_datasets/processed/oakink/allegro/bimanual/pick_spoon_bowl/0/
|
|
|
|
# Check config values
|
|
python -c "from spider.config import Config, process_config; \
|
|
c = process_config(Config()); print(c.output_dir)"
|
|
```
|
|
|
|
### Missing Assets
|
|
|
|
Ensure assets are in shared location:
|
|
|
|
```bash
|
|
# Check object assets
|
|
ls example_datasets/processed/oakink/assets/objects/
|
|
|
|
# Check robot assets
|
|
ls example_datasets/processed/oakink/assets/robots/
|
|
```
|
|
|
|
### Corrupted NPZ Files
|
|
|
|
Validate NPZ integrity:
|
|
|
|
```python
|
|
import numpy as np
|
|
|
|
try:
|
|
data = np.load('trajectory.npz')
|
|
print("Keys:", list(data.keys()))
|
|
print("Shapes:", {k: v.shape for k, v in data.items()})
|
|
except Exception as e:
|
|
print(f"Error loading: {e}")
|
|
```
|