自創機器人的強化學習

從你自己的 CAD 圖,到一隻真的會動的機器人。這份手冊把整條路拆成十一個步驟,每個數字都來自實際的原始碼。

整條路上最重要的一個數字:61

這是 Microduck 策略每 20 毫秒讀進去的觀測向量。它由六段組成,六段全部來自真實機器人身上量得到的東西——這不是巧合,是被刻意設計成這樣的。

IMU 量到的 馬達編碼器回報的 程式自己記得的 你下的指令 3 3 14 14 14 13 gyro gravity joint_pos joint_vel last_action command 14 顆關節角度 14 顆關節轉速 上一次的輸出 3 4 6 移動 頭部姿態 身體姿態
真機的感測器直接量到 程式內部產生,模擬與真機都有

整份手冊的主旨就在這張圖裡。你設計自己的機器人時,這 61 個格子的內容會完全不同——但「每一格在真機上都必須拿得到」這條規則不會變。違反它,模擬裡跑得再漂亮,搬到真機就是一堆空欄位。

適用對象:打算自行設計機構、用 RL 訓練動作的人 標「實測」者取自 microduck_rl 原始碼
00

這份手冊怎麼讀

全篇分成三種資訊,用顏色區分,不用回頭找出處:

實測

直接從 microduck_rl 原始碼讀出來的數字、檔名、指令。這些是事實,不是估計。

踩過的坑

官方開發日誌(CLAUDE.md)裡記錄下來的教訓,每一條都是用實際除錯時間換來的。這類內容最值錢,因為它們通常不會寫在教學文章裡。

通則

強化學習與機器人學的一般原理,換任何機器人都成立。

閱讀順序建議照編號走,不過有三個節點可以先挑著看:

  • 第 05 節(觀測空間)是整份手冊的核心。時間有限的話,至少讀那一節。
  • 第 12 節介紹另一條路——一個完全開源、附 3D 列印檔、而且不用自己畫電路板的版本。如果你的目標是真的動手做,那節比前面十一節更直接。
  • 第 13 節是可勾選的開工檢查清單,勾選狀態會留在你的瀏覽器裡。

最後附了名詞速查表,MJCF、ONNX、BAM 這些縮寫忘記了可以回去查,每個詞都標了在哪一節有完整說明。

01

先分清楚三層:框架、範例、你的專案

這是最容易一開始就走錯的地方。很多人打開 microduck_rl,開始改裡面的檔案、改名字,想把它「改造」成自己的機器人。這個起點是錯的。

你的機器人 你要寫的那一份設定 ← 這才是你的專案 Unitree G1 現成範例 Unitree Go1 現成範例 microduck_rl 鴨子的那一份設定 =你的參考書 mjlab 通用的機器人強化學習框架 MuJoCo 模擬 觀測/動作管線 PPO 訓練迴圈 獎勵工具庫 資產管理
mjlab 是地基,上面站著好幾個機器人。microduck_rl 跟你的專案是「兄弟」,不是「母子」。

所以正確的起點長這樣

✕ 錯的起點

把 microduck_rl 整包複製 → 改名字 → 一個一個改參數,直到它變成我的機器人。

問題:你會一直在跟「鴨子留下來的假設」打架,而且分不清哪些數字是通用的、哪些是鴨子專屬的。

✓ 對的起點

先回答「我的機器人長什麼樣、有哪些感測器、RL 看得到什麼、控制得了什麼」,再把答案寫成一份 mjlab 設定。

microduck_rl 的角色:教科書範例。看它怎麼寫,不要住在裡面。

實測

microduck_rl 目前註冊了 18 個任務,全部共用同一套框架。這 18 個就是你最好的參考範本庫:走路(平地/崎嶇)、走路+跌倒自復原、站起來、坐↔站、地面撿取、踢球、前滾翻,以及六種輪滑相關任務。

02

全景:從你的想法到真機

在鑽進任何一個細節之前,先把整條路看一遍。下面每個方塊都是一個具體的產出物——一個檔案或一份資料,不是抽象概念。

你的 CAD 圖 Onshape / Fusion / SolidWorks onshape-to-robot MJCF 模擬器看得懂的機器人 + STL 網格 機器人模型 連桿・關節・質量・慣量 馬達模型 BAM 或簡化 PD 感測器模型 模擬 IMU 與編碼器 訓練迴圈(在模擬世界裡跑幾千萬步) Observation(61 維) PPO 策略 Action(14 個目標角度) MuJoCo 算下一步 Reward 打分數 export.py → .onnx 真實機器人 伺服馬達・編碼器・IMU,每 20ms 跑一次
虛線框裡是模擬世界,底下橘色框是真實世界。兩者靠 ONNX 檔案銜接——而能不能銜接得上,取決於第 05 節的那份契約。
03

第一步:把 CAD 變成 MJCF

MuJoCo 看不懂你的 SolidWorks 檔案。它需要的是另一種描述,回答這幾個問題:這台機器由哪些零件組成?零件之間怎麼連?哪裡可以轉?多重?重量分布在哪?哪些地方會撞到東西?

這份描述叫 MJCF,本質上就是一個 XML 檔。CAD 描述的是「長什麼樣」,MJCF 描述的是「怎麼動」。

MJCF 必須填滿的六件事

要填什麼意思舉例
連桿剛性的零件本身,以及它的長度與形狀大腿 120mm、小腿 130mm
關節零件之間可以轉動的地方,以及轉軸方向hip_pitchkneeankle
活動範圍每個關節轉得到哪裡,超過就是機構撞死膝蓋 −120° ~ +10°
質量每個連桿多重大腿 48g、小腿 22g
慣量重量「分布」在哪裡,不只是總重見下方說明
碰撞幾何哪些表面會跟地面或自己碰撞腳底板、身體外殼

