Hugging Face Blog

# Cách Tôi Huấn Luyện Action Chunking Transformer (ACT) trên SO-101: Hành Trình, Những Cạm Bẫy và Bài Học Kinh Nghiệm --- ## Mở Đầu Vài tháng trước, tôi quyết định thử sức với việc huấn luyện một policy robot từ đầu bằng cách sử dụng **Action Chunking Transformer (ACT)** trên cánh tay robot **SO-101**. Đây là câu chuyện thực tế — không phải bản demo được đánh bóng — bao gồm cả những lần thất bại, những đêm thức khuya debug, và cuối cùng là khoảnh khắc robot thực sự hoàn thành nhiệm vụ. --- ## ACT Là Gì và Tại Sao Tôi Chọn Nó? **Action Chunking Transformer (ACT)** là một kiến trúc học模仿 (imitation learning) được giới thiệu trong bài báo *"Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware"* (Chi et al., 2023). Ý tưởng cốt lõi: - Thay vì dự đoán **từng hành động một**, ACT dự đoán một **"chunk" (khối) hành động** — một chuỗi các hành động trong tương lai ngắn hạn - Sử dụng kiến trúc **Transformer encoder-decoder** với **CVAE (Conditional Variational Autoencoder)** để xử lý tính đa dạng trong dữ liệu biểu diễn - Giảm thiểu hiện tượng **compounding errors** (lỗi tích lũy) vốn là vấn đề kinh điển của behavioral cloning Tôi chọn ACT vì: 1. Nó hoạt động tốt với **dữ liệu ít** (50–200 demo) 2. Phù hợp với phần cứng giá rẻ như SO-101 3. Cộng đồng **LeRobot (HuggingFace)** đã tích hợp sẵn --- ## Thiết Lập Phần Cứng ### Robot SO-101 - 6 bậc tự do (6-DOF) - Servo Feetech STS3215 - Camera góc nhìn từ cổ tay + camera quan sát từ trên - Tổng chi phí: ~$300 ### Máy Tính Huấn Luyện - GPU: RTX 3090 (24GB VRAM) - RAM: 64GB - Hệ điều hành: Ubuntu 22.04 > **Lưu ý quan trọng:** Bạn *có thể* huấn Luyện trên RTX 3060 (12GB), nhưng phải giảm batch size và sẽ mất nhiều thời gian hơn đáng kể. --- ## Quy Trình Thu Thập Dữ Liệu ### Nhiệm Vụ Tôi Chọn Tôi bắt đầu với nhiệm vụ **"nhặt khối lego và đặt vào hộp"** — đơn giản nhưng đủ thách thức để kiểm tra policy. ### Số Lượng Demo | Lần thử | Số demo | Kết quả | |---------|---------|---------| | 1 | 30 | Thất bại hoàn toàn | | 2 | 80 | Robot di chuyển nhưng không nhặt được | | 3 | 150 | Thành công ~40% | | 4 | 200 | Thành công ~75% | ### Những Cạm Bẫy Khi Thu Thập Dữ Liệu **❌ Cạm bẫy #1: Demo không nhất quán** ``` Vấn đề: Tôi đặt khối lego ở các vị trí khác nhau mỗi lần nhưng không đủ đa dạng — tạo ra "vùng chết" mà robot không bao giờ học được. Giải pháp: Chia không gian làm việc thành grid 3x3, thu thập đều ở mỗi ô. ``` **❌ Cạm bẫy #2: Tốc độ demo không đồng đều** ``` Vấn đề: Đôi khi tôi thao tác nhanh, đôi khi chậm. ACT rất nhạy cảm với timing. Giải pháp: Đặt metronome 1Hz, thao tác theo nhịp. ``` **❌ Cạm bẫy #3: Ánh sáng thay đổi** ``` Vấn đề: Thu thập demo vào buổi sáng và chiều tối — điều kiện ánh sáng khác nhau hoàn toàn. Giải pháp: Dùng đèn LED cố định, che cửa sổ. ``` --- ## Cài Đặt và Cấu Hình ### Cài Đặt LeRobot ```bash # Clone repository git clone https://github.com/huggingface/lerobot.git cd lerobot # Tạo môi trường conda conda create -n lerobot python=3.10 conda activate lerobot # Cài đặt dependencies pip install -e ".[feetech]" ``` ### Cấu Hình ACT cho SO-101 File cấu hình `configs/policy/act_so101.yaml`: ```yaml policy: name: act # Kiến trúc chunk_size: 100 # Số bước hành động trong mỗi chunk n_obs_steps: 1 # Số frame quan sát đầu vào # Encoder dim_model: 512 n_heads: 8 n_encoder_layers: 4 n_decoder_layers: 7 # CVAE latent_dim: 32 kl_weight: 10.0 # Quan trọng! Xem phần bên dưới # Camera camera_names: ["top", "wrist"] image_size: [480, 640] # Backbone vision vision_backbone: resnet18 pretrained_backbone: true training: batch_size: 8 lr: 1e-5 num_epochs: 2000 grad_clip_norm: 10.0 ``` --- ## Quá Trình Huấn Luyện ### Lệnh Huấn Luyện ```bash python lerobot/scripts/train.py \ policy=act_so101 \ env=so101_real \ dataset_repo_id=your_username/so101_lego_task \ hydra.run.dir=outputs/act_so101_run1 \ training.num_epochs=2000 \ wandb.enable=true ``` ### Monitoring Với W&B Tôi theo dõi các metrics sau: - **`train/loss`**: Loss tổng thể - **`train/l1_loss`**: Reconstruction loss của hành động - **`train/kl_loss`**: KL divergence của CVAE ### Đường Cong Loss Điển Hình ``` Epoch 0: loss=2.847 Epoch 100: loss=1.203 Epoch 500: loss=0.412 Epoch 1000: loss=0.187 Epoch 1500: loss=0.134 Epoch 2000: loss=0.098 ``` --- ## Những Cạm Bẫy Khi Huấn Luyện ### 🔥 Cạm Bẫy Lớn Nhất: `kl_weight` Đây là thứ khiến tôi mất **3 ngày** để debug. ``` Vấn đề: Robot học được cách di chuyển nhưng hành động rất "run rẩy" và không mượt mà. Nguyên nhân: kl_weight quá thấp (tôi đặt = 1.0) → CVAE không học được latent space có cấu trúc tốt → Policy không ổn định Giải pháp: Tăng kl_weight lên 10.0 (giá trị mặc định trong paper gốc). Đừng thay đổi giá trị này trừ khi bạn hiểu rõ lý do. ``` ### ⚠️ Cạm Bẫy #2: Chunk Size Không Phù Hợp ``` chunk_size=50: Robot dừng và "suy nghĩ" quá thường xuyên → Hành động bị ngắt quãng chunk_size=100: Hoạt động tốt cho nhiệm vụ ~3 giây chunk_size=200: Robot "cam kết" quá lâu với một kế hoạch → Không thể phục hồi khi có sai lệch nhỏ ``` ### ⚠️ Cạm Bẫy #3: Learning Rate ```python # ❌ Sai - LR quá cao lr = 1e-4 # Loss giảm nhanh nhưng policy không ổn định # ✅ Đúng - LR thấp hơn, ổn định hơn lr = 1e-5 # Huấn luyện chậm hơn nhưng kết quả tốt hơn nhiều ``` ### ⚠️ Cạm Bẫy #4: Normalization Dữ Liệu ```bash # LUÔN kiểm tra stats normalization trước khi train python lerobot/scripts/compute_stats.py \ --dataset-repo-id your_username/so101_lego_task # Output mẫu: # action mean: [0.023, -0.156, 0.891, ...] # action std: [0.234, 0.445, 0.123, ...] ``` Nếu std của một joint gần bằng 0 → joint đó hầu như không di chuyển trong demo → **xem lại dữ liệu của bạn**. --- ## Triển Khai và Đánh Giá ### Script Inference ```python from lerobot.common.policies.act.modeling_act import ACTPolicy import torch # Load policy policy = ACTPolicy.from_pretrained("outputs/act_so101_run1/checkpoints/last") policy.eval() policy.to("cuda") # Inference loop with torch.inference_mode(): while True: # Lấy observation từ robot obs = get_robot_observation() # Dự đoán action chunk action_chunk = policy.select_action(obs) # Thực thi từng action trong chunk for action in action_chunk: robot.send_action(action) time.sleep(1/50) # 50Hz control ``` ### Kết Quả Đánh Giá Tôi đánh giá trên **50 lần thử** với vị trí khối lego ngẫu nhiên: | Metric | Giá trị | |--------|---------| | Tỷ lệ thành công tổng thể | 74% | | Thành công khi khối ở trung tâm | 92% | | Thành công khi khối ở góc | 58% | | Thời gian hoàn thành trung bình | 4.2 giây | | Tỷ lệ va chạm | 3% | --- ## Phân Tích Thất Bại ### Tại Sao 26% Còn Lại Thất Bại? **Phân tích bằng cách record video:** 1. **~12%**: Khối lego ở vị trí chưa từng thấy trong training 2. **~8%**: Ánh sáng phản chiếu bất thường trên bề mặt khối 3. **~4%**: Robot nhặt được nhưng thả trước khi đến hộp 4. **~2%**: Lỗi servo (vấn đề phần cứng) ### Cải Thiện Tiếp Theo ``` TODO: [ ] Thêm data augmentation (color jitter, random crop) [ ] Thu thập thêm demo ở các góc cạnh [ ] Thử ACT+ với diffusion head [ ] Thêm camera thứ 3 (góc bên) ``` --- ## Bài Học Tổng Kết ### ✅ Những Gì Hoạt Động Tốt 1. **Bắt đầu đơn giản**: Nhiệm vụ đơn giản + ít camera + ít demo → iterate nhanh 2. **Giữ nguyên hyperparameters mặc định**: Đặc biệt là `kl_weight=10.0` 3. **Chất lượng > Số lượng demo**: 150 demo nhất quán tốt hơn 300 demo lộn xộn 4. **Kiểm soát môi trường**: Ánh sáng cố định, nền đồng nhất ### ❌ Những Gì Không Hoạt Động 1. Thay đổi nhiều hyperparameter cùng lúc 2. Thu thập demo khi mệt (chất lượng giảm rõ rệt) 3. Bỏ qua bước kiểm tra normalization stats 4. Dùng `chunk_size` quá lớn cho nhiệm vụ ngắn ### 💡 Quy Tắc Ngón Tay Cái ``` Thời gian nhiệm vụ (giây) × Tần số điều khiển (Hz) × 0.5 = chunk_size lý tưởng Ví dụ: 3 giây × 50Hz × 0.5 = 75 → dùng chunk_size=100 ``` --- ## Tài Nguyên Tham Khảo - 📄 [Paper ACT gốc](https://arxiv.org/abs/2304.13705) - 🤗 [LeRobot Documentation](https://github.com/huggingface/lerobot) - 🛠️ [SO-101 Build Guide](https://github.com/TheRobotStudio/SO-ARM100) - 💬 [Discord LeRobot Community](https://discord.gg/lerobot) --- *Nếu bạn đang x

