23 — 物理模擬不可以偷懶
2026-08-06 在一段現地場域的 Isaac Sim 6.0 調校裡下的結論。 這一篇不談某個參數怎麼調,談的是為什麼「先讓它動起來」的每一個捷徑, 最後都會以一個看起來毫無關聯的症狀回來找你。
一句話
在物理模擬裡繞過物理,症狀不會消失,只會搬家 —— 而且會搬到一個 沒有人會聯想到成因的地方。
這篇的來源:一個花了兩週追錯方向的案例
症狀:堆高機把棧板放進貨架後退出時,棧板被帶出來;載貨轉彎時棧板歪斜、 最後陷進車體。
追過的方向(依序,每一個都花了數十輪模擬):
| 追的方向 | 結果 |
|---|---|
| 叉齒插入高度 | 掃了四輪,收斂到一個可行帶 |
| 叉齒傾角 | 改了正負號、掃了角度 |
| 叉齒↔棧板摩擦 | 調到 μ=2.0(真實值的四倍) |
| 板車檯面摩擦 | 調到 2.0/1.6,十輪後還原 |
| 退出時的 z 補償基準 | 改了程式,一輪後還原 |
| 退出時多降一點 | 加了參數,效果是雜訊 |
| 放貨高度、releaseDrop | 整套推導套用,n=5 無結論 |
真正的成因:底盤不是輪系車,是三個直接寫世界座標的關節
(world_x / world_y / world_yaw),偏航關節的 maxForce 是 2×10⁶ N·m,
而指令端的角加速度上限設成 0(= 不限制)。
於是每一次轉彎都是瞬時角速度階躍,棧板承受的側向加速度遠超過任何 真實摩擦係數能提供的上限。
棧板不滑的條件 a_側 < μ·g
a_側 = α·r
→ α_max = μ·g / r ≈ 3.3 rad/s²(μ=0.5、r=1.5 m)
現況可達 α = τ/I = 2×10⁶ / 1800 ≈ 1100 rad/s² ← 上限的 330 倍
在這個數字面前,前面那七個方向沒有一個能起作用。 μ 就算調到 2.0(四倍真實值),也只是把上限從 3.3 抬到 13 —— 還差 85 倍。
捷徑清單:每一個都「有效」,每一個都在別處收費
這個場景裡至少有五個為了「先讓它動起來」而做的簡化。它們的共同點是 當下真的解決了眼前的問題,所以沒有人回頭質疑。
| # | 捷徑 | 當下解決了什麼 | 後來在哪裡收費 |
|---|---|---|---|
| 1 | 底盤用 3-DOF 自由關節,不用輪系 | 車能精準走到任何位姿 | 轉彎的角加速度無上限 → 棧板滑 |
| 2 | 叉齒 restOffset = −0.025 |
叉齒能通過滑軌之間的空隙 | 叉孔裡多出 25 mm 餘隙 → 咬不住 |
| 3 | μ 調到 2.0(真實 0.5 的四倍) | 棧板在叉上抓得住一點 | 退出時同一個摩擦把棧板拖出來 |
| 4 | max_yaw_accel_deg = 0 |
轉向反應快、路徑跟得緊 | 角速度階躍 |
| 5 | drive maxForce = 真實牽引力的 100 倍 |
定位誤差小、settle 快 | 任何指令都被當成運動學瞬移執行 |
注意 2 和 3 的關係:2 造成咬合變鬆,於是有人用 3 去補;而 3 又造成退出時 拖動變嚴重,於是又去調退出的 z 補償。捷徑會生出捷徑。
為什麼這麼難被發現:三個結構性原因
① 捷徑在失敗訊息裡完全不可見
「退出時棧板被帶走」這句話裡,沒有任何一個字指向「底盤沒有輪子」。 兩者之間隔著:轉彎 → 角加速度 → 側向力 → 摩擦上限 → 棧板相對叉齒滑移 → 落點偏移 → 咬合姿態改變 → 退出時接觸面不同。
症狀與成因之間隔了七層,而每一層都有自己的可調參數。 於是排查會停在第一個「調了有反應」的參數上。
② 捷徑當初是對的,紀錄也還在,但沒人回頭讀
set_fork_rest_offset 的 docstring 裡,寫下捷徑的人自己就預測了副作用:
[副作用]叉齒與棧板叉孔的咬合也會鬆 2.5 cm —— 若之後出現抓不住/打滑, 先把這個值調回 0 再排查,不要同時動摩擦。
「抓不住/打滑」正是後來的症狀。而我們調了幾十輪摩擦、高度、傾角、補償, 沒有人回頭讀那段 docstring。
可操作的規則:排查一個症狀時,先 grep 所有「為了繞過問題而加的參數」 的註解,看有沒有人預測過這個症狀。寫捷徑的人通常知道它會在哪裡壞。
③ 捷徑的前提會過期,而捷徑不會自己失效
restOffset = −0.025 是為了解決「叉齒左緣 −1.212 壓到滑軌 −1.19」的 2.2 cm 干涉。
但那是叉齒中心在 y=−0.820 時的事。後來對位機制生效,叉齒中心變成
y=−0.650(正好在軌道空隙中心),干涉隨之消失 ——
而那個 -0.025 還每一輪自動套用著。
實測(vertex_scan):在叉齒的高度上,最左的實體是 y=−1.093,
而叉齒左緣是 −1.042 —— 有 51 mm 餘裕。docstring 說的「左軌 −1.19」
是位於 z ≤ 0.985 的結構件,叉齒根本碰不到。
可操作的規則:每一個繞過型的參數,註解裡要寫死它的前提是什麼、 以及怎麼驗證那個前提還成立。沒有前提檢查的捷徑會活得比它的理由更久。
怎麼判斷「這是簡化還是捷徑」
不是所有簡化都是捷徑。判準是它有沒有移除一個會約束結果的物理機制。
| 問題 | 是簡化(可接受) | 是捷徑(會收費) |
|---|---|---|
| 它移除了什麼? | 只移除計算量、外觀細節 | 移除了一個上限或約束 |
| 待測的量會受影響嗎? | 不會 | 會,但要隔好幾層才看得出來 |
| 例子 | 輪子的視覺網格簡化 | 輪子的關節改成 Fixed |
| 例子 | 遠處物件不做碰撞 | 摩擦調到非物理值去補償 |
核心問句:這個簡化有沒有拿掉一個「本來會讓某件事做不到」的限制?
真車轉不快,是因為輪胎摩擦圓限住了它。把輪子拿掉,就沒有任何東西限制轉速 —— 限制消失了,而限制正是物理模擬的價值所在。
反面:什麼時候該用參數限制
不是說永遠要模擬完整物理。但用參數模仿限制與讓限制自己長出來要分清楚:
用參數模仿: max_yaw_accel_deg = 180 ← 我猜一個值,而它與載重、地面、輪胎無關
自己長出來: 輪胎摩擦圓 ← 載重變了、地面變了,上限自動跟著變
參數版在單一工況下可以調準;一旦工況變化(換 lane、換載重、換路徑), 就要重調。而重調的人不會知道那個值當初是怎麼來的。
在這個案例裡,「任意 lane 互相搬動」正是那個讓參數版失效的工況變化 —— 每條 lane 的轉角都不同,沒有一個固定的
max_yaw_accel_deg對所有 lane 都對。
檢查清單
開始調參之前,先過一遍:
- [ ] 待測的物理量,它的上限由什麼決定? 那個機制在模型裡存在嗎?
- [ ] 算一次數量級:需要的力/加速度 vs 模型能提供的。差一個數量級以上就不要調參
- [ ] grep 所有繞過型參數(
*offset*、*filter*、*disable*、非物理的摩擦值) 的註解,看有沒有人預測過眼前這個症狀 - [ ] 每個繞過型參數,它的前提還成立嗎?去量,不要讀舊註解
- [ ] 質量、力、慣量與真實設備比對一次(真實規格見 doc 18)
- [ ] 如果答案是「模型缺了一個限制」,不要用參數去模仿它 —— 評估把那個機制放回去的成本,通常比追七層症狀便宜
相關
- doc 18 — 怎麼找真車的物理參數(VDI 2198 型錄)
- doc 19 — 調參實驗方法論
- doc 22 — 幾何與量測紀律
- 該專案的具體實施計畫:內部調校專案 docs/171(未公開)