為什麼慣量不能只填重量

兩根一樣重的棍子,一根重量集中在手握處、一根集中在末端,甩起來的感覺完全不同——後者難甩得多。機器人甩腿、轉頭時,這個差異會直接改變它的動態。只給總重而不給分布,模擬算出來的動作會跟真機對不上。

好消息是:慣量通常不用自己算。CAD 軟體知道每個零件的形狀和材料,可以直接算出來,匯出工具也會一併帶走。

實例

下面是 Microduck 的分件與實際重量。這張圖剛好示範了 MJCF 需要的資訊長什麼樣子——每個色塊是一個連桿,旁邊的公克數就是它的質量。注意頭部總成 189g,是整機最重的單一零件。

Microduck 爆炸圖:15 個結構分件,每件標註顏色與重量,從頭部總成 189g 到頸俯仰件 6g
這張圖來自社群逆向工程專案,不是原廠工程圖。它的用處在於示範「一台機器人要拆成幾個剛體、每個剛體多重」——這正是 MJCF 第一段要寫的東西。
踩過的坑

官方開發日誌寫得很直白:目標高度這類數字,一定要從模擬裡的實際機器人量出來,絕對不要從舊版本沿用。曾經有一個站立目標高度差了 5mm,結果那個目標在物理上根本站不到,團隊卡了好幾天才發現問題不在獎勵函數、而在那個數字本身。

MJCF 實際長什麼樣

MJCF 是 MuJoCo 自己的模型描述格式,本質就是一個 XML 檔(副檔名 .xml)。你在上面看到的 robot_walk.xml 就是一份 MJCF。

它的核心設計是:巢狀結構代表機構連接。零件包在零件裡面,包法就是實際的連接順序。看一段真實寫法就懂了:

<body name="left_thigh" pos="0 0 -0.03">
  <joint name="left_hip_pitch" type="hinge" axis="0 1 0" range="-90 90"/>
  <geom type="capsule" fromto="0 0 0  0 0 -0.06" size="0.008" mass="0.048"/>

  <body name="left_shin" pos="0 0 -0.06">
    <joint name="left_knee" type="hinge" axis="0 1 0" range="-120 10"/>
    <geom type="capsule" fromto="0 0 0  0 0 -0.05" size="0.007" mass="0.022"/>
  </body>
</body>

三個標籤分工很清楚:

  • <body> 一個剛體(連桿)。pos 是它相對於上一層的位置。小腿寫在大腿的 <body> 裡面,就代表小腿長在大腿下面——這個巢狀關係就是運動鏈。
  • <joint> 這個零件怎麼轉。type="hinge" 是鉸鏈(單軸旋轉),axis 是轉軸方向,range 是活動範圍。
  • <geom> 形狀跟質量。它同時負責兩件事:碰撞判定用的形狀,以及這個零件多重。複雜外型會改成 type="mesh" 指向一個 STL 檔。
XML 裡的巢狀結構 實體上的機構 <body name="left_thigh"> <joint name="left_hip_pitch"> 大腿相對於上一層怎麼轉 <geom mass="0.048"> 大腿的形狀與質量 48g <body name="left_shin"> <joint name="left_knee"> 小腿相對於大腿怎麼轉 <geom mass="0.022"> 小腿的形狀與質量 22g </body> </body> 機身 髖關節 left_hip_pitch 大腿 48g 膝關節 left_knee 小腿 22g 腳掌
左邊的縮排層級,就是右邊的機構層級。小腿的 <body> 寫在大腿裡面,實體上小腿就接在大腿下面。
一個很容易搞混的規則

寫在某個 <body> 裡的 <joint>,描述的是「這個零件相對於它上一層」怎麼轉,不是它跟下一層之間。所以大腿的 <body> 裡寫的是髖關節(大腿接到機身的地方),膝關節則寫在小腿的 <body> 裡。第一次讀別人的 MJCF 時,這點最容易看反。

一份完整的 MJCF 除了機器人本身,還會描述整個場景:地面、重力、光源、要踢的球,以及 <actuator>(哪些關節是馬達驅動的)跟 <sensor>(模擬哪些感測器)。

容易踩的小坑

range 的角度單位預設是弧度,除非檔案開頭寫了 <compiler angle="degree"/>。照抄別人的片段時,這個沒對到會讓活動範圍差一個數量級——寫 -120 10 本來想要 −120°~10°,結果變成 −120 弧度(約 −6875°),關節等於完全沒有限制。

如果你熟 ROS,MJCF 的地位相當於 URDF——同一類東西,但 MJCF 是 MuJoCo 專用,能描述 URDF 表達不了的接觸參數、肌腱那些。onshape-to-robot 這類工具兩種格式都匯得出來。

你要用哪套 CAD?這會決定轉檔有多麻煩

Onshape 是一套 CAD 軟體,跟 SolidWorks、Fusion 360 同類型。特別的地方是它完全跑在瀏覽器裡,不用安裝,檔案存在雲端。個人非商業用途有免費方案,但免費版的專案是公開的。

它之所以跟這個專案有關,是因為 Pollen 當初就是用 Onshape 畫 Microduck,然後用 onshape-to-robot 直接匯出成 MJCF。那支工具是專門為 Onshape 寫的——它透過 Onshape 的 API 去讀取零件結構、相對位置、質量,自動組成模擬器要的格式。

用 Onshape 畫

onshape-to-robot 直接可用,連桿層級、關節位置、質量慣量一次帶出來。

代價:要接受雲端 CAD,免費方案的專案是公開的。