🕐 20/07/2026 08:14 📰 Hugging Face Blog ✅ Đã dịch sang tiếng Việt

# Hành Trình Huấn Luyện ACT trên Robot SO-101: Những Bài Học Xương Máu

Tôi đã muốn thử nghiệm **Action Chunking Transformer (ACT)** trên robot thật từ khi xem video mô hình này nấu tôm 🦐. Một tháng trước, tôi cuối cùng cũng có trong tay một chiếc SO-101. Sau khi xem vô số video người ta huấn luyện cánh tay này nhặt và đặt một khối gỗ, tôi tự hỏi: liệu mình có làm được không?

Và thế là bắt đầu một hành trình đầy rẫy cạm bẫy và chướng ngại vật! Nhìn lại bây giờ, nhiều thứ trong số đó trông có vẻ khá đơn giản. 🤷🏻‍♀️ Vì vậy tôi quyết định chia sẻ ở đây - biết đâu sẽ hữu ích cho mọi người.

Đây rồi - hành trình, những cạm bẫy, và bài học rút ra từ việc huấn luyện ACT trên SO-101 để nhặt khối gỗ và đặt vào hộp chứa.

---

## Bắt Đầu

**Tutorial của LeRobot** là điểm khởi đầu khá tốt. Tôi lấy hai **webcam** và gắn lên chân đế. Sau đó kẹp chặt hai cánh tay - một cánh tay dẫn (leader) và một cánh tay theo (follower). Thứ ba, tôi cắm tất cả vào chiếc máy tính gaming mà bạn trai tôi hào phóng cho mượn vì mục đích khoa học. Thế là xong! Tôi đã sẵn sàng thu thập dữ liệu.

Kế hoạch rất đơn giản: dùng cánh tay dẫn để điều khiển cánh tay theo nhặt khối gỗ và đặt vào hộp chứa. Tôi sẽ ghi lại 50 episode - "10 episode mỗi vị trí", khởi động huấn luyện kéo dài vài tiếng, và bùm - robot hầu bàn ra đời.

---

## Thu Thập Dữ Liệu: Tốn Thời Gian Hơn Tưởng

Việc thu thập dữ liệu thực ra... tốn thời gian đến bất ngờ. Các camera của tôi cứ mất kết nối sau mỗi ~5 episode và làm crash script ghi hình. Sau khi hỏi ChatGPT, tôi nhận ra nguyên nhân là vì hai webcam của tôi **hoàn toàn giống nhau**. Điều đó khiến máy tính bị nhầm lẫn, và đường dẫn USB của camera bị thay đổi ngẫu nhiên theo thời gian.

Tôi quyết định tiếp tục thay vì dừng lại sửa lỗi. Phải mất khoảng **2 tiếng đồng hồ** (nửa buổi chiều) để thu thập đủ 50 demonstration.

Sau đó, script huấn luyện khá dễ thiết lập nhờ pipeline huấn luyện và logging của LeRobot. Tôi khởi động huấn luyện trước khi đi ngủ. Mô hình ACT (52M tham số) của tôi huấn luyện trong **~4 tiếng** trên NVIDIA RTX 3080 12GB.

Sáng hôm sau thức dậy, tôi thấy những đường loss curve đẹp mê hồn trên Weights & Biases. Không thể chờ để thử trên SO-101!

---

## Thực Tế Phũ Phàng

Ngày hôm sau, tôi load policy vừa huấn luyện xong, đặt khối gỗ vào một trong năm vị trí trong tập huấn luyện, và nhấn chạy.

SO-101 của tôi không học được cách nhặt khối gỗ, mà thay vào đó cứ **mổ xuống bàn hết lần này đến lần khác**... như thể đang mơ hồ nhớ lại "tiến đến khối gỗ, đóng gripper, di chuyển đến hộp chứa", trong khi hoàn toàn bỏ qua phần quan trọng nhất là thực sự **cầm được khối gỗ**.

Tôi thử đặt khối gỗ gần hơn, nghĩ rằng có thể nó chỉ cần thêm chút trợ giúp. Không ăn thua! Cánh tay vẫn tiếp tục tiến đến khối gỗ nhưng gần hơn mức cần thiết... Kinh điển.

Tôi thất vọng vô cùng. Người ta làm trông dễ thế trong các video! Mình đã làm sai ở đâu?

---

## Những Nguyên Nhân Gốc Rễ

**Góc camera không thuận lợi cho việc gắp đồ:** Nhìn từ góc camera bên phải, các đầu gripper bị chồng lên nhau trong quá trình gắp. Điều này khiến footage từ camera phải ít hữu ích hơn cho thao tác gắp.

**Lệch calibration giữa huấn luyện và kiểm tra:** Calibration của cánh tay khác nhau giữa lúc huấn luyện và lúc kiểm tra! Tôi đã không...

*(Bài viết gốc bị cắt tại đây)*

## Bản dịch

khi script eval record phàn nàn về việc thiếu file hiệu chỉnh cánh tay và yêu cầu hiệu chỉnh lại. Hóa ra tôi đã vô tình mất các file hiệu chỉnh cũ trong một lần mất điện vì chúng được lưu trong thư mục tạm thời! Và khi hiệu chỉnh lại, tôi đã không đưa tất cả các khớp về vị trí giữa của dải chuyển động, điều này làm lệch offset homing của một số khớp… vì vậy dù model đã học được góc khớp chính xác, chúng lại bị ánh xạ sang các lệnh điều khiển servo không đúng.

**Dữ liệu thiếu đa dạng:** Tập huấn luyện của tôi chứa 10 episode mỗi vị trí tại 5 vị trí cố định, theo gợi ý trong hướng dẫn. Điều này khiến model dễ overfit bằng cách ghi nhớ các quỹ đạo, và do đó thực sự không học được cách nhặt khối từ một vị trí nằm ngoài tập huấn luyện. Điều này cũng dẫn tôi đến điểm tiếp theo…

**Không có tập eval trong quá trình huấn luyện:** Hồi còn đại học khi huấn luyện các model thị giác máy tính, tôi luôn tách riêng một tập eval nhỏ khỏi tập huấn luyện để theo dõi hiệu suất model. Điều này giúp phát hiện overfitting và lựa chọn checkpoint. Nhưng script huấn luyện từ LeRobot chỉ thực hiện eval cho các policy được huấn luyện trong môi trường mô phỏng.

Nhưng khoan đã, còn nhiều hơn thế! Những phiền toái về mặt vận hành cũng ngày càng chồng chất:

Đã đến lúc cần kỹ thuật nghiêm túc. Tôi xắn tay áo và bắt tay vào việc.

Để tăng cường độ đa dạng của dữ liệu, tôi cần bắt đầu bằng việc định nghĩa chính thức không gian tác vụ và các biến thể của nó.

Dựa trên định nghĩa tác vụ, tiêu chí lấy mẫu và phân chia train/eval đã được hình thức hóa, tôi định nghĩa dataset mới của mình như sau:

Quá trình huấn luyện hoàn tất trong vòng 4 giờ! Nhìn vào eval loss, có vẻ như model không bị overfit. Tôi sử dụng checkpoint có eval loss thấp nhất để đánh giá trên robot thực.

**Episode thành công (Trong phân phối)**

**Episode thất bại (Ngoài phân phối)**

Tôi dành thời gian điều tra độ phủ của dataset. Hóa ra việc lấy mẫu vị trí xy đồng đều mà tôi dùng để thu thập dữ liệu lại không thực sự ngẫu nhiên như vậy. Nhiều điểm dữ liệu bị tập trung thành cụm, để lại những khoảng trống lớn giữa các điểm. Điều này giải thích tại sao các episode gần mép thùng lại thất bại, và tại sao model không giỏi tổng quát hóa sang các episode ngoài phân phối.

Đã đến lúc nghiêm túc về độ đa dạng dữ liệu. Nhưng trước khi tiếp tục, hãy để tôi kể cho bạn nghe về vụ hỏng cánh tay nghiêm trọng suýt làm dừng cả dự án của tôi…

