# SARM: Modelagem de Recompensa Consciente de Etapas

SARM (Stage-Aware Reward Modeling) é um framework de modelagem de recompensas baseado em vídeo para tarefas de manipulação robótica de longo horizonte. Este guia aborda como treinar modelos de recompensa SARM e, opcionalmente, como usá-los com Reward-Aligned Behavior Cloning (RA-BC).

**Artigo**: [SARM: Stage-Aware Reward Modeling for Long Horizon Robot Manipulation](https://arxiv.org/abs/2509.25358)

<img
  src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/lerobot/lerobot-sarm.png"
  alt="An overview of SARM"
  width="80%"
/>

## Por que Modelos de Recompensa?

O clonagem de comportamento padrão trata todos os quadros de demonstração de forma igual, mas os conjuntos de dados de robôs do mundo real são desorganizados. Eles contêm hesitações, correções e trajetórias de qualidade variável. Os modelos de recompensa resolvem isso aprendendo uma noção generalizável de **progresso da tarefa** a partir das demonstrações: dados os quadros de vídeo e uma descrição da tarefa, eles preveem o quão perto o robô está de concluir a tarefa (0→1). Esse "sinal de progresso" aprendido pode ser usado de várias maneiras, sendo duas aplicações promissoras: (1) **aprendizado por imitação ponderado** (RA-BC), em que os quadros com alto progresso recebem mais peso durante o treinamento da política, e (2) **aprendizado por reforço**, em que o modelo de recompensa fornece recompensas densas para o aprimoramento online ou offline da política.

## Visão geral

O SARM possui os seguintes recursos:

1. **Arquitetura consciente de etapas**: Prediz simultaneamente a etapa de alto nível da tarefa e o progresso detalhado dentro de cada etapa
2. **Anotações de subtarefas**: Usa anotações de subtarefas em linguagem natural para derivar rótulos de progresso consistentes
3. **Proporções temporais**: Calcula os priors no nível do conjunto de dados (α̅\_k) para cada subtarefa, a fim de normalizar o progresso entre demonstrações de comprimento variável

O SARM treina em um alvo compacto **stage+tau** para cada quadro:

- **stage**: índice do estágio inteiro `k ∈ {0, ..., K-1}`
- **τ (tau)**: progresso dentro do estágio `τ ∈ [0, 1]`
- **codificação de alvo**: `y = k + τ` (é isso que o processador de conjunto de dados produz)

No momento da inferência (e no RA-BC de nível inferior), o SARM converte o valor bruto `k + τ` em um **progresso normalizado** em `[0, 1]` usando as **proporções temporais** de nível de conjunto de dados `α̅_k` (armazenadas em `meta/temporal_proportions_*.json`).

Isso corresponde à **Fórmula (2)** do artigo:

```
progress_t = P_{k-1} + α̅_k × τ_t
```

Onde:

- `τ_t = (t - s_k) / (e_k - s_k)` é o tempo normalizado dentro da subtarefa
- `P_{k-1}` é o anterior cumulativo (soma das proporções das subtarefas anteriores)
- `α̅_k` é a proporção temporal para a subtarefa k

Isso garante que estados de tarefa idênticos sejam mapeados para valores de progresso consistentes, mesmo em demonstrações de diferentes comprimentos.

## Entradas e Alvos (O que o novo código espera)

O SARM é treinado por meio de seu processador (`src/lerobot/rewards/sarm/processor_sarm.py`), que:

- **Codifica** imagens e texto da tarefa com CLIP (ViT-B/32) em `video_features` e `text_features`
- **Preenche/trunca** o estado do robô em `state_features` (até `max_state_dim`)
- **Constrói os alvos** como `sparse_targets` (e `dense_targets` em `dense_only`/`dual`) usando a codificação stage+tau `y = k + τ`
- **Máscaras reprocessam quadros** usando um tensor `lengths` por amostra (reprocessar é uma aumentação no momento do treinamento)

No mínimo, cada amostra de treinamento precisa de:

- `task` (string): descrição da tarefa
- imagens de `reward_model.image_key` e estados de `reward_model.state_key` do conjunto de dados

---

## Modos de Anotação

Você pode escolher entre **3 modos de anotação** que determinam como os rótulos de progresso são calculados:

| Modo           | Anotações Necessárias | Cabeças                        | Caso de Uso                                                     |
| -------------- | -------------------- | ---------------------------- | ------------------------------------------------------------ |
| `single_stage` | None                 | Apenas esparsos                  | Tarefas simples, experimentos rápidos, sem necessidade de VLM               |
| `dense_only`   | Dense (VLM)          | Dual (auto-gerado esparsamente) | Rastreamento detalhado de subtarefas sem definir estágios de alto nível |
| `dual`         | Esparsa + Densa (VLM) | Dual                         | Configuração completa do paper SARM com ambas as granularidades                |

### Detalhes do Modo

**Sem anotações necessárias.** O episódio inteiro é tratado como um único estágio chamado `"task"`, e o progresso é linear de 0 a 1 ao longo da duração do episódio.

- **Cabeça esparsa**: 1 estágio ("task"), progresso linear
- **Cabeça densa**: Não utilizado
- **Melhor para**: Tarefas simples, experimentos rápidos ou quando a anotação de VLM não está disponível

## Configure seu ambiente

1. Instale o LeRobot seguindo nosso [Guia de Instalação](./installation).
2. Instale as dependências do SARM executando:

```bash
pip install -e ".[sarm]"
```

Fluxo de trabalho:

```
1. Train SARM → 2. Visualize predictions → 3. (Optional) Train policy with RA-BC
```

**Somente anotações densas (de granularidade fina) de um VLM.** A cabeça esparsa usa automaticamente um único estágio `"task"` que cobre o episódio completo, enquanto a cabeça densa aprende a progressão detalhada dos subtarefas.

- **Cabeça esparsa**: 1 estágio ("task"), progresso linear (gerado automaticamente)
- **Cabeça densa**: Múltiplos estágios de granularidade fina a partir de anotações de VLM
- **Melhor para**: Quando você deseja um rastreamento detalhado de subtarefas, mas não precisa definir etapas de alto nível

Fluxo de trabalho:

```
1. Annotate (dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BC
```

**Anotações esparsas e densas do VLM.** Modo completo de duas cabeças, conforme descrito no paper SARM, com previsões de estágio de alto nível (esparsas) e de granulação fina (densas).

- **Cabeça esparsa**: Estágios de alto nível a partir de anotações de VLM
- **Cabeça densa**: Estágios de granularidade fina a partir de anotações de VLM
- **Melhor para**: Tarefas complexas de múltiplas etapas em que ambas as granularidades são úteis

Fluxo de trabalho:

```
1. Annotate (sparse+dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BC
```

---

## Etapa 1: Anotação de subtarefas

**Nenhuma anotação necessária!** Pule esta etapa por completo. O modelo usará a descrição da tarefa do episódio e calculará o progresso linear automaticamente.

Gere apenas **anotações densas (de granularidade fina)** usando um VLM. O estágio esparsa será gerado automaticamente.

```bash
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
  --repo-id your-username/your-dataset \
  --dense-only \
  --dense-subtasks "Bring robot arms up from starting position,Grab near side and do 1st fold,Grab side and do 2nd fold,Grab side and do 3rd fold to finish folding" \
  --video-key observation.images.base \
  --num-workers 4 \
  --push-to-hub
```

**O que é salvo:**

- `meta/temporal_proportions_sparse.json` - Proporções esparsas geradas automaticamente (`{"task": 1.0}`)
- `meta/temporal_proportions_dense.json` - Proporções temporais densas
- Colunas por episódio em `episodes/*.parquet`:
  - `dense_subtask_names`, `dense_subtask_start_frames`, `dense_subtask_end_frames`
  - (colunas baseadas em tempo também: `dense_subtask_start_times`, `dense_subtask_end_times`)

Gere **anotações tanto esparsas (de alto nível) quanto densas (de granularidade fina)** usando um VLM.

```bash
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
  --repo-id your-username/your-dataset \
  --sparse-subtasks "Bring arms up from starting position,Fold the towel (3 folds in total)" \
  --dense-subtasks "Bring robot arms up from starting position,Grab near side and do 1st fold,Grab side and do 2nd fold,Grab side and do 3rd fold to finish folding" \
  --video-key observation.images.base \
  --num-workers 4 \
  --push-to-hub
```

**O que é salvo:**

- `meta/temporal_proportions_sparse.json` - Proporções temporais esparsas
- `meta/temporal_proportions_dense.json` - Proporções temporais densas
- Colunas por episódio em `episodes/*.parquet`:
  - `sparse_subtask_names`, `sparse_subtask_start_frames`, `sparse_subtask_end_frames`
  - `dense_subtask_names`, `dense_subtask_start_frames`, `dense_subtask_end_frames`
  - (além das colunas baseadas em tempo: `*_subtask_start_times`, `*_subtask_end_times`)

### Argumentos de Anotação

| Argumento               | Descrição                                                                     |
| ---------------------- | ------------------------------------------------------------------------------- |
| `--repo-id`            | ID do repositório de dataset do HuggingFace                                               |
| `--sparse-subtasks`    | Lista de nomes de subtarefas de alto nível separadas por vírgulas                                |
| `--dense-subtasks`     | Lista de nomes de subtarefas de alto nível separadas por vírgulas                              |
| `--dense-only`         | Gerar apenas anotações densas (cria automaticamente o estágio esparsa "task")              |
| `--video-key`          | Chave da câmera/vídeo a ser usada (por exemplo, `observation.images.top`)                        |
| `--num-workers`        | Número de workers de GPU em paralelo (padrão: 1)                                     |
| `--episodes`           | Índices específicos de episódios para anotar (padrão: todos)                             |
| `--skip-existing`      | Pular episódios que já possuem anotações                                     |
| `--model`              | Modelo VLM (padrão: `Qwen/Qwen3-VL-30B-A3B-Instruct`)                           |
| `--num-visualizations` | Número de episódios a visualizar após a anotação (padrão: 5, defina como 0 para pular) |

> **Observação**: Após a conclusão da anotação, 5 episódios são visualizados automaticamente por padrão. Use `--num-visualizations 0` para pular esta etapa.

---

## Etapa 2: Verificar Anotações

**Nenhuma verificação necessária!** Pule esta etapa.

Visualize as anotações usando a flag `--visualize-only`:

```bash
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
  --repo-id your-username/your-dataset \
  --visualize-only \
  --visualize-type dense \
  --num-visualizations 5 \
  --video-key observation.images.base \
  --output-dir ./subtask_viz
```

Visualize as anotações usando a flag `--visualize-only`:

```bash
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
  --repo-id your-username/your-dataset \
  --visualize-only \
  --visualize-type both \
  --num-visualizations 5 \
  --video-key observation.images.base \
  --output-dir ./subtask_viz
```

Isso gera visualizações mostrando os quadros do vídeo com as fronteiras das subtarefas sobrepostas e a linha do tempo das subtarefas.

### Argumentos de Visualização

| Argumento               | Descrição                                                    |
| ---------------------- | -------------------------------------------------------------- |
| `--visualize-only`     | Visualizar apenas as anotações existentes (sem geração)            |
| `--num-visualizations` | Número de episódios a visualizar (padrão: 5)                   |
| `--visualize-type`     | Tipo de anotações a visualizar: `sparse`, `dense` ou `both` |

**Dica**: Se as anotações forem imprecisas, ajuste as descrições dos subtarefas para serem mais específicas e execute novamente.

---

## Etapa 3: Treinar SARM

Treine com **sem anotações** - usa progresso linear de 0 a 1:

```bash
lerobot-train \
  --dataset.repo_id=your-username/your-dataset \
  --reward_model.type=sarm \
  --reward_model.annotation_mode=single_stage \
  --reward_model.image_key=observation.images.base \
  --output_dir=outputs/train/sarm_single \
  --batch_size=32 \
  --steps=5000 \
  --trackio.enable=true \
  --trackio.project=sarm \
  --reward_model.repo_id=your-username/your-model-name
```

Treine com **apenas anotações densas** (geradas automaticamente de forma esparsa):

```bash
lerobot-train \
  --dataset.repo_id=your-username/your-dataset \
  --reward_model.type=sarm \
  --reward_model.annotation_mode=dense_only \
  --reward_model.image_key=observation.images.base \
  --output_dir=outputs/train/sarm_dense \
  --batch_size=32 \
  --steps=5000 \
  --trackio.enable=true \
  --trackio.project=sarm \
  --reward_model.repo_id=your-username/your-model-name
```

Treine com **anotações esparsas e densas**:

```bash
lerobot-train \
  --dataset.repo_id=your-username/your-dataset \
  --reward_model.type=sarm \
  --reward_model.annotation_mode=dual \
  --reward_model.image_key=observation.images.base \
  --output_dir=outputs/train/sarm_dual \
  --batch_size=32 \
  --steps=5000 \
  --trackio.enable=true \
  --trackio.project=sarm \
  --reward_model.repo_id=your-username/your-model-name
```

### Treinamento Multi-GPU

Adicione `accelerate launch --multi_gpu --num_processes=4` para usar múltiplas GPUs para treinamento.

### Argumentos de Treinamento

| Argumento                         | Descrição                                                       | Padrão                  |
| -------------------------------- | ----------------------------------------------------------------- | ------------------------ |
| `--reward_model.annotation_mode` | `single_stage`, `dense_only` ou `dual`                           | `single_stage`           |
| `--reward_model.image_key`       | Chave da câmera para imagens                                             | `observation.images.top` |
| `--reward_model.state_key`       | Chave para estados conjuntos                                              | `observation.state`      |
| `--reward_model.n_obs_steps`     | Etapas do histórico de observação (total de quadros de obs = `n_obs_steps + 1`)  | `8`                      |
| `--reward_model.frame_gap`       | Intervalo (em quadros) entre as observações amostradas (a 30 fps: 30 ≈ 1s) | `30`                     |

---

## Etapa 4: Visualizar Previsões

Use `compute_rabc_weights.py` com `--visualize-only` para visualizar as previsões do modelo (e, se disponíveis, os alvos derivados das anotações) sem gravar um arquivo parquet.

```bash
python -m lerobot.rewards.sarm.compute_rabc_weights \
  --dataset-repo-id your-username/your-dataset \
  --reward-model-path your-username/sarm-model \
  --visualize-only \
  --num-visualizations 5 \
  --head-mode sparse \
  --output-dir ./sarm_viz
```

```bash
python -m lerobot.rewards.sarm.compute_rabc_weights \
  --dataset-repo-id your-username/your-dataset \
  --reward-model-path your-username/sarm-model \
  --visualize-only \
  --num-visualizations 5 \
  --head-mode dense \
  --output-dir ./sarm_viz
```

```bash
python -m lerobot.rewards.sarm.compute_rabc_weights \
  --dataset-repo-id your-username/your-dataset \
  --reward-model-path your-username/sarm-model \
  --visualize-only \
  --num-visualizations 5 \
  --head-mode both \
  --output-dir ./sarm_viz
```

A visualização mostra:

- **Gráfico de progresso**: Progresso previsto (e anotação opcional derivada de “GT” quando disponível e `--stride 1`)
- **Probabilidades de estágio**: Gráfico de área empilhado das probabilidades de estágio previstas
- **Quadros de amostra**: Quadros-chave do episódio com rótulos de progresso/etapa

### Argumentos de Visualização

| Argumento               | Descrição                                               |
| ---------------------- | --------------------------------------------------------- |
| `--visualize-only`     | Visualizar apenas as previsões (sem o cálculo do RABC)          |
| `--num-visualizations` | Número de episódios a visualizar (padrão: 5)              |
| `--head-mode`          | Cabeça SARM a ser usada: `sparse`, `dense` ou `both`            |
| `--stride`             | Calcule a cada N quadros e interpole o restante (padrão: 1) |

---

## Etapa 5 (Opcional): Treinar a Política com RA-BC

O Behavior Cloning Alinhado a Recompensas (RA-BC) usa o modelo SARM treinado para ponderar as amostras de treinamento com base na melhoria de progresso prevista. Isso requer duas etapas:

1. **Pré-compute progress values** para todos os frames usando o modelo SARM treinado
2. **Treinar a política** com ponderação RA-BC usando os valores pré-computados

### Como o RA-BC funciona

Para cada amostra de treinamento, o RA-BC calcula o delta de progresso:

```
r_i = φ(o_{t+Δ}) - φ(o_t)
```

Onde `φ` é a previsão de progresso do SARM e `Δ` é o `chunk_size` da política. Amostras com progresso positivo (boas demonstrações) recebem pesos maiores, enquanto amostras com progresso negativo ou zero têm seus pesos reduzidos.

A ponderação segue as **Equações 8-9** do artigo:

- **Peso suave**: `w̃_i = clip((r_i − (μ − 2σ)) / (4σ + ε), 0, 1)`
- **Peso final**: `w_i = 𝟙{r_i > κ} + 𝟙{0 ≤ r_i ≤ κ} × w̃_i`

### Etapa 5a: Calcule os valores de progresso do SARM

Primeiro, execute o modelo SARM em todos os frames do seu conjunto de dados para calcular os valores de progresso:

```bash
python -m lerobot.rewards.sarm.compute_rabc_weights \
  --dataset-repo-id your-username/your-dataset \
  --reward-model-path your-username/sarm-model \
  --head-mode sparse \
  --num-visualizations 5 \
  --push-to-hub
```

Este script:

- Processa todos os quadros e calcula os valores de progresso
- Salva os valores de progresso em um arquivo parquet ao lado do conjunto de dados no disco (padrão: `<dataset_root>/sarm_progress.parquet`)
- Gera visualizações dos primeiros N episódios (padrão: 5)

**Argumentos:**

| Argumento               | Descrição                                                    | Padrão    |
| ---------------------- | -------------------------------------------------------------- | ---------- |
| `--reward-model-path`  | Caminho para o modelo SARM treinado                                     | (obrigatório) |
| `--head-mode`          | Cabeça SARM a ser usada: `sparse`, `dense` ou `both`                 | `sparse`   |
| `--device`             | Dispositivo para inferência                                           | `cuda`     |
| `--visualize-only`     | Visualizar apenas as previsões (sem o cálculo de RA-BC)              | `false`    |
| `--num-visualizations` | Número de episódios para visualizar (padrão: 5, defina como 0 para pular) | `5`        |

**Formato de saída** (`sarm_progress.parquet`):

| Coluna            | Descrição                                    |
| ----------------- | ---------------------------------------------- |
| `index`           | Índice global do quadro no conjunto de dados                  |
| `episode_index`   | Número do episódio                                 |
| `frame_index`     | Índice do quadro local dentro do episódio               |
| `progress_sparse` | Valor de progresso da cabeça esparsa [0, 1]              |
| `progress_dense`  | Valor de progresso da cabeça densa [0, 1] (se calculado) |

### Etapa 5b: Treinar a Política com RA-BC

Depois de obter o arquivo de progresso, treine sua política com ponderação RA-BC. O arquivo de progresso é detectado automaticamente a partir do caminho do dataset (`sarm_progress.parquet`) se não for fornecido explicitamente. Atualmente, PI0, PI0.5, SmolVLA e FastWAM são suportados com RA-BC:

```bash
lerobot-train \
  --dataset.repo_id=your-username/your-dataset \
  --policy.type=pi0 \
  --sample_weighting.type=rabc \
  --sample_weighting.head_mode=sparse \
  --sample_weighting.kappa=0.01 \
  --output_dir=outputs/train/policy_rabc \
  --batch_size=32 \
  --steps=40000
```

O script de treinamento faz automaticamente:

- Carrega os valores de progresso pré-computados do arquivo parquet
- Usa o `chunk_size` da política para calcular os deltas de progresso (Δ)
- Calcula pesos de amostras com base na melhoria do progresso
- Aplica perda ponderada durante o treinamento

**Argumentos do RA-BC:**

| Argumento                           | Descrição                                            | Padrão                 |
| ---------------------------------- | ------------------------------------------------------ | ----------------------- |
| `--sample_weighting.type`          | Tipo de estratégia de ponderação (`rabc` ou `uniform`)          | `rabc`                  |
| `--sample_weighting.progress_path` | Caminho para o arquivo parquet de progresso                          | `sarm_progress.parquet` |
| `--sample_weighting.head_mode`     | Qual progresso da cabeça SARM usar: `sparse` ou `dense` | `sparse`                |
| `--sample_weighting.kappa`         | Limiar κ para amostras de alta qualidade                   | `0.01`                  |
| `--sample_weighting.epsilon`       | Constante pequena para estabilidade numérica                 | `1e-6`                  |

### Ajuste do Kappa RA-BC

O parâmetro `kappa` é o limiar que determina quais amostras recebem peso total (w=1). Compreender como ajustá-lo é fundamental para que o RA-BC funcione de forma eficaz.

**Como a ponderação funciona:**

| Condição           | Peso                  |
| ------------------- | ----------------------- |
| `delta > kappa`     | 1.0 (limiar rígido)    |
| `0 ≤ delta ≤ kappa` | Peso suave da Eq. 8  |
| `delta < 0`         | 0.0 (progresso negativo) |

**Diagnóstico de problemas de kappa:**

Monitore estas métricas do Trackio durante o treinamento:

| Métrica                        | Faixa Saudável | Indicador de Problema         |
| ----------------------------- | ------------- | ------------------------- |
| `sample_weight_mean_weight`   | 0.3 - 0.8     | ≈ 1.0 significa que o kappa é baixo demais |
| `sample_weighting/delta_mean` | > 0           | Deve ser positivo        |
| `sample_weighting/delta_std`  | > 0           | Variação na qualidade dos dados  |

**Se `sample_weight_mean_weight ≈ 1.0`:** seu kappa está muito baixo. A maioria das amostras tem `delta > kappa` e ignora completamente o ponderamento suave. O RA-BC se torna equivalente ao BC vanilla.

**Definindo kappa com base nos seus dados:**

O valor padrão `kappa=0.01` foi ajustado para a tarefa de dobrar camisetas do artigo (episódios de ~90s a 30fps). Para o seu conjunto de dados, verifique os valores registrados em `sample_weighting/delta_mean` e `sample_weighting/delta_std`:

```
# If delta_mean ≈ 0.03 and delta_std ≈ 0.02:
# Most deltas fall in range [0.01, 0.05]

# Option 1: Set kappa = delta_mean (medium selectivity)
--sample_weighting.kappa=0.03

# Option 2: Set kappa = delta_mean + delta_std (high selectivity)
--sample_weighting.kappa=0.05

# Option 3: Set kappa = delta_mean + 2*delta_std (very selective)
--sample_weighting.kappa=0.07
```

**Quando o RA-BC pode não ajudar:**

Se o seu conjunto de dados já for de alta qualidade (progresso consistente em todas as demonstrações), o RA-BC não trará muitos benefícios, pois não há nada para filtrar.

### Treinamento Multi-GPU com RA-BC

```bash
accelerate launch \
  --multi_gpu \
  --num_processes=4 \
  src/lerobot/scripts/lerobot_train.py \
  --dataset.repo_id=your-username/your-dataset \
  --policy.type=pi0 \
  --sample_weighting.type=rabc \
  --sample_weighting.kappa=0.01 \
  --output_dir=outputs/train/policy_rabc \
  --batch_size=32 \
  --steps=40000
```

---

## Dicas e Boas Práticas

### Escolhendo um Modo

- **Comece com `single_stage`** para experimentos rápidos - sem sobrecarga de anotação
- Use **`dense_only`** quando você deseja um rastreamento de progresso detalhado, mas as tarefas não possuem etapas de alto nível claras
- Use **`dual`** para tarefas complexas em que o progresso em nível grosseiro e fino é significativo

### Qualidade da Anotação

1. **Seja específico com os nomes das subtarefas**: em vez de "dobrar", use "pegue o lado próximo e dobre em direção ao centro"
2. **Verifique com visualização**: Sempre verifique alguns episódios antes do treinamento
3. **Nomenclatura consistente**: Use os mesmos nomes de subtarefas em todos os episódios

### RA-BC

1. **Treine o SARM primeiro**: a qualidade do RA-BC depende inteiramente da qualidade do SARM
2. **Monitore `sample_weight_mean_weight`**: se for ≈ 1.0, aumente kappa (veja [Ajuste do Kappa do RA-BC](#tuning-ra-bc-kappa))

---

## Citação

```bibtex
@article{chen2025sarm,
  title={SARM: Stage-Aware Reward Modeling for Long Horizon Robot Manipulation},
  author={Chen, Qianzhong and Yu, Justin and Schwager, Mac and Abbeel, Pieter and Shentu, Yide and Wu, Philipp},
  journal={arXiv preprint arXiv:2509.25358},
  year={2025}
}
```

