跳轉到

D-20261028-05|bounded-step 接受容差放寬至 0.2 毫米

背景與驅動力

issue 001074 的 cost-r22 量測顯示,每幀至少 2 次原生試算,P95 以上的尖峰幀幾乎都有拒絕細分。試算 2 次的幀佔 70%,4 次佔 19%,6 次以上佔 11%。取捨分析把 bounded-step 尾巴修正(4a)列為「不回滾底線」的必要條件。使用者裁決(2026-10-27):細分可以放寬,但先研究;CCD 維持現狀。

另外,001077 記錄停損常數 BOUNDED_STEP_STALL_FRAMES(60)的量測依據已經過時:依據寫「正常賽況最長連續細分 40 幀」,但 Gr 起跑實測達 82/95 幀,並觸發停損 23/36 幀。

現行機制(src/physics-engine/bounded-step.ts)只有程式預設值,canon 沒有寫:

  • 初始分段 initial: 'half':每幀先做兩個半幀試算。
  • 接受條件:原生 jointConstraintErrors 的兩個值都要 ≤ 0.1 mm,且 CCD 查詢不可未決(needsSubdivision)。
  • 第一個值是關節錨點的線性誤差,取幀末與子步峰值的較大者。
  • 第二個值是旋轉表面位移:非自由軸的角誤差乘以 body2 包圍半徑,取 2r·sin(θ/2)。
  • 原生只回傳全世界的最大值。
  • 被拒時還原,二分重試。depthLimit 為 10,全部分支共用最多 63 次試算;用盡就整幀失敗。
  • 細分停損:依 D-20261025-01。

研究方法

研究在 src 凍結複本上進行,src 樹 digest 為 c689b5cc…,核心 SDK 為 8493a474…(wasm cf640ca2)。只替換 bounded-step.ts 的政策。不設定變體時,state hash 與原檔逐位元相同。

  • 篩選:15 個變體,各跑公版三場地 × mixed8/heavy8 × 2 種起跑(G、P0),每組 600 幀,逐試算記錄拒絕原因、接受段品質與關節歸因。另以 Gr 起跑 900 幀量測停損。
  • 品質:
  • CI 品質閘:track-collision-gates 的 CI 格、105 次穿牆守門、動態薄目標 4 組。
  • 公版平地航向掃描:3 車 × 24 航向 × 20 s。
  • 單車完賽:3 車 × 3 場 × 15 起跑(S01–S15,原定 30 起跑,因時程只跑一半)。
  • 成本:B0、T2、T5 三組。三場地 × 兩車組 × 三種起跑(P0、L+、G),每組 60 幀暖機後量 600 幀。同一場景內各變體交錯串行執行,量測前 CPU 忙碌 6–13%。每幀實付=engine.step+saveState;saveState 是 D-20261028-01 以前的舊版,在變體之間固定不變。

機制與研究結果

拒絕來自哪裡(現況 B0)

  • 篩選共 20,988 次試算,其中 3,294 次被拒:
  • 89% 只因旋轉表面位移超標;只因錨點超標佔 3%,兩者都超標佔 8%。
  • CCD 未決只有 1 次。
  • 拒絕當下的最大誤差多數來自輪胎鉸接(tire hinge),其次是導輪(roller),武器關節很少。
  • 被拒試算的錨點誤差 P95 只有 0.35 mm,旋轉位移 P95 為 0.53 mm。

哪些拒絕是真的需要

  • 關節爆開的上界(不檢查容差):
  • 整幀試算時,接受段的錨點誤差最大 58.7 mm(CC),等於關節已經爆開。所以接受檢查不能拿掉。
  • 兩個半幀時,最大錨點誤差 2.3 mm、旋轉位移最大 2.2 mm(CCh)。
  • 0.5 mm 容差下仍被拒的試算:錨點 P95 2.4 mm、旋轉 P95 1.6 mm,屬於 mm 級的真實事件。
  • 0.1–0.5 mm 之間的拒絕:佔現況拒絕的大多數,誤差在輪胎半徑(約 13 mm)的 4% 以下。研究判定這部分是保守過度。