Giữa chừng quá trình thu thập dữ liệu, thảm họa ập đến. Script của tôi bị crash do sự cố camera (vấn đề băng thông USB). Sau đó, cánh tay follower của tôi đột nhiên từ chối hoạt động. Thông báo lỗi rất khó hiểu: `Failed to sync read 'Present_Position' on ids=[1, 2, 3, 4, 5, 6]...[TxRxResult] Incorrect status packet`. Nhưng khi gọi `sync_read` riêng lẻ thì lại hoạt động bình thường.

Sau cả một ngày điều tra 🕵🏻, tôi phát hiện ra rằng lỗi này chỉ xảy ra trong các thao tác `sync_read()` và `sync_write()` tần số cao (30Hz). Sau khi loại trừ từng motor một, tôi tìm ra thủ phạm! Motor gripper không theo kịp tốc độ. Ở tần số cao, đôi khi nó không gửi lại được gói trạng thái và

đã chặn toàn bộ giao tiếp trên bus động cơ.

😮 Động cơ kẹp bị hỏng như thế nào vậy??

Sau khi suy nghĩ mãi về chuyện này... Tôi nhận ra rằng, trong quá trình điều khiển từ xa (teleop), tôi sợ khối gỗ sẽ rơi khỏi kẹp nên đã kẹp rất chặt. Nhưng điều đó đã tạo ra quá nhiều lực lên động cơ kẹp của robot follower, khiến nó dần bị mài mòn.

Sau khi tôi thay động cơ mới, chiếc SO-101 của tôi đã hoạt động trở lại!

Để tránh điều này xảy ra lần nữa, tôi đã luyện tập cách kẹp khối gỗ bằng cách nhẹ nhàng giữ đầu kẹp ở khoảng cách bằng chiều rộng của khối. Tôi cũng cấu hình thông số `max_relative_target` của các khớp robot follower để giới hạn vận tốc, giúp ngăn chúng không bị quá tải.

Bài học rút ra: hãy nhẹ nhàng với robot của bạn... và luôn giữ sẵn một số servo dự phòng!

Lần này, tôi nghiêm túc hơn về sự đa dạng dữ liệu:

**Mật độ và độ phủ:** Tăng từ 10 episode mỗi bin lên 25, trực quan hóa vị trí xy để đảm bảo tôi đã lấp đầy các khoảng trống.

**Nhiễu loạn hướng:** Thêm các biến thể xoay (từ -45 đến 45 độ theo trục yaw) vì các khối không phải lúc nào cũng nằm thẳng hàng hoàn hảo trong thực tế. Tôi hy vọng điều này sẽ giúp robot phục hồi sau các lần kẹp thất bại.

**Hành vi kẹp tốt hơn:** Đảm bảo có thêm nhiều ví dụ về việc kẹp từ giữa khối. Cũng thử tiếp cận khối từ các góc độ khác nhau.

*Timelapse quá trình thu thập dữ liệu của tôi...*

Đây là những gì tôi học được sau 3 tuần vật lộn với robot:

Giờ đây khi đã có một robot pick-and-place hoạt động được (phần lớn là vậy), đây là các bước tiếp theo của tôi:

Đôi khi tôi quên mất, khi xem các video demo robot trên mạng, rằng robot trong thực tế rất lộn xộn và chúng hỏng hóc liên tục theo những cách bí ẩn. (Dù tôi làm việc tại một startup robotics và phải đối mặt với sự cố robot cả ngày, lol) Nhưng khi nó cuối cùng hoạt động được—khi bạn thấy robot của mình thành công nhặt một khối gỗ và đặt nó đúng vào nơi bạn muốn—cảm giác thật kỳ diệu!! Tất cả công sức của tôi đều xứng đáng. 🤗

Đó chính là lý do tôi cố gắng ghi lại tất cả trong blog này. Hy vọng điều này có thể giúp ích cho ai đó mới bắt đầu với SO-101, LeRo

📎 Nguồn gốc: Hugging Face Blog Xem bài gốc →