用 SolidWorks / Fusion / Creo 畫

匯出 STL,再自己手寫 MJCF 的骨架把連桿層級接起來;或者找對應你那套軟體的轉換工具。

代價:多一段工,關節位置與慣量要自己核對。

兩條路都走得通,差別只在前者有現成的自動化管線。如果你已經熟悉某套桌面 CAD,不必為了這個改用 Onshape——手寫 MJCF 骨架並不難,難的是量測數字要正確。

檔案放哪裡

你自己的機器人應該在 src/<你的套件>/robot/ 底下開一個新資料夾,仿照現有結構擺放。

實測

Microduck 的機器人資料夾裡有好幾個 XML,對應不同用途的模型:robot_walk.xml(走路用,碰撞幾何較精簡,跑得快)、robot_allcollisions.xml(全碰撞,站起來/地面撿取用)、*_rollers.xml(加輪子)、*_backlash.xml(每個關節串一個 ±1° 的被動齒隙關節)。同一台機器人會有多個模型檔,依任務挑用——這是值得抄的架構。

04

第二步:馬達模型

RL 輸出的不是「膝蓋轉到 30 度」這種指令就結束了。中間還有一層:真實的馬達收到目標角度之後,會用多大的力、多快去追這個目標?追得到嗎?負載重的時候會掉速嗎?停住的時候會抖嗎?

這層就是馬達模型。模擬裡如果沒有它,你等於假設馬達是完美的——而真實馬達從來不完美。

30° 時間 → 你下的指令:去 30°(理想馬達就長這樣) 真實伺服實際走出來的樣子 反應延遲 負載重時 爬得比較慢 穩態誤差 摩擦與電壓 下降造成
青色是你下的指令,橘色是真實伺服的反應。模擬器如果只模擬青色那條線,訓練出來的策略就是建立在一個不存在的馬達上。

兩種做法

簡化 PD 控制器

看目前角度跟目標角度差多少,再看目前轉速,算出一個力矩。白話版:「你還差 10 度,那我就用這麼大的力把你推過去。」

適合:剛開始,先讓整套 RL 系統能跑起來。之後再慢慢加真實特性。

BAM(實測馬達模型)

拿真實馬達上測試台,量出它在各種電壓、負載、速度下的實際行為,建成模型放進模擬器。

適合:要真的搬到實機。模擬與真機的落差,有很大一部分來自這裡。

實測

Microduck 用的是 BAM,官方描述為「電壓控制的 XL330 模型,摩擦力由致動器自行計算」。也就是說它模擬的不是理想力矩源,而是「一顆吃 7.4V、有內阻、有摩擦的真實伺服」。如果你也用 XL330,這份模型可以直接沿用;換別顆馬達,就得自己上測試台量一份。

BAM 到底是什麼?是理論還是軟體?

兩者都是:一套建模方法(那些數學),加上實作它的程式碼。

BAM 是 Better Actuator Model 的縮寫,由法國 Rhoban 團隊開發——那是波爾多大學的機器人實驗室,長期參加 RoboCup 人形足球,對「模擬跟真機差在哪」這件事累積了很多經驗。

它的做法是把伺服當成電壓驅動的馬達來建模,而不是理想力矩源。輸入是電壓,模型內部去算摩擦、反電動勢這些,再推出實際輸出的力矩。參數不是憑空設的,是把真實馬達裝上測試台實際量出來的——這也是為什麼官方 repo 裡有 xl330_test_bench 那個治具(一顆馬達加一根配重手臂)。

它不是你會打開的應用程式,而是一個跑在模擬器裡的程式庫。在 microduck_rl 裡,BAM 以致動器類別的形式存在,寫在 src/mjlab_microduck/actuator.py,設定檔 import 進來用,例如 FrictionDRBamActuatorCfg

所以「用 BAM」的實際意思是:在你的設定檔裡指定用這個致動器類別,並給它一組從真實馬達量出來的參數。

踩過的坑

BAM 之下,MuJoCo 原本的關節摩擦欄位 dof_frictionloss 是被歸零的。你如果照一般教學去隨機化這個欄位,程式不會報錯、訓練照跑,但那段設定完全沒有作用——是個安靜的空操作。正確做法是去縮放致動器自己的 friction_scale。這種「不報錯但無效」的陷阱最難抓。

05

第三步:觀測空間——整份手冊最重要的一節

一句話講完:觀測空間裡只能放真實機器人量得到的東西。

聽起來像廢話,但這是模擬訓練搬到真機時最常見、也最致命的失敗原因。原因在於,模擬器知道的事情遠比真機多。

模擬器什麼都知道

  • 機身在世界座標的精確位置
  • 機身的精確線速度
  • 每顆關節的精確角度與角速度
  • 每顆馬達的實際輸出力矩
  • 腳有沒有碰地、碰在哪一點
  • 地面反作用力 17.83 N
  • 所有物體的接觸法線方向

真機只量得到

  • 每顆馬達編碼器回報的角度
  • 由角度差分出來的角速度
  • IMU 的角速度(陀螺儀)
  • IMU 的重力方向(傾斜)

就這樣。沒有腳底力感測器,沒有外部定位系統,不知道自己在房間裡的哪個位置。

不守這條規則會發生什麼事

假設你在觀測裡偷偷加了「腳底接觸力」。訓練過程中,策略會發現這是個超好用的訊號:接觸力一變化,就知道該換腳了。模擬裡它走得非常漂亮。

然後你把它放到真機上。真機沒有那顆感測器,那幾個欄位只能餵零或餵雜訊。策略最依賴的輸入消失了,它立刻不知道自己在幹嘛——摔倒。

