Bạn đã bao giờ thấy robot nhấn đúng nút 3 lần rồi dừng lại chính xác chưa? Không phải vì nó nhìn thấy màn hình đếm số, mà vì nó nhớ từng lần tay chạm vào nút qua tín hiệu lực. Đó chính xác là vấn đề mà FM-VLA giải quyết — và nó làm được điều đó với chỉ 3ms overhead thêm vào inference latency của model gốc.
Paper FM-VLA: Force-based Memory for Vision-Language-Action Models in Contact-Rich Manipulation của nhóm nghiên cứu từ Tsinghua University và Microsoft Research (tháng 7/2026) đặt ra một câu hỏi căn bản: tại sao chúng ta chỉ cho VLA nhớ qua visual memory, trong khi robot có cả sensor lực?
Vấn đề gốc rễ: VLA là robot có tâm hồn goldfish
Hầu hết các VLA model hiện tại — từ RT-2, π0, π0.5, cho đến OpenVLA — đều hoạt động theo nguyên lý Markovian: mỗi bước, model chỉ nhìn vào trạng thái hiện tại (ảnh + câu lệnh ngôn ngữ) để ra quyết định, hoàn toàn không nhớ gì về quá khứ.
Với đa số task đơn giản như "pick and place", cách tiếp cận này hoạt động tốt. Nhưng thực tế sản xuất và dịch vụ đòi hỏi nhiều hơn thế:
- Đếm hành động lặp lại: Nhấn nút 3 lần rồi dừng. Nếu không có memory, robot sẽ cứ nhấn mãi hoặc dừng sai.
- Theo dõi tiến trình ẩn: Lau bát 2 vòng. Lần lau thứ nhất và lần lau thứ hai trông y hệt nhau qua camera.
- Tìm kiếm trong môi trường bị che: Nhấc 2 cái cốc úp ngược để tìm khối gỗ ẩn. Sau khi đặt cốc đầu xuống, cảnh quay y chang cảnh ban đầu — không có dấu hiệu visual nào cho biết cái cốc đó đã được kiểm tra.
Các task này đều không-Markovian — quyết định đúng đắn phụ thuộc vào lịch sử tương tác, không chỉ trạng thái hiện tại.
Tại sao visual memory chưa đủ?
Một số paper gần đây như MemoryVLA và MEM đã thêm visual memory cho VLA bằng cách lưu trữ các frame ảnh quá khứ. Nhưng cách tiếp cận này có 2 điểm yếu nghiêm trọng:
Thứ nhất: chi phí tính toán. Mỗi frame ảnh là hàng trăm token. Lưu K=5 frame quá khứ tăng inference latency thêm 39ms (60.7ms → 99.8ms); K=16 frame tăng thêm 129ms. Đây là vấn đề lớn với robot real-time.
Thứ hai: visual ambiguity. Khi bạn nhấn nút với hành trình chỉ vài mm, ảnh gần như không thay đổi sau mỗi lần nhấn. Visual memory không thể phân biệt "đã nhấn 1 lần" với "đã nhấn 2 lần" vì về mặt pixel, hai trạng thái này giống nhau đến 99%.
Trong thí nghiệm của FM-VLA, π-MEM (visual memory baseline mạnh nhất) chỉ đạt 33.3% success rate trên task "nhấn nút N lần" — tệ hơn cả không có memory trong một số điều kiện. FM-VLA đạt 72.2% trên cùng task đó.
Insight cốt lõi: lực tiếp xúc là memory tự nhiên
Khi robot nhấn nút, tín hiệu lực từ F/T sensor tăng vọt rồi giảm — rõ ràng và nhất quán hơn bất kỳ thay đổi pixel nào. Mỗi lần tiếp xúc để lại "dấu vân tay" trong chuỗi lực, không phụ thuộc vào ánh sáng, camera angle, hay màu sắc vật thể.
FM-VLA khai thác insight này: thay vì nhớ ảnh, model nhớ lịch sử lực/torque từ F/T sensor gắn trên cổ tay robot. Chuỗi lực này được nén thành một vài Force Memory Tokens nhỏ gọn, sau đó được "tiêm" vào action expert của VLA để điều hướng quyết định.
Kiến trúc FM-VLA

FM-VLA xây dựng trên nền π0.5 (Physical Intelligence) — một VLA gồm VLM backbone (PaliGemma + SigLIP) kết hợp flow-matching action expert. Kiến trúc bổ sung của FM-VLA gồm 3 phần:
1. Xử lý tín hiệu lực thô
Cảm biến F/T ở 100Hz sinh ra tín hiệu nhiễu nhiều. Trước khi đưa vào encoder, lực thô được lọc qua:
- EMA smoothing nhân quả:
f̃_τ = α·f_τ + (1-α)·f̃_{τ-1}— loại bỏ nhiễu cao tần, giữ lại các sự kiện tiếp xúc (onset và peak). - Randomized noise pre-padding: Trong lúc training, thêm tiền tố ngẫu nhiên vào đầu chuỗi lực để mô hình không học shortcut từ độ dài chuỗi (ví dụ: "chuỗi ngắn = sớm trong episode"). Tắt augmentation này ở inference.
2. Force-VAE encoder (Perceiver-IO)
Đây là phần trái tim của FM-VLA. Một Variational Autoencoder (VAE) được huấn luyện để nén toàn bộ lịch sử lực của một episode thành K = 8 latent token (mỗi token là vector d_z chiều).
Kiến trúc VAE dùng Perceiver-IO — cho phép xử lý chuỗi có độ dài tùy ý (episode ngắn 10s hay dài 60s đều được) thành cùng số token cố định. Quá trình:
- Lực tại mỗi timestep được normalize theo phân vị toàn dataset
- Project qua input MLP + thêm Fourier positional encoding
- Encoder cross-attention: 8 learnable query token "hút" thông tin từ chuỗi lực → posterior parameters (μ_k, log σ²_k)
- Sample:
z_k = μ_k + σ_k ⊙ ε_k, ε~N(0,I) - Decoder reverse quá trình để reconstruct lại chuỗi lực
Objective huấn luyện: masked ELBO với free-bits KL để tránh posterior collapse:
# Pseudo-code: Force-VAE loss
L_recon = masked_mse(f_hat, f, mask) # only over valid (non-padded) frames
L_kl = sum(max(KL_per_dim, lambda_free_bits)) / (K * d_z)
L_vae = L_recon + beta * L_kl
Tại sao VAE mà không phải GRU hay Q-Former?
- GRU bị vanishing gradient khi chuỗi 100Hz dài (ví dụ task 60s = 6000 timestep)
- Q-Former overfits vào peak tức thời thay vì structure dài hạn
- VAE ép model học latent space liên tục, có cấu trúc — dễ cho action expert khai thác
So sánh trong ablation: FM-VLA(GRU) = 33.3%, FM-VLA(Q-Former) = 57.4%, FM-VLA(VAE) = 83.3%.
3. Short state history token
Chỉ có force memory thôi chưa đủ. Trước khi tiếp xúc, robot cần biết "tay đang ở đâu" để di chuyển chính xác. FM-VLA thêm một state history token từ cửa sổ ngắn joint position gần nhất (covering ~1 giây).
Cửa sổ joint-state S_t ∈ R^{W×d_s} (W frames, d_s = 16 cho bimanual 7-DoF + 2 gripper) được flatten và project qua 1 linear layer (không cần pretraining) thành 1 token duy nhất.
4. Memory Token Injection
Trong inference, layout của action expert:
[noisy action tokens] || [Z_f^(1), ..., Z_f^(8)] || [z_s]
Force tokens và state token được append sau noisy action tokens, giữ nguyên RoPE positions của base model (tránh phá vỡ pretrained positional encoding).
Tổng số token bổ sung: 9 token (8 force + 1 state). So sánh: π-MEM với K=5 frame thêm hàng trăm visual token.
Pipeline huấn luyện 2 giai đoạn
Stage 1: Force-VAE Pretraining (task-agnostic)
# Huấn luyện VAE trên wrench histories của TẤT CẢ task
# Không cần label gì — chỉ cần reconstruct lực
python train_force_vae.py \
--data_root ./data/wrench_histories \
--tasks task1 task2 task3 \
--n_latents 8 \
--latent_dim 64 \
--beta 0.1 \
--free_bits_lambda 0.5 \
--epochs 100 \
--batch_size 32
Lưu ý quan trọng: Huấn luyện chung trên tất cả task với inverse-frequency sampling — task ít demo được sample nhiều hơn để không bị dataset lớn lấn át. Ví dụ: Task 1 (200 demo) và Task 3 (200 demo) được boost so với Task 2 (350 demo).
Stage 2: VLA Fine-tuning
Sau khi có Force-VAE, đóng băng encoder và fine-tune VLA:
python finetune_vla.py \
--base_model pi0.5 \
--force_vae_ckpt ./checkpoints/force_vae_best.pt \
--data_root ./data/demonstrations \
--n_force_tokens 8 \
--state_window_size 15 \
--lr 1e-4 \
--epochs 50 \
--tasks task1 task2 task3
Các component được train trong Stage 2:
- VLM encoder (PaliGemma + SigLIP) — unfreeze
- Flow-matching action expert — unfreeze
- Short state projector
Proj_ψ— unfreeze (zero-init) - Wrench latent projector
W_f— unfreeze (zero-init) - Force-VAE encoder — frozen (chỉ dùng posterior mean μ_f, không sample)
Objective vẫn là rectified flow matching của π0.5, chỉ thêm Z_f và z_s vào điều kiện:
# Flow matching loss với force + state conditioning
v_pred = action_expert(a_k, timestep=k, context=c_t,
force_tokens=Z_f, state_token=z_s)
loss = mse(v_pred, epsilon - a_0)
Thu thập dữ liệu: Teleop VR trên AgiBot G1
FM-VLA được test trên AgiBot G1 — robot humanoid bimanual với 2 cánh tay 7-DoF và 2 gripper 1-DoF. Điểm đặc biệt: mỗi cổ tay có 6-axis wrench sensor đo lực 3 trục + torque 3 trục ở 100Hz.
Dữ liệu thu thập qua giao diện VR teleop:
- Task 1 (Tìm khối dưới 2 cốc): 200 demo
- Task 2 (Nhấn nút N lần): 350 demo
- Task 3 (Lau bát N lần): 200 demo
Tất cả demo đều lưu cả RGB (3 camera: đầu + 2 wrist), joint states, và wrist wrench readings đồng bộ.
Nếu bạn muốn setup tương tự trên robot khác, điều kiện cần thiết:
- F/T sensor 6-axis trên end-effector (hoặc wrist)
- Sampling rate ≥ 50Hz (100Hz tốt hơn)
- Đồng bộ timestamp giữa F/T sensor và camera
Tín hiệu lực trông như thế nào?

