Evo-1: Lightweight Vision-Language-Action Model with Preserved Semantic Alignment [CVPR 2026]

August 5, 2026 Β· View on GitHub

πŸ“„ Paper
πŸ€— HuggingFace Models
πŸ€— HuggingFace Models πŸ“¦ Dataset
🌍 Website

πŸ“° News

  • πŸ—“οΈ 2026-08-05 β€” Evo-1 is now supported in the official RLinf framework πŸ”₯: full-parameter SFT and GRPO fine-tuning on the LIBERO simulator (doc).
  • πŸ—“οΈ 2026-07-21 β€” Released RoboTwin evaluation (Evo-1 policy plugin + 50 bimanual tasks). See RoboTwin benchmark part.
  • πŸ—“οΈ 2026-07-20 β€” Release LIBERO-plus benchmark evaluation scripts and results. See LIBERO-plus benchmark part.
  • πŸ—“οΈ 2026-07-05 β€” Evo-1 has been added to the official LeRobot framework πŸŽ‰πŸŽ‰.
  • πŸ—“οΈ 2026-06-07 β€” Evo-1 received the πŸŽ–οΈ Efficient CVPR Badge πŸŽ–οΈ.
  • πŸ—“οΈ 2026-04-10 β€” Updated the evo1-flash branch: faster training with reduced GPU memory usage.
  • πŸ—“οΈ 2026-04-10 β€” Updated the evo1-lerobot branch: Evo-1 is now fully integrated into the LeRobot framework.
  • πŸ—“οΈ 2026-04-08 β€” Evo-1 is now fully integrated into the LeRobot framework!
  • πŸ—“οΈ 2026-04-08 β€” We released Evo-1 Docker support for Jetson (https://huggingface.co/datasets/MINT-SJTU/Evo-1_JetsonOrin).
  • πŸ—“οΈ 2026-02-20 β€” Evo-1 is accepted by CVPR 2026 πŸŽ‰πŸŽ‰
  • πŸ—“οΈ 2025-12-15 β€” Added Evo-1 inference code in Aloha dual arm (Implemented by community user @meijie-jesse)
  • πŸ—“οΈ 2025-11-15 β€” Added Evo-1 inference in the LeRobot framework for SO100/SO101
  • πŸ—“οΈ 2025-11-10 β€” Released inference script in xarm6
  • πŸ—“οΈ 2025-11-06 β€” Released Meta-World & LIBERO evaluation scripts
  • πŸ—“οΈ 2025-11-06 β€” Uploaded model weights to HuggingFace
  • πŸ—“οΈ 2025-11-06 β€” Released official code

βœ… To-Do List

  • βœ… Release inference script in xarm6
  • βœ… Update evo1-flash branch (faster training + reduced GPU memory usage)
  • βœ… Update evo1-lerobot branch (fully integrated Evo-1 into the LeRobot framework)
  • βœ… Release instructions for deploying Evo-1 on Jetson Orin (https://huggingface.co/datasets/MINT-SJTU/Evo-1_JetsonOrin)
  • βœ… Release RoboTwin evaluation script
  • βœ… Release results of all 50 RoboTwin tasks

βš™οΈ Installation

Prepare the environment for Evo-1

# Clone this repo
git clone https://github.com/MINT-SJTU/Evo-1.git

cd Evo-1/

# Create a Conda environment
conda create -n Evo1 python=3.10 -y

conda activate Evo1

# Install requirements
cd Evo_1

pip install -r requirements.txt

# You may need to reduce MAX_JOBS to suit your computer
# (!!! This is a critical step β€” skipping it may cause lower success rate or unstable robot motion !!!)
MAX_JOBS=64 pip install -v flash-attn --no-build-isolation

πŸ§ͺ Simulation Benchmark

πŸ’‘ Tips

In downstream tasks, the client script needs to be processed according to different task configurations. Because the client executes states or actions in the absolute/original action space, while the actions sent by the server are in the relative action space. Therefore, if the benchmark evaluation uses absolute actions, an additional logic is required in the client script: convert the relative actions returned by the model back to absolute actions through accumulation. If the evaluation uses relative actions, no modification is needed.

πŸ§ͺ Meta-World Benchmark

1️⃣ Prepare the environment for Meta-World

conda create -n metaworld python=3.10 -y
conda activate metaworld
pip install mujoco
pip install metaworld
pip install websockets
pip install opencv-python
pip install packaging
pip install huggingface_hub

2️⃣ Model Preparation

πŸ“₯ 2.1 Download Model Weight
hf download MINT-SJTU/Evo1_MetaWorld --local-dir /path/to/save/checkpoint/
✏️ 2.2 Modify config
  1. Modify checkpoint dir: Evo1_server.py
  2. Modify arm key and dataset key: The arm_key and dataset_key used during server inference must completely match the corresponding arm-dataset keys in the norm_stats.json in checkpoint. Located in Evo1_server.py
arm_key = "metaworld_robot"
dataset_key = "metaworld_dataset"

Tips: When using the old version model checkpoints, you only need to change the arm_key to the key name of the norm_stats.json file in the model checkpoint. For the new model checkpoints trained by the new version, you need to modify both arm_key and dataset_key.

  1. (Optional) Modify server port: Evo1_server.py
  2. (Optional) Modify client port: mt50_evo1_client_prompt.py

3️⃣ Run Meta-World Evaluation

# Terminal 1
conda activate Evo1

cd Evo_1

python scripts/Evo1_server.py
# Terminal 2
conda activate metaworld

cd MetaWorld_evaluation

python mt50_evo1_client_prompt.py

πŸ§ͺ LIBERO Benchmark

1️⃣ Prepare the environment for LIBERO

conda create -n libero python=3.8.13 -y

conda activate libero

cd LIBERO_evaluation/

git clone https://github.com/Lifelong-Robot-Learning/LIBERO.git

cd LIBERO

pip install -r requirements.txt

pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 torchaudio==0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113

pip install -e .

pip install websockets

pip install huggingface_hub

2️⃣ Model Preparation

πŸ“₯ 2.1 Download Model Weight
hf download MINT-SJTU/Evo1_LIBERO --local-dir /path/to/save/checkpoint/
✏️ 2.2 Modify config
  1. Modify checkpoint dir: Evo1_server.py
  2. Modify ckpt name: libero_client_4tasks.py
  3. Modify arm key and dataset key: The arm_key and dataset_key used during server inference must completely match the corresponding arm-dataset keys in the norm_stats.json in checkpoint. Located in Evo1_server.py
# Specialized for different downstream tasks.

# Old version checkpoint:
arm_key = "libero_robot"
dataset_key = "libero_4_datasets"

# New version trained checkpoint:
# Keep the same name as in your norm_stats.json, which is usually the same as the arm name in config.yaml
arm_key = "franka_ee_pose_delta"
dataset_key = "libero_10_no_noops_lerobot"

Tips: When using the old version model checkpoints, you only need to change the arm_key to the key name of the norm_stats.json file in the model checkpoint. For the new model checkpoints trained by the new version, you need to modify both arm_key and dataset_key.

  1. (Optional) Modify server port: Evo1_server.py
  2. (Optional) Modify client port: libero_client_4tasks.py

3️⃣ Run LIBERO Evaluation

# Terminal 1
conda activate Evo1

cd Evo_1

python scripts/Evo1_server.py
# Terminal 2
conda activate libero

cd LIBERO_evaluation

python libero_client_4tasks.py


πŸ§ͺ LIBERO-plus Benchmark

1️⃣ Prepare the environment for LIBERO-plus

Prepare LIBERO-plus evaluation environment, detailed instructions can be found in libero-plus-eval/README.md. Follow Step 1 to Step 6 to set up the environment and download the necessary assets.

2️⃣ Model Preparation

πŸ“₯ 2.1 Download Model Weight

LIBERO-plus model use the same Evo-1 model weight as LIBERO, you can download it from HuggingFace:

hf download MINT-SJTU/Evo1_LIBERO --local-dir /path/to/save/checkpoint/
✏️ 2.2 Modify server config
  1. Modify checkpoint dir: Evo1_server.py
  2. Modify arm key and dataset key to match your LIBERO checkpoint's norm_stats.json, same as step 2.2 of the LIBERO Benchmark: Evo1_server.py
  3. (Optional) Modify server port: Evo1_server.py

3️⃣ Run LIBERO-plus Evaluation

# Terminal 1
conda activate Evo1

cd Evo_1

python scripts/Evo1_server.py

Open another terminal and run the evaluation script for LIBERO-plus, more specific instructions can be found in libero-plus-eval/README.md Step 7. Following is an simple example of running the evaluation for the libero_spatial suite:

# Terminal 2
conda activate libero_plus
export LIBERO_CONFIG_PATH="$HOME/.libero-plus"
cd /path/to/libero-plus-eval

bash test_libero_plus.sh libero_spatial


πŸ§ͺ RoboTwin Benchmark

RoboTwin (50 bimanual manipulation tasks in SAPIEN) uses a policy-plugin architecture: start the Evo-1 server, drop the Evo-1 policy adapter into a RoboTwin checkout, and launch RoboTwin's evaluator as the client.

1️⃣ Prepare the environment for RoboTwin

RoboTwin is not bundled in this repo. Clone and install it separately (SAPIEN + CuRobo are required):

conda create -n RoboTwin python=3.10 -y
conda activate RoboTwin

git clone https://github.com/TianxingChen/RoboTwin.git
cd RoboTwin
pip install -r script/requirements.txt
pip install websockets

# CuRobo is REQUIRED β€” expert solvability check and scene setup use its motion planner
cd envs && git clone https://github.com/NVlabs/curobo.git
cd curobo && python -m pip install -e . --no-build-isolation && cd ../..

See the RoboTwin README for full setup (assets, SAPIEN, mplib).

2️⃣ Model Preparation

πŸ“₯ 2.1 Download Model Weight
hf download MINT-SJTU/Evo1_RoboTwin --local-dir /path/to/save/checkpoint/
✏️ 2.2 Modify config
  1. Modify checkpoint dir: Evo1_server.py
  2. arm_key / dataset_key: no server edit needed for RoboTwin. The client sends them per request (arm_key=aloha_joint, per-task dataset_key=robotwin_<task>) and the server reads them from the payload. RoboTwin norm_stats.json is keyed per task (50 keys under aloha_joint), so a single fixed dataset_key would be wrong for 49/50 tasks β€” the per-request key is required.
  3. (Optional) Modify server port: Evo1_server.py
πŸ”Œ 2.3 Install the Evo-1 policy plugin into RoboTwin
cp -r RoboTwin_evaluation/policy/Evo1  /path/to/RoboTwin/policy/Evo1

3️⃣ Run RoboTwin Evaluation

# Terminal 1 β€” Evo-1 server (PYTHONPATH=. lets the server import scripts.* and config)
conda activate Evo1
cd Evo_1
PYTHONPATH=. python scripts/Evo1_server.py
# Terminal 2 β€” RoboTwin client, one task
conda activate RoboTwin
cd /path/to/RoboTwin/policy/Evo1

# Usage: bash eval.sh <task_name> [task_config] [ckpt_setting] [seed] [gpu_id] [server_url] [horizon]
bash eval.sh place_burger_fries demo_clean step_20000 0 0 ws://0.0.0.0:9000 37

Each task runs 100 episodes with expert solvability check; results are written under RoboTwin/eval_result/.


🧠 Training on Your Own Dataset

We support lerobot v2.1 format, please convert your data to this format.

Below we will demonstrate how to prepare the dataset, modify configurations, and start the two-stage pretraining along with standard VLA finetuning.

πŸ—‚οΈ 1. Data Preparation

First, you need to download the dataset for pretraining. Taking the Libero dataset as an example, assuming you are located in the project root directory, run the following commands to download the dataset:

mkdir -p Evo1_training_dataset/libero_standard
cd Evo1_training_dataset/libero_standard

# 1) spatial
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/datasets/IPEC-COMMUNITY/libero_spatial_no_noops_1.0.0_lerobot
cd libero_spatial_no_noops_1.0.0_lerobot
git lfs pull
cd ..

# 2) object
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/datasets/IPEC-COMMUNITY/libero_object_no_noops_1.0.0_lerobot
cd libero_object_no_noops_1.0.0_lerobot
git lfs pull
cd ..

# 3) goal
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/datasets/IPEC-COMMUNITY/libero_goal_no_noops_1.0.0_lerobot
cd libero_goal_no_noops_1.0.0_lerobot
git lfs pull
cd ..

# 4) libero_10
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/datasets/IPEC-COMMUNITY/libero_10_no_noops_1.0.0_lerobot
cd libero_10_no_noops_1.0.0_lerobot
git lfs pull
cd ../../..

✏️ 2. Data Configuration & Norm Stats

✏️ 2.1 Modify config.yaml

You need to modify the detailed config. For detailed dataset configuration (how to define suite, modify config.yaml, configure cache_dir, etc.), please refer to: Detailed Tutorial for Dataset Configuration.

✏️ 2.2 Compute Norm Stats

After configuring the config.yaml, it is necessary to calculate the statistical values (norm_stats) of each feature in the dataset:

cd Evo_1/
python -m dataset.compute_normstats dataset/config.yaml --action_horizon 50

Run the command above to compute the statistical characteristics for the datasets included in the config, which consist of max, min, q01, q99, mean, and std.

If an OOM error occurs while running the above code, you can run the streaming version instead:

cd Evo_1/
python -m dataset.compute_normstats_streaming dataset/config.yaml --action_horizon 50

πŸš€ 3. Start Training

We use the two-stage training paradigm.

πŸš€ 3.1 Setup deepspeed

accelerate config     

You can check this setup guide


πŸš€ 3.2 Training Stage 1

We only train the integration module and action expert in stage 1.

If you are training with multiple GPU, set --num_processes to the GPU number.
You need to change the --wandb_project, --run_name, --save_dir, and --cache_dir base on your own config. You may also change other options based on your need.

conda activate Evo1

cd Evo_1/

accelerate launch --num_processes 1 --num_machines 1 --deepspeed_config_file ds_config.json scripts/train.py --wandb_project your_project_name --run_name Evo1_flash_libero4_stage1 --action_head flowmatching --use_augmentation --lr 1e-5 --dropout 0.2 --weight_decay 1e-3 --batch_size 16 --image_size 448 --max_steps 20000 --log_interval 10 --ckpt_interval 5000 --warmup_steps 1000 --grad_clip_norm 1.0 --num_layers 8 --horizon 50 --finetune_action_head --disable_wandb --prefetch_factor 2 --video_backend av --cache_dir /your/path/to/dataset_cache/evo1_libero_4_cache --vlm_name OpenGVLab/InternVL3-1B --dataset_config_path dataset/config.yaml --per_action_dim 24 --state_dim 24 --save_dir /your/path/checkpoints/stage1

πŸš€ 3.3 Training Stage 2

We perform Full-scale training in stage 2. You need to change the --wandb_project, --run_name, --save_dir, --resume_path and --cache_dir base on your own config. You may also change other options based on your need.

conda activate Evo1

cd Evo_1/

accelerate launch --num_processes 1 --num_machines 1 --deepspeed_config_file ds_config.json scripts/train.py --wandb_project your_project_name --run_name Evo1_libero_stage2 --action_head flowmatching --use_augmentation --lr 1e-5 --dropout 0.2 --weight_decay 1e-3 --batch_size 16 --image_size 448 --max_steps 80000 --log_interval 10 --ckpt_interval 5000 --warmup_steps 1000 --grad_clip_norm 1.0 --num_layers 8 --horizon 50 --finetune_vlm --finetune_action_head --disable_wandb --prefetch_factor 2 --video_backend av --cache_dir /your/path/to/dataset_cache/evo1_libero_4_seg_cache --vlm_name OpenGVLab/InternVL3-1B --dataset_config_path dataset/config.yaml --per_action_dim 24 --state_dim 24 --save_dir /your/path/checkpoints/stage2 --resume --resume_pretrain --resume_path /your/path/checkpoints/stage1/step_10000

πŸš€ 3.4 (Optional) Resume Training

If you want to resume the training process, you can use the following command (we use stage 2 as an example):

accelerate launch --num_processes 1 --num_machines 1 --deepspeed_config_file ds_config.json scripts/train.py --wandb_project your_project_name --run_name Evo1_la2vla_libero_stage2 --action_head flowmatching --use_augmentation --lr 1e-5 --dropout 0.2 --weight_decay 1e-3 --batch_size 16 --image_size 448 --max_steps 80000 --log_interval 10 --ckpt_interval 5000 --warmup_steps 1000 --grad_clip_norm 1.0 --num_layers 8 --horizon 50 --finetune_vlm --finetune_action_head --disable_wandb --prefetch_factor 2 --video_backend av --cache_dir /your/path/to/dataset_cache/evo1_libero_4_seg_cache --vlm_name OpenGVLab/InternVL3-1B --dataset_config_path dataset/config.yaml --per_action_dim 24 --state_dim 24 --save_dir /your/path/checkpoints/stage2 --resume  --resume_path /the/checkpoint/path/you/want/to/resume/from/step_20000

🦾 4. Inference in Your Own Embodiment

We provide an example of inference client script Evo1_client_xarm6 for xArm6.

The key is to construct an observation dict and pass it to the server.

      obs = {
            # You need to change the image size to 448x448 before send in obs
            "image": [base_proc.tolist(), wrist_proc.tolist(), dummy_proc.tolist()],  
            # This shows which image is valid.
            "image_mask": [int(i) for i in [1, 1, 0]],
            # This is the state of the robot.
            "state": state.astype(float).tolist(),
            # This is the action mask that shows which action is valid.
            "action_mask": [[int(i) for i in action_mask[0]]],
            # This is the instruction of the task
            "prompt": task_instruction
      }

      try:
            # Send the observation to the server
            await ws.send(json.dumps(obs))
            result = await ws.recv()
            # Get the action chunk
            action_chunk = torch.tensor(json.loads(result))
            
      except Exception as e:
            print(f"❌ Inference Error: {e}")
            await asyncio.sleep(0.5)
            continue


πŸ€– 5.Inference in Lerobot SO100/SO101

For detailed instructions, please check out the evo1-lerobot branch.

πŸ“š Citation

@article{lin2025evo,
  title={Evo-1: Lightweight Vision-Language-Action Model with Preserved Semantic Alignment},
  author={Lin, Tao and Zhong, Yilei and Du, Yuxin and Zhang, Jingjing and Liu, Jiting and Chen, Yinxinyu and Gu, Encheng and Liu, Ziyan and Cai, Hongyi and Zou, Yanwen and others},
  journal={arXiv preprint arXiv:2511.04555},
  year={2025}
}

πŸ“¬ Contact

If you encounter any issues or have suggestions,
please open an issue or start a discussion on GitHub.
We sincerely welcome your feedback and contributions.

You can also scan the QR code below to connect with me or join chatting group on WeChat: