ボールが転がらなかった日 — モデリングの罠と次元解析【アヒルで学ぶフィジカルAI 番外編】

Ember 編集部 ・ 2026-08-28 ・ 約5分 ・ robotics

本編全8回を読んでくれた方に、開発現場からの実録を1本。FUTSAL DUCKSのボールを球から多面体に作り替えたら、 まったく転がらなくなった 話です。キックしても、同じ面を下にしたままツーッと滑っていく。氷の上のホッケーパックのように。 犯人は摩擦ではありませんでした。面の数でもありませんでした。犯人は、私が「空気抵抗のつもり」で書き足した、たった1つのパラメータ。そしてこの事件は、本編で扱った「シミュレータと現実」の関係を、教科書のどんな説明よりも鮮やかに見せてくれます。 物理シミュレーションのバグは、エラーメッセージを出してくれません。 「それらしく間違った物理」として、しれっと動き続ける のです。

事件の背景 — なぜボールを多面体にしたのか 発端はゲームデザインの問題でした。アヒルの歩行速度は最大0.25m/s(本物の実機ポリシーの仕様です)。ところが球のボールは一度転がり出すと延々と転がり続け、誰も追いつけない。フットサルが「ボールを眺める会」になっていました。 そこで「多面体にして転がりにくくしたら?」というアイデアが出ます。平らな面で接地すれば自然に止まるはず。まず20面体(サイコロのd20)で試し、最終的に 切頂二十面体 ——五角形12枚+六角形20枚、つまり本物のサッカーボールの形——に落ち着きました。白黒の柄も幾何学から自然に出る、我ながら気の利いた解だと思っていました。 ところが、これが転がらない。滑るだけ。 捜査1: 摩擦をいじっても何も変わらない 最初に疑ったのは摩擦です。ボールの摩擦係数を上げ下げして、キック後の到達距離を測るテストハーネスを作りました(このゲームは物理も全部ブラウザで動くので、こういう計測が数十秒で回せます)。 結果、 摩擦係数を6倍変えても、到達距離が小数点以下まで一致 。パラメータが効いていません。 これが最初の学びでした。MuJoCoでは、2つの物体が接触したときの摩擦は 両者の摩擦係数の要素ごとの最大値 で決まります。床はアヒルが歩くために高摩擦(滑る床では歩けないことは、本編第2回でやりました)。だからボール側をいくら低摩擦にしても、床の高摩擦が常に勝っていたのです。解決には priority という属性を使います——優先度の高いジオムの摩擦だけが採用され、ボールと床の接触にはボールの値が、アヒルと床の接触には床の値が効くようになる。ボールだけツルツル、アヒルはグリップ、が両立します。 捜査2: 「転がっているか」を数値で測る 摩擦が効くようになっても、まだ滑ります。ここで見た目の印象に頼るのをやめて、 転がりを定義して測る ことにしました。 純粋に転がっていれば1.0、完全に滑っていれば0。測ってみると—— ほぼ0 。ボールは回転そのものをしていない。摩擦の問題なら「回転が足りない」はずで、「回転がゼロ」は別の何かです。回転を殺している犯人がいる。 真犯人 — 1つの数字、2つの次元 犯人は、ボールの自由関節に私が書いたこの1行でした。 「速度に比例する抵抗=空気抵抗っぽくなるだろう」という軽い気持ちの1行です。問題は、自由関節が並進3軸+回転3軸の6自由度を持ち、 この係数が6軸すべてに同じ数値で掛かる こと。 ここで高校物理の出番です。抵抗が運動をどれだけ速く殺すかは「係数÷慣性」で決まります。 並進 : 慣性は質量 = 0.01kg。減速の時定数は約10秒。穏やかなブレーキ。 回転 : 慣性は慣性モーメント = 質量×半径²のオーダー。半径3.5cmなら約8×10⁻⁶。時定数は 約10ミリ秒 。 同じ「0.001」が、回転側では並進の千倍以上の強さで効いていたのです。キックの瞬間にどんな回転が生まれても10ミリ秒で消滅する。回れない物体は、滑るしかない。 そもそも並進の減衰係数はN·s/m、回転のそれはN·m·s/radで、 単位からして別の量 です。「1つの数字で両方まかなえる」と思った時点で、次元解析を怠っていました。ちなみにMuJoCoには本物の空気力学モデル(空気の密度と粘性を設定すると、形状に応じた抗力や回転抵抗を計算してくれる機能)が最初から用意されています。正しい道具があったのに、雑なノブで代用した私の完敗です。 これは「シミュレータの欠陥」なのか 修正後、ボールはドリブル速度でrollRatio 1.08(完全な転がり)、シュート速度では最初スキッドしてから転がり出す——実球そっくりの挙動になりました。白黒の柄がコロコロ回るのが見えます。 さて、この事件をどう総括するか。「シミュレータは現実を写せていなかった」のでしょうか。 違います。 MuJoCoは、私が仕様書に書いた世界を寸分違わず解いていました。 「並進にはごく弱く、回転には猛烈に効く粘性の中にあるボール」——奇妙ですが数学的に矛盾のない世界です。シミュレータはその世界の正確な答えを返し続けていた。現実とズレていたのは、解く側(ソルバ)ではなく、<strong 私が書いた世界の記述(モデル)</strong の方でした。 この区別—— モデルの欠陥とソルバの欠陥は別物 ——は、フィジカルAI全体を貫く背骨です。その上で、本編最終回で話したことを思い出してください。どんなに注意深く書いても、モデルは現実とどこかズレます(剛体近似、摩擦モデルの単純化、接触の柔らかさのパラメータ…)。だから業界は「正しい世界を1つ作る」ことを諦め、「 十分に近い世界の集合を作り、そのどれでも動けるように育てる 」——ドメインランダム化——を定石にしたのです。今回のバグは意図せざる「変な世界」でしたが、訓練では変な世界を わざと 大量生産している、とも言えます。 今回の道具箱 ① 次元解析 — パラメータを触る前に単位を確認する。「同じ数字」は「同じ強さ」を意味しない。係数÷慣性で時定数を暗算するだけで、この事件は未然に防げた。 ② 接触パラメータの合成規則 — 2物体の接触では、摩擦は要素ごとの最大値で合成される( priority で上書き可)。「片方だけいじったのに効かない」ときはまず合成規則を疑う。 ③ 物理量でデバッグする — 物理のバグはエラーを出さず「それらしく間違った物理」として現れる。見た目の印象ではなく、rollRatioのような 仮説を切り分ける量を定義して測る 。「摩擦不足なら回転は減るだけ、ゼロにはならない」——測定が犯人の候補を絞ってくれた。 結び シミュレータは嘘をつきません。ただし、 あなたが書いた通りの世界について 正直なだけです。物理シミュレーションと付き合うとは、ソルバを信じ、モデルを疑い続けることです。 ボールは今日もピッチで転がっています。今度は、ちゃんと回りながら。 本編を最初から読む方は第1回からどうぞ。