Mỗi task có "signature" lực riêng biệt:
- Task 1 (tìm khối): 2 spike rõ ràng tương ứng 2 lần nhấc cốc
- Task 2 (nhấn nút): N spike nhọn, ngắn (vài ms mỗi lần nhấn)
- Task 3 (lau bát): N chu kỳ sóng tương ứng N vòng lau
VAE học cách mã hóa pattern này vào 8 latent token mà không cần nhãn nào. Đây là unsupervised temporal structure learning.
Kết quả thực nghiệm
So sánh với baselines
| Method | Cups (%) | Buttons (%) | Wipe (%) | Trung bình (%) |
|---|---|---|---|---|
| π0.5 (no memory) | 72.2 | 11.1 | 0.0 | 27.8 |
| TA-VLA (short force window) | 50.0 | 11.1 | 5.6 | 22.2 |
| π-MEM (visual memory) | 77.8 | 33.3 | 50.0 | 53.7 |
| FM-VLA (ours) | 100.0 | 72.2 | 77.8 | 83.3 |
Một vài điểm đáng chú ý:
- TA-VLA thậm chí tệ hơn base: Cửa sổ lực ngắn tạo ra nhiễu cho action prediction, không giúp được gì cho memory. Thêm thông tin sai cách còn hại hơn không thêm.
- Visual memory thất bại hoàn toàn ở Buttons (33.3%): Đúng như dự đoán — nhấn nút không để lại dấu vết visual.
- FM-VLA đạt 100% trên Cups: Đây là task hybrid — cần cả spatial reasoning (nhớ cup nào đã nhấc) lẫn force feedback. Kết hợp force + state history giải quyết hoàn hảo.
Demo task chi tiết
Hiệu quả inference
| Method | Latency (ms) | Tăng thêm (ms) |
|---|---|---|
| π0.5 (base) | 60.7 ± 0.3 | — |
| π-MEM (K=5 frames) | 99.8 ± 0.4 | +39.1 |
| π-MEM (K=16 frames) | 190.0 ± 1.0 | +129.3 |
| FM-VLA (ours) | 64.0 ± 0.4 | +3.3 |
9 token vs hàng trăm visual token — đó là lý do overhead của FM-VLA nhỏ đến vậy. Trên RTX 4090, chạy thực tế ở 15+ Hz hoàn toàn khả thi.
Ablation studies: Mổ xẻ từng quyết định thiết kế
Modality: nhớ gì?
| Cấu hình | Cups | Buttons | Wipe | TB |
|---|---|---|---|---|
| Force only | 55.6 | 0.0 | 22.2 | 25.9 |
| State only | 100.0 | 11.1 | 11.1 | 40.7 |
| Force + State (FM-VLA) | 100.0 | 72.2 | 77.8 | 83.3 |
- Force only: Thiếu state history → hành vi bất ổn trước khi tiếp xúc (không biết tay đang ở đâu)
- State only: Hoàn hảo cho Cups (spatial navigation), nhưng không đếm được contact events
- Kết hợp: Tốt nhất cả hai chiều
Số lượng token: K = ?
K được test ở {4, 8, 16, 32}:
- K=4: Nút thắt cổ chai thông tin — 77.8% trên Wipe
- K=8: Tối ưu — 77.8% (peak)
- K=16: Giảm performance — quá nhiều token làm vượt context window của pretrained action expert (π0.5 thấy tối đa ~50 token lúc pretraining)
- K=32: Phá vỡ action generation
Đây là bài học thực tế quan trọng: không phải nhiều token là tốt hơn. Phải tôn trọng constraint của base model.
Cài đặt và chạy FM-VLA
Code đang được chuẩn bị release tại github.com/qft-333/FM-VLA. Trong khi chờ, bạn có thể setup môi trường tương thích:
# Clone repo (khi code release)
git clone https://github.com/qft-333/FM-VLA.git
cd FM-VLA
# Cài dependencies (tương tự pi0.5 + thêm wrench processing)
conda create -n fmvla python=3.10
conda activate fmvla
pip install torch==2.4.0 torchvision
pip install transformers accelerate
pip install einops perceiver-pytorch
# Nếu dùng AgiBot G1 với ROS 2
sudo apt install ros-jazzy-ros-base
pip install rclpy sensor_msgs_py
Data format cần chuẩn bị
# Mỗi demo episode cần lưu dưới dạng:
{
"images": {
"head": [...], # list of H×W×3 arrays
"left_wrist": [...],
"right_wrist": [...]
},
"wrench": {
"left": [...], # T×6 array [Fx, Fy, Fz, Tx, Ty, Tz] at 100Hz
"right": [...]
},
"joint_states": [...], # T×16 array [q_left×7, gripper_left, q_right×7, gripper_right]
"actions": [...], # T×16 array
"language": "press the button 2 times"
}
Inference với model đã train
import torch
from fm_vla import FMVLA, WrenchHistory
# Load model
model = FMVLA.from_pretrained("./checkpoints/fmvla_final")
model.eval()
# Khởi tạo wrench history buffer
wrench_buf = WrenchHistory(ema_alpha=0.3)
# Inference loop
for obs in robot.stream():
# Cập nhật wrench history
wrench_buf.update(obs["wrench"]) # 6D wrench reading
# Chạy FM-VLA
with torch.no_grad():
action = model.predict(
images=obs["images"],
language=task_instruction,
wrench_history=wrench_buf.get_history(),
joint_states=obs["joint_states"]
)
robot.execute(action)
So với các VLA thông thường sử dụng force-conditioned approach như FD-VLA, FM-VLA nổi bật ở chỗ lực không chỉ dùng để sửa lỗi thực thi mà còn để nhớ lịch sử tương tác.
Giới hạn và hướng phát triển
Giới hạn hiện tại:
- VAE latent space cố định 8 token — với task cực dài (>100 contact events), 8 token có thể chưa đủ
- VAE huấn luyện trên data demo của nhóm nghiên cứu — pretraining trên dataset lực lớn hơn (nhiều robot, nhiều môi trường) có thể cải thiện khả năng generalize
Hướng mở rộng tiềm năng:
- Hierarchical compression: Dùng 2 cấp VAE — cấp thấp capture raw contact, cấp cao capture semantic pattern (ví dụ: "đã nhấn 1 lần")
- Cross-robot transfer: Force pattern của một task tương tự nhau trên nhiều robot → có thể share Force-VAE
- Kết hợp tactile sensor: Không chỉ F/T wrist sensor mà cả tactile skin trên gripper
Trong khi chờ code release, Pelican-VLA π0.5 tutorial cho bạn thấy cách fine-tune π0.5 base model mà FM-VLA xây dựng trên đó.
Tổng kết
FM-VLA giải quyết một vấn đề thực sự bị bỏ qua trong cộng đồng VLA: manipulation không-Markovian — những task đòi hỏi nhớ lịch sử tương tác qua nhiều bước. Insight cốt lõi đơn giản nhưng mạnh mẽ: force sensor sinh ra memory tự nhiên và chính xác hơn camera trong nhiều tình huống contact-rich.
Thiết kế 2 giai đoạn (Force-VAE pretraining → VLA finetuning) cho phép encoder học representation tốt mà không cần nhãn, trong khi chỉ thêm 9 token và 3ms overhead vào base model.
Với VLA manipulation series đang phát triển nhanh và nhiều bài toán bimanual phức tạp hơn trong tương lai, FM-VLA đặt nền tảng cho một thế hệ VLA có bộ nhớ cảm biến — không chỉ nhớ qua mắt, mà còn nhớ qua tay.