糟糕的是,這種失敗在模擬階段完全看不出來。你會以為問題出在別的地方,然後浪費好幾天找錯方向。

Microduck 實際的 61 維長什麼樣

區段維度內容真機從哪來
gyro3機身三軸角速度IMU 陀螺儀
projected_gravity3重力在機身座標的方向,等於知道自己傾斜多少IMU 加速度計
joint_pos1414 顆伺服的目前角度馬達編碼器
joint_vel1414 顆伺服的目前角速度馬達編碼器
last_action14上一次策略自己輸出的 14 個目標角度程式自己記得
command13移動指令 3+頭部姿態 4+身體姿態 6你的遙控器/App

沒有一項需要真機裝額外的感測器。這是設計的結果,不是運氣。

踩過的坑

官方把這 61 維訂為整個策略家族共用的硬性契約:走路、站起來、踢球、輪滑……每一個策略都必須是 61 維,一模一樣的排列順序。原因是真機執行時要能熱切換策略——按一個鍵就從走路切到站起來。某個任務用不到的欄位怎麼辦?補零,但絕對不能刪掉。刪了維度就對不上,ONNX 根本載入不進去。

IMU 是什麼,它量得到什麼、量不到什麼

IMU 是 Inertial Measurement Unit(慣性量測單元),一顆實體晶片,通常同時包含兩種感測器:

  • 陀螺儀——量「我正在以多快的速度轉」,三個軸各一個數字。這就是 gyro 那 3 維。
  • 加速度計——量加速度。靜止時它量到的就是重力,所以能反推出「哪邊是下面」,也就是機身傾斜多少。這就是 projected_gravity 那 3 維。

Microduck 裝了兩顆(機身一顆、頭部一顆),型號是 LSM6DSV16X。

IMU 晶片 陀螺儀 量轉動 加速度計 量重力方向 量得到:我「怎麼轉」 三軸角速度 我現在轉多快 傾斜角度 哪邊是下面、我歪了幾度 量不到:我「在哪裡」 絕對位置 我站在房間的哪一格 移動距離 我往前走了幾公尺
這是原理上的限制,不是精度問題——再貴的 IMU 也一樣。想知道「在哪裡」,需要另外的定位系統。

這個限制會直接影響你的觀測空間設計:不要放任何需要絕對位置才算得出來的東西。策略只知道自己正在怎麼動,不知道自己在哪裡——它就像閉著眼睛走路,靠平衡感而不是靠看路。

這條規則會反過來決定你買什麼硬體

順序不是「先買馬達,之後再想 RL 要什麼資料」。是反過來的。

20ms 每一圈的時間預算 1 秒 ÷ 50 = 0.02 秒 讀 14 顆編碼器 + IMU 馬達必須回報得了角度 組成 61 維觀測 送進神經網路 輸出 14 個目標角度 寫進伺服匯流排 機器人實際動了 姿態改變,回到上一步
50Hz 就是這個迴圈每秒轉 50 圈。橘色的兩個節點發生在真實硬體上——它們的規格決定了這個迴圈跑不跑得起來。

所以便宜的 PWM 舵機不行

一般 PWM 舵機是單向的:你告訴它「去 90 度」,它說好,然後就沒有然後了。它不會告訴你「我現在實際在 87.3 度」。這叫開迴路——你永遠不知道它到底在 85 度還是 94 度。

而這個迴圈的第一步就是「讀 14 顆編碼器」。讀不到,joint_posjoint_vel 這 28 個欄位就是空的,占了 61 維裡的 46%。整套方法從根本上不成立。

Dynamixel 這類智慧型伺服之所以是這類專案的標配,就是因為它回報得了目前位置、速度,部分型號還回報負載與電流。這不是品牌偏好,是方法論的硬性要求。

踩過的坑

訓練時對 IMU 加的隨機擾動是零中心的——它訓練出來的是「對隨機晃動的容忍度」,治不了系統性的安裝偏差。如果你的 IMU 裝歪了一個固定角度,每次都偏同一邊,再怎麼訓練也解決不了。官方原文直接寫明:那是執行時的校正問題,不是訓練問題。

06

第四步:動作空間

動作空間就是在回答一個問題:RL 到底能控制什麼?通常答案就是「每一顆受控馬達的目標角度」,所以維度等於受控自由度數量。

實測

Microduck 的動作是 14 維,對應 14 顆受控伺服。注意這跟整機馬達數不一樣——實體有 15 顆,第 15 顆驅動鴨嘴,不歸策略管。「機器人有幾顆馬達」和「RL 控制幾顆」是兩個不同的數字,設計時要分清楚。

關節怎麼排

索引部位關節
0–4左腿hip_yaw, hip_roll, hip_pitch, knee, ankle
5–8頸與頭neck_pitch, head_pitch, head_yaw, head_roll
9–13右腿hip_yaw, hip_roll, hip_pitch, knee, ankle

每條腿 5 個自由度:髖部三軸(偏擺、側傾、前後)、膝、踝。這是雙足機器人相當典型的配置。

踩過的坑

不要在程式裡寫死關節索引。加了輪子或齒隙模型之後,被動關節會插進序列中間,原本的 0–13 就整個位移了。官方的做法是提供一組輔助函式去解析,在單純模型上是恆等對應,在複雜模型上自動校正。另外,所有不受控的關節一律以 passive_ 開頭命名,選擇器再用正規表示式排除——這個命名慣例很值得抄。

07

第五步:獎勵設計

獎勵函數就是 RL 的評分標準——你不是在教它「怎麼走」,而是在定義「什麼叫走得好」,剩下的動作細節讓它自己試出來。

