Sim Env Builder
目標:從一個描述開始,以盡可能少的用戶互動次數(理想情況:請求 -> 一次確認 -> 完成),交付一個功能正常、物理正確且美觀的模擬環境。 品質標準:所有冒煙測試都必須通過,你必須查看過渲染結果,且它們必須符合請求。
SKILL_DIR 以下代表本檔案所在的目錄。Python 輔助函式庫位於 SKILL_DIR/scripts/simkit。
階段 0 - 設定(1 分鐘)
PY=$(bash SKILL_DIR/scripts/setup_env.sh) # 包含 mujoco, trimesh, usd-core, gymnasium 的 Python
$PY SKILL_DIR/scripts/check_platform.py # 檢查作業系統、GPU、Isaac 可用性
選擇環境資料夾:除非使用者有指定名稱,否則使用當前目錄下的 ./<簡短蛇形命名>/。
階段 1 - 選擇後端(參考 reference/backend_choice.md)
Isaac Sim 需要 NVIDIA RTX GPU 和 Linux/Windows 系統。MuJoCo 可在任何平台上執行。
- MuJoCo:預設選項。適用於接觸豐富的操作與運動、快速迭代、CPU/筆電/macOS、中小型場景、基於模型的控制、可重複的研究、以及 MJX/Warp 的 GPU 強化學習。
- Isaac Sim:適用於逼真的 RTX 渲染或合成資料(Replicator)、逼真的 RTX 雷射雷達/相機感測器、Isaac Lab 中的數千個並行環境、大型 USD/Omniverse 場景,或使用者已在使用 Isaac 的情況。
- 如果 Isaac 是更合適的選擇,但
isaac_runnable_here為 false,請在確認訊息中說明,並提供選項:在此處使用 MuJoCo 建構並完整測試,同時匯出 USD 場景和 Isaac 冒煙測試腳本,供 GPU 機器使用(參考 reference/isaac.md)。切勿聲稱你並未實際執行的 Isaac 結果。
階段 2 - 在建構前與使用者確認(參考 reference/confirmation.md)
- 首先進行快速資產掃描,以使需求摘要有所依據:使用
find_assets(透過 Poly Pizza MCP)列出每個物體角色,每個角色提供 1-3 個查詢。 - 發送一份簡短的需求摘要(範本請見 reference/confirmation.md):包含後端及其原因、場景中將包含的內容、機器人/代理人及其控制介面、哪些資產來自 Poly Pizza 以及哪些為程序化生成、關鍵物理參數(重力、摩擦力、時間步長)、交付物,以及 2-4 個問題(每個問題都附上你建議的預設答案)。
- 如果存在 AskUserQuestion 工具,請使用它來提問。否則,以摘要作為你的回合訊息並等待。在使用者回答之前,不要開始建構。
例外情況:使用者要求不要詢問 / "直接建構" -> 將假設寫入
SPEC.md,然後繼續。 - 將確認的決定記錄在
<env>/SPEC.md中(範本請見 reference/confirmation.md)。
階段 3 - 資產:優先使用 Poly Pizza,程序化生成作為備案(參考 reference/assets.md)
對於每個物體角色:
- 使用
find_assets/search_models-> 讀取判斷結果(已找到 / 弱符合 / 未找到)。 - 找到或弱符合 -> 使用
preview_models([...IDs])並查看:物體是否正確?風格是否一致?在逼真場景中拒絕卡通風格的玩具、多物體捆綁包和比例錯誤的物體。 - 接受 -> 使用
download_model(id, "<絕對環境路徑>/assets/polypizza", "<角色>")(版權資訊會自動記錄在assets/polypizza/CREDITS.md中)。 - 未找到、被拒絕或下載錯誤 -> 程序化生成:使用
simkit.procedural的配方(樹木、建築物、家具、街道設施、大門、餐具、地形...)或自行從基本幾何體 /lathe/extrude/rock組裝。絕不能遺漏任何要求的物體。 - 撰寫
<env>/assets/ASSETS.md:包含角色 | 來源(Poly Pizza ID + 標題 + 授權,或程序化配方) | 真實尺寸 | 原因。如果模型的標題與角色不同(例如使用 "公園長椅" 作為野餐桌),請說明原因以及它為何仍然適用 - 否則重新搜尋或改用程序化生成。 同時列出被拒絕的候選資產(ID + 原因:"卡通風格"、"12 個物體的捆綁包"),以便即使最終全部使用程序化生成,搜尋過程仍可追溯。 Poly Pizza 模型是靜態網格:關節型機器人和車輛來自simkit.robots(你可以使用 Poly Pizza 網格作為視覺幾何體來包覆它們)。
階段 4 - 建構(參考 reference/robots.md, templates/)
mkdir -p <env> && cp -r SKILL_DIR/scripts/simkit <env>/simkit # 內嵌:環境是自包含的
cp SKILL_DIR/templates/build_env.py SKILL_DIR/templates/env.py SKILL_DIR/templates/view.py <env>/ # 編輯前兩個檔案
build_env.py會確定性地重新生成所有內容(使用種子) ->scene.xml。 使用simkit.scene.Scene(天空、地面、燈光、注視相機、用於原始 MJCF 的章節)、simkit.assets.glb_to_mujoco(glb, env, name, target={"height": m}, yaw_deg=..)處理 Poly Pizza 模型 (真實世界尺寸、Z 軸朝上、按顏色分組的部件、盒子/凸包/部件碰撞)、simkit.procedural處理 備案和地形、simkit.robots處理經過測試的機器人(四軸飛行器、汽車、差速驅動、機械臂- 夾爪、四足機器人)及其控制器。
- 必須使用真實世界比例(參考 reference/assets.md 中的尺寸表)。物體應安放在它們的支撐物上。
- 使用者命名的每個物體類別都必須在總覽中清晰可見且尺寸可讀("岩石小徑"需要你能看見的岩石,而不是 7 公分的微粒;"粗糙地形"需要在機器人比例下可見的起伏)。冒煙測試會將微粒列在
hints下。 - 至少需要 2 個命名的相機:一個總覽視角,另一個聚焦在主體上(機器人/任務物體約占畫面的 30-50%;如果有機器人,可以使用機器人上的相機)。固定的 感測器相機不得被機器人的初始姿勢阻擋(冒煙測試會標記 > 15% 的遮擋率)。
world.py(當場景有自己的動力學時):make_world(model)->world(model, data), 在每個物理步進之前由冒煙測試、檢測器、展示、env.py、view.py 和儀表板呼叫。 浮力/阻力、風、輸送帶、交通號誌週期、腳本化的行人和 NPC 車輛都位於此處, 絕不在 controller.py 內部(否則被動測試或 RL 策略將在一個船舶下沉、號誌從不變化的世界中執行)。 需要步進模擬的任務檢查也必須呼叫它。controller.py:一個腳本化的示範策略 (make_controller(model)->ctrl(model, data)) 用於 展示環境的功能(無人機飛過閘門、汽車行駛路線、機械臂觸及物體...)。 在模組層級設定DEMO_SECONDS = <完整示範長度>:冒煙測試將錄製並 檢查整個示範(抓取、抬起、放置 / 每個閘門),而不僅僅是前 4 秒。task_checks.py:函式check_<名稱>(model, data) -> {"status", "value", "expect", "details"}用於任務層級的物理檢查。為使用者指定或確認的每個成功標準建立一個檢查(以其敘述命名:"feet in contact" ->check_feet_in_contact), 加上生成合理性檢查(機器人從其支撐位置開始)。至少 2 個。在首次執行前將閾值 AND 每個指標的明確定義(參考 點、單位:"rover base centre to lander centre, metres")寫入 SPEC.md 並保持 不變:窄幅未達標意味著需要修正控制器/場景,絕不能放鬆檢查(任何閾值 變更必須出現在 "Changes from the confirmed plan" 下)。通過但誤差在限制值的 15% 以內的檢查 應報告其裕度("tilt 25.8 deg vs 30 deg limit")。你聲稱的任何行為 (障礙物減速、交通號誌週期、浮力、夾爪夾持)都需要一個能測試該行為的檢查, 例如在路徑上放置一個障礙物並斷言機器人停止。場景的每個關鍵物理屬性 都需要一個帶有物理目標的量化檢查:球的滾動減速度、 船的吃水線、探測車的爬坡能力、低重力下的墜落時間、夾爪的夾持力。 必需的感測器也需要檢查:在示範期間渲染機器人相機,並斷言 非空白影像顯示了任務(例如,前方的樹行);對於雷射雷達,斷言合理的距離。 在腳本化的執行軌跡 AND 2-3 個擾動版本(其他種子、起始姿勢、 初始速度、燈光相位)上執行每個任務檢查——僅在一個特定軌跡上成立的檢查 證明力不足。 夾持檢查要求兩個手指墊在整個抬起和搬運過程中都與物體接觸 (讀取data.contact),而不僅僅是 "物體上升了"。- 碰撞體:簡單且誠實。樹木:樹幹碰撞體,樹冠僅視覺顯示,除非使用者想要
樹冠碰撞。多旋翼飛行器:手臂膠囊或一個外殼,而不是一個會吞噬螺旋槳的盒子。
非凸面道具(顯微鏡、椅子、拱門、架子)使用
collision="parts"或幾個基本幾何體; 圍繞它們的一個凸包會阻擋明顯空曠的空間(檢查 collision_sheet.png)。 env.py:Gymnasium 風格的reset/step/render,包含任務觀察、獎勵存根、終止條件。 其__main__自我測試會透過 env API 驅動環境,並使用示範控制器(或一個 參考策略)執行,並列印任務目標是否達成——證明目標是可達的。- 除錯/相機骨架物體和標記:使用 geom group 4-5(永不渲染),不在資料相機中。
- 懸掛物體(天花板燈、掛牌、牆架):將它們命名為
mounted_*或hanging_*,以便浮動物體檢查跳過它們。 - 燈光:中性白色(無色調燈光);最多 2-3 個額外燈光;檢查白牆在渲染中保持白灰色。
- 不要添加超出使場景可讀所需的未經請求的裝飾;在需求摘要中列出可選的附加項目, 以便使用者可以選擇加入。
- 時間盒:記錄開始建構的時間(
date)。獎勵/延伸項目("摺疊是獎勵") 僅在所有必須項目通過後才添加,並且會優先被放棄。大約建構 60 分鐘後, 停止調整剩餘內容:將 REPORT.md 和 README 結果最終化並報告 (列出已放棄的項目)。從不回報的建構比一個有已知差距的建構更糟糕。 - 預算:一旦所有必須項目都存在且所有檢查通過,停止添加額外功能——完成視覺 檢查、README 和報告。額外功能作為想法放在 README 中。
- 每次更改後執行
$PY build_env.py;編譯錯誤會包含行號。
階段 5 - 冒煙測試(參考 reference/smoke_tests.md)。全部三個都是必需的。
cd <env> && $PY -m simkit.smoke scene.xml --out smoke --controller controller.py --task-checks task_checks.py
- 渲染測試 - 每個命名的相機 + 自動視圖都渲染出非空白、有照明、細節豐富的幀; 分割圖顯示哪些物體是可見的(未見的物體會被標記)。
- 物理檢測器 - 使用場景自身的設定對求解器/接觸/摩擦力/重力進行探測, 滾動穩定性、穿透、掉落、沉降、致動器階躍響應、確定性、 時間步長敏感性、即時因子、佈局(浮動/下沉/相互穿透的道具), 加上你的任務檢查。
- 視覺檢查 - 你必須打開並查看
smoke/contact_sheet.png、smoke/filmstrip.png、smoke/collision_sheet.png以及smoke/renders/中的特寫(使用 Read 工具),然後將 檢查清單填入smoke/visual_check.md,並附上具體的觀察。 修復每一個 FAIL。調查每一個 WARN(修復它,或在 README 中解釋為什麼這是預期的)。 重新建構並重新執行,直到判定結果為 PASS / PASS_WITH_WARNINGS-explained,並且視覺 檢查誠實地全部是 "是"。預計需要 2-3 次迭代;第一次渲染很少是正確的。 同時執行$PY env.py(隨機動作,有限觀察)。
階段 6 - Isaac Sim 交付物(僅在選擇 Isaac 時需要;參考 reference/isaac.md)
$PY -m simkit.usd_export scene.xml isaac/scene.usda(匯出 + 本地驗證),複製
SKILL_DIR/templates/isaac_smoke.py 到 isaac/,記錄如何在 GPU 機器上執行它。
明確說明哪些內容已執行,哪些尚未執行。
階段 7 - 交付
<env>/README.md:場景中有什麼,如何執行(建構、冒煙測試、環境、檢視器:
$PY view.py,它會即時執行示範控制器;單純的 mujoco.viewer 僅顯示被動物理),相機、致動器、控制器、任務檢查、資產
版權資訊、已知限制。向使用者的最終訊息:判定結果行、建構了什麼、哪些資產
來自 Poly Pizza 以及哪些來自程序化生成、冒煙測試結果、指向 smoke/showcase.mp4(人們觀看的影片)
、聯絡表和 filmstrip 的路徑、任何未完成或未驗證的內容,以及一個 "Changes from the
confirmed plan" 列表(替換的資產或授權、變更的尺寸/數量、放棄的附加項目)- 如果沒有則為空,
但始終存在。從 smoke/smoke_report.json 和 physics_report.json 複製每個數字(通過的檢查數、分數、錯誤);
逐字粘貼其 summary_line("Smoke PASS: examiner 19 pass / 0 warn / 0 fail; task checks 9/9 pass; render ok"),絕不憑記憶書寫計數。
一旦第一次冒煙測試執行完成,就開始撰寫 <env>/REPORT.md,並在每次修復後更新,以便
中斷的工作階段仍能留下報告;最終訊息是其最後的版本。
交付物佈局(嚴格遵守;工具和儀表板依賴於此)
<env>/
SPEC.md README.md REPORT.md build_env.py scene.xml env.py view.py controller.py task_checks.py
world.py (僅在場景有自己的動力學時需要)
simkit/ 內嵌輔助函式庫
assets/polypizza/*.glb assets/polypizza/CREDITS.md assets/polypizza/polypizza_assets.json
assets/meshes/ assets/procedural/ assets/ASSETS.md
smoke/ (showcase.mp4 contact_sheet.png filmstrip.png collision_sheet.png renders/ rollout.mp4
physics_report.json render_report.json smoke_report.json env_manifest.json
SMOKE.md visual_check.md)
isaac/ (僅用於 Isaac:scene.usda, isaac_smoke.py, README section)
歷經艱辛得出的規則(每一條都曾導致真實環境建構失敗)
- 保持在確認的範圍內。示範應展示使用者同意的內容(例如,"standing is enough" 意味著穩定的站立,而不是新的運動控制器)。應將時間花在場景、 資產、檢查和視覺審查上;將延伸想法列在 README 中。
<visual>設定:sc.set_visual("map", znear=.., zfar=..)等。每個元素只能出現一次。- 絕不手動計算相機的
xyaxes:使用Scene.camera(name, pos, target)/simkit.scene.lookat。 - 絕不手寫關鍵幀的 qpos 列表:設定
scene.home[joint] = value(機器人會為你處理);Scene.write會按關節名稱建構home關鍵幀,並設定位置致動器的 ctrl 以匹配。 - 扁平/薄的網格需要
inertia="shell"(glb_to_mujoco 會處理)。視覺幾何體:contype=0 conaffinity=0 group=2 mass=0;碰撞代理物件放在 group 3。 - 一個焊接到世界的機器人會與自己的底座碰撞:為底座/第一個連桿添加
<exclude>。