机の上でカチカチやってるあの玉、実は衝突力学の本質を全部含んでいるって知ってましたか?
「端の1個を引っ張って離すと、反対側の1個だけ飛び出す」——これ、運動量保存とエネルギー保存が同時に成り立つ唯一の解なんです。しかも、この原理は自動車の衝突安全設計から宇宙機のドッキングまで、現場でガチで使われている。
今回は、この「ニュートンのゆりかご」を数式→実装→数値検証→実務応用まで一気に解説します。最後にはブラウザで動くシミュレーターで実験もできるので、ぜひ手を動かしてみてください。
▶ 今回のシミュレーター: ニュートンのゆりかご シミュレーター(ブラウザで動作・登録不要)
ざっくり本質:なぜ「k個入ればk個出る」のか
まず直感を掴みましょう。5個の球が一直線に並んでいます。端の球を1個引っ張って離すと、反対側から球が1個飛び出す。なぜ2個や3個じゃないのか?
たとえ話で考えると:
- もし「1個が超高速で飛び出す」とすると、運動量は合うけど運動エネルギーが合わない(エネルギーが大きすぎる)
- もし「全部がゆっくり動く」とすると、エネルギーは合うけど運動量が合わない(運動量が小さすぎる)
- 両方を同時に満たせるのは「同じ個数が同じ速さで出る」だけ
一言でまとめる:ニュートンのゆりかごは「運動量保存則」と「運動エネルギー保存則」という2つの制約を同時に満たす、唯一の解を自動的に選んでいる装置なんです。
数式で理解する:2つの保存則が決める世界
運動量保存則
衝突前後で、系全体の運動量は保存されます(外力が働かない限り)。
m_1 v_1 + m_2 v_2 = m_1 v_1' + m_2 v_2'
ここで $m_1, m_2$ は質量、$v_1, v_2$ は衝突前の速度、$v_1', v_2'$ は衝突後の速度です。
反発係数(e)の定義
衝突前後の相対速度の比を反発係数 $e$ と呼びます。
e = \frac{v_2' - v_1'}{v_1 - v_2}
- $e = 1$:完全弾性衝突(運動エネルギーも保存)
- $e = 0$:完全非弾性衝突(くっつく)
- $0 < e < 1$:非弾性衝突(エネルギーが一部失われる)
等質量の場合の衝突後速度
質量がすべて等しい場合($m_1 = m_2 = m$)、衝突後の速度は驚くほどシンプルになります。
運動量保存と反発係数の式を連立すると:
v_1' = \frac{1-e}{2} v_1 + \frac{1+e}{2} v_2
v_2' = \frac{1+e}{2} v_1 + \frac{1-e}{2} v_2
$e = 1$(完全弾性衝突)の場合:
v_1' = v_2, \quad v_2' = v_1
つまり、速度が完全に入れ替わる。動いていた球は止まり、止まっていた球が同じ速度で動き出す。これが「k個入ればk個出る」現象の正体です。
コードで実装する:JavaScriptで再現
ここが最重要パートです。上記の数式を実際のコードに落とし込みます。このコードは、シミュレーターと同じ考え方で書いた最小実装です。
/**
* ニュートンのゆりかご 衝突計算コア
* @param {Array} balls - 球の状態配列 [{x, v, m}, ...]
* @param {number} e - 反発係数 (0〜1)
* @param {number} dt - 時間刻み (秒)
*/
function updateCollisions(balls, e, dt) {
const n = balls.length;
// 隣接球同士の衝突をチェック
for (let i = 0; i < n - 1; i++) {
const ball1 = balls[i];
const ball2 = balls[i + 1];
// 球の直径(今回は1.0と仮定)
const diameter = 1.0;
// 球同士の距離を計算
const distance = ball2.x - ball1.x;
// 衝突判定:距離が直径より小さい → 接触
if (distance < diameter) {
// 相対速度
const relativeV = ball1.v - ball2.v;
// 接近中のみ衝突処理(すでに離れているならスキップ)
if (relativeV > 0) {
// 等質量なので、衝突後速度は式(3)(4)で計算
const v1After = ((1 - e) / 2) * ball1.v + ((1 + e) / 2) * ball2.v;
const v2After = ((1 + e) / 2) * ball1.v + ((1 - e) / 2) * ball2.v;
// 速度を更新
ball1.v = v1After;
ball2.v = v2After;
// めり込み防止:球を正しい位置に戻す
const overlap = diameter - distance;
ball1.x -= overlap / 2;
ball2.x += overlap / 2;
}
}
}
// 重力による速度更新(振り子の近似:単振動として扱う)
const g = 9.81; // 重力加速度 [m/s^2]
const L = 1.0; // 弦の長さ [m]
for (let i = 0; i < n; i++) {
const ball = balls[i];
// 単振動の復元力(微小角近似)
const theta = Math.asin(ball.x / L);
const acceleration = -g * Math.sin(theta);
ball.v += acceleration * dt;
ball.x += ball.v * dt;
}
}
// 使用例:5球の初期化
const balls = [
{ x: -2.0, v: 0.0, m: 1.0 }, // 左端の球(引っ張られた状態)
{ x: -1.0, v: 0.0, m: 1.0 },
{ x: 0.0, v: 0.0, m: 1.0 },
{ x: 1.0, v: 0.0, m: 1.0 },
{ x: 2.0, v: 0.0, m: 1.0 }
];
// シミュレーションループ(1ステップ)
const dt = 0.001; // 1ms刻み
updateCollisions(balls, 0.8, dt); // e=0.8で非弾性衝突
このコードのポイント:
- 隣接球のみチェック:一直線に並んでいるので、隣同士だけ調べれば十分
- 接近中のみ処理:離れていく衝突は無視(二重処理防止)
- めり込み補正:数値誤差で球が重ならないように位置を調整
- 振り子の復元力:単振動近似で重力の効果を計算
数値例で確かめる:実際に計算してみる
具体的な数値で検証しましょう。5球のニュートンのゆりかごで、左端の球を速度1.0 m/sで衝突させます。
条件設定
- 球の質量:$m = 0.1 \text{ kg}$(すべて等しい)
- 反発係数:$e = 1.0$(完全弾性衝突)
- 左端の球(球1)の初期速度:$v_1 = 1.0 \text{ m/s}$
- 残りの球(球2〜球5)の初期速度:$v_2 = v_3 = v_4 = v_5 = 0 \text{ m/s}$
衝突1:球1 vs 球2
運動量保存:
0.1 \times 1.0 + 0.1 \times 0 = 0.1 \times v_1' + 0.1 \times v_2'
0.1 = 0.1(v_1' + v_2') \quad \Rightarrow \quad v_1' + v_2' = 1.0
反発係数($e=1.0$):
1.0 = \frac{v_2' - v_1'}{1.0 - 0} \quad \Rightarrow \quad v_2' - v_1' = 1.0
連立して:
v_1' = 0 \text{ m/s}, \quad v_2' = 1.0 \text{ m/s}
球1が止まり、球2が速度1.0 m/sで動き出す。これが「速度の入れ替わり」です。
衝突2:球2 vs 球3
球2が速度1.0 m/sで球3(静止)に衝突。同じ計算で:
v_2' = 0 \text{ m/s}, \quad v_3' = 1.0 \text{ m/s}
最終結果
これが球4→球5と連鎖し、最終的に:
- 球1〜球4:速度 $0 \text{ m/s}$
- 球5:速度 $1.0 \text{ m/s}$
運動量の確認:
- 衝突前:$0.1 \times 1.0 = 0.1 \text{ kg·m/s}$
- 衝突後:$0.1 \times 1.0 = 0.1 \text{ kg·m/s}$ ✓ 保存
運動エネルギーの確認:
- 衝突前:$\frac{1}{2} \times 0.1 \times 1.0^2 = 0.05 \text{ J}$
- 衝突後:$\frac{1}{2} \times 0.1 \times 1.0^2 = 0.05 \text{ J}$ ✓ 保存
完全に一致。これが「1個入れば1個出る」の数学的根拠です。
シミュレーターで遊ぶ:3つの実験
ここからは実際にシミュレーターを開いて試してみましょう。
実験1:反発係数を変えてみる(e=0.8)
操作:反発係数のスライダーを0.8に設定。左端の球を1個引いて離す。
結果:
- 右端の球は飛び出すが、徐々に振幅が減衰
- 運動量バーは基準線に張り付いたまま(保存されている)
- 運動エネルギーバーが衝突ごとに少しずつ下がる
なぜ?
$e=0.8$ だと、衝突ごとに運動エネルギーが失われます。例えば球1と球2の衝突:
v_1' = \frac{1-0.8}{2} \times 1.0 + \frac{1+0.8}{2} \times 0 = 0.1 \text{ m/s}
v_2' = \frac{1+0.8}{2} \times 1.0 + \frac{1-0.8}{2} \times 0 = 0.9 \text{ m/s}
球1が完全には止まらず、少し動いてしまう。これがエネルギー損失の原因です。
実験2:k=2で2個同時に引く
操作:「引き上げる球の数」を2に設定。左端2個を引いて離す。
結果:
- 右端から2個の球が同時に飛び出す
- 真ん中の1個はほとんど動かない
なぜ?
2個の球が速度 $v$ で衝突すると、運動量は $2mv$、エネルギーは $mv^2$。これを1個の球で受け止めようとすると、速度 $2v$ で運動量は合うがエネルギーは $2mv^2$ になって合わない。両方を満たせるのは「2個が同じ速度で出る」だけ。
実験3:重力を極端に変える
操作:重力を0.1(通常の約1/100)に設定。球を1個引いて離す。
結果:
- 球の落下が非常にゆっくり
- 衝突のタイミングが間延び
- 衝突後の球の動きもスローモーション
なぜ?
重力が小さいと復元力が弱くなり、振り子の周期が長くなります。単振動の周期は $T = 2\pi\sqrt{L/g}$ なので、$g$ が小さいほど周期が長くなります。
現場でハマるポイント:適用限界と落とし穴
落とし穴1:「eは材質だけで決まる」は誤解
反発係数 $e$ は衝突速度や温度、表面状態でも変わります。例えばゴルフボールとクラブの衝突:
- 低速(パター):$e \approx 0.5$
- 高速(ドライバー):$e \approx 0.83$
CAEで衝突解析する際は、$e$ を定数とせず速度依存モデルを使うのが一般的です。
落とし穴2:「重力=0」にすると振り子じゃなくなる
重力を0に設定すると、球は衝突後一直線に等速運動して戻ってこなくなります。これは振り子モデルの破綻です。実務でもパラメータの極端な設定はモデルの適用範囲を超える典型例。
落とし穴3:完全弾性衝突は現実には存在しない
$e=1$ は理想状態。現実の材料では:
- 鋼球同士:$e \approx 0.95$
- ガラス玉:$e \approx 0.97$
- ゴムボール:$e \approx 0.8$
自動車の衝突安全設計では、$e=0.1〜0.3$ の衝撃吸収材を意図的に使ってエネルギーを散逸させます。
まとめ:この1記事で押さえる3つのポイント
-
ニュートンのゆりかごは「運動量保存」と「エネルギー保存」の可視化装置
$k$ 個入れば $k$ 個出る——これは2つの保存則を同時に満たす唯一の解 -
反発係数 $e$ が現実世界との架け橋
$e=1$ で完全弾性、$e<1$ で非弾性。実務では $e$ を速度依存でモデル化する -
コードで理解すると、物理が「動く」
たった20行のJavaScriptで衝突計算のコアを実装できる。数式とコードが対応しているから、パラメータを変えたときの挙動が予測できる
もっと深く知りたい方へ:
- この記事で使ったシミュレーターで実際に実験:
▶ ニュートンのゆりかご シミュレーター - 1,600以上の工学シミュレーターを無料公開中:
👉 NovaSolver ツール一覧
「カチカチ」が「ガチガチ」になる——そんな体験をしてみてください。