大致長這樣:往前走得越快加越多分、摔倒扣大分、動作抖動扣一點分、浪費電力扣一點分。策略慢慢就會學成「往前走,但別摔倒,也別亂耗力」。

通則

獎勵是整個流程裡最需要自己動腦、也最沒辦法抄的部分。架構可以參考,數值不能直接相信——一組對著矮胖鴨子調出來的權重,套到高瘦雙足機器人身上,通常直接學不起來。

六條用除錯時間換來的規則

下面這些全部來自官方開發日誌,每一條都標註是實際踩過的。對第一次設計獎勵函數的人來說,這是整份手冊性價比最高的一段。

一、符號寫反會讓策略學會鑽漏洞

專案裡有兩種懲罰寫法:一種回傳正值、要配負權重;另一種自己已經回傳負值、要配正權重。配錯就是雙重否定——本來要罰的行為變成有獎勵,策略會拚命去做。實際後果包括學會用屁股蹦跳、故意墜坐。萬無一失的檢查法:每次訓練,監看面板上每一個懲罰項的數值都必須 ≤ 0。

二、RL 只認獎勵的字面意思

任何你沒講清楚的自由度,它一定會鑽。要它前滾翻,它學會用力甩頭代替翻滾;要它側滾,它用肩膀滾而不是照你想的軸向;要它站起來,它學會用頭當三腳架撐住。「什麼才算完成這個動作」必須寫成硬性的狀態閘門(支撐點接觸、姿態軸向檢查、狀態鎖存),不能只靠幾個小小的扣分去暗示。

三、不要設「頭獎」

任何「到達某狀態就給分」的獎勵,如果到了之後每一步都繼續領,那就是頭獎——策略會用任何暴力手段衝過去,因為早到就是多領。做法是追蹤一個等速推進的內部目標,超前於進度不給分,於是「慢慢來」才是最佳解。單純加個速度上限懲罰沒有用,那個成本是有限的,策略算得出來衝過去比較划算。

四、不要把正獎勵綁在壞狀態上

如果「倒下」或「趴低」這種狀態底下掛著某個正分項目,策略會找出最省力的合格姿勢,然後停在那裡一直領。改用「進步才給分」的寫法:姿態每往上改善一點就付一點分,維持不動付零分,這樣就farm 不了。設計休息類任務時,要逐一檢查每個正分項目對照各種癱倒姿勢——只要癱著還能拿到大部分分數,它就會去癱著。

五、正則化項有兩種,別混為一談

動作阻礙型(角速度、角動量懲罰)會懲罰劇烈動作本來就需要的東西,做特技類任務時權重要壓低。平滑型(動作變化率)只抑制抖動、不擋大幅慢動作,比較安全——但要等技能學會之後再逐步加上去。在還在摸索困難動作的階段就課這種稅,「什麼都不做」會變成最佳解。

六、收緊容忍度之前,先問這個誤差躲不躲得掉

真實案例:頭部在走路時會下垂約 15 度。直覺反應是把頭部追蹤的容忍度調緊——結果策略乾脆完全不走了,因為一顆佔全身 38% 質量的頭在走路時必然會晃,那個誤差躲不掉,站著不動反而分數更高。正確做法是只針對躲得掉的部分收費(那個固定的下垂偏移),讓躲不掉的晃動自行抵消。

08

什麼能沿用,什麼不能

這張表可以直接當成開工時的判斷依據。

項目為什麼
可以沿用mjlab 框架本身它本來就是通用的,跟你做什麼機器人無關
可以沿用PPO 訓練迴圈同上
可以沿用獎勵工具函式庫那些是數學工具,不含鴨子的假設
可以沿用程式架構與檔案組織設定檔怎麼寫、任務怎麼註冊,照抄就好
可以沿用開發方法論先跑煙霧測試、檢查符號、不要一次改十個地方
可以沿用BAM 馬達模型但前提是你也用 XL330
不能沿用訓練好的權重檔裡面學到的是鴨子的身體、質量、關節動態。維度不同的話連載入都載入不了
不能沿用獎勵權重數值那組數字是對著鴨子的體型調出來的
不能沿用觀測/動作維度你的自由度數量不同,整個向量就不同
不能沿用目標高度等物理常數必須從你自己的模型量出來

一句話總結:架構可以參考,數值不能直接相信。

09

如果這是你第一個 RL 機器人,慎選雙足

雙足是強化學習裡最難的題目之一,原因很物理:支撐點太少。

四足:抬起一隻腳 重心 還有三角形可以站,重心落在裡面 有容錯空間 雙足:抬起一隻腳 重心 只剩一個小小的腳掌面積 重心稍微偏出去就開始倒
支撐多邊形:重心的垂直投影落在多邊形內才站得住。四足抬腳後還有三角形,雙足抬腳後只剩一個腳掌。

這對訓練效率的影響很直接。雙足在學會之前,每一次嘗試常常是「站起來 → 走一步 → 重心偏掉 → 摔倒 → 這回合結束」,能收集到的有效資料很少。四足則是「站著 → 抬一隻腳 → 還有三隻撐著 → 調整 → 繼續」,學習空間寬容得多。

建議

既然是自創,形態你自己決定。如果設計上能多幾個接觸點——四足、輪式、輪腿、三點支撐——訓練難度會降不只一個檔次。先用比較好訓練的形態把整套流程跑通一遍,確定自己掌握了 CAD→MJCF→訓練→部署的每一環,再挑戰雙足,會少走很多冤枉路。

踩過的坑

另一個相關的提醒:不要拿人類的速度直覺去限制小機器人。一台 25 公分高的機器人翻滾時,3.5–5.5 弧度/秒是它的自然速度。如果你照人類的感覺加上轉速上限,等於在禁止它做物理上正常的動作。要壓制粗暴行為,應該去罰衝擊力和抖動,不是罰轉速。