initial: 'half' 的理由(新確認)

所有整幀起跑的變體(F1、F5、FA2R5、FR2、CC)都在動態薄目標守門失敗:自旋刀片從 0.3 m 外以 30 m/s 打 2 mm 板,7 次中 7 次嚴格穿過,幀末穿入 3.7–4.4 mm。所有半幀起跑的變體(B0、T2、T5、R1、CCh)都通過。

D-20261027-02 的薄目標保證是在兩個半幀上驗證的。半幀起跑等於薄目標品質的一部分,不是成本上的保守設定。半幀起跑的成本下限因此是每幀 2 次試算。

depthLimit 降低加「到底就接受」

  • D2:depthLimit 2。試算均 2.66,接受段錨點最大 1.2 mm。
  • FD1:整幀起跑、depthLimit 1。試算均 1.93,接受段錨點最大 5.6 mm。

這兩個變體在誤差最大的地方停止細分,把真正需要細分的事件也一起接受。收益小,品質最差,否決。

篩選摘要(12 場景合計)

變體 設定 試算均 拒絕幀/600 接受段錨點/旋轉 max mm 最長連續細分 薄目標
B0(現況) 半幀,0.1 mm 2.92 179 0.100/0.100 31 過
T2 半幀,0.2 mm 2.17 41 0.19/0.20 8 過
T5 半幀,0.5 mm 2.04 9 0.48/0.49 32 過
R1 半幀,錨點 0.1/旋轉 1.0 mm 2.10 20 0.10/0.68 4 過
CCh 半幀,只看 CCD 2.00 0 2.34/2.24 0 過
F1 整幀,0.1 mm 2.54 281 0.10/0.10 41 敗
F5 整幀,0.5 mm 1.13 34 0.47/0.50 4 敗
FA2R5 整幀,錨點 0.2/旋轉 0.5 mm 1.15 36 0.20/0.50 9 敗
CC 整幀,只看 CCD 1.00 0 58.7/8.6 0 敗
D2 半幀,depth 2+到底接受 2.66 160 1.20/1.86 47 —

成本(串行,18 組場景與起跑,幾何平均)

變體 world P50/P95/P99 ms 對 B0 每幀實付 P50/P95/P99 對 B0 試算 2 次/3–4 次/5 次以上
B0 40.2/86.3/— 1.00 1.00/1.00/1.00 71%/18%/11%
T2 33.4/59.2/— 0.83/0.69/0.67 0.89/0.75/0.73 93%/6%/1.4%
T5 32.0/50.6/— 0.80/0.59/0.48 0.88/0.69/0.57 99%/1%/0.4%
  • 改善最大的是 jungle-mixed8-G:world P95 169→60 ms,P99 237→84 ms。
  • 尾巴最差的仍是 urban-mixed8-G:T5 P95 77 ms,拒絕 61 幀。

品質

  • CI 閘(22 項):
  • B0 有 3 項紅,都是既有紅燈:A 20° yaw 差、B 斜向 15°、B 沿格線。
  • T5 也是 3 項紅:A 20° 轉綠;新增 A 牆端「試算拒絕 0–2,上限 ≤1(基線 0)」。該閘的拒絕上限是以 0.1 mm 容差的拒絕頻率訂的。
  • 105 次穿牆守門與薄目標 4 組全過。
  • 航向掃描(一次諧波幅/平均 |偏航|,單位 °):
車 B0 T5
speed 0.92/1.46 1.01/1.58
heavy 0.40/0.55 0.31/0.59
control 0.51/0.80 0.52/0.81

T5 與 B0 相當。最大偏航 speed 8.1→10.5°。 - 單車完賽(135 起跑):

