32Hz在線動作預測)
VLA 動作預測在具身智能里已經(jīng)是高頻詞但很多項目把 VLA 與 LLM 直接畫等號。TurboVLA 用 0.2B 參數(shù)在 RTX 4090 上跑到 32Hz 在線動作預測正好把這個誤區(qū)擺到臺面上動作預測不等于必須經(jīng)過幾十B甚至上百B的 LLM。文章圍繞一張圖、一段指令、一個動作頭的鏈路拆解 VLA 為什么默認綁定了 LLM、輕量模型如何繞開它、以及在一臺 RTX 4090 上把推理頻率做到 32Hz 的工程路徑。這里先給結(jié)論動作預測的本質(zhì)是從“視覺觀測 語言指令”到“控制動作”的條件映射。LLM 是這類映射的一種實現(xiàn)方式不是唯一實現(xiàn)方式。TurboVLA 走的是一條更接近傳統(tǒng)機器人策略的路線用小參數(shù)模型壓縮視覺和語言特征直接產(chǎn)出動作。它的參數(shù)規(guī)模只有 0.2B單看部署體積和推理延遲明顯更適合在線閉環(huán)控制。1. 先搞清楚 VLA 動作預測為什么常用 LLM1.1 VLA 的任務鏈路VLA 全稱 Vision-Language-Action視覺-語言-動作模型。輸入通常是相機圖像或者視頻片段再加上一條自然語言指令例如“把左側(cè)的紅色方塊放到托盤里”。輸出是一段動作序列可能是機械臂末端位姿、關(guān)節(jié)角度、速度指令也可能是機器人底盤線速度與角速度。動作預測在具身智能里的地位很特殊。它不像圖像分類那樣只輸出一個類別也不像文本生成那樣只輸出 token 序列。它需要連續(xù)、實時、可落地的控制信號。也就是說網(wǎng)絡不僅要“知道”應該做什么還要在硬實時限制內(nèi)給出低延遲的動作。這條鏈路在傳統(tǒng)機器人流程里被拆成感知、規(guī)劃、控制。感知負責識別物體規(guī)劃負責生成軌跡控制負責跟蹤執(zhí)行。VLA 想做的事情是把感知和規(guī)劃壓縮成一個端到端網(wǎng)絡讓圖像和指令直接映射到動作。這樣做的收益是省去大量手寫規(guī)則也能利用預訓練視覺模型和語言模型的知識。1.2 LLM 在 VLA 中扮演什么角色很多 VLA 會把一個 LLM 作為骨干網(wǎng)絡。原因是語言指令具有開放性機器人需要理解“把紅色方塊放過去”和“把那個紅色的東西移到另一邊”這種同義表達。LLM 經(jīng)過大規(guī)模語料預訓練具備很好的語義對齊能力可以直接把指令變成 token 序列再與視覺 token 一起融合輸出文本形式的動作 token 或者數(shù)值動作參數(shù)。這種設計在 benchmark 上效果很好尤其是需要開放詞表、多步推理、復雜語義消解的任務。LLM 的注意力機制可以在長上下文里把圖像、歷史狀態(tài)和指令關(guān)聯(lián)起來。因此早期 VLA 論文幾乎都會選擇 7B、13B 甚至更大的語言模型作為推理主干。從工程視角看這種架構(gòu)也能復用成熟的 LLM 推理棧。圖像編碼器輸出 vision token文本 tokenizer 輸出 text token拼在一起送進 transformer decoder最后接動作頭。訓練時可以用現(xiàn)成的語言建模監(jiān)督推理時可以用 vLLM、TensorRT-LLM、llama.cpp 等工具加速。1.3 大 LLM 在在線動作預測里的代價在線動作預測和離線生成有本質(zhì)區(qū)別。離線場景可以接受幾百毫秒延遲在線閉環(huán)控制必須匹配機器人的控制頻率。以常見的 30Hz 控制周期為例每周期只有 33ms。這個時間要覆蓋相機曝光、圖像傳輸、預處理、模型推理、后處理、通信下發(fā)。留給模型推理的時間通常不到 25ms。7B 模型在 FP16 下權(quán)重約 14GB即使能放進 24GB 顯存單次 forward 往往也要 40ms 以上。若用 batch size 1 再疊加 Python 調(diào)度和預處理很容易超過 100ms。算上 KV cache、視覺編碼器和動作頭顯存壓力也很大。更不要說 70B 級別模型單卡基本無法部署需要多卡或分布式延遲會進一步放大。這就是 TurboVLA 這類輕量模型出現(xiàn)的原因不是要替代所有 LLM-based VLA而是解決“在線動作預測需要實時響應”這一工程痛點。當任務空間是有限閉集、指令模板相對固定、環(huán)境變化可控時0.2B 參數(shù)足以完成從觀測到動作的映射。注意如果機器人任務需要開放世界的常識推理比如用戶隨意說“把地面上最奇怪的東西拿開”輕量模型會非常吃力。輕量 VLA 的適用前提是任務邊界清晰指令可以被模板化和蒸餾。1.4 關(guān)鍵判斷動作預測需要的是條件映射不是語言生成很多團隊設計 VLA 時習慣性先選 LLM是因為“VLA 里的 L 是 Language Model”。但仔細看動作預測的目標大部分場景并不要求模型生成自然語言只要求從指令中抽出約束條件抓哪個物體、放到哪里、避開什么。這些約束可以編碼成低維向量不需要以 token 形式一步步生成。所以輕量 VLA 的核心改動是用一個小文本編碼器替代 LLM 的生成式解碼器。模型只在特征空間對齊視覺和語言不經(jīng)過“逐個 token 輸出”的過程。少掉的這一大段解碼計算正是 32Hz 能跑起來的關(guān)鍵。2. TurboVLA 的輕量思路0.2B 參數(shù)如何撐起動作預測2.1 從“大模型當大腦”到“小模型當策略頭”TurboVLA 的路線可以理解成把 VLA 拆成“眼睛 小腦 動作肌”。視覺編碼器負責提取圖像特征文本編碼器負責提取指令特征融合層負責對齊最后的動作頭輸出連續(xù)動作。整個網(wǎng)絡不生成文本 token因此沒有自回歸解碼過程。這是一種典型的非自回歸策略模型延遲天然比 LLM 低。這里要說明TurboVLA 沒有公開詳盡的網(wǎng)絡結(jié)構(gòu)文檔時下面描述的是復現(xiàn)輕量 VLA 的通用模塊。實際模型可能調(diào)整了視覺編碼器大小、融合層深度、動作頭維度但整體思路是一致的把預算集中在特征融合和動作預測上而不是放在語言生成上。0.2B 參數(shù)在今天的視覺模型里算很小。一個 ViT-L 大約 300M 參數(shù)加上小型文本編碼器和一個 transformer 融合層正好接近這個規(guī)模。視覺 backbone 可以預訓練文本編碼器可以用小型 sentence 模型或輕量 BERT動作頭通常只有幾百萬參數(shù)。這種組合在推理時不會產(chǎn)生巨大計算量也因此能在 RTX 4090 上跑出高頻率。2.2 模塊分配視覺、文本、融合與動作頭一個可運行的輕量 VLA 推理結(jié)構(gòu)大致如下視覺編碼器輸入 224x224 或更高分辨率圖像輸出空間特征圖。文本編碼器輸入指令文本輸出一個指令 embedding或者一小段 token 特征。融合模塊將視覺特征和文本特征通過 cross-attention 或 concatenation 結(jié)合。動作頭從融合特征回歸動作向量例如 7 維關(guān)節(jié)角度、6 維位姿、速度等。參數(shù)分配通常視覺編碼器占大頭融合 transformer 占一部分文本編碼器和動作頭較小。這樣做的好處是視覺特征可以復用通用預訓練權(quán)重不需要像 LLM-based VLA 那樣完整跑一遍大語言模型解碼器。訓練時可以用大 VLA 模型蒸餾出偽標簽也可以用真實機器人軌跡數(shù)據(jù)做行為克隆Behavior Cloning。如果數(shù)據(jù)量充足直接端到端監(jiān)督學習就能收斂。由于輸出不是 token 而是連續(xù)向量訓練損失可以選擇 L1 平滑損失或 MSE也可以配合動作分桶離散化變成分類問題。2.3 為什么 0.2B 也能有不錯的語義理解小模型能完成語義指令理解聽起來反直覺。關(guān)鍵在于任務邊界。工業(yè)分揀、桌面抓取、固定工作區(qū)操作這些場景語言指令往往來自固定的命令集例如“抓紅色方塊”“把杯子放到盤子上”“按下開關(guān)”。這類指令沒有開放世界的復雜指代一個輕量文本編碼器就能提取出“目標物體”“目標位置”這兩個必要約束。如果讓 0.2B 模型去處理隨機口語、長對話、多輪指代它一定不如 7B 模型。但 TurboVLA 的目標是動作預測不是通用對話。它只需要理解當前這一條指令并把指令中的語義約束和視覺特征對齊。把不需要的能力去掉模型自然可以做得小。此外0.2B 模型可以結(jié)合工程手段彌補能力不足。比如使用固定的指令模板把“紅色方塊”解析成屬性標記或者用少量提示詞嵌入讓文本編碼器只關(guān)注顏色、類別、位置三個槽位。這些手段在真實機器人項目里非常常見并不影響 VLA 的端到端特性。2.4 與 LLM-based VLA 的架構(gòu)差異對比維度LLM-based VLATurboVLA 這類輕量 VLA語言模塊7B 及以上 LLM輕量文本編碼器輸出形式動作 token 或語言動作連續(xù)動作向量是否自回歸生成通常是否參數(shù)量數(shù) B 到上百 B0.2B 級別單幀推理延遲高通常數(shù)十毫秒起步低可壓縮到幾毫秒到二三十毫秒部署設備需要大顯存 GPU單張消費級顯卡即可開放語義理解強有限依賴指令模板在線閉環(huán)控制困難更容易滿足 30Hz 周期這張表不是要證明一個方案優(yōu)于另一個。兩者的取舍在于“能力”和“時延”之間的平衡。TurboVLA 選擇了犧牲開放語義換取在線動作預測的實時性。3. 在 RTX 4090 上把推理預算壓到 33ms 以內(nèi)3.1 32Hz 到底意味著多緊張32Hz 對應每幀 31.25ms。在機器人控制里這個頻率不僅要跑模型推理還要包含一整套感知到控制的流程。按常見預算拆分相機采集和傳輸可能需要 3-5ms圖像預處理需要 1-3ms模型推理需要 15-25ms后處理和動作平滑需要 1-2ms通信下發(fā)需要 1-3ms。這樣算下來已經(jīng)沒有多少余量。很多團隊在實驗室里測模型 forward 時間覺得 30Hz 沒什么問題。等接入真實機器人后頻率立刻掉到 10Hz原因就是沒有把整條流水線納入計時。TurboVLA 能在 RTX 4090 上跑出 32Hz說明它至少已經(jīng)從模型、推理框架、調(diào)度方式三個層面做了優(yōu)化而不只是模型參數(shù)量小。3.2 環(huán)境要求和版本建議要復現(xiàn)或者驗證輕量 VLA 的在線動作預測建議準備如下環(huán)境項目推薦配置說明GPURTX 4090 或同級別 24GB 顯卡顯存不是瓶頸性能充裕操作系統(tǒng)Ubuntu 22.04 或 Windows 11生產(chǎn)環(huán)境優(yōu)先 LinuxCUDACUDA 12.1 或更高對接 PyTorch 和 TensorRTPythonPython 3.10 或 3.11兼顧 PyTorch 兼容性PyTorch2.1 及以上支持 CUDA Graph 和編譯優(yōu)化推理框架TensorRT 8.6 以上或 ONNX Runtime GPU需要進一步壓延遲時使用相機RGB 工業(yè)相機或 USB 相機需要配合 V4L2 / OpenCV這里沒有寫死版本因為不同 PyTorch 版本對應的 CUDA 版本不同。落地前先確認本機 CUDA 驅(qū)動版本再裝對應 PyTorch避免后面出現(xiàn) “CUDA driver too old” 這類基礎問題。3.3 模型推理優(yōu)化為什么小模型還要做 TensorRT0.2B 模型在 PyTorch 的 eager 模式下單次 forward 可能也能跑到 20ms 到 30ms但離穩(wěn)定的 32Hz 有風險。原因是 PyTorch 默認有大量 kernel 啟動開銷和 Python 操作特別是 batch size 1 時GPU 利用率并不高。這時真正的瓶頸往往不是浮點運算量而是 kernel launch 和 tensor 拷貝。TensorRT 會把網(wǎng)絡編譯成優(yōu)化好的 kernel 圖減少層間調(diào)度和臨時顯存分配。對結(jié)構(gòu)化固定的小模型來說收益非常明顯有可能把推理時間從 25ms 降到 10ms 以內(nèi)。CUDA Graph 也能在 PyTorch 里達到類似效果把一系列 kernel 捕獲成一張圖降低 launch 開銷。實際選擇時可以先用 PyTorch eager 模式跑通功能再用 torch.compile 或 CUDA Graph 查看收益最后才考慮用 TensorRT 導出。一步到位上 TensorRT 的風險是導出過程遇到不支持算子排錯成本高。3.4 在線流水線設計在線動作預測的推理循環(huán)不能寫成“圖像來了才開始算”的同步模式。推薦使用生產(chǎn)者-消費者結(jié)構(gòu)相機線程負責取幀預處理線程負責圖像縮放和歸一化推理線程負責模型 forward控制線程負責下發(fā)動作。多線程之間通過隊列或緩存數(shù)組傳遞數(shù)據(jù)避免圖像數(shù)據(jù)反復拷貝。CPU 和 GPU 之間可以使用 pinned memory 和異步 H2D 拷貝。圖像預處理盡量在 GPU 上完成例如使用 Torch 的 resize 或 TensorRT 內(nèi)置 preprocess 層。Python 的全局解釋鎖會影響多線程性能必要時用 multiprocessing 或者把推理放到 C 封裝里。注意32Hz 是端到端頻率不是模型 forward 頻率。驗證時必須給完整流水線加時間戳統(tǒng)計連續(xù)多幀的幀間隔而不是只測一次 model.forward() 的耗時。4. 最小在線推理循環(huán)從相機幀到動作指令4.1 一個可運行的工程結(jié)構(gòu)下面的結(jié)構(gòu)用于演示輕量 VLA 在線動作預測的框架不限定為 TurboVLA 官方實現(xiàn)。實際項目可以按自己的模型接口替換。turbovla-demo/ ├── config.yaml ├── model.py ├── inference.py ├── smoother.py ├── utils.py └── weights/ └── turbovla_0_2b.ptconfig.yaml保存模型路徑、圖像尺寸、指令模板、控制頻率等參數(shù)。model.py定義網(wǎng)絡結(jié)構(gòu)。inference.py是主入口負責啟動相機線程和推理循環(huán)。smoother.py對動作輸出做低通濾波和限幅。weights/存放模型權(quán)重實際部署時不要把模型文件提交到代碼倉庫。這個結(jié)構(gòu)足夠小也方便后面加入 C 推理模塊或 TensorRT engine。4.2 配置文件與模型加載配置文件的作用是把環(huán)境相關(guān)的參數(shù)從代碼中分離出來。調(diào)整圖像尺寸或控制頻率時不需要改動 Python 代碼。model: weight_path: weights/turbovla_0_2b.pt image_size: 224 fp16: true use_cuda_graph: true instruction: template: move the {object} to the {placeholder} object: red cube placeholder: tray robot: action_dim: 7 control_freq: 30 action_scale: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] camera: device_id: 0 width: 640 height: 480加載模型時有一個容易忽視的點權(quán)重文件里的 key 要和網(wǎng)絡結(jié)構(gòu)完全對上。很多 VLA 推理報錯來自預訓練權(quán)重保存了module.前綴而加載時沒有做strip_prefix。建議加載后先打印模型參數(shù)數(shù)量核對是否接近 0.2B再測試一個假輸入。import torch from model import TurboVLA model TurboVLA.from_config(config.yaml) state torch.load(weights/turbovla_0_2b.pt, map_locationcpu) if all(k.startswith(module.) for k in state.keys()): state {k[len(module.):]: v for k, v in state.items()} model.load_state_dict(state) model.half().cuda().eval()如果加載后 forward 報 shape 不匹配優(yōu)先檢查輸入圖像尺寸和指令編碼維度這是最常見的兩類錯誤。4.3 推理循環(huán)代碼在線推理循環(huán)的核心是控制幀間隔。下面這段代碼用固定頻率控制循環(huán)并統(tǒng)計端到端延遲。import time import cv2 import torch import numpy as np from smoother import ActionSmoother from utils import preprocess_frame, encode_instruction model TurboVLA.from_config(config.yaml).cuda().eval() model.half() cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) smoother ActionSmoother(alpha0.4) instr_feat encode_instruction(move the red cube to the tray).cuda() period 1.0 / 30.0 next_cycle time.perf_counter() fps_buffer [] with torch.inference_mode(): while True: ret, frame cap.read() if not ret: continue start time.perf_counter() frame_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) obs preprocess_frame(frame_rgb, size224).cuda() action model.predict(obs, instr_feat) action smoother.filter(action.cpu().numpy()) action np.clip(action, -1.0, 1.0) # 在這里把 action 發(fā)送給機器人控制器 dt time.perf_counter() - start fps_buffer.append(dt) if len(fps_buffer) 60: avg_dt np.mean(fps_buffer) print(favg latency: {avg_dt * 1000:.1f} ms, fps: {1.0 / avg_dt:.1f}) fps_buffer.clear() next_cycle period sleep_time next_cycle - time.perf_counter() if sleep_time 0: time.sleep(sleep_time)這段代碼有兩個要點。一是用torch.inference_mode()替代torch.no_grad()關(guān)閉梯度跟蹤減少內(nèi)存開銷。二是固定頻率循環(huán)用next_cycle累加而不是每次用time.sleep(period)從零開始這樣能避免誤差堆積。4.4 動作平滑與安全過濾低延遲模型容易輸出高頻抖動尤其是在真實機器人上動作序列含有噪聲。一個簡單的一階低通濾波器就能改善大部分問題。class ActionSmoother: def __init__(self, alpha0.4, action_dim7): self.alpha alpha self.smooth np.zeros(action_dim, dtypenp.float32) def filter(self, action): self.smooth self.alpha * action (1.0 - self.alpha) * self.smooth return self.smooth.copy()alpha 越大輸出越跟手但越抖動alpha 越小輸出越平滑但滯后越明顯。推薦開始時設 0.4再根據(jù)機器人實際表現(xiàn)調(diào)整。除了濾波還應該做 NaN/Inf 檢查一旦發(fā)現(xiàn)異常就停止下發(fā)避免機械臂執(zhí)行錯誤指令。4.5 驗證是否真的跑到 32Hz驗證方法不是看打印出的單次 forward 耗時而是統(tǒng)計連續(xù) 5 秒到 10 秒的真實幀間隔。比較好的做法是在相機幀進入循環(huán)和動作下發(fā)完成時分別記錄時間戳計算中位數(shù)和 95 分位延遲。如果平均延遲是 28ms但 95 分位到了 60ms說明存在周期性卡頓比如相機的幀緩沖抖動或 CUDA graph 重建。在線控制關(guān)注的不只是平均頻率更關(guān)注最大抖動因為任何一幀超時都會導致控制周期跳變影響機器人穩(wěn)定性。5. 關(guān)鍵參數(shù)與性能調(diào)優(yōu)精度、batch、CUDA Graph 怎么選5.1 FP16、BF16、INT8 的取舍0.2B 模型在 FP32 下就能跑但為了延遲優(yōu)化通常使用半精度或量化。FP16 和 BF16 都能減半顯存提升訪存效率但兩者數(shù)字范圍不同。FP16 在小數(shù)值場景容易出現(xiàn)精度溢出BF16 保留更大動態(tài)范圍。如果模型訓練時用的是