Isaac Sim 實戰筆記 GitHub

33 · 電梯與多樓層:轎廂、門檻,以及車站在會動的地板上

多樓層把一個新東西帶進場景:一塊會動的地板。車站在上面、跟著它走、在它停之前不該動——而這三件事各自有一個不會報錯的失敗方式。

這一篇記八個位置:轎廂為什麼升不起來、承重接觸怎麼驗、跳動是指令還是接觸、陰性結果怎麼寫才算數、門檻上那個 200 mm 的洞、擋住轎廂的其實是它自己的門、車在移動中的轎廂裡繼續開,以及運動學該由誰算。

驗證狀態:內容來自一段多車 + 多樓層的 Isaac Sim 場域前期研究(2026-07 至 08),兩台電梯、五台車的實測。數字連同量測條件列出。

1. 轎廂升不起來,而每一層檢查都是綠的

症狀是「發了升降指令而轎廂高度一直是 0」,看起來像驅動端沒送出去

而且沒有任何錯誤訊息:USD 檢查過、joint 建得出來、DriveAPI 也在,資產建置腳本印的 joints 清單裡那個關節也在——那是 USD 的視角,不是 PhysX 的

真正的證據要問 articulation 自己:

articulation 的自由度:['door_joint', 'door_joint_mirror']

轎廂的關節不在裡面。

1.1 成因:根附著不能帶自由度

原本的接法是 cabin_jointShaft(靜態剛體)→ Cabin

PhysX 把「靜態物體到 link」的關節當成 articulation 的根附著,而 reduced-coordinate articulation 的根只能是固定或浮動、不帶自由度——於是轎廂變成被釘住的根,自由度清單只剩兩片門。

修法是加一個沒有幾何、1 kg 的 Base,用 FixedJoint(body0 留空 = 世界)釘住,再讓 cabin_jointBaseCabin。這樣它是 articulation 的內部關節,也就是真的自由度。

井道那些牆維持靜態碰撞體——它們不該進 articulation

1.2 physics:targetPosition 在模擬中改沒有用

USD 屬性是初始值,PhysX 建 articulation 時讀一次。執行期要走 isaacsim.core.prims.Articulationset_joint_position_targets(..., joint_indices=...),指定單一自由度而不是整條餵。

這是 17 篇那五個「無聲失效條件」在關節上的具體形狀:寫得進去,不代表 runtime 會採用

1.3 判定要把「沒被推動」與「跟不上」分開

只看「指令 vs 實際」的落差時,兩者長得一模一樣——沒動的落差正好等於全行程

先問走到的最高點,再談誤差。

2. 承重接觸站得住:三個數字,三個量測坑

滿載 650 kg(轎廂 450 + 兩台車各 100)跑 0 → 8 m → 0:

量什麼 理論 實測
靜態下沉 mg/k 4.01 mm 4.22 mm(+5%)
等速追隨誤差 c·v/k 28.3 mm 27.2 mm
車相對轎廂的橫向滑動 0 0.0 mm

三個數字分別驗了彈簧勁度、阻尼、橫向約束三個獨立的假設,而且理論與實測對得上。承重接觸站得住——這是「讓車真的站在轎廂上」相對於「每步 teleport 車的位置」多出來的東西,而它成立。

2.1 三個會讓數字說謊的量法

① 靜態下沉不能在最低層量。 轎廂停在最低層時正好壓在 lowerLimit = 0.0 上,載重由限位承受而不是由驅動彈簧,量到的下沉是 0 ——一個看起來完美、實際上什麼都沒驗到的數字。要在行程中間量。

② 等速追隨誤差不能拿去跟「靜態下沉 ≤ 5 mm」的需求比。 那是兩種東西:追隨誤差是 c·v/k(阻尼力要有位置誤差才產生,與載重無關),下沉才是 mg/k。混著比會把一個正常的 31 mm 讀成「剛度不足」。

③ 載重要對上設計條件。 場景裡的車是空車 85 kg,而剛度是由「滿載 650 kg」反推的。用空車跑會得到一個偏寬鬆、而且看起來剛好擦過門檻的數字(5.07 對門檻 5.00)。

2.2 設計餘裕要留在設計目標上,不是靠調求解器

原本的剛度 k 是讓理論下沉正好等於需求上限反推出來的,餘裕是零。而 PhysX 的實際值比線性模型高 5~6%,於是必然超標。