指標 B0 T5
完賽 79 70
翻覆 12 12
掉頭 7 15
卡停 37 38

完賽差 9 場,兩比例 z≈1.1,不顯著;但 control-urban 從 6/15 完賽降到 1/15,掉頭從 4 增到 8,以完整 30 起跑確認(見下方「30 起跑定案比較」)。 - 停損: - B0 在 Gr 起跑最長連續細分 82/95 幀,觸發停損 23/36 幀,重現了 001077 的觀察。 - T2 在 Gr 起跑最長 6 幀,T5 最長 21 幀,兩者都未觸發停損。 - T5 在完賽集合中最長 9 幀,B0 為 80 幀,並觸發停損 32 幀。 - 決定性:同變體同場景的串行成本趟(每幀 saveState)與並行篩選趟(不呼叫 saveState),每 60 幀的 state hash 共 36 組全同。另外,各變體在無設定時與原檔逐位元相同。

30 起跑定案比較(3 車 × 3 場 × 30 起跑,1,080 場,零錯誤)

串行對並行逐幀 hash 9/9 相同;與前一輪 S01–S15 的 B0/T5 結果 270/270 逐值相同。

變體 完賽/270 對 B0 翻覆/掉頭/卡停 停損觸發幀 最長連續細分
B0(0.1 mm) 149 — 24/16/81 32 80
T2(0.2 mm) 146 −1.1 pt(Fisher p 0.86、McNemar 24/21) 28/17/79 0 9
T5(0.5 mm) 137 −4.4 pt(Fisher p 0.34、McNemar 31/19) 28/21/84 0 10
R1(錨點 0.1/旋轉 1.0 mm) 148 −0.4 pt 32/11/79 0 29
  • 完賽率三個變體都不顯著,但次要訊號都對 T5、R1 不利:
  • 翻覆提早:翻覆秒中位 B0 16.5 s、T2 11.5 s、R1 9.9 s、T5 7.8 s。speed-jungle 完賽 B0 5、T2 3、T5 與 R1 皆 0。
  • 輪胎磨耗:heavy 兩者皆完賽配對的磨耗比值中位 T2 ×1.10(p 0.0045)、T5 ×1.16(p 0.0001)、R1 ×1.11。
  • 研究閘 C 弧牆抬車:T2 4.9 mm,T5 與 R1 12.9–14.6 mm(上限 10 mm)。
  • control-urban(15 起跑時 T5 退步的格):B0 7、T2 8、T5 3、R1 14;T5 掉頭 7→13。
  • 結論:採 T2。實付 P95 ×0.75,比 T5 少省約 6 個百分點,換取完賽與翻覆行為和現況相當。

考慮過的選項

  • 維持現況(半幀,0.1 mm):P95 尾巴主要來自拒絕細分,不回滾底線無法達標。否決。
  • 整幀起跑(initial 'full'),可搭配任何容差:成本最低(試算均 1.0–1.15,world P50 約 ×0.5),但動態薄目標守門 7/7 嚴格穿過,牴觸 D-20261027-02。否決。若要整幀起跑,必須先讓 CCD 在整幀下達到薄目標保證;這屬於 CCD 範圍,與「CCD 維持現狀」的裁決衝突。
  • 只看 CCD 未決、不檢查關節誤差(CCh):成本降到下限(每幀 2 次試算),但關節誤差上界升到 mm 級(2.3 mm),失去爆開保護。否決。
  • depthLimit 降低加到底接受(D2、FD1):只在誤差最大的事件上停止細分。收益小,品質最差。否決。
  • 分車、分零件類別或依物體尺度的相對容差:原生 jointConstraintErrors 只回傳全世界的最大值,要分類或分車就得改 vendor,讓原生回傳逐關節(或逐類)的值與半徑。研究以 JS 逐關節重算做歸因,可行,但沒有量到這個方案的收益。旋轉位移本身已經乘上包圍半徑,是一種尺度化的指標。不在本決策範圍,視 T2 實施後的殘餘尾巴再評估。
  • 錨點與旋轉分開設容差(R1):30 起跑完賽 148,但翻覆提早、弧牆抬車超限。否決。
  • 容差 0.5 mm(T5):每幀實付 P50 ×0.88、P95 ×0.69、P99 ×0.57。30 起跑完賽 137(不顯著),但翻覆提早、heavy 磨耗 ×1.16、弧牆抬車超過 10 mm。否決。
  • 容差 0.2 mm(T2):每幀實付 P50 ×0.89、P95 ×0.75、P99 ×0.73;完賽 146,與 B0 相當;停損不觸發。採納。