10

實際動手:檔案與指令

你的任務怎麼被系統認得

寫好設定之後,要在 tasks/__init__.py 裡登記,它才會變成一個合法的任務代號。實際長這樣:

# 官方 Microduck 走路任務的登記方式,照這個格式改成你的
register_mjlab_task(
    task_id="Mjlab-Velocity-Flat-MicroDuck",   # 你自己取的代號
    env_cfg=make_microduck_velocity_env_cfg(),      # 你寫的設定 ← 只有這裡要花心思
    play_env_cfg=make_microduck_velocity_env_cfg(play=True),
    rl_cfg=MicroduckRlCfg,                          # 通常沿用
    runner_cls=MicroduckOnPolicyRunner,             # 通常沿用
)

五個參數裡,真正屬於「你的機器人」的只有 env_cfg 那一個——獎勵、觀測、終止條件都寫在裡面。另外兩個(rl_cfg 學習率那些、runner_cls 訓練迴圈機制)跟你做什麼機器人無關,直接沿用。

指令清單

指令做什麼
uv run list-envs列出目前所有已登記的任務,確認你的有登記成功
uv run train <任務> --env.scene.num-envs 64 --agent.max_iterations 5煙霧測試,永遠先跑這個
uv run train <任務> --env.scene.num-envs 4096正式訓練(加 --hf-jobs 送雲端 GPU)
uv run play <任務> --wandb-run-path <...>開 3D 檢視器看訓練好的策略實際跑
uv run scripts/export.py <任務> --wandb-run-path <...>匯出 ONNX,準備上真機
uv run scripts/infer_policy.py --walking out.onnx在電腦上預演部署,用鍵盤操控
實測

官方原文:64 個環境、5 次迭代的煙霧測試,能用幾分錢的成本抓出約 95% 的設定錯誤。絕對不要沒跑它就啟動長時間訓練。它會檢查:能不能建起來、步進時有沒有數值爆掉、觀測維度對不對、每個獎勵項算不算得出來、ONNX 匯不匯得出去。

建立新任務的標準流程

  1. 挑最接近的範本延伸,不要從零開始。移動類抄走路、以某個姿勢收尾的招式抄站起來、可切換兩種狀態的抄坐↔站、劇烈動作抄前滾翻。
  2. 訓練前先在模擬裡驗證物理假設。目標姿勢真的站得穩嗎?從有雜訊的初始狀態放手 3 秒,檢查傾斜角而不只是高度(只看高度的話,倒下去的狀態也會被判定成「休息得很好」)。
  3. 寫設定測試:關節索引在實際模型上解析得出來、獎勵權重的正負號是你要的。這些在 CPU 上就能跑。
  4. 跑煙霧測試
  5. 正式訓練,然後預期要來回調整二到五輪——官方原文說,跟策略鑽漏洞的行為玩打地鼠是正常的,第 07 節那些規則能幫你跳過大部分。
11

ONNX:模擬與真機的交接點

ONNX(Open Neural Network Exchange)是一種神經網路的檔案格式,副檔名 .onnx。可以把它想成神經網路界的 PDF:不管用什麼軟體做出來的,存成這個格式後,到哪裡都打得開、都跑得動。

它在這條流程裡的位置很關鍵——訓練和執行是兩個完全不同的世界。訓練在電腦上用 Python 和 PyTorch 跑,吃 GPU;但機器人身上跑的是另一套東西(官方版是 Rust,開源版是 Pi 上的 Python)。兩邊語言、框架、硬體全都不一樣。ONNX 就是雙方都認得的中介格式。

訓練世界 你的電腦或雲端 GPU Python + PyTorch MuJoCo + mjlab CUDA GPU 跑幾千萬步,要幾小時 export.py .onnx 網路的結構 學好的權重 觀測正規化參數 最容易漏掉的一塊 複製過去 機器人世界 機器人身上的小電腦 Rust / Python ONNX Runtime 沒有 GPU 每 20ms 算一次,只推論不學習 同一個檔案,兩邊都認得——這就是 ONNX 存在的理由
左右兩邊的語言、框架、硬體完全不同,中間那個檔案是唯一的共同語言。

檔案裡裝的是什麼

已經訓練完成的成品:網路的結構,加上學好的權重數值。它只負責做一件事——把輸入的觀測向量換成輸出的動作向量。官方版是「61 維進、14 維出」。

不含訓練邏輯,也不能再繼續學。想改進行為,只能回到訓練世界重跑,再匯出一個新檔案蓋掉。

踩過的坑

匯出必須走官方那支 scripts/export.py,因為觀測正規化的參數要一起烤進檔案裡(就是上圖那個紅框)。自己手動轉檔會漏掉這段,而且在模擬器裡測試完全看不出來——模擬環境會自己補上正規化,所以你會看到它跑得好好的。等搬到真機才爆掉,而那時你會以為問題出在硬體。

12

另一條路:完全可列印的開源版本

前面十一節講的是 Pollen 官方那套。但如果你的目標是真的做一台出來,有另一條路繞過了兩個最大的障礙——自製電路板,以及逆向工程的公差問題。

先搞清楚:市面上有三個「Microduck」

Rhoban / microban 法國波爾多實驗室,共同源頭 MarcDcls / microduck 「完全可 3D 列印的開源人形機器人」 學術專案,有 DOI,2026 年 6 月發布 程式 GPL-3.0 機構 CC BY-NC-SA pollen-robotics / microduck 商品,售價 399 美元 Rust runtime,61 維觀測,15 顆伺服 程式 Apache-2.0 機構未開源 ← 前面十一節講的是這個 AI-FanGe / Microduck-build-tutorial 把左邊那個包成組裝教學 +3D 列印檔 +預燒好的 SD 卡映像
名字一樣,但是三個不同的東西。你如果要動手做,目標是左邊那條線。