改成需求 5 mm、設計目標 4 mm(留 1.25 倍),實測 4.22 mm,過。

從需求反推設計值 = 零餘裕 = 必然超標。 數值方法與線性模型之間永遠有個幾個百分比,那個百分比要事先讓出來,不是等超標了再去調 solver。

3. 跳動 22 mm:指令形狀,不是接觸

階躍速度指令下,兩台車在啟動 / 到頂反向 / 到底停的那一刻相對轎廂跳 22.0 mm(等速段是 0)。換成限加速度 1.0 m/s² 的梯形速度之後是 0.0 mm

時間分布本身就是答案:只在速度不連續的那三個瞬間出現,等速段乾淨。650 kg 在階躍速度下會產生遠大於 g 的減速度,車離地是必然的。

所以修法是在驅動端加加速度上限,不是solverPositionIterationCount 或摩擦。

這一項的判定一定要有對照。 「接觸太硬」與「指令太猛」在單一個跳動數字上長得一模一樣,分不開就會去調接觸參數——而那會把一個指令問題蓋成一組看起來有效的魔數。

4. 陰性結果怎麼寫才算數

要驗的是「剛體與碰撞分層有沒有造成間歇接觸失效」。五台車靜置 20 s(沉降 120 步之後看 1200 步):z 抖動、z 漂移、x/y 漂移全部 0.000 mm

全零。而全零最危險:

全 0 的結果一定要配正對照,否則「很穩」與「我的量尺沒在量」產生的輸出一模一樣

腳本內建的對照是把最後一台抬高 20 mm——它必須掉回地面。實測 −0.000 mm,量尺有效。

4.1 對照的基準要選對

第一版拿被抬高那台跟其他車的平均高度比,而五台車在不同樓層(z = 0 / 4 / 8),算出 −3000 mm,看起來像嚴重失效——實際上是基準錯了

要跟它自己被抬高之前的高度比。

「假失效」和「真失效」一樣會讓你花掉半天,而且它更難察覺,因為數字很大、很嚇人、看起來很像真的。

5. 門檻上那個 200 mm 的洞,只在「開進去」時發作

樓板的井道開口切在井道外緣,而轎廂地板在內緣,中間 panel/2 + clearance + wall 是一個什麼都沒有的洞——實測 0.20 m

車的驅動輪半徑 0.09 m。掉進 0.20 m 的洞再爬出來就是一次彈跳。

5.1 方向不對稱本身就是診斷指紋

開進轎廂那 1.6 m   →  19% 的步數輪子離地、彈到 76 mm、傾角 12.3°
開出來那一段       →  完全乾淨(0.2°、1.5 mm、0%)

同一台車、同樣的控制律,差別只有那一段有沒有跨過那個洞

若成因是控制律、摩擦或速度這類對稱的條件,進出兩個方向應該同樣受影響。只有幾何上的、有方向性的東西才會做出這種不對稱。

修法是補門檻板,與轎廂留 30 mm(真電梯是 20~35 mm)——不能補滿,轎廂會撞到它:

之前 之後
電梯 B 進入 1.6 m 11.6 s(4.3×)、傾角 12.3°、離地 19% 3.0 s(1.1×)、傾角 2.1°、離地 5%
電梯 A 進入 1.0 m (沒量過) 2.0 s(1.2×)、傾角 0.2°、離地 0%

5.2 ⚠ 負對照證明的是時間順序,不是因果

補上門檻板之後,某台電梯變成「卡在門口、退了 55 次」,看起來像新改動的 regression。負對照(暫時停用門檻板重建再跑)顯示確實是改動之後才出現的

而成因不是它。 逐步資料顯示:車起步時朝 0°、目標在正左方 90°,於是它在淨開口裡原地轉,卡在 31°。那個 90° 是離線工具放的(它一律把車放成 yaw=0),真實輪次裡車是從大廳開過來、本來就朝著門。

負對照證明「是這次改動之後才有的」,不證明「是這次改動造成的」——兩者之間還隔著逐步資料。

這一條很反直覺,因為負對照通常被當成因果的黃金標準。它在這裡失效的原因是:改動同時改變了觸發條件(門檻板讓車能開進去,於是離線工具擺放姿態的問題才有機會現形)。

6. 門檻板擋住的不是車,是轎廂自己的門片

