公園のブランコを漕ぐとき、あなたは自然と一定のリズムで前後に揺れます。振り子時計は何百年もの間、正確な時を刻み続けてきました。心臓は1日に約10万回、規則正しく鼓動を繰り返しています。エレキギターの弦は毎秒数百回も振動して美しい音を生み出します。これらの現象に共通するのは、ある平衡位置のまわりを周期的に行ったり来たりする運動、すなわち振動です。
そして、あらゆる振動現象の中で最も基本的で、最も重要なものが単振動(Simple Harmonic Motion, SHM)です。なぜ単振動がそれほど重要なのでしょうか。その理由は2つあります。
第一に、自然界のほぼ全ての振動は、微小振動の範囲では単振動に帰着します。振り子、分子の振動、建物の揺れ、LC回路の電流振動 — どれほど複雑なシステムでも、平衡位置からのずれが小さければ、その運動は単振動で近似できます。これは、任意のポテンシャルを平衡点まわりでテイラー展開すると、最低次の項が2次関数(放物線)になるためです。
第二に、単振動はより高度な振動現象を理解するための出発点です。実際の振動には空気抵抗などの減衰がありますし、外部から周期的な力が加わる強制振動もあります。さらに、フーリエ級数の理論によれば、どんな複雑な周期運動でも単振動の重ね合わせとして分解できます。つまり、単振動を理解することは、振動・波動現象全体を理解するための鍵なのです。
本記事では、バネに取り付けられた質量の運動から出発し、単振動の運動方程式の立式から一般解の導出、エネルギー解析、単振り子への応用、さらに位相空間での描像まで、一つ一つ丁寧に解説していきます。
本記事の内容
- 復元力の直感的理解と単振動の運動方程式の導出
- 特性方程式を用いた一般解の導出(振幅・角振動数・位相の物理的意味)
- エネルギー保存の法則と運動/位置エネルギーの交換
- 単振り子への応用と小角近似の意味・限界
- 位相空間図の直感的理解
- Pythonによるバネ振動の可視化、位相空間図、エネルギー図
前提知識
この記事を読む前に、以下の内容を理解しておくとスムーズに読み進められます。
- ニュートンの運動法則 — 特に第2法則 $F = ma$
- 常微分方程式入門 — 2階線形微分方程式の基礎
単振動とは — 自然界で最も基本的な振動
復元力の直感的理解
単振動を理解するために、まずは身近な例から始めましょう。水平な机の上に、バネの一端を壁に固定し、もう一端に質量 $m$ のおもりを取り付けた状況を想像してください。バネが自然長のとき、おもりは静止しています。この位置を平衡位置と呼びます。
ここでおもりを右に引っ張ると、バネは縮もうとしておもりを左に引き戻します。逆に、おもりを左に押し込むと、バネは伸びようとしておもりを右に押し返します。つまり、おもりが平衡位置からどちらの方向にずれても、バネは必ず元の位置に戻す方向に力を及ぼすのです。この力を復元力(restoring force)と呼びます。
復元力の最も重要な特徴は、平衡位置からのずれが大きいほど力が強くなることです。少しだけ引っ張ったときはバネの引き戻す力は弱く、大きく引っ張ったときは強く引き戻される — これは日常的な感覚とも一致するでしょう。
フックの法則によれば、バネの復元力は変位に比例します。平衡位置からの変位を $x$ とすると、復元力 $F$ は次のように表されます。
$$ \begin{equation} F = -kx \end{equation} $$
ここで $k > 0$ はバネ定数(spring constant)で、バネの硬さを表す量です。$k$ が大きいほど、同じ変位に対してより強い力が作用します。マイナスの符号は、力が常に変位と反対方向に向くことを意味しています。$x > 0$(右にずれている)のとき $F < 0$(左向きの力)、$x < 0$(左にずれている)のとき $F > 0$(右向きの力)という具合です。
ここで注目すべきは、復元力が変位の1乗に比例する線形の関係であるということです。もし力が $x^2$ や $x^3$ に比例していたら、運動はもっと複雑になります。線形であるからこそ、単振動の運動方程式はきれいに解けるのです。
復元力が変位に比例する状況はバネだけに限りません。弦楽器の弦を弾いたとき、振り子を少しだけ傾けたとき、LC回路でコンデンサに電荷が溜まったとき — いずれも平衡状態からの微小なずれに対して、ずれに比例した復元力(あるいは復元トルク、復元起電力)が働きます。このことが、単振動が自然界に遍在する根本的な理由です。
それでは、この復元力をニュートンの運動法則と組み合わせて、おもりの運動を記述する方程式を導出しましょう。
運動方程式の導出
ニュートンの第2法則は「物体に作用する力の合力は、質量と加速度の積に等しい」という法則です。数式で表すと $F = ma$ です。ここで加速度 $a$ は変位 $x$ の時間に関する2階微分 $\ddot{x} = d^2x/dt^2$ です。
バネに取り付けられたおもりの場合、作用する力は復元力 $F = -kx$ のみです(摩擦や空気抵抗は無視します)。これをニュートンの第2法則に代入すると、次のようになります。
$$ m\ddot{x} = -kx $$
この式の両辺を質量 $m$ で割って整理します。
$$ \ddot{x} = -\frac{k}{m}x $$
ここで $k/m$ は正の定数ですから、$\omega^2 = k/m$ と置きましょう。$\omega$ を角振動数(angular frequency)と呼び、その物理的意味は後ほど詳しく説明します。この置き換えによって、運動方程式は次の簡潔な形になります。
$$ \begin{equation} \ddot{x} + \omega^2 x = 0 \end{equation} $$
この方程式が単振動の運動方程式です。物理的に読み取ると、「加速度は常に変位に比例し、変位と反対方向を向く」ということを述べています。おもりが右にずれていれば左向きの加速度が生じ、左にずれていれば右向きの加速度が生じる — こうして、おもりは平衡位置のまわりを永遠に行ったり来たりすることになります。
この運動方程式は2階の常微分方程式であり、$x$ の2階導関数が $x$ 自身に比例するという構造を持っています。「2回微分しても形が変わらず、符号だけが反転する関数」といえば、三角関数 $\cos$ や $\sin$ が思い浮かぶでしょう。実際、一般解はこれらの三角関数で表されます。次のセクションで、特性方程式を用いてこの一般解を系統的に導出していきます。
一般解の導出 — 特性方程式から三角関数へ
指数関数の仮定と特性方程式
2階線形常微分方程式 $\ddot{x} + \omega^2 x = 0$ を解くための標準的な方法は、解を指数関数の形 $x(t) = e^{\lambda t}$ と仮定することです。この仮定の背景にある考え方は、「指数関数は微分しても形が変わらないので、微分方程式に代入すると代数方程式に帰着する」という点にあります。
$x(t) = e^{\lambda t}$ を運動方程式に代入してみましょう。まず、$\dot{x} = \lambda e^{\lambda t}$、$\ddot{x} = \lambda^2 e^{\lambda t}$ です。これらを $\ddot{x} + \omega^2 x = 0$ に代入すると、次のようになります。
$$ \lambda^2 e^{\lambda t} + \omega^2 e^{\lambda t} = 0 $$
$e^{\lambda t}$ は常に正なので、両辺を $e^{\lambda t}$ で割ることができます。
$$ \begin{equation} \lambda^2 + \omega^2 = 0 \end{equation} $$
この代数方程式を特性方程式(characteristic equation)と呼びます。微分方程式を解く問題が、2次方程式を解く問題に変換されたのです。
特性方程式を $\lambda$ について解きます。
$$ \lambda^2 = -\omega^2 $$
右辺が負ですから、実数解は存在しません。虚数単位 $i$($i^2 = -1$)を用いると、次の2つの複素数解が得られます。
$$ \lambda = \pm i\omega $$
「実数解が存在しない」という事実は、運動が発散も減衰もしないことを意味しています。もし $\lambda$ が正の実数を含んでいたら解は指数的に発散し、負の実数を含んでいたら減衰してしまいます。純虚数であるということは、永遠に振動し続けることに対応します。減衰や発散を含む場合については、減衰振動と強制振動で詳しく扱います。
複素指数関数から三角関数へ
特性方程式の解 $\lambda = \pm i\omega$ から、微分方程式の2つの独立な解は $e^{i\omega t}$ と $e^{-i\omega t}$ です。したがって、一般解は次の形になります。
$$ x(t) = c_1 e^{i\omega t} + c_2 e^{-i\omega t} $$
ここで $c_1, c_2$ は複素定数です。しかし、変位 $x(t)$ は物理量ですから実数でなければなりません。複素指数関数を実数の三角関数に変換するために、オイラーの公式を使います。
$$ e^{i\theta} = \cos\theta + i\sin\theta $$
オイラーの公式を $e^{i\omega t}$ と $e^{-i\omega t}$ に適用します。
$$ e^{i\omega t} = \cos(\omega t) + i\sin(\omega t) $$
$$ e^{-i\omega t} = \cos(\omega t) – i\sin(\omega t) $$
これらを一般解に代入して整理すると、次のようになります。
$$ x(t) = (c_1 + c_2)\cos(\omega t) + i(c_1 – c_2)\sin(\omega t) $$
$x(t)$ が実数であるためには、$c_1 + c_2$ と $i(c_1 – c_2)$ がともに実数でなければなりません。そこで、$C_1 = c_1 + c_2$、$C_2 = i(c_1 – c_2)$ とおくと($c_1$ と $c_2$ が互いに複素共役のとき $C_1, C_2$ はともに実数になります)、一般解は次のように書けます。
$$ x(t) = C_1 \cos(\omega t) + C_2 \sin(\omega t) $$
この形は正しいのですが、物理的な意味をもっと直感的に把握するために、振幅と位相を用いた形に書き換えましょう。三角関数の合成公式を使います。$C_1 = A\cos\phi$、$C_2 = -A\sin\phi$ と置くと(ここで $A = \sqrt{C_1^2 + C_2^2}$、$\tan\phi = -C_2/C_1$)、次のようにまとまります。
$$ \begin{equation} x(t) = A\cos(\omega t + \phi) \end{equation} $$
これが単振動の一般解です。2階微分方程式の一般解には2つの任意定数が含まれるはずであり、ここでは振幅 $A$ と初期位相 $\phi$ がそれに対応しています。これらの値は初期条件(初期位置と初速度)によって決まります。
各パラメータの物理的意味
一般解 $x(t) = A\cos(\omega t + \phi)$ に現れる各パラメータの物理的意味を、具体的なイメージとともに説明します。
振幅 $A$(amplitude)は、おもりが平衡位置から最も遠ざかる距離です。$\cos$ 関数の最大値は1、最小値は$-1$ ですから、$x(t)$ は $-A$ と $+A$ の間を往復します。振幅はバネをどれだけ引っ張って離すか(初期条件)によって決まり、バネの性質($k$ や $m$)には依存しません。振幅が大きいほどエネルギーの大きい振動になります。
角振動数 $\omega = \sqrt{k/m}$(angular frequency)は、振動の「速さ」を表す量です。単位は rad/s で、1秒間に何ラジアン分の位相が進むかを表します。$\omega$ が大きいほど速い振動、小さいほどゆっくりした振動です。バネが硬い($k$ が大きい)ほど復元力が強いので速く振動し、おもりが重い($m$ が大きい)ほど慣性が大きいのでゆっくり振動する — これは直感にも合います。
角振動数から、周期 $T$ と振動数 $f$ が求まります。
$$ T = \frac{2\pi}{\omega} = 2\pi\sqrt{\frac{m}{k}}, \qquad f = \frac{1}{T} = \frac{\omega}{2\pi} = \frac{1}{2\pi}\sqrt{\frac{k}{m}} $$
周期 $T$ は1往復にかかる時間(単位: s)、振動数 $f$ は1秒間に何回振動するか(単位: Hz)です。
初期位相 $\phi$(initial phase)は、時刻 $t = 0$ での振動の「スタート位置」を決めます。$\phi = 0$ なら $x(0) = A$ で、おもりは最大変位の位置から運動を始めます。$\phi = \pi/2$ なら $x(0) = A\cos(\pi/2) = 0$ で、おもりは平衡位置から運動を始めます。位相は「波形を時間軸方向にどれだけずらすか」を制御するパラメータだと考えてください。
これらのパラメータを表にまとめておきます。
| パラメータ | 記号と定義 | 物理的意味 | 単位 |
|---|---|---|---|
| 振幅 | $A = \sqrt{C_1^2 + C_2^2}$ | 最大変位(初期条件で決まる) | m |
| 角振動数 | $\omega = \sqrt{k/m}$ | 振動の速さ(系の固有量) | rad/s |
| 周期 | $T = 2\pi/\omega$ | 1往復にかかる時間 | s |
| 振動数 | $f = 1/T = \omega/(2\pi)$ | 1秒あたりの振動回数 | Hz |
| 初期位相 | $\phi = \arctan(-C_2/C_1)$ | $t = 0$ での振動の位置 | rad |
特に重要なのは、角振動数 $\omega$ が初期条件に依存せず、系のパラメータ $k$ と $m$ のみで決まるという点です。バネをどんなに強く引っ張っても、どんなに弱く引っ張っても、振動の周期は同じです。変わるのは振幅(と初期位相)だけです。この性質を等時性と呼び、振り子時計の精度を支える原理でもあります。
速度と加速度
変位の一般解 $x(t) = A\cos(\omega t + \phi)$ を時間で微分すると、速度と加速度が得られます。
速度 $v(t)$ は変位の1階時間微分です。$\cos$ を微分すると $-\sin$ になることを使います。
$$ \begin{equation} v(t) = \dot{x}(t) = -A\omega\sin(\omega t + \phi) \end{equation} $$
加速度 $a(t)$ は速度をさらに時間で微分します。$-\sin$ を微分すると $-\cos$ になります。
$$ \begin{equation} a(t) = \ddot{x}(t) = -A\omega^2\cos(\omega t + \phi) = -\omega^2 x(t) \end{equation} $$
最後の等号 $a(t) = -\omega^2 x(t)$ は、もとの運動方程式 $\ddot{x} = -\omega^2 x$ そのものです。つまり、得られた解は確かに運動方程式を満たしていることが確認できます。
速度と変位の関係に注目すると、興味深いことがわかります。$x(t)$ が $\cos$ で表されるのに対し、$v(t)$ は $\sin$ で表されます。$\cos$ と $\sin$ は位相が $\pi/2$(90度)ずれた関数ですから、速度は変位よりも位相が $\pi/2$ だけ進んでいるのです。これは物理的には、おもりが平衡位置を通過する瞬間($x = 0$)に速度が最大になり、折り返し点($|x| = A$)で速度がゼロになることを意味しています。
速度の最大値は $v_{\max} = A\omega$ で、平衡位置で達成されます。加速度の最大値は $a_{\max} = A\omega^2$ で、折り返し点(変位が最大の位置)で達成されます。折り返し点では速度はゼロですが、復元力が最大なので加速度も最大になる — やや直感に反するかもしれませんが、「速度ゼロの瞬間こそ最も強く加速されている」のは、ボールを真上に投げたときの最高点と同じ状況です。
ここまでで、単振動の運動学的な記述(位置・速度・加速度の時間変化)が完成しました。次に、エネルギーの観点から単振動を見てみましょう。運動エネルギーと位置エネルギーがどのように入れ替わるのかを理解すると、単振動のメカニズムがさらに深く見えてきます。
エネルギー保存 — 運動エネルギーとポテンシャルの行き来
エネルギーの直感的理解
単振動するおもりのエネルギーを考える前に、直感的なイメージを持っておきましょう。
ブランコを想像してください。最も高い位置(折り返し点)では一瞬静止します。このとき速度はゼロなので運動エネルギーもゼロですが、高い位置にいるのでポテンシャルエネルギー(位置エネルギー)は最大です。ブランコが下がるにつれて速度が増し、最下点(平衡位置)を通過するとき速度は最大、つまり運動エネルギーは最大になります。一方、最下点では高さが最も低いのでポテンシャルエネルギーは最小です。
このように、振動においては運動エネルギーとポテンシャルエネルギーが絶えず入れ替わっているのです。摩擦や空気抵抗がなければ、両者の合計(全エネルギー)は常に一定に保たれます。これがエネルギー保存則です。
バネの単振動でも全く同じことが起きています。バネが最も伸びた(または縮んだ)位置ではおもりは静止しており、弾性ポテンシャルエネルギーが最大です。おもりが平衡位置を通過するときには速度が最大で、運動エネルギーが最大になります。エネルギーは「バネの弾性エネルギー」と「おもりの運動エネルギー」の間を、波のように行き来しているのです。
運動エネルギーと位置エネルギーの数式
この直感を数式で確認しましょう。まず、運動エネルギー $K$ は次のように計算できます。
$$ K = \frac{1}{2}mv^2 = \frac{1}{2}m\left(-A\omega\sin(\omega t + \phi)\right)^2 $$
ここで $\omega^2 = k/m$ の関係、すなわち $m\omega^2 = k$ を使って整理すると、次のようになります。
$$ \begin{equation} K = \frac{1}{2}mA^2\omega^2\sin^2(\omega t + \phi) = \frac{1}{2}kA^2\sin^2(\omega t + \phi) \end{equation} $$
次に、バネの弾性ポテンシャルエネルギー $U$ は、バネを変位 $x$ だけ伸ばす(または縮める)のに必要な仕事として定義されます。
$$ \begin{equation} U = \frac{1}{2}kx^2 = \frac{1}{2}kA^2\cos^2(\omega t + \phi) \end{equation} $$
全エネルギーの保存
全エネルギー $E$ は運動エネルギーとポテンシャルエネルギーの和です。
$$ E = K + U = \frac{1}{2}kA^2\sin^2(\omega t + \phi) + \frac{1}{2}kA^2\cos^2(\omega t + \phi) $$
右辺から $\frac{1}{2}kA^2$ を括り出すと、次のようになります。
$$ E = \frac{1}{2}kA^2\left[\sin^2(\omega t + \phi) + \cos^2(\omega t + \phi)\right] $$
三角関数の基本恒等式 $\sin^2\theta + \cos^2\theta = 1$ を用いると、括弧内は常に1ですから、最終的に次の結果が得られます。
$$ \begin{equation} E = K + U = \frac{1}{2}kA^2 = \text{一定} \end{equation} $$
全エネルギーは振幅 $A$ の2乗に比例し、時間によらず一定です。これが単振動におけるエネルギー保存則の数式的表現です。
この結果から、いくつかの重要な物理的含意が読み取れます。
-
エネルギーは振幅の2乗に比例する: 振幅を2倍にすると、系のエネルギーは4倍になります。ギターの弦を強く弾くほど大きな音が出る(音のエネルギーが大きい)のは、このためです。
-
$K$ と $U$ は相補的に変化する: $K$ が最大のとき $U$ は最小(ゼロ)、$U$ が最大のとき $K$ は最小(ゼロ)です。両者の和が常に $\frac{1}{2}kA^2$ に保たれるように、エネルギーが行き来しています。
-
$K$ と $U$ の時間平均は等しい: $\sin^2$ と $\cos^2$ の1周期にわたる時間平均はどちらも $1/2$ ですから、$\langle K \rangle = \langle U \rangle = \frac{1}{4}kA^2$ です。平均的に見ると、エネルギーは運動エネルギーとポテンシャルエネルギーに均等に分配されています。これはビリアル定理の一例でもあります。
エネルギー保存則は、単振動だけでなく物理学全体を貫く基本原理です。ここまでで、水平なバネ-質量系の単振動について、運動方程式・一般解・エネルギーの3つの観点から理解しました。次に、もう一つの代表的な単振動の例として、重力場中の振り子(単振り子)を取り上げます。
単振り子 — 重力がつくる単振動
単振り子とは
単振り子は、長さ $l$ の伸びない軽い糸の一端を固定点に結び、もう一端に質量 $m$ のおもりをつけたものです。ブランコ、振り子時計、メトロノームなど、日常で目にする多くの振動がこのモデルで記述できます。
バネの単振動では復元力の源はバネの弾性力でしたが、単振り子の場合は重力が復元力の役割を果たします。おもりが鉛直方向から角度 $\theta$ だけ傾くと、重力の接線方向成分 $-mg\sin\theta$ がおもりを平衡位置(最下点)に引き戻そうとします。
運動方程式の導出
おもりは半径 $l$ の円弧上を運動するので、円弧方向の変位は $s = l\theta$ です。接線方向のニュートンの運動方程式は次のようになります。
$$ m\ddot{s} = -mg\sin\theta $$
$s = l\theta$ より $\ddot{s} = l\ddot{\theta}$ ですから、両辺を $ml$ で割ると、角度 $\theta$ に関する運動方程式が得られます。
$$ \begin{equation} \ddot{\theta} + \frac{g}{l}\sin\theta = 0 \end{equation} $$
この方程式には $\sin\theta$ が含まれているため、非線形の微分方程式です。非線形微分方程式は一般に解析的に解くことが難しく、楕円積分を用いた厳密解は存在しますが、簡潔な三角関数の形にはなりません。
小角近似 — 非線形から線形への橋渡し
ここで小角近似(small angle approximation)の出番です。$\theta$ がラジアンで十分小さいとき、$\sin\theta$ を $\theta$ で近似できます。
$$ \sin\theta \approx \theta \quad (\theta \ll 1) $$
この近似は、$\sin\theta$ のテイラー展開から正当化できます。
$$ \sin\theta = \theta – \frac{\theta^3}{6} + \frac{\theta^5}{120} – \cdots $$
$\theta$ が小さいとき、$\theta^3$ 以降の項は $\theta$ に比べて無視できるほど小さくなります。具体的な数値で確認すると、$\theta = 0.1$ rad(約5.7度)のとき $\sin(0.1) = 0.09983$ であり、$\theta$ との誤差は約0.017%です。$\theta = 0.3$ rad(約17度)でも $\sin(0.3) = 0.2955$ で、誤差は約1.5%に留まります。一方、$\theta = 1$ rad(約57度)では $\sin(1) = 0.8415$ となり、誤差は16%に達します。
したがって、おおよそ $\theta \lesssim 15°$(0.26 rad)程度までが小角近似の適用範囲であり、それを超えると近似の精度が悪化します。大振幅の振り子の運動は厳密には単振動ではなく、周期が振幅に依存するようになります(振幅が大きいほど周期が長くなります)。
小角近似を適用すると、運動方程式は次のように線形化されます。
$$ \begin{equation} \ddot{\theta} + \frac{g}{l}\theta = 0 \end{equation} $$
これはバネの単振動の方程式 $\ddot{x} + \omega^2 x = 0$ と全く同じ形です。角振動数は $\omega = \sqrt{g/l}$ であり、周期は次のようになります。
$$ \begin{equation} T = \frac{2\pi}{\omega} = 2\pi\sqrt{\frac{l}{g}} \end{equation} $$
この結果から、2つの注目すべき性質が読み取れます。
第一に、周期は質量 $m$ に依存しません。 重いおもりでも軽いおもりでも、糸の長さが同じなら周期は同じです。これはガリレオの等時性として知られ、伝説によればガリレオがピサ大聖堂の天井のシャンデリアの揺れから発見したとされています。物理的には、重力(復元力の源)と慣性力はどちらも質量に比例するため、運動方程式で質量が消去されるのです。
第二に、周期は糸の長さ $l$ の平方根に比例します。 糸を4倍に長くすると周期は2倍になります。振り子時計の精度は、糸の長さ(振り子のアーム長)を精密に制御することで実現されています。地球上($g \approx 9.81$ m/s$^2$)で周期がちょうど2秒になる振り子の長さは $l = g/\pi^2 \approx 0.994$ m であり、これを秒振り子と呼びます。
バネの単振動と単振り子の対応関係を表にまとめておきましょう。
| 量 | バネ-質量系 | 単振り子 |
|---|---|---|
| 変位 | $x$(直線変位) | $\theta$(角変位) |
| 復元力/復元トルク | $-kx$ | $-mg\sin\theta \approx -mg\theta$ |
| 角振動数 | $\omega = \sqrt{k/m}$ | $\omega = \sqrt{g/l}$ |
| 周期 | $T = 2\pi\sqrt{m/k}$ | $T = 2\pi\sqrt{l/g}$ |
| 質量依存性 | あり($T \propto \sqrt{m}$) | なし |
バネの単振動でも単振り子でも、本質的に同じ構造の微分方程式が現れることがお分かりいただけたでしょう。この「同じ方程式が異なる物理系に現れる」という事実は、物理学の普遍性を象徴しています。LC回路の電荷の振動も $\ddot{q} + q/(LC) = 0$ という同じ形の方程式に従い、$\omega = 1/\sqrt{LC}$ で振動します。
次に、単振動をより高い視点から眺めるために、位相空間という強力な道具を導入します。位相空間を使うと、振動の全体像が一目でわかるようになります。
位相空間 — 振動を俯瞰する
位相空間とは
これまでの議論では、変位 $x(t)$ や速度 $v(t)$ を時間の関数としてグラフに描くことで振動を可視化してきました。しかし、もう一つの強力な可視化方法があります。それが位相空間図(phase space diagram)です。
位相空間図では、横軸に変位 $x$、縦軸に速度 $v$ をとり、時間をパラメータとして $\bigl(x(t),\, v(t)\bigr)$ の軌跡を描きます。時間の情報は直接的には表示されませんが、代わりに系の状態の全体的な振る舞いが一目で把握できるようになります。
日常的なアナロジーで言えば、時間波形グラフが「旅行の日程表」(いつどこにいるか)だとすると、位相空間図は「地図上の経路」(どこからどこへどう動くか)のようなものです。日程表からは時系列の情報が読み取れますが、地図からは経路の全体像やパターンが見えてきます。
単振動の位相空間図
単振動の一般解 $x(t) = A\cos(\omega t + \phi)$ と速度 $v(t) = -A\omega\sin(\omega t + \phi)$ から、$\cos$ と $\sin$ を消去してみましょう。
$x/A = \cos(\omega t + \phi)$ と $v/(A\omega) = -\sin(\omega t + \phi)$ ですから、両辺をそれぞれ2乗して足すと、三角関数の恒等式 $\cos^2 + \sin^2 = 1$ により次の関係が得られます。
$$ \begin{equation} \frac{x^2}{A^2} + \frac{v^2}{(A\omega)^2} = 1 \end{equation} $$
これは楕円の方程式です。つまり、単振動の位相空間での軌道は楕円を描きます。楕円の横半径は振幅 $A$、縦半径は最大速度 $A\omega$ です。
この楕円軌道は、エネルギー保存則の幾何学的表現でもあります。実際、エネルギー保存の式 $\frac{1}{2}mv^2 + \frac{1}{2}kx^2 = E$ を変形すると、
$$ \frac{x^2}{2E/k} + \frac{v^2}{2E/m} = 1 $$
となり、$E = \frac{1}{2}kA^2$ を代入すれば上の楕円の方程式と一致します。エネルギーが大きい(振幅が大きい)ほど大きな楕円になり、エネルギーが小さいほど小さな楕円になります。異なるエネルギーの楕円は互いに交差しません — エネルギーが保存されるので、ある楕円上にいる状態点は永遠にその楕円上を周回し続けます。
位相空間図から読み取れる重要な情報をまとめます。
-
閉じた軌道は周期運動を意味する: 楕円が閉じている(始点と終点が一致する)ことは、運動が周期的であることに対応します。
-
軌道の大きさはエネルギーに対応する: 大きな楕円ほど大きなエネルギー(大きな振幅)を持ちます。
-
状態点は時計回りに動く: $x > 0$ のとき $\dot{x} = v < 0$($\sin$ が正)となる場合が多く、状態点は位相空間を時計回りに周回します(ただし位相 $\phi$ に依存します。標準的には時計回りです)。
-
楕円の中心(原点)は平衡点: $x = 0, v = 0$ はおもりが平衡位置に静止している状態で、安定平衡点です。
位相空間の考え方は、単振動に限らず、減衰振動と強制振動や非線形振動、さらにはカオス理論を理解するための基盤になります。減衰振動では楕円が徐々に小さくなる渦巻き軌道を描き、強制振動ではリミットサイクルと呼ばれる特定の閉曲線に収束します。制御工学では、2次遅れ系の応答を位相空間で解析することが標準的な手法です。
それでは、ここまでの理論をPythonで実装し、グラフで確認していきましょう。
Pythonでの実装と可視化
バネ振動の時間波形
まずは最も基本的な可視化として、バネに取り付けられたおもりの変位と速度の時間波形を描きましょう。$k = 4.0$ N/m、$m = 1.0$ kg のバネ-質量系を考え、振幅 $A = 2.0$ m、初期位相 $\phi = 0$ とします。
import numpy as np
import matplotlib.pyplot as plt
# --- パラメータ設定 ---
k = 4.0 # バネ定数 [N/m]
m = 1.0 # 質量 [kg]
omega = np.sqrt(k / m) # 角振動数 [rad/s]
A = 2.0 # 振幅 [m]
phi = 0.0 # 初期位相 [rad]
T = 2 * np.pi / omega # 周期 [s]
t = np.linspace(0, 3 * T, 500)
# --- 変位・速度・加速度の計算 ---
x = A * np.cos(omega * t + phi)
v = -A * omega * np.sin(omega * t + phi)
a = -A * omega**2 * np.cos(omega * t + phi)
# --- 時間波形の描画 ---
fig, axes = plt.subplots(2, 1, figsize=(10, 7), sharex=True)
axes[0].plot(t, x, "b-", linewidth=2, label=r"変位 $x(t)$")
axes[0].plot(t, v, "r-", linewidth=2, label=r"速度 $v(t)$")
axes[0].set_ylabel("変位 [m] / 速度 [m/s]", fontsize=12)
axes[0].set_title("単振動の時間波形", fontsize=14)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
axes[0].axhline(y=0, color="k", linewidth=0.5)
axes[1].plot(t, a, "g-", linewidth=2, label=r"加速度 $a(t)$")
axes[1].set_xlabel("時間 [s]", fontsize=12)
axes[1].set_ylabel("加速度 [m/s²]", fontsize=12)
axes[1].set_title("加速度の時間変化", fontsize=14)
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
axes[1].axhline(y=0, color="k", linewidth=0.5)
plt.tight_layout()
plt.show()
print(f"角振動数 ω = {omega:.2f} rad/s")
print(f"周期 T = {T:.2f} s")
print(f"振動数 f = {1/T:.2f} Hz")
print(f"最大速度 v_max = {A * omega:.2f} m/s")
print(f"最大加速度 a_max = {A * omega**2:.2f} m/s²")
上のグラフから、いくつかの重要な特徴が読み取れます。
第一に、変位 $x(t)$ と速度 $v(t)$ の位相差が明瞭に見えます。速度は変位に対して $\pi/2$(90度)だけ位相が進んでおり、変位がゼロを通過する瞬間に速度が最大(または最小)になっています。これは先ほど理論で確認した通りです。
第二に、加速度は変位と逆位相(位相差 $\pi$)であることがわかります。変位が正の最大値をとる瞬間に加速度は負の最大値をとり、その逆も同様です。これは運動方程式 $a = -\omega^2 x$ の直接的な表れです。
第三に、全ての波形が正確に同じ周期 $T \approx 3.14$ s で繰り返されています。$k = 4.0$、$m = 1.0$ のとき $\omega = 2.0$ rad/s ですから、$T = 2\pi/2 = \pi \approx 3.14$ s となり、理論値と一致します。
エネルギーの時間変化
次に、運動エネルギー $K$、ポテンシャルエネルギー $U$、および全エネルギー $E$ の時間変化を可視化して、エネルギー保存則を視覚的に確認しましょう。
import numpy as np
import matplotlib.pyplot as plt
# --- パラメータ設定 ---
k = 4.0
m = 1.0
omega = np.sqrt(k / m)
A = 2.0
phi = 0.0
T = 2 * np.pi / omega
t = np.linspace(0, 3 * T, 500)
x = A * np.cos(omega * t + phi)
v = -A * omega * np.sin(omega * t + phi)
# --- エネルギー計算 ---
K_energy = 0.5 * m * v**2
U_energy = 0.5 * k * x**2
E_total = K_energy + U_energy
# --- エネルギーの描画 ---
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(t, K_energy, "r-", linewidth=2, label=r"運動エネルギー $K$")
ax.plot(t, U_energy, "b-", linewidth=2, label=r"位置エネルギー $U$")
ax.plot(t, E_total, "k--", linewidth=2, label=r"全エネルギー $E$")
ax.fill_between(t, 0, K_energy, alpha=0.15, color="red")
ax.fill_between(t, 0, U_energy, alpha=0.15, color="blue")
ax.set_xlabel("時間 [s]", fontsize=12)
ax.set_ylabel("エネルギー [J]", fontsize=12)
ax.set_title("単振動におけるエネルギーの時間変化", fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"全エネルギー E = 1/2 * k * A² = {0.5 * k * A**2:.1f} J")
print(f"全エネルギーの最大値と最小値の差: {E_total.max() - E_total.min():.2e} J")
このグラフから、エネルギー保存則の美しい構造が視覚的に確認できます。
赤い曲線(運動エネルギー $K$)と青い曲線(ポテンシャルエネルギー $U$)は、完全に相補的な関係にあります。一方が最大のとき他方はゼロになり、両者の和(黒い破線)は常に一定値 $E = \frac{1}{2}kA^2 = 8.0$ J を維持しています。数値計算上の全エネルギーの変動は浮動小数点の丸め誤差程度($10^{-15}$ J 以下)であり、事実上厳密にエネルギーが保存されていることが確認できます。
また、$K$ と $U$ の振動周期は元の振動の周期 $T$ の半分になっていることに注目してください。これは $\sin^2$ や $\cos^2$ の周期が $\pi/\omega = T/2$ であることに対応しています。1回の振動(1往復)の間に、エネルギーの交換は2回起きるのです。
位相空間図
ここまでの可視化は全て「時間 vs 物理量」のグラフでした。次に、位相空間図を描いて、振動の全体像を異なる角度から眺めてみましょう。異なる振幅(=異なるエネルギー)の楕円軌道を複数描くことで、エネルギーと軌道の関係も可視化します。
import numpy as np
import matplotlib.pyplot as plt
# --- パラメータ設定 ---
k = 4.0
m = 1.0
omega = np.sqrt(k / m)
t = np.linspace(0, 2 * np.pi / omega, 500)
# --- 異なる振幅での位相空間軌道 ---
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
amplitudes = [0.5, 1.0, 1.5, 2.0, 2.5]
colors = plt.cm.viridis(np.linspace(0.2, 0.9, len(amplitudes)))
for A, c in zip(amplitudes, colors):
x = A * np.cos(omega * t)
v = -A * omega * np.sin(omega * t)
E = 0.5 * k * A**2
axes[0].plot(x, v, color=c, linewidth=2, label=f"$A={A:.1f}$ m ($E={E:.1f}$ J)")
axes[0].plot(0, 0, "k+", markersize=12, markeredgewidth=2)
axes[0].set_xlabel("変位 $x$ [m]", fontsize=12)
axes[0].set_ylabel("速度 $v$ [m/s]", fontsize=12)
axes[0].set_title("位相空間図(異なる振幅)", fontsize=14)
axes[0].legend(fontsize=9, loc="upper right")
axes[0].set_aspect("equal")
axes[0].grid(True, alpha=0.3)
axes[0].axhline(y=0, color="k", linewidth=0.5)
axes[0].axvline(x=0, color="k", linewidth=0.5)
# --- 単一軌道で時間の進行方向を矢印で示す ---
A = 2.0
x = A * np.cos(omega * t)
v = -A * omega * np.sin(omega * t)
axes[1].plot(x, v, "b-", linewidth=2)
axes[1].plot(x[0], v[0], "go", markersize=10, label="開始点 ($t=0$)")
# 等時間間隔の点を表示
n_markers = 12
idx = np.linspace(0, len(t) - 1, n_markers + 1, dtype=int)[:-1]
axes[1].scatter(x[idx], v[idx], c="red", s=40, zorder=5)
# 矢印で進行方向を表示
for i in idx[::2]:
dx = x[min(i + 5, len(x) - 1)] - x[i]
dv = v[min(i + 5, len(v) - 1)] - v[i]
axes[1].annotate("", xy=(x[i] + dx, v[i] + dv),
xytext=(x[i], v[i]),
arrowprops=dict(arrowstyle="->", color="red", lw=1.5))
axes[1].set_xlabel("変位 $x$ [m]", fontsize=12)
axes[1].set_ylabel("速度 $v$ [m/s]", fontsize=12)
axes[1].set_title("位相空間での時間発展(時計回り)", fontsize=14)
axes[1].legend(fontsize=11)
axes[1].set_aspect("equal")
axes[1].grid(True, alpha=0.3)
axes[1].axhline(y=0, color="k", linewidth=0.5)
axes[1].axvline(x=0, color="k", linewidth=0.5)
plt.tight_layout()
plt.show()
左のグラフから、振幅(エネルギー)が異なる軌道が同心楕円を成していることが確認できます。振幅 $A$ が大きいほど楕円も大きく、エネルギーが高い状態に対応します。これらの楕円は決して交差しません — 交差するとしたら、同じ状態($x, v$)から異なる未来が分岐することになり、ニュートン力学の決定論に矛盾するためです。
右のグラフでは、位相空間上の状態点が時計回りに周回する様子を赤い点と矢印で示しています。開始点(緑の点)は $(x, v) = (A, 0)$、つまり最大変位で速度ゼロの状態です。ここから状態点は下方に動き始め(速度が負になる = 左に動き始める)、楕円の下半分を通って左端 $(-A, 0)$ に到達し、さらに上半分を通って元の位置に戻ります。この1周が1周期 $T$ に対応します。
バネの位置をアニメーション的に可視化
時間波形と位相空間図を並べて、バネの振動の各瞬間がどのように対応するかを同時に可視化してみましょう。ここでは複数の時刻におけるスナップショットを描画します。
import numpy as np
import matplotlib.pyplot as plt
# --- パラメータ設定 ---
k = 4.0
m = 1.0
omega = np.sqrt(k / m)
A = 2.0
phi = 0.0
T = 2 * np.pi / omega
# --- スナップショットの時刻 ---
n_snapshots = 8
t_snapshots = np.linspace(0, T, n_snapshots, endpoint=False)
t_full = np.linspace(0, T, 500)
x_full = A * np.cos(omega * t_full + phi)
v_full = -A * omega * np.sin(omega * t_full + phi)
fig, axes = plt.subplots(2, 4, figsize=(16, 8))
axes = axes.flatten()
for i, ts in enumerate(t_snapshots):
ax = axes[i]
xs = A * np.cos(omega * ts + phi)
vs = -A * omega * np.sin(omega * ts + phi)
# バネの描画(ジグザグ線で表現)
n_coils = 10
spring_x = np.linspace(-3, xs, n_coils * 4 + 1)
spring_y = np.zeros_like(spring_x)
for j in range(1, len(spring_x) - 1):
spring_y[j] = 0.3 * (-1)**j if j % 2 != 0 else 0
ax.plot(spring_x, spring_y, "b-", linewidth=1.5)
ax.plot(xs, 0, "ro", markersize=15) # おもり
# 速度の矢印
if abs(vs) > 0.1:
ax.annotate("", xy=(xs + vs * 0.15, 0),
xytext=(xs, 0),
arrowprops=dict(arrowstyle="->", color="green", lw=2))
ax.set_xlim(-4, 4)
ax.set_ylim(-1, 1)
ax.axvline(x=0, color="gray", linestyle="--", alpha=0.5)
ax.set_title(f"$t = {ts:.2f}$ s\n$x = {xs:.2f}$ m", fontsize=10)
ax.set_aspect("equal")
ax.set_xticks([])
ax.set_yticks([])
fig.suptitle("バネ振動のスナップショット(1周期分)", fontsize=14, y=1.02)
plt.tight_layout()
plt.show()
8枚のスナップショットは、1周期 $T$ を等間隔に分割した各瞬間のバネの状態を示しています。赤い丸がおもり、青い線がバネ、緑の矢印が速度の方向と大きさを表します。灰色の破線が平衡位置です。
$t = 0$ では最も右に伸びた状態(最大変位 $x = A$)で、速度はゼロです。そこからおもりは左に動き始め、平衡位置($x = 0$)を通過するとき速度が最大になります。さらに左に進んで最も縮んだ状態($x = -A$)で再び静止し、今度は右に動き始めて元の位置に戻ります。この過程が際限なく繰り返されるのが単振動です。
単振り子の周期と振幅の関係
最後に、小角近似の限界を数値的に確認しましょう。単振り子の厳密な周期(数値積分で計算)と小角近似による周期 $T_0 = 2\pi\sqrt{l/g}$ を比較します。
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
# --- 単振り子の厳密な周期を数値的に計算 ---
def pendulum_period_numerical(theta_max, l=1.0, g=9.81):
"""数値積分で単振り子の厳密な周期を計算する"""
omega0 = np.sqrt(g / l)
def equations(t, y):
theta, omega = y
return [omega, -omega0**2 * np.sin(theta)]
# 初期条件: theta = theta_max, omega = 0(端から放す)
y0 = [theta_max, 0.0]
# 十分長い時間を積分
t_span = (0, 20 * 2 * np.pi / omega0)
t_eval = np.linspace(*t_span, 10000)
sol = integrate.solve_ivp(equations, t_span, y0, t_eval=t_eval,
method="RK45", rtol=1e-10, atol=1e-12)
# ゼロクロスを検出して周期を計算
theta = sol.y[0]
zero_crossings = []
for i in range(1, len(theta)):
if theta[i - 1] > 0 and theta[i] <= 0:
# 線形補間でゼロクロスの正確な時刻を求める
t_cross = sol.t[i - 1] - theta[i - 1] * (sol.t[i] - sol.t[i - 1]) / (theta[i] - theta[i - 1])
zero_crossings.append(t_cross)
if len(zero_crossings) >= 3:
# 連続する下向きゼロクロス間の間隔が周期
periods = np.diff(zero_crossings)
return np.mean(periods)
return 2 * np.pi / omega0
# --- 周期の振幅依存性 ---
l = 1.0
g = 9.81
T0 = 2 * np.pi * np.sqrt(l / g) # 小角近似の周期
theta_max_deg = np.linspace(1, 90, 30)
theta_max_rad = np.radians(theta_max_deg)
T_exact = np.array([pendulum_period_numerical(th, l, g) for th in theta_max_rad])
T_approx = np.full_like(T_exact, T0)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左: 周期の比較
axes[0].plot(theta_max_deg, T_exact, "b-", linewidth=2, label="厳密な周期(数値計算)")
axes[0].plot(theta_max_deg, T_approx, "r--", linewidth=2, label=r"小角近似 $T_0 = 2\pi\sqrt{l/g}$")
axes[0].set_xlabel("最大振幅 [deg]", fontsize=12)
axes[0].set_ylabel("周期 [s]", fontsize=12)
axes[0].set_title("単振り子の周期と振幅の関係", fontsize=14)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
# 右: 相対誤差
relative_error = (T_exact - T0) / T0 * 100
axes[1].plot(theta_max_deg, relative_error, "g-", linewidth=2)
axes[1].axhline(y=1, color="r", linestyle="--", alpha=0.7, label="誤差 1%")
axes[1].axhline(y=5, color="orange", linestyle="--", alpha=0.7, label="誤差 5%")
axes[1].set_xlabel("最大振幅 [deg]", fontsize=12)
axes[1].set_ylabel("周期の相対誤差 [%]", fontsize=12)
axes[1].set_title("小角近似の誤差", fontsize=14)
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 特定の角度での誤差を表示
for deg in [5, 10, 15, 30, 45, 60, 90]:
idx = np.argmin(np.abs(theta_max_deg - deg))
print(f"θ_max = {deg:3d}° : T_exact = {T_exact[idx]:.4f} s, "
f"T_approx = {T0:.4f} s, 誤差 = {relative_error[idx]:.2f}%")
左のグラフは、単振り子の厳密な周期(青い実線)と小角近似による周期(赤い破線)を振幅の関数として比較したものです。小角近似の周期は振幅に依存しない一定値ですが、厳密な周期は振幅が大きくなるほど長くなります。
右のグラフは、小角近似の相対誤差を示しています。振幅が約23度以下であれば誤差は1%以内に収まっており、小角近似は十分実用的です。しかし、45度になると誤差は約4%、90度では約18%に達します。精密な計算が必要な場合は、楕円積分を用いた厳密解や数値計算に頼る必要があります。
この結果は、小角近似が「角度が小さい」という定性的な条件だけでなく、定量的にもどの程度の精度で成り立つかを示しており、物理的な近似の限界を正しく理解するための重要な教材です。
総合可視化 — 4パネルダッシュボード
最後に、単振動の全体像を1つの図にまとめた総合ダッシュボードを作成します。
import numpy as np
import matplotlib.pyplot as plt
# --- パラメータ設定 ---
k = 4.0
m = 1.0
omega = np.sqrt(k / m)
A = 2.0
phi = 0.0
T = 2 * np.pi / omega
t = np.linspace(0, 3 * T, 500)
x = A * np.cos(omega * t + phi)
v = -A * omega * np.sin(omega * t + phi)
K_energy = 0.5 * m * v**2
U_energy = 0.5 * k * x**2
E_total = K_energy + U_energy
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 左上: 変位と速度の時間波形
axes[0, 0].plot(t, x, "b-", linewidth=2, label=r"変位 $x(t)$")
axes[0, 0].plot(t, v, "r-", linewidth=2, label=r"速度 $v(t)$")
axes[0, 0].set_xlabel("時間 [s]", fontsize=12)
axes[0, 0].set_ylabel("変位 / 速度", fontsize=12)
axes[0, 0].set_title("単振動の時間波形", fontsize=14)
axes[0, 0].legend(fontsize=11)
axes[0, 0].grid(True, alpha=0.3)
axes[0, 0].axhline(y=0, color="k", linewidth=0.5)
# 右上: エネルギーの時間変化
axes[0, 1].plot(t, K_energy, "r-", linewidth=2, label=r"$K$(運動)")
axes[0, 1].plot(t, U_energy, "b-", linewidth=2, label=r"$U$(位置)")
axes[0, 1].plot(t, E_total, "k--", linewidth=2, label=r"$E$(全)")
axes[0, 1].set_xlabel("時間 [s]", fontsize=12)
axes[0, 1].set_ylabel("エネルギー [J]", fontsize=12)
axes[0, 1].set_title("エネルギーの時間変化", fontsize=14)
axes[0, 1].legend(fontsize=11)
axes[0, 1].grid(True, alpha=0.3)
# 左下: 位相空間図
t_one = np.linspace(0, T, 500)
x_one = A * np.cos(omega * t_one + phi)
v_one = -A * omega * np.sin(omega * t_one + phi)
axes[1, 0].plot(x_one, v_one, "b-", linewidth=2)
axes[1, 0].plot(x_one[0], v_one[0], "go", markersize=10, label="開始点")
axes[1, 0].set_xlabel("変位 $x$ [m]", fontsize=12)
axes[1, 0].set_ylabel("速度 $v$ [m/s]", fontsize=12)
axes[1, 0].set_title("位相空間図", fontsize=14)
axes[1, 0].set_aspect("equal")
axes[1, 0].legend(fontsize=11)
axes[1, 0].grid(True, alpha=0.3)
axes[1, 0].axhline(y=0, color="k", linewidth=0.5)
axes[1, 0].axvline(x=0, color="k", linewidth=0.5)
# 右下: 振り子の周期と長さの関係
g = 9.81
L_range = np.linspace(0.1, 5, 100)
T_pendulum = 2 * np.pi * np.sqrt(L_range / g)
axes[1, 1].plot(L_range, T_pendulum, "b-", linewidth=2)
axes[1, 1].axhline(y=2.0, color="r", linestyle="--", alpha=0.7,
label="$T = 2$ s(秒振り子)")
axes[1, 1].axvline(x=g / np.pi**2, color="r", linestyle="--", alpha=0.7)
axes[1, 1].set_xlabel("振り子の長さ $l$ [m]", fontsize=12)
axes[1, 1].set_ylabel("周期 $T$ [s]", fontsize=12)
axes[1, 1].set_title(r"単振り子の周期 $T = 2\pi\sqrt{l/g}$", fontsize=14)
axes[1, 1].legend(fontsize=11)
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
この4パネルダッシュボードは、単振動の主要な特徴を一目で把握できるように構成しています。左上の時間波形からは変位と速度の位相関係が、右上のエネルギー図からはエネルギー保存則の成立が、左下の位相空間図からは振動の全体構造が、右下の振り子グラフからは周期と糸の長さの $\sqrt{l}$ 依存性が、それぞれ読み取れます。特に右下のグラフでは、秒振り子($T = 2$ s)に対応する長さ $l \approx 0.994$ m を赤い破線で示しています。
発展的な話題 — 複素振幅表示と回転するベクトル
ここまでの議論を補足する形で、単振動を複素平面上の回転するベクトル(フェーザー, phasor)として捉える見方を紹介します。これは電気工学でのLC回路の解析や、フーリエ級数の理解に直結する重要な視点です。
単振動の一般解 $x(t) = A\cos(\omega t + \phi)$ は、複素指数関数 $\tilde{x}(t) = Ae^{i(\omega t + \phi)}$ の実部と見なすことができます。
$$ x(t) = \text{Re}\left[\tilde{x}(t)\right] = \text{Re}\left[Ae^{i(\omega t + \phi)}\right] $$
複素平面上で $\tilde{x}(t)$ を描くと、原点を中心に半径 $A$ の円周上を角速度 $\omega$ で反時計回りに回転するベクトルです。このベクトルの実軸への射影が変位 $x(t)$、虚軸への射影(の $-1$ 倍を $\omega$ で割ったもの)が速度に関連する量になります。
この描像の利点は、複数の単振動の重ね合わせを考える際に、ベクトルの足し算として視覚的に扱えることです。同じ振動数を持つ2つの単振動 $x_1(t) = A_1\cos(\omega t + \phi_1)$ と $x_2(t) = A_2\cos(\omega t + \phi_2)$ の和は、複素平面上の2つのベクトルのベクトル和で表されます。この考え方は交流回路のインピーダンス計算や、フーリエ級数の各成分の理解に不可欠です。
まとめ
本記事では、単振動(Simple Harmonic Motion)の理論を、復元力の直感的な理解から出発して体系的に解説しました。
-
復元力と運動方程式: 平衡位置からの変位に比例する復元力 $F = -kx$ をニュートンの第2法則に代入することで、単振動の運動方程式 $\ddot{x} + \omega^2 x = 0$ が導出されます。
-
一般解の導出: 特性方程式 $\lambda^2 + \omega^2 = 0$ の解が純虚数 $\lambda = \pm i\omega$ であることから、オイラーの公式を経て一般解 $x(t) = A\cos(\omega t + \phi)$ が得られます。振幅 $A$ と初期位相 $\phi$ は初期条件で決まり、角振動数 $\omega = \sqrt{k/m}$ は系の固有量です。
-
エネルギー保存: 運動エネルギー $K$ とポテンシャルエネルギー $U$ は相補的に振動しますが、全エネルギー $E = \frac{1}{2}kA^2$ は時間によらず一定に保たれます。
-
単振り子: 小角近似 $\sin\theta \approx \theta$ のもとで、単振り子も $\ddot{\theta} + (g/l)\theta = 0$ という同じ形の運動方程式に従います。周期 $T = 2\pi\sqrt{l/g}$ は質量に依存しません。
-
位相空間: 位相空間($x$-$v$ 空間)上で単振動は楕円軌道を描き、これはエネルギー保存の幾何学的表現です。
単振動は、振動現象を理解するための最も基本的な出発点です。現実の系では摩擦や外力が存在し、運動方程式にはそれらの効果が追加されます。しかし、どんな複雑な振動も、その骨格には単振動の構造が存在しています。
次のステップとして、以下の記事も参考にしてください。