它拿掉了什麼障礙

最關鍵的差別在伺服控制那一段。官方版用的是一塊自製的小轉接板——也就是我們一路討論、但原廠從未開源的那塊。這個開源版本根本不走那條路:它直接用 ROBOTIS 自己賣的現成控制板 OpenRB-150,樹莓派透過 USB 接上去就好。

Pollen 官方路線 開源可列印路線 Radxa Zero 3W 自製轉接板 電路圖未開源,要自己畫 這就是卡住的地方 XL330 伺服匯流排 IMU 也掛在那塊自製板上 Raspberry Pi Zero 2 W USB ROBOTIS OpenRB-150 原廠現成品,直接買得到 不用畫電路 XL330 伺服匯流排 IMU 走 I2C 直接接樹莓派 GPIO
同一件事,兩種做法。右邊那條把「自己設計電路板」整個從清單上劃掉了。
實測

3D 列印檔就附在 repo 裡(microduck3D打印.3mf,5.3MB)。我實際解開驗證過,是完整可切片的專案檔:Bambu Lab P1S、5 個列印盤、PLA、0.2mm 層高、15% 填充、2 層牆、樹狀支撐、自動裙邊、0.4mm 噴嘴。這不是模擬用網格,是真的排好版可以直接送印的檔案。

硬體清單

類別物料數量說明
主控Raspberry Pi Zero 2 W1跑控制程式、藍牙手把、Wi-Fi、SSH
伺服控制ROBOTIS OpenRB-1501USB 接樹莓派,負責跟 XL330 匯流排通訊
伺服Dynamixel XL330-M288-T14雙腿 10+頭頸 4。嘴部(ID 15)選配,走路模型不用
電池成品 6V 充電電池包1不必自組 2S 電池,但要確認瞬時電流夠
IMUBNO080 / 085 / 086 模組1I2C 接 GPIO2/GPIO3,程式會自動探測位址
儲存16GB 以上 microSD1刷入官方映像檔
結構3D 列印件1 套PLA,用 repo 裡的 3MF
五金M2 / M2.5 自攻螺絲若干建議多備
線材Dynamixel 3-pin 線若干伺服串聯與分線
其他電源開關1主電源

跟官方版相比,它沒有攝影機、沒有深度感測、沒有 NFC——它只做「會走路」這一件事。對第一次要把整條流程跑通的人來說,這反而是優點:變數少很多。

接線

電源鏈(橘) 資料鏈(青) 6V 電池包 成品,不用自組 電源開關 OpenRB-150 電源輸入+伺服通訊 5V 穩壓 USB 供電 Raspberry Pi Zero 2 W USB 資料 BNO08x IMU SDA→GPIO2 SCL→GPIO3 I2C 分線 右腿 5→4→3→2→1 左腿 10→9→8→7→6 頭頸 12→11→13→14 兩側電源必須共地。上電前用萬用表確認正負極。 焊點要用熱縮管或熱熔膠絕緣,避免短路
伺服的實體串接順序可以照佈線方便調整,但 ID 編號必須跟下表一致。

伺服 ID 對照

組裝前要先用 Dynamixel Wizard 一顆一顆設定 ID。設錯的話機器人會立刻摔倒,而且很難從現象看出原因。

ID名稱ID名稱
1right_ankle 右踝8left_hip_pitch 左髖俯仰
2right_knee 右膝9left_hip_roll 左髖橫滾
3right_hip_pitch 右髖俯仰10left_hip_yaw 左髖偏航
4right_hip_roll 右髖橫滾11head_pitch 頭部俯仰
5right_hip_yaw 右髖偏航12neck_pitch 頸部俯仰
6left_ankle 左踝13head_yaw 頭部偏航
7left_knee 左膝14head_roll 頭部橫滾

Dynamixel Wizard 建議參數:Protocol 2.0、鮑率 1Mbps、Return Delay Time 0、PWM Slope 255。另外要取消 Shutdown 裡的「輸入電壓錯誤」觸發項,否則電池電壓稍微波動就會誤停。

軟體:有預燒好的映像檔

最省事的地方在這裡——官方提供 microduck.img.xz,系統、Python 環境、走路模型、手把服務全部裝好了。用 Raspberry Pi Imager 刷進 SD 卡、設好 Wi-Fi、SSH 進去就能跑。

安全提醒

映像檔的預設帳號是 user預設密碼就是 password。第一次登入後立刻用 passwd 改掉——這台機器會連上你家的 Wi-Fi。

啟動控制程式後,鍵盤操作是:v 開關行走、方向鍵前後左右、x 速度歸零、i 顯示 IMU 資訊、q 停止。手把模式則是按住 START 兩秒啟動、A 開關行走、左搖桿移動、右搖桿轉向、B 停止、同時按住兩個扳機兩秒安全關機。

一個有意思的設計

手把連上時,服務會自動關閉 Wi-Fi,因為藍牙和 2.4GHz Wi-Fi 會互相干擾。所以你會發現手把一連上,SSH 就斷了——這是正常的,關掉手把 Wi-Fi 就會自己回來。

它的觀測空間是 51 維,不是 61 維

我實際讀了它附的 walk.onnx:輸入 51 維,輸出 14 維。這剛好是第 05 節那個道理的活教材——維度是照你的機器人和任務決定的,不是抄來的數字。