補上樓層門檻板之後,即時輪次裡某台電梯的轎廂卡在 1.62 m 整整 480 秒,而它的目標一直是 8.0 m。

沒有錯誤訊息:仲裁器照樣發出授權,車端互鎖照樣正確地拒絕進入,整條鏈路每一段都在做對的事,只有轎廂沒動。

成因是幾何差了半個門厚。要讓開的是轎廂最外側的可動件,而算的時候拿的是轎廂地板的邊緣:

局部 y 世界 y
轎廂地板前緣 half_d = 1.60 13.60
門片外緣 half_d + door_thickness/2 = 1.63 13.57
舊的門檻板前緣 half_d + SILL_GAP = 1.63 13.57 ← 正好貼上

門片跟著轎廂上下,門檻板固定在樓層——兩者在同一個 y 上,轎廂一升起就撞。停在 1.62 m 是因為門片頂端(轎廂 +2.3 m)那時剛好抵到二樓的門檻板(4.0 m)。

6.1 只量相對量的量尺,量不到參考系本身有沒有動

驗「車搭電梯」的那支工具在壞掉的幾何上是綠的。因為它量的三件事全是「車相對轎廂」——轎廂卡死時車跟著不動,傾角 0.0°、相對位移 0 mm,漂亮的滿分

一支只量相對量的量尺,量不到參考系本身有沒有動。

補上第四項:讀轎廂關節的實際位置,一段走完之後離目標超過 50 mm 就判失敗。負對照(把門檻板搬回舊位置重跑)印出「轎廂沒到,停在 1.62 m」——與即時輪次同一個數字。

6.2 互鎖門檻要比停穩誤差大

順帶量到:轎廂停穩後的殘餘誤差 0.0~3.6 mm,而車端的對位門檻是 20 mm,約 5 倍餘裕。

門檻訂得比停穩誤差還小的話,車會被自己的互鎖永遠擋在門外,而那個症狀跟「轎廂真的沒來」一模一樣。

7. 車在移動中的轎廂裡繼續開

一輪五台物理車的實測:某台車在 t=97 進了三樓的轎廂,t=106 傾角 91.8°,t=120 躺在一樓的井道裡。

中間發生的事:轎廂開始下降,而車還在開。

下一個路網節點與當前節點是同一組 (x, y),只差樓層,所以車瞬間視為抵達,接著朝一樓大廳的節點走——那時轎廂還在 4 m 高。它撞上關著的轎廂門、卡住退出三次、爬上門,翻了。

7.1 兩道既有防線都沒接住,而各自都沒有錯

兩道防線的檢查範圍中間有一條縫,而那條縫剛好是這個情境。沒有任何一道防線寫錯,是它們的聯集不完整。

7.2 ⚠ 判準不能用「車與轎廂的高度差」

補上的規則是:車自己在轎廂框裡的時候,轎廂沒停妥就不准動

但判準不能寫成「轎廂高度與車的高度差多少」——車站在轎廂裡時,它的世界 z 跟著轎廂走,兩者永遠相等,那個條件恆真。

要問的是轎廂本身的狀態:

停在某一層(|cabin − round(cabin/樓高)×樓高| ≤ 20 mm)
  且 門開到八成以上

兩個都讀關節的實際位置。門全開的量從關節上限讀,不寫死——那是資產側由轎廂內寬算出來的。

這是一個參考系陷阱的乾淨例子:在會動的參考系裡,相對量會恆真或恆零,而那看起來像「一直都正常」。

8. 運動學只算一次

電梯的高度與門位移由調度那一側算(從車隊管理層的電梯狀態推),Isaac 這側不算運動學,只把收到的關節目標設進去(兩片門是鏡像,右門吃原值、左門吃負值)。

理由很直接:

兩邊各算一次的話,兩個「電梯在哪一層」遲早會分岔,而分岔的症狀是車掉進井道或被門夾住——不是錯誤訊息。

這是 12 篇講的「兩份狀態分歧」在多樓層上的版本,而它的後果更具體:分歧的那幾公分正好是門的開口。

傳輸走一條共用的 MQTT 通道,retain=True。兩個選擇都是量出來的:

9. 檢查清單


延伸閱讀:31 OmniGraph 與 ROS 2 橋接的真相(門是有的,只是沒有人下指令)、32 把車做成真的會動的車12 長跑維運(兩份狀態分歧)、17 6.0 的物理調參(寫得進去不代表被採用)。