決定

  • bounded-step 的接受容差由 0.1 mm 改為 0.2 mm,錨點線性誤差與旋轉表面位移共用同一個值。
  • 維持半幀起跑:明文記錄理由是 D-20261027-02 的動態薄目標保證以半幀驗證;不得改為整幀起跑,除非另立 ADR 在整幀下重新取得薄目標保證。
  • 維持 depthLimit 10、全部分支最多 63 次試算、CCD 未決必細分,以及用盡即整幀失敗。
  • 以上四個值改為 physics-pinned 的 protocol 常數,寫入 程式參數/protocol.md,不再只是程式預設值:
  • BOUNDED_STEP_TOLERANCE_M=0.0002(Math.fround)
  • BOUNDED_STEP_INITIAL_SPLIT=half
  • BOUNDED_STEP_DEPTH_LIMIT=10
  • BOUNDED_STEP_TRIAL_MAX=63
  • BOUNDED_STEP_STALL_FRAMES 維持 60,只改寫量測依據:
  • 0.2 mm 下,篩選 G/P0 起跑最長連續細分 8 幀,Gr 起跑最長 6 幀,30 起跑完賽集合最長 9 幀,都未觸發停損。
  • 0.1 mm 下,Gr 起跑最長 82/95 幀。
  • 停損改為只保護真實卡住的情境。

後果與影響

  • 玩家可見:關節允許的瞬時誤差上限由 0.1 mm 升為 0.2 mm,約為公版輪胎半徑的 1.5%,在遊戲鏡頭距離下為次像素。完賽率與 B0 相當;heavy 輪胎磨耗約 ×1.10。
  • 成本:
  • 每幀實付 P50 ×0.89、P95 ×0.75、P99 ×0.73。
  • 每幀試算 2 次的比例從 71% 升到 93%。
  • 成本下限是每幀 2 次試算,無法再透過 bounded-step 降低。要再降,只能從 CCD(薄目標保證)或原生單步成本下手。
  • 決定性與共識:容差是 physics-pinned 常數,改值會改變軌跡。全部 peer 同值,同幀同判,決定性不受影響(已以兩趟加串行對並行驗證)。
  • 範圍:
  • vendor:不改。
  • protocol 常數:新增上列四個常數;BOUNDED_STEP_STALL_FRAMES 改寫依據說明。
  • src:bounded-step.ts 的預設值改從 protocol 讀取;deterministic-world.ts 的呼叫端不變。
  • 重基線:canonical golden 不變(見已裁決第 3 點);conformance physics/track-entity-snapshot 的軌跡雜湊不變,快照雜湊隨 D-20261028-02 同批重基線;pre_launch 不保留相容。
  • 測試:
    • bounded-step.spec.ts 的預設值案例需要更新。
    • bounded-stall-world.spec.ts 的 001077 頂牆回歸(試算 ≤ 2)在新容差下仍應成立。
    • track-collision-gates 中以拒絕次數訂的上限依新容差重量,不得單純放寬(結果見已裁決第 4 點)。
    • 動態薄目標守門加入「半幀起跑」前提的明文斷言。
  • canon:
    • 程式架構/physics-engine.md 第 2.8 節補 bounded-step 接受條件、容差、半幀理由與上限。
    • 程式參數/protocol.md 新增常數。
    • 程式架構/testing.md 在量測口徑中加入試算數分布。
  • pinning:vendored 的版本值若涉及 physics version,需另行取得同意(比照 001057)。
  • 與 D-20261025-01 的關係:本決策修訂其「停損常數初值於實作研究時量測後定」的依據;世界層計數與接受規則不變。
  • 與 D-20261028-03/04 的關係:本決策只降低每步成本,不改 D、R 的推導;L0 與 L2 是否達標,要在三項降本(本案、D-20261028-01、D-20261028-02)都完成後重新量測。

