Sausage Erectos X 台座が無限に伸びていく ── 重心計算の自己参照バグとPayload CoMの発明
Payload CoMの無限ループ──自己参照問題との戦い
Sausage Erectosの物理シミュレーションで遭遇した最も厄介なバグは、重心(Center of Mass)の計算が無限ループに陥る問題だった。この記事では、自己参照問題の本質と解決策を深掘りする。
問題の概要
フィギュアの重心位置は、各パーツの質量と位置から計算される。しかし、パーツの位置はポーズ(関節角度)に依存し、ポーズの安定性は重心位置に依存する。自動ポーズ補正機能を入れた瞬間、この循環依存が表面化した。
重心位置 → ポーズの安定性判定 → ポーズ補正 → パーツ位置の変化 → 重心位置の再計算 → ...
自己参照問題の数学的定式化
この問題を数学的に表現すると、関数の不動点問題になる。
重心位置をC、ポーズパラメータをPとする。
C = f(P) ... 重心はポーズから計算される
P = g(C) ... ポーズは重心に基づいて補正される
これを合成すると:
C = f(g(C)) ... 重心は自分自身の関数
求めたいのはC* = f(g(C*))を満たす不動点C*だ。
収束条件の解析
不動点の存在と収束性は、バナッハの不動点定理で保証できる。合成関数h(C) = f(g(C))が縮小写像であれば、反復計算は必ず収束する。
|| h(C1) - h(C2) || ≤ k * || C1 - C2 || (k < 1 のとき収束)
実際の実装では、ポーズ補正の「強さ」を制御するパラメータα(0 < α < 1)を導入した:
function iterateCoM(initialCoM, maxIter = 20, alpha = 0.3) {
let com = initialCoM.clone();
for (let i = 0; i < maxIter; i++) {
const newPose = correctPose(com); // g(C)
const newCoM = calculateCoM(newPose); // f(g(C))
// 減衰付き更新(αが小さいほど安定するが収束が遅い)
const delta = newCoM.clone().sub(com);
com.add(delta.multiplyScalar(alpha));
// 収束判定
if (delta.length() < 0.001) {
console.log(`Converged at iteration ${i}`);
return com;
}
}
console.warn('CoM iteration did not converge');
return com;
}
ニュートン法との類似性
この反復解法は、ニュートン法(方程式の根を求める反復法)と構造が似ている。
ニュートン法: x_{n+1} = x_n - f(x_n) / f'(x_n)
CoM反復法: C_{n+1} = C_n + α * (h(C_n) - C_n)
ニュートン法はヤコビアン(導関数)を使って最適なステップサイズを計算する。私たちの実装ではヤコビアンの計算コストが高いため、固定ステップサイズαを使っている。これは「緩和法」とも呼ばれるアプローチだ。
他分野での類似問題
自己参照的な計算の問題は、様々な分野で出現する。
経済学の一般均衡: 価格が需要に影響し、需要が価格に影響する。ワルラスの模索過程(tatonnement)は、均衡価格を反復計算で求める手法だ。
制御工学のフィードバック制御: PID制御器は、出力と目標の差(偏差)に基づいて入力を調整する。ゲインが大きすぎるとオーバーシュートや発振が起こる──これはまさに私たちのCoM問題と同じ構造だ。
コンパイラの再帰下降構文解析: 相互再帰的な文法規則の解析で、同様の不動点計算が行われる。
ページランクアルゴリズム: Googleの検索ランキングも、Webページの重要度が相互参照によって決まる自己参照問題を解いている。べき乗法による反復計算で収束させる。
物理シミュレーションにおけるフィードバックループの回避策
物理シミュレーションの分野では、自己参照問題への一般的な対処法がいくつか確立されている。
1. 時間ステップの分離: 入力と出力を同じタイムステップで計算せず、1ステップ遅延させる。
// 今フレームの重心は、前フレームのポーズから計算
const com = calculateCoM(previousPose);
// 今フレームのポーズ補正は、今計算した重心に基づく
const newPose = correctPose(com);
previousPose = newPose; // 次フレームで使用
2. 反復回数の固定: 収束するまで反復するのではなく、固定回数(例:3回)で打ち切る。リアルタイムアプリケーションでは、完全な収束よりも処理時間の予測可能性が重要だ。
3. 履歴ベースの予測: 過去数フレームのCoM変化から次の値を外挿し、初期値を改善する。
最終的に、Sausage Erectosでは「時間ステップの分離」+「反復回数の固定(最大5回)」の組み合わせを採用した。視覚的な違和感が生じない範囲で、計算コストを抑えられる妥協点だ。
自己参照は、コンピュータサイエンスの至るところに潜んでいる。無限ループに陥るか、美しい不動点に収束するかは、系の設計次第だ。そしてその設計こそが、エンジニアリングの醍醐味である。