官方 61D 13 開源版 51D 3 gyro grav joint_pos joint_vel last_action 前 48 維完全一樣 移動3+頭部4+身體6 只有移動 3 沒有頭部姿態控制 官方多出來的 10 維,是為了讓走路、站起來、踢球等策略能在執行時互相熱切換
差別只在最後那一段。開源版只有走路一個策略,不需要熱切換,也就不需要保留那些欄位。

授權:這裡有三層,而且比官方版更嚴格

部分授權意思
最外層包裝MIT最寬鬆,可商用
部署控制程式碼GPL-3.0-or-later改了再散布,衍生作品也必須開源成 GPL
機構 CAD 檔CC BY-NC-SA 4.0禁止商業使用
訓練環境Apache-2.0可商用,保留聲明即可
想拿來賣的話

兩個地方擋住:機構檔是 NC(禁止商業),跟官方版的限制一樣;而且控制程式碼是 GPL-3.0,比 Apache 嚴格得多——你改過的版本只要對外散布,就必須連同原始碼一起以 GPL 釋出。這兩點跟「開源」不衝突,但跟「做成商品賣」衝突。

實際使用上要注意的幾點

  • 3MF 裡的零件名稱是亂碼(像 ersgtbgertgestrg.objdfbdfbbbbb1.obj),分辨不出哪個是哪個零件,只能靠切片軟體裡的盤面預覽圖對照。
  • 切片參數是綁 Bambu Lab P1S 的。換印表機要自己重新設定,尤其是樹狀支撐和裙邊。
  • 它的膝關節有兩個固定的硬體零位偏移(左右各 ±45°),寫在程式的常數裡。這是對應作者的實體組裝方式的——你如果組裝方式不同,這兩個數字要跟著改。
  • 官方的除錯清單特別點名:機器人一放就倒,優先檢查左右腿的伺服 ID 有沒有對調、膝關節偏移對不對、IMU 安裝方向對不對。
怎麼選

如果你的目標是「有一台會動的鴨子」,買官方成品最省事。如果目標是「搞懂每顆螺絲跟每行程式在幹嘛」,這條開源路線是目前最完整的:列印檔、接線圖、程式碼、訓練環境、預燒映像全都有,而且不用自己畫電路板。

13

開工前的檢查清單

在寫任何一行程式之前,先把這幾題答完。勾選狀態會存在你自己的瀏覽器裡,下次打開還在。

0 / 10 完成
最後一句

整份手冊如果只記一件事,記這個:不要從「我要怎麼改 microduck」開始,要從「我的機器人長什麼樣、它量得到什麼、控制得了什麼」開始。把這些答案寫成 mjlab 的設定,microduck_rl 從頭到尾只是放在旁邊的參考書。

名詞速查表

按出現順序排列。「詳見」欄指向本手冊有完整說明的章節。

名詞一句話解釋詳見
MuJoCo物理模擬引擎,負責算「這樣施力機器人會怎麼動」。整套訓練都跑在它上面。02
mjlab架在 MuJoCo 上的通用機器人強化學習框架。你的專案是建在它上面的一份設定。01
Onshape跑在瀏覽器裡的 CAD 軟體。官方用它畫 Microduck,所以匯出工具是為它寫的。03
CAD電腦輔助設計,就是畫 3D 零件圖。描述「長什麼樣」。03
MJCFMuJoCo 的模型描述格式,一個 XML 檔。描述「怎麼動」:連桿、關節、質量、慣量、碰撞。03
URDFROS 世界的同類格式。MJCF 能表達的接觸參數等細節它表達不了。03
STL純三角網格檔,只有形狀,沒有零件關係或質量資訊。03
onshape-to-robot從 Onshape 自動匯出 MJCF/URDF 的工具,會一併帶出質量與慣量。03
慣量重量「分布在哪裡」,不只是總重。決定甩腿轉頭時的動態。03
PD 控制器最簡單的馬達模型:看位置誤差加上速度,算出該施多大力。04
BAMBetter Actuator Model。把伺服當成電壓驅動的馬達來模擬,參數從真實馬達量出來。是程式庫,不是應用程式。04
IMU慣性量測單元。陀螺儀量轉速、加速度計量傾斜。量得到「怎麼轉」,量不到「在哪裡」。05
Observation觀測空間。策略每一步看得到的所有數字。只能放真機量得到的東西。05
Action動作空間。策略每一步輸出的數字,通常是每顆受控馬達的目標角度。06
Reward獎勵函數。定義「什麼叫做好」,是最花腦力也最不能抄的部分。07
PPO一種強化學習演算法,這套流程用的訓練方法。屬於可以直接沿用的部分。08
Sim2real把模擬裡訓練好的東西搬到真機上。落差主要來自馬達模型、感測器與機構公差。05, 12
Domain randomization訓練時隨機擾動各種參數,讓策略對現實的不確定性有容忍度。注意:零中心的擾動治不了系統性偏差。05
煙霧測試用極小規模(64 環境、5 迭代)快速驗證設定沒錯。官方說能抓出約 95% 的設定錯誤。10
ONNX神經網路的通用檔案格式。訓練世界與機器人世界之間的交接點,裡面是訓練完成的成品。11
DynamixelROBOTIS 的智慧型伺服系列。關鍵特性是會回報目前角度——這是整套方法的硬性前提。05
XL330-M288-T這些專案用的那顆伺服,18 公克級。BAM 那份參數就是針對它量的。04
OpenRB-150ROBOTIS 的現成伺服控制板,USB 接主機。開源版用它取代自製轉接板。12
3MF3D 列印的專案檔格式,除了模型還帶著切片參數與排版。比 STL 完整。12