已裁決

  1. 完賽率:30 起跑比較完成,採 T2(見上節)。產品實作與研究 T2 的三場抽樣完賽逐幀 hash 鏈相同 (urban S17、snowy S19、jungle S24,各 842/2,982/4,931 幀)。
  2. 常數與測試:四個常數寫入 src/system-constants/protocol/physics.ts,bounded-step.ts 的預設值與 deterministic-world.ts 的初始分段改讀常數。bounded-step.spec.ts 新增預設值案例(容差相等時接受、 超出一個 ulp 即細分、0.1–0.2 mm 之間接受);動態薄目標守門新增「正式 bounded-step 以兩個半幀起跑」的 明文前提斷言;001077 頂牆回歸(每幀試算 ≤ 2)在 0.2 mm 下仍成立。
  3. golden 與 conformance:canonical golden 不需重基線:0.2 mm 下 canonical 兩車 120 幀沒有誤差落在 0.1–0.2 mm 之間而被拒的試算,revision-19 80b1c05a… 在 Node 與 Chromium 不變;八車公版場景的軌跡則改變 (snowy-mixed8 自第 35 幀)。conformance physics/track-entity-snapshot 的 state hash 與 runtime hash 不變,snapshotSha256 因 D-20261028-02 的線上格式改變而與該決策同批重基線。
  4. 閘門判準:track-collision-gates 的試算拒絕判準在 0.2 mm 下重量,未放寬任何上限:倍數規則(分段 max ≤ 基線 max × 1.6)與基線無拒絕時「≤ 1 次」不變;騎縫例外的固定上限依 0.2 mm 研究閘重量(平地 沿格線與坡道沿格線上坡最大 3 次),保留同樣的 1.6 倍裕度由 8 次收緊為 5 次;量測容差寫成常數並守門, 容差改值時「閘設定」測試會紅。0.2 mm 下拒絕次數多為 0–5 次,小整數集合的 max 比值主要反映抽樣: 研究格 C 弧牆 heavy x=−0.8 基線 1–5、分段 0–10(上限 8)轉紅(0.1 mm 時 12–33/10–22)。曾試以拒絕來源的 量級(統計窗內逐幀全部試算最大接受指標超出容差之和)取代次數:該格 ×1.24 可過,但 CI 格 A 貼牆 20° 基線 0–55.4、分段 10–129.4 µm(×2.3)新紅,同樣受小樣本支配,未採用,改列觀察。判準如何在小計數下 維持鑑別力,交由維護者裁決(見後續 issue)。CI 格紅燈仍為既有的三格軌跡判準(A 20°、B 斜向 15°、 B 沿格線),拒絕列全過。
  5. 研究閘:C、D 以產品 T2 重跑,與研究 T2 逐值相同:C heavy x=−0.7 牆穿入 2.24 mm(上限 2 mm,另案 調查)、C heavy x=−0.8 拒絕列(見上點)、D 直行上坡地穿入 2.56 mm 與 C 判準窗長 0 的既有紅燈。
  6. 能力邊界與導輪:13 邊形起步、坡道等公版能力邊界案例已在 30 起跑研究以 T2 重驗通過;導輪觸地與 衝量比值中位 0.98,無退步。分類容差或相對容差視殘餘尖峰再評估,不在本決策範圍。