器人仿真)
很多初學(xué)者拿到 MuJoCo 第一反應(yīng)是“又是個仿真器是不是得配 ROS、搞 URDF、再裝一堆環(huán)境依賴”結(jié)果折騰半天連個模型都沒跑起來。我自己的建議很簡單先裝 MuJoCo 的 Python 綁定用一個最簡單的關(guān)節(jié)模型把循環(huán)跑通之后再考慮 Gazebo、MoveIt 這些重型工具鏈。你會有一種恍然大悟的感覺原來物理引擎的仿真循環(huán)就那么幾步。MuJoCo 是 DeepMind 開源的高性能物理引擎名字全稱是 Multi-Joint dynamics with Contact主打快速、穩(wěn)定的剛體接觸仿真。相比 PyBullet、Webots 這類工具M(jìn)uJoCo 的 Python API 尤其干凈模型描述語言 MJCF 也足夠直觀非常適合做機(jī)器人運(yùn)動學(xué)、動力學(xué)和強(qiáng)化學(xué)習(xí)的快速驗(yàn)證。這篇內(nèi)容沒有復(fù)雜數(shù)學(xué)推導(dǎo)全部是能直接復(fù)制運(yùn)行的代碼和我在實(shí)際使用中踩過的坑。你只需要一個能跑 Python 的電腦不用裝 ROS不用買硬件五分鐘足夠看到第一個機(jī)械臂在你的屏幕上動起來。1. 為什么入門機(jī)器人仿真我先推薦 MuJoCo1.1 主流仿真引擎的差別到底在哪機(jī)器人仿真圈子里能叫上名字的引擎不少Gazebo、Webots、PyBullet、CoppeliaSim 都是常見選項(xiàng)但它們的定位差別不小。Gazebo 強(qiáng)在和 ROS 生態(tài)的集成傳感器模型多適合做完整機(jī)器人軟件棧的集成測試可代價是安裝重、運(yùn)行慢新手經(jīng)常被插件版本、網(wǎng)絡(luò)配置搞到心態(tài)崩潰。Webots 有自己的 IDE 和場景編輯器上手直觀但是模型格式偏封閉Python 控制接口的歷史包袱也比較重。PyBullet 和 MuJoCo 算是輕量級方案里的兩個代表。PyBullet 直接用 URDFAPI 更像一個“物理接口”很多強(qiáng)化學(xué)習(xí)腳本拿它做環(huán)境但它的接觸求解穩(wěn)定性一般高速碰撞場景容易出現(xiàn)抖動。MuJoCo 的優(yōu)勢集中在三件事一是求解器效率極高能跑到比實(shí)時快很多的仿真速率二是接觸處理非常穩(wěn)腳踩地面、機(jī)械臂抓取這類場景不容易穿模三是模型格式 MJCF 寫起來比 URDF 簡潔很多一個簡單機(jī)械臂只需要幾十行 XML。2022 年 DeepMind 把 MuJoCo 完全開源以后Python 綁定被重構(gòu)得越來越順手目前最新版本已經(jīng)到 3.x內(nèi)置查看器和離屏渲染能力都很完整訓(xùn)練強(qiáng)化學(xué)習(xí)任務(wù)的前后臺支持也做得不錯。當(dāng)然我不是說 MuJoCo 能完全替代 Gazebo。真要做 SLAM、多傳感器融合、orchestrating 多個節(jié)點(diǎn)的聯(lián)調(diào)Gazebo 依然有它的位置。但如果你想快速驗(yàn)證一個算法思路或者剛?cè)胄邢肜斫鈾C(jī)器人仿真到底是怎么跑起來的MuJoCo 的學(xué)習(xí)成本是最低的。1.2 上手 MuJoCo 之前最低需要準(zhǔn)備什么一個容易勸退新手的點(diǎn)是你以為要懂很多物理、數(shù)學(xué)和 C。其實(shí)不需要。MuJoCo 的 Python API 已經(jīng)把核心操作封裝成了幾個函數(shù)你只需要了解三個概念模型、數(shù)據(jù)、步進(jìn)。模型是靜態(tài)的物理描述數(shù)據(jù)是仿真過程中的動態(tài)狀態(tài)步進(jìn)就是讓時間往前推一小步。你的 Python 基礎(chǔ)也不需要太深會安裝第三方庫、能運(yùn)行腳本、看懂for循環(huán)和數(shù)組賦值就夠了。如果連 Python 環(huán)境都沒搞定可以先用 Anaconda 創(chuàng)建一個干凈的虛擬環(huán)境或者直接用系統(tǒng)自帶的 Python只要版本不低于 3.8。我日常用的是 Windows 11 加 PyCharmLinux 服務(wù)器上則用 VS Code 遠(yuǎn)程跑渲染任務(wù)。不同系統(tǒng)在安裝上有一些差異我會在第 2 節(jié)中專門說明。另外要提醒一點(diǎn)雖然 MuJoCo 自帶查看器但很多人在服務(wù)器上跑是沒有屏幕的。別擔(dān)心離線渲染這一塊我也寫了自己的實(shí)操方法后面會給出可以復(fù)用的代碼。2. 環(huán)境搭建MuJoCo 安裝與驗(yàn)證2.1 創(chuàng)建干凈的 Python 環(huán)境很多人安裝 mujoco 時碰到的第一個問題不是裝不上而是把包裝到了錯誤的 Python 解釋器里。最省心的方式是給 MuJoCo 單獨(dú)建一個虛擬環(huán)境。如果你裝了 Anaconda在終端里執(zhí)行conda create -n mujoco python3.10 conda activate mujoco如果你不想用 Anaconda用 Python 自帶工具也可以python -m venv mujoco_envWindows 下激活這個虛擬環(huán)境mujoco_env\Scripts\activate.batLinux 或 macOS 下激活則是source mujoco_env/bin/activate這樣后面所有 pip 安裝的包都只屬于這個環(huán)境不會污染系統(tǒng) Python也不會出現(xiàn)“pip list 里有 mujoco 但 import 時不認(rèn)識”的情況。如果你在 Windows 上運(yùn)行python時看到一句 python was not found; run without arguments to install from the Microsoft Store大概率是 Python 沒被加入 PATH或者微軟商店的 Python 別名把命令攔截了。這時候先用py --version試試如果能用說明系統(tǒng)里裝了多版本 Python。要么手動修改環(huán)境變量把 Python 的安裝目錄加入 PATH要么去“應(yīng)用執(zhí)行別名”里關(guān)掉 python.exe 的兩個商店安裝入口然后重新打開終端。這個錯誤出現(xiàn)的頻率實(shí)在太高我后文排查清單里還會再提。2.2 用 pip 安裝 mujoco 并完成快速自檢激活環(huán)境后直接安裝python -m pip install --upgrade pip python -m pip install mujocoMuJoCo 的安裝包體積不小因?yàn)樗瑫r包含了 C 引擎、Python 綁定以及一套用于渲染的底層實(shí)現(xiàn)第一次下載可以稍微耐心一下。安裝完成后用一行命令驗(yàn)證是否成功python -c import mujoco; print(mujoco.__version__)如果你看到了類似3.2.7的輸出說明核心引擎已經(jīng)就緒。Linux 用戶如果在這個步驟報libGL.so.1: cannot open shared object file說明系統(tǒng)缺少 OpenGL 運(yùn)行庫需要先安裝sudo apt-get install -y libgl1 libglfw3 libglew2.2Windows 用戶一般不需要單獨(dú)裝依賴但要確保顯卡驅(qū)動是比較新的版本否則后續(xù)打開查看器時可能黑屏。macOS 用戶如果用的是 Apple SiliconMuJoCo 官方提供了原生 arm64 輪子正常安裝即可。2.3 跑一個“最小物理實(shí)驗(yàn)”驗(yàn)證引擎在打開查看器之前我想先讓你對 MuJoCo 的仿真循環(huán)有個手感。下面是讓一個球體在重力作用下自由落體的完整代碼邏輯極其簡單但足以證明引擎在正常工作import mujoco import numpy as np XML mujoco modelfalling_ball option timestep0.01/ worldbody light namelight pos0 0 5/ geom namefloor typeplane size5 5 0.1 rgba0.3 0.5 0.3 1/ geom nameball typesphere pos0 0 1 size0.15 rgba0.8 0.2 0.2 1/ /worldbody /mujoco model mujoco.MjModel.from_xml_string(XML) data mujoco.MjData(model) for step in range(100): mujoco.mj_step(model, data) if step % 10 0: print(ft{data.time:.3f}, z{data.qpos[0]:.3f})運(yùn)行后你會看到球體高度逐步下降直到落地后輸出高度不再明顯變化。這說明 MuJoCo 已經(jīng)完成了從模型解析、數(shù)據(jù)初始化到物理步進(jìn)的完整鏈路。如果這一步順利后面所有機(jī)器人模型基本都能跑起來。3. 理解 MuJoCo 的核心對象和仿真循環(huán)3.1 model 與 data一張圖紙和一份動態(tài)賬本MuJoCo 的 Python 接口中最容易混淆的兩個對象是MJModel和MJData。一句話解釋model是靜態(tài)物理模型相當(dāng)于一張工程圖紙里面記錄了連桿長度、質(zhì)量、關(guān)節(jié)類型、執(zhí)行器參數(shù)、碰撞體形狀data是動態(tài)仿真狀態(tài)相當(dāng)于仿真過程中不斷更新的賬本記錄了每一刻的關(guān)節(jié)角度、速度、力矩、接觸力、傳感器讀數(shù)。每次調(diào)用mujoco.MjModel.from_xml_path()或from_xml_string()解析完模型后都必須通過mujoco.MjData(model)創(chuàng)建對應(yīng)的數(shù)據(jù)對象。同一個人可以反復(fù)看同一張圖紙但每一次實(shí)際施工都需要一本新賬本。所以你在很多強(qiáng)化學(xué)習(xí)例子里會看到每個環(huán)境都維護(hù)一個獨(dú)立的data因?yàn)樗强勺儬顟B(tài)。model通常不再修改但你可以讀取它的 nq、nv、nu 這些屬性來了解自由度數(shù)量和執(zhí)行器數(shù)量。data則是在循環(huán)中不斷變化的。初始化時data.qpos會等于model.qpos0也就是模型里定義的默認(rèn)關(guān)節(jié)位置。很多新手直接改data.qpos來擺姿勢這沒有錯但要清楚這本質(zhì)上是“篡改物理狀態(tài)”如果不設(shè)置對應(yīng)的速度接下來的動力學(xué)自然會根據(jù)重力重新調(diào)整。3.2 mj_step 到底做了什么MuJoCo 的仿真時間推進(jìn)靠的是mujoco.mj_step(model, data)。這一步做了三件事計算當(dāng)前狀態(tài)下的廣義慣性矩陣和動力學(xué)項(xiàng)求解帶約束的動力學(xué)方程處理接觸、關(guān)節(jié)限位、驅(qū)動器力矩更新data.time以及所有相關(guān)狀態(tài)量。model.opt.timestep決定了每一步的物理時間跨度默認(rèn)為 0.002 秒。也就是說如果你想仿真 1 秒的物理過程大概需要跑 500 步。如果你發(fā)現(xiàn)模型一動就飛起來大概率是時間步長過大或者約束求解配置不對。后面我會單獨(dú)講這個問題。Python API 里面還有一個常用技巧把每一步的物理計算拆成半隱式積分用mujoco.mj_step1(model, data)和mujoco.mj_step2(model, data)組合使用。這個對大多數(shù)初學(xué)者不必要我提一下只是因?yàn)槟承┬阅苊舾袌鼍跋履銜趧e人的代碼里看到這種寫法。3.3 數(shù)據(jù)讀取qpos、qvel、ctrl、time仿真過程中最常打交道的四個 data 字段是data.qpos廣義坐標(biāo)記錄每個關(guān)節(jié)的位置對于旋轉(zhuǎn)關(guān)節(jié)是角度值data.qvel廣義速度對應(yīng)關(guān)節(jié)角速度data.ctrl控制輸入給執(zhí)行器的指令值長度等于執(zhí)行器數(shù)量data.time當(dāng)前仿真時間單位秒。很多人在讀取data.qpos時習(xí)慣于直接打印但需要注意它底層是 numpy 數(shù)組打印前最好用data.qpos.copy()截取一個快照否則后續(xù)狀態(tài)更新會連帶改變已經(jīng)保存的數(shù)組。這是新手最容易踩的隱形坑之一。4. 實(shí)戰(zhàn)5 分鐘跑通你的第一個機(jī)器人仿真4.1 自己寫一個兩自由度機(jī)械臂模型用一個球體做仿真可以驗(yàn)證環(huán)境但要叫“機(jī)器人仿真”還是得讓機(jī)械臂動起來。下面這個 MJCF 模型是一個最簡單的兩自由度平面機(jī)械臂包含肩關(guān)節(jié)和肘關(guān)節(jié)由兩個 motor 執(zhí)行器驅(qū)動mujoco modelplanar_arm option timestep0.002/ worldbody light namelight pos0 0 5 directionaltrue/ geom nameground typeplane size2 2 0.1 rgba0.3 0.5 0.3 1/ body namebase pos0 0 0.5 joint nameshoulder typehinge axis0 0 1/ geom namelink1 typecapsule fromto0 0 0 0.35 0 0 size0.045 rgba0.4 0.6 1 1/ body namemid pos0.35 0 0 joint nameelbow typehinge axis0 0 1/ geom namelink2 typecapsule fromto0 0 0 0.3 0 0 size0.035 rgba1 0.6 0.3 1/ body nametip pos0.3 0 0 site nametool size0.02 rgba1 0 0 1/ /body /body /body /worldbody actuator motor jointshoulder nameshoulder_motor ctrllimitedtrue ctrlrange-1 1/ motor jointelbow nameelbow_motor ctrllimitedtrue ctrlrange-1 1/ /actuator /mujoco把這個 XML 保存成arm.xml或者直接用 Python 字符串讀入都可以。MJCF 語法里joint定義自由度geom定義可視和物理碰撞外形body通過父子關(guān)系構(gòu)成運(yùn)動鏈。我對關(guān)節(jié) axis 設(shè)成0 0 1意思是關(guān)節(jié)繞世界坐標(biāo)的 Z 軸旋轉(zhuǎn)這樣機(jī)械臂只會在 XY 平面內(nèi)運(yùn)動方便觀察。4.2 手寫仿真循環(huán)并觀察機(jī)械臂運(yùn)動下面這段代碼會加載模型讓肩關(guān)節(jié)做一個正弦擺動肘關(guān)節(jié)做一個余弦擺動然后每 50 步打印一次時間和關(guān)節(jié)角度import mujoco import numpy as np import time model mujoco.MjModel.from_xml_path(arm.xml) data mujoco.MjData(model) for step in range(1000): data.ctrl[0] 0.5 * np.sin(step / 50.0) data.ctrl[1] 0.3 * np.cos(step / 40.0) mujoco.mj_step(model, data) if step % 50 0: print(ft{data.time:.3f}, qpos{data.qpos.copy()}, ctrl{data.ctrl.copy()}) print(末端工具點(diǎn)位置:, data.site_xpos[0])這里把ctrl數(shù)組直接給了電機(jī)力矩。因?yàn)槲以O(shè)定了執(zhí)行器的ctrlrange在正負(fù) 1 之間所以力矩被限制在一個小范圍里機(jī)械臂不會瞬間甩飛。運(yùn)行的時候你會在終端看到關(guān)節(jié)角度一次次更新末端工具點(diǎn)位置也會跟著變化。如果你把控制量改成固定值比如data.ctrl[0] 0.0機(jī)械臂就會因?yàn)橹亓Φ淖饔米匀幌麓棺詈笸T谀硞€目標(biāo)角度上。這種“不給指令看動態(tài)”的測試方法是我排查仿真模型問題時的標(biāo)準(zhǔn)動作。4.3 用內(nèi)置查看器看實(shí)時畫面純打印數(shù)據(jù)還是不夠直觀MuJoCo 自帶一個輕量級查看器mujoco.viewer通過launch_passive接口可以啟動一個非阻塞的交互窗口import mujoco import time model mujoco.MjModel.from_xml_path(arm.xml) data mujoco.MjData(model) viewer mujoco.viewer.launch_passive(model, data) while viewer.is_running(): data.ctrl[0] 0.5 data.ctrl[1] 0.2 mujoco.mj_step(model, data) viewer.sync() viewer.close()啟動后你會看到一個 3D 場景機(jī)械臂在重力作用下抖動并趨于穩(wěn)定說明電機(jī)在阻止它下墜。鼠標(biāo)左鍵可以旋轉(zhuǎn)視角中鍵平移右鍵縮放。查看器頂部有暫停和重置按鈕方便你觀察某一幀的物理狀態(tài)。需要特別說明的是launch_passive不會自動按真實(shí)時間推進(jìn)仿真所以我在循環(huán)里調(diào)用了viewer.sync()讓畫面跟隨數(shù)據(jù)更新。如果你跑得太快可以加一行輕量延時比如time.sleep(0.002)讓畫面變化更接近實(shí)時。如果你想讓它全速跑數(shù)據(jù)而不在意觀看直接把延時代碼刪掉就行。4.4 把仿真畫面錄制成視頻有時候你沒有顯示器或者想把仿真過程放進(jìn)報告里這時可以走離線渲染。MuJoCo 提供了Renderer類可以直接把場景渲染成 numpy 圖像數(shù)組再用 OpenCV 寫成視頻import mujoco import cv2 import numpy as np model mujoco.MjModel.from_xml_path(arm.xml) data mujoco.MjData(model) renderer mujoco.Renderer(model, width640, height480) writer cv2.VideoWriter(arm_demo.avi, cv2.VideoWriter_fourcc(*MJPG), 60, (640, 480)) for step in range(1000): data.ctrl[0] 0.5 * np.sin(step / 30.0) data.ctrl[1] 0.3 * np.cos(step / 25.0) mujoco.mj_step(model, data) if step % 2 0: renderer.update_scene(data, cameraside) frame renderer.render() writer.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)) writer.release() renderer.close()這里我每隔一步采一幀最后用 60 幀率寫出視頻播放速度看起來和實(shí)時幾乎一致。camera參數(shù)可以用默認(rèn)視圖也可以指定模型里定義的固定相機(jī)名。用 OpenCV 保存時一定要注意顏色通道轉(zhuǎn)換MuJoCo 輸出的是 RGBOpenCV 默認(rèn)按 BGR 寫入漏掉這一步視頻顏色會發(fā)藍(lán)發(fā)紅。5. 從簡單示例到真實(shí)機(jī)器人加載 MuJoCo Menagerie 模型5.1 MuJoCo Menagerie 到底是什么自己搭一個兩連桿機(jī)械臂對學(xué)習(xí)概念足夠了但真要做算法研究得用更有代表性的機(jī)器人模型。MuJoCo 官方維護(hù)了一個模型倉庫叫 MuJoCo Menagerie里面有大量高精度機(jī)器人模型包括 Franka Panda、UR5e、ANYmal、Shadow Hand 等經(jīng)典平臺。這些模型經(jīng)過了物理參數(shù)校準(zhǔn)關(guān)節(jié)限位、摩擦、驅(qū)動配置都比較接近真實(shí)設(shè)備可以直接拿來做控制實(shí)驗(yàn)和強(qiáng)化學(xué)習(xí)。獲取方式很簡單直接克隆倉庫到本地git clone https://github.com/google-deepmind/mujoco_menagerie.git倉庫里的目錄結(jié)構(gòu)通常是每個機(jī)器人一個文件夾里面放著 MJCF 文件或者 URDF 文件以及對應(yīng)的網(wǎng)格輔助文件。5.2 用 Franka Panda 機(jī)械臂開始第一個“真實(shí)”仿真Franka Panda 是實(shí)驗(yàn)室里常見的七自由度機(jī)械臂Menagerie 里給出了非常完整的模型。加載它只需要把路徑指到對應(yīng)的 scene 文件import mujoco model mujoco.MjModel.from_xml_path(mujoco_menagerie/franka_emika_panda/scene.xml) data mujoco.MjData(model) viewer mujoco.viewer.launch_passive(model, data) while viewer.is_running(): mujoco.mj_step(model, data) viewer.sync() viewer.close()打開后你會看到一個帶桌面的完整場景。在 MuJoCo 查看器里你可以按住 Shift 加鼠標(biāo)左鍵拖動機(jī)器人的末端執(zhí)行器觀察不同姿勢下的運(yùn)動學(xué)表現(xiàn)。要注意這種拖拽直接改的是廣義坐標(biāo)并不完全符合動力學(xué)規(guī)律但作為姿態(tài)瀏覽和運(yùn)動范圍檢查已經(jīng)足夠方便。如果你想做更規(guī)律的動作仿真可以主動修改關(guān)節(jié)角度并觀察末端位置。比如讓機(jī)械臂從初始姿態(tài)移動到另一個姿態(tài)再返回import mujoco import numpy as np model mujoco.MjModel.from_xml_path(mujoco_menagerie/franka_emika_panda/scene.xml) data mujoco.MjData(model) # 記錄初始關(guān)節(jié)角 init_q data.qpos.copy() # 目標(biāo)關(guān)節(jié)角不同機(jī)器人的自由度數(shù)量和順序不同這里只演示前7個關(guān)節(jié) target_q init_q.copy() target_q[:7] [0, -0.5, 0, -1.8, 0, 1.2, 0] duration 3.0 steps int(duration / model.opt.timestep) for step in range(steps): alpha min(step / steps, 1.0) # 簡單的線性插值 data.qpos[:7] init_q[:7] * (1 - alpha) target_q[:7] * alpha mujouco.mj_step(model, data) print(演示完畢末端位置:, data.site_xpos)把data.qpos當(dāng)作可配置狀態(tài)直接賦值這種方式適合運(yùn)動學(xué)規(guī)劃和可視化但不適合精確的力矩控制。真想用真實(shí)動力學(xué)做運(yùn)動規(guī)劃通常需要結(jié)合逆動力學(xué)或者 PD 控制器。對新手來說先理解“設(shè)置狀態(tài)再步進(jìn)”的邏輯能幫你快速熟悉不同機(jī)器人的運(yùn)動空間。5.3 機(jī)器人仿真平臺的選擇思路既然聊到這里順勢說下現(xiàn)實(shí)中的選型問題。如果你的目標(biāo)是 ROS 生態(tài)內(nèi)的完整導(dǎo)航、機(jī)械臂抓取、SLAM 部署那 MuJoCo 暫時替代不了 Gazebo因?yàn)樵鷻C(jī)器人驅(qū)動和感知接口要讓位給 ROS 的插件體系。但如果你只是想研究強(qiáng)化學(xué)習(xí)里的連續(xù)控制或者驗(yàn)證數(shù)據(jù)采集流程、跑跑阻抗控制算法MuJoCo 在速度和易用性上都占優(yōu)。所以我的建議是做兩手準(zhǔn)備仿真驗(yàn)證用 MuJoCo機(jī)器人實(shí)機(jī)和 ROS 集成再切到 Gazebo。MuJoCo 提供的模型和代碼往往可以移植到 ROS 里的 MoveIt 配置前期的邏輯驗(yàn)證并不是白費(fèi)。6. 常見問題與排查技巧實(shí)錄6.1 安裝階段高頻報錯我結(jié)合自己踩過和幫別人排查過的坑整理了一個速查表排序大致按出現(xiàn)頻率從高到低報錯信息原因解決辦法python was not found; run without arguments to install from the Microsoft StorePython 未加入 PATH或商店別名干擾用py --version確認(rèn)是否有 Python修改 PATH或關(guān)閉應(yīng)用執(zhí)行別名ModuleNotFoundError: No module named mujoco包裝到了別的環(huán)境確認(rèn)激活了正確的虛擬環(huán)境重新執(zhí)行pip install mujocolibGL.so.1: cannot open shared object fileLinux 缺少 OpenGL 運(yùn)行庫安裝libgl1 libglfw3 libglew2.2ImportError: DLL load failed while importing mujocoWindows 下顯卡驅(qū)動過舊或缺少運(yùn)行庫更新顯卡驅(qū)動或安裝 Visual C RedistributableAttributeError: module mujoco has no attribute viewer版本太舊或太新API 變化pip install --upgrade mujoco檢查版本應(yīng)為 3.x這里重點(diǎn)提醒一個很多人忽略的問題不要用系統(tǒng)自帶的 PyCharm 項(xiàng)目解釋器直接跑先在終端把虛擬環(huán)境激活再用 PyCharm 的“項(xiàng)目解釋器”指向同一個環(huán)境。我見過很多次“剛才明明裝好了為什么代碼還是找不到 moudle”其實(shí)就是 IDE 用了另一個 Python。6.2 仿真運(yùn)行階段的常見現(xiàn)象運(yùn)行過程中發(fā)現(xiàn)的奇怪現(xiàn)象大多數(shù)不是代碼寫錯而是物理模型本身的問題?,F(xiàn)象一模型一仿真就“爆炸”連桿飛出屏幕。這通常是時間步長設(shè)置過大或者關(guān)節(jié)初始速度過大。把model.opt.timestep從默認(rèn)的 0.002 改小到 0.0005 試試如果不再發(fā)散說明問題就在數(shù)值積分上?;蛘邫z查data.qpos是否和關(guān)節(jié)限位沖突模型里有大量穿模時接觸求解也會不穩(wěn)定。現(xiàn)象二機(jī)械臂掛著不動像被凍結(jié)。先檢查你是否給關(guān)節(jié)設(shè)置了執(zhí)行器并且data.ctrl是否在合理范圍。沒有執(zhí)行器時如果關(guān)節(jié)摩擦特別大或者模型處在重力方向上的穩(wěn)定狀態(tài)機(jī)械臂確實(shí)會停滯不動。用print(data.qvel)看速度是否一直為零再手動加大ctrl值排除執(zhí)行器問題?,F(xiàn)象三查看器打開后一片漆黑。先把燈光加進(jìn)去MJCF 里至少要有一個light再用鼠標(biāo)右鍵旋轉(zhuǎn)視角排除相機(jī)被包在機(jī)器人模型里。某些顯卡驅(qū)動不支持高版本 OpenGL 時也會黑屏這時更新驅(qū)動或調(diào)整查看器的硬件加速選項(xiàng)。現(xiàn)象四仿真過程和真實(shí)機(jī)器人對不上。MuJoCo 默認(rèn)使用 MKS 單位長度是米質(zhì)量是千克時間單位是秒。如果你從 URDF 導(dǎo)入時單位沒轉(zhuǎn)好半身模型會顯得輕飄飄或者重得離譜。我建議在 MJCF 里顯式檢查質(zhì)量參數(shù)而不是依賴默認(rèn)密度推導(dǎo)。6.3 幾個非常實(shí)用的調(diào)試技巧調(diào)試機(jī)器人仿真時我習(xí)慣把“視覺反饋”和“數(shù)據(jù)打印”同時打開。先讓機(jī)械臂在查看器里動起來觀察它的行為模式再用print打印 qpos、qvel、time 三組數(shù)據(jù)。只看數(shù)字很難定位穿模只看畫面又沒法確認(rèn)數(shù)值是否準(zhǔn)確兩者結(jié)合才夠高效。還有一個技巧是“極簡復(fù)現(xiàn)”從一個大場景里刪掉所有不相干對象只保留一個機(jī)械臂和一塊地面看問題是否仍然存在。絕大多數(shù)模型異常都能通過這種裁剪法縮小到具體某個 geom 或 joint 上。不要在大場景里硬猜浪費(fèi)時間。7. 寫在最后的實(shí)踐體會玩 MuJoCo 這段時間我最深的感受是它的門檻并不在安裝而在理解“物理引擎不是一個渲染工具而是一個不斷更新的動力學(xué)系統(tǒng)”。你隨手改一個data.qpos下一秒的力和加速度會完全不同。這種因果鏈非常直接也特別適合訓(xùn)練機(jī)器人算法直覺。給新手的建議是先改一個現(xiàn)成的小模型比如我今天寫的兩自由度機(jī)械臂把控制量、關(guān)節(jié)限位、時間步長挨個調(diào)一遍觀察變化。然后去找 Menagerie 里的模型看真實(shí)機(jī)器人模型的自由度定義和驅(qū)動器配置最后再研究 URDF 轉(zhuǎn) MJCF 或 ROS 集成這些進(jìn)階內(nèi)容。如果你已經(jīng)裝了 MuJoCo別光收藏這篇文章現(xiàn)在就去跑一個簡單的循環(huán)。第一次看到機(jī)械臂在你的屏幕里點(diǎn)頭下墜的時候那種感覺遠(yuǎn)比看任何教程都有說服力。后面真做強(qiáng)化學(xué)習(xí)訓(xùn)練時你可以再研究dm_control和 Gymnasium 的封裝但前提還是先把這一步跑明白。