# 簡諧運動
> 作者:王一哲
> 日期:2018/4/5
<br />
在水平光滑桌面上有一個木塊質量為 m,用一條彈性係數為 k 的彈簧連接到左側的牆壁上,若將木塊向右拉一段距離 R 再由靜止釋放,木塊所受合力與加速度的關係為
$$ F = -kx = ma ~\Rightarrow -kx = m \frac{d^2 x}{dt^2} $$
此時木塊的運動方式稱為簡諧運動(simple harmonic motion, S.H.M.),由上式可以解出
$$ x(t) = R \cos (\omega t + \phi) $$
$$ v(t) = -\omega R \sin (\omega t + \phi) $$
$$ a(t) = -\omega^2 R\cos (\omega t + \phi) $$
上式中 $\omega$ 為角頻率
$$ \omega = \sqrt{\frac{k}{m}} $$
週期為
$$ T = 2\pi \sqrt{\frac{m}{k}} $$
理論上我們只要在 VPython 中設定好木塊所受的彈簧回復力與木塊離開平衡點位置的關係,應該就能畫出簡諧運動的過程與週期。以下共有2個程式:
1. 理想的簡諧運動。 ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/8-1SHM))
2. 考慮阻尼(damping)的簡諧運動。 ([GlowScript 網站動畫連結](http://www.glowscript.org/#/user/yizhe/folder/Public/program/8-2SHMdamp))
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://i.imgur.com/3HwfNHG.png">
<div style="text-align:center">簡諧運動畫面截圖</div>
<br />
## 程式 8-1:簡諧運動 ([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/08.%E7%B0%A1%E8%AB%A7%E9%81%8B%E5%8B%95/8-1_SHM.py))
```python=
"""
VPython教學: 8-1.簡諧運動
Ver. 1: 2018/2/25
Ver. 2: 2019/9/8
Ver. 3: 2022/4/21 加上週期理論值, 修改 print 的格式
作者: 王一哲
"""
from vpython import *
"""
1. 參數設定, 設定變數及初始值
"""
m = 4 # 木塊質量 4 kg
size = 1 # 木塊邊長 1 m
R = 5 # 振幅 5 m
k = 1 # 彈性常數 1 N/m
L0 = R + size # 彈簧原長
i = 0 # 木塊回到初位置的次數
t = 0 # 時間
dt = 0.001 # 時間間隔
print("週期理論值為 {:f} s".format(2*pi*sqrt(m/k)))
"""
2. 畫面設定
"""
# 產生動畫視窗、地板、木塊、彈簧
scene = canvas(title="Simple Harmonic Motion", width=800, height=400, x=0, y=0, background=vec(0, 0.6, 0.6))
floor = box(pos=vec(0, -(size+0.1)/2, 0), size=vec(2*L0, 0.1, R), texture=textures.metal)
wall = box(pos=vec(-L0, 0, 0), size=vec(0.1, size, R), texture=textures.metal)
block = box(pos=vec(R+size/2, 0, 0), size=vec(size, size, size), texture=textures.wood, v=vec(0, 0, 0))
spring = helix(pos=vec(-L0, 0, 0), radius=0.3*size, thickness=0.05*size, color=color.yellow)
spring.axis = block.pos - spring.pos - vec(size/2, 0, 0)
# 產生表示速度、加速度的箭頭
arrow_v = arrow(pos=block.pos + vec(0, size, 0), axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.green)
arrow_a = arrow(pos=block.pos + vec(0, 2*size, 0), axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.magenta)
# 繪圖部分
gd = graph(title="plot", width=600, height=450, x=0, y=400, xtitle="<i>t</i>(s)",
ytitle="blue: <i>x</i>(m), green: <i>v</i>(m/s), magenta: <i>a</i>(m/s<sup>2</sup>)")
xt = gcurve(graph=gd, color=color.blue)
vt = gcurve(graph=gd, color=color.green)
at = gcurve(graph=gd, color=color.magenta)
"""
3. 物體運動部分, 重複3個週期
"""
while i < 3:
rate(1000)
# 計算彈簧長度、伸長量、回復力
spring.axis = block.pos - spring.pos - vec(0.5*size, 0, 0)
F = -k * (spring.axis - vec(L0, 0, 0))
# 計算木塊加速度, 更新速度、位置
block.a = F/m
block.v += block.a*dt
block.pos += block.v*dt
# 更新代表速度、加速度的箭頭位置、方向、長度
arrow_v.pos = block.pos + vec(0, size, 0)
arrow_a.pos = block.pos + vec(0, 2*size, 0)
arrow_v.axis = block.v
arrow_a.axis = block.a
# 畫出 x-t, v-t, a-t 圖
xt.plot(pos=(t, block.pos.x - size/2))
vt.plot(pos=(t, block.v.x))
at.plot(pos=(t, block.a.x))
# 判斷木塊是否回到出發點, 計算回到出發點的次數
if block.pos.x > R + size/2 and block.v.x > 0:
i += 1
print("第 {:d} 次回到右端點,經過時間為 {:f} s".format(i, t))
# 更新時間
t += dt
```
<br />
### 參數設定
在此定義的變數有 m、size、R、k、L0、i、t、dt,用途都已經寫在該行的註解中。
<br />
### 畫面設定
1. 由於我希望木塊的中心位於 x 軸上,因此將地板的位置向下移動到 y = -(size+0.1)/2 處。
2. 為了讓木塊在 $-R \leq x \leq R$ 之間來回運動,因此將牆壁向左移動到 x = -L0 處。
3. 由於木塊的位置是以中心點為準,而且彈簧連接在木塊左,因此需要將木塊放在彈簧右端點的右側 size/2 處。
4. 為了畫出彈簧需要用到一個新的物件 helix(螺旋線)[[3](https://www.glowscript.org/docs/VPythonDocs/helix.html)],可以調整的選項主要有
-- 位置(pos),以螺旋線的端點為基準。
-- 半徑(radius)
-- 軸(axis),以位置為起點,格式為向量。
-- 厚度(thickness),線條本身的半徑,預設值為 radius/20。
-- 顏色(color)
<br />
### 物體運動
1. 先用 spring.axis = block.pos - spring.pos - vector(size/2, 0, 0) 計算並更新彈簧的軸方向及長度,再用 F = -k * (spring.axis - vector(L0, 0, 0)) 計算彈簧的伸長量、回復力。
2. 判斷木塊是否回到出發點的部分有很多種寫法,我是用 block.pos.x > R + size/2 且 木塊速度方向向右來判斷,若計算小球回到出發點時將次數 i 加 1 並印出時間。
<br />
### 模擬結果
由下圖可以看出 x、v、a 與時間的關係符合理論預測,的確是 cos、-sin、-cos 的型式。週期模擬值為 12.565,理論值為 12.566,相當接近。
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/TUw9Eum.png">
<div style="text-align:center">m = 4、k = 1、R = 5 的簡諧運動 x - t、v - t、a - t 關係圖</div>
<br />
## 程式 8-2:簡諧運動, 有阻尼 ([取得程式碼](https://github.com/YiZheWangTw/VPythonTutorial/blob/master/08.%E7%B0%A1%E8%AB%A7%E9%81%8B%E5%8B%95/8-2_SHM_damp.py))
```python=
"""
VPython教學: 8-2.簡諧運動, 有阻尼
Ver. 1: 2018/2/25
Ver. 2: 2019/9/8
Ver. 3: 2022/4/21 加上週期理論值, 修改 print 的格式
作者: 王一哲
"""
from vpython import *
"""
1. 參數設定, 設定變數及初始值
"""
m = 4 # 木塊質量 4 kg
size = 1 # 木塊邊長 1 m
R = 5 # 振幅 5 m
k = 1 # 彈性常數 1 N/m
L0 = R + size # 彈簧原長
b = 0.1*sqrt(4*m*k) # 阻尼 f = -bv, overdamped: b^2 > 4mk, critical damping: b^2 = 4mk, underdamped: b^2 < 4mk
T = 2*pi*sqrt(m/k) # 週期理論值
i = 0 # 木塊運動經過的週期次數
t = 0 # 時間
dt = 0.001 # 時間間隔
print("週期理論值為 {:f} s".format(T))
"""
2. 畫面設定
"""
# 產生動畫視窗、地板、木塊、彈簧
scene = canvas(title="Simple Harmonic Motion", width=800, height=400, x=0, y=0, background=vec(0, 0.6, 0.6))
floor = box(pos=vec(0, -(size+0.1)/2, 0), size=vec(2*L0, 0.1, R), texture=textures.metal)
wall = box(pos=vec(-L0, 0, 0), size=vec(0.1, size, R), texture=textures.metal)
block = box(pos=vec(R+size/2, 0, 0), size=vec(size, size, size), texture=textures.wood, v=vec(0, 0, 0))
spring = helix(pos=vec(-L0, 0, 0), radius=0.3*size, thickness=0.05*size, color=color.yellow)
spring.axis = block.pos - spring.pos - vec(size/2, 0, 0)
# 產生表示速度、加速度的箭頭
arrow_v = arrow(pos=block.pos + vec(0, size, 0), axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.green)
arrow_a = arrow(pos=block.pos + vec(0, 2*size, 0), axis=vec(0, 0, 0), shaftwidth=0.3*size, color=color.magenta)
# 繪圖部分
gd = graph(title="plot", width=600, height=450, x=0, y=400, xtitle="<i>t</i>(s)",
ytitle="blue: <i>x</i>(m), green: <i>v</i>(m/s), magenta: <i>a</i>(m/s<sup>2</sup>)")
xt = gcurve(graph=gd, color=color.blue)
vt = gcurve(graph=gd, color=color.green)
at = gcurve(graph=gd, color=color.magenta)
"""
3. 物體運動部分, 重複5個週期
"""
vp = block.v.x
while i < 5 and t < 5*T:
rate(1000)
# 計算彈簧長度、伸長量、回復力
spring.axis = block.pos - spring.pos - vec(0.5*size, 0, 0)
F = -k * (spring.axis - vec(L0, 0, 0)) - b * block.v
# 計算木塊加速度, 更新速度、位置
block.a = F/m
block.v += block.a*dt
block.pos += block.v*dt
# 更新代表速度、加速度的箭頭位置、方向、長度
arrow_v.pos = block.pos + vec(0, size, 0)
arrow_a.pos = block.pos + vec(0, 2*size, 0)
arrow_v.axis = block.v
arrow_a.axis = block.a
# 畫出 x-t, v-t, a-t 圖
xt.plot(pos = (t, block.pos.x - size/2))
vt.plot(pos = (t, block.v.x))
at.plot(pos = (t, block.a.x))
# 判斷木塊是否經過一個週期
vc = block.v.x
if vp > 0 and vc < 0:
i += 1
print("完成第 {:d} 次振盪,經過時間為 {:f} s".format(i, t))
vp = vc
# 更新時間
t += dt
```
<br />
### 理論計算 [[6](http://hyperphysics.phy-astr.gsu.edu/hbase/oscda.html)]
假設木塊所受阻尼 $f = -bv$,由牛頓第二定律可得
$$ ma + bv + kx = 0 $$
$$ m \frac{d^2 x}{dt^2} + b \frac{dx}{dt} + kx = 0 $$
共有 3 種情形:
1. $b^2 > 4mk ~~~~~$過阻尼 (overdamped)
2. $b^2 = 4mk ~~~~~$臨界阻尼 (critical damping)
3. $b^2 < 4mk ~~~~~$次阻尼 (underdamped)
<br />
### 程式設計部分
程式 8-2 與 8-1 很像,只有 2 個不同之處。
1. 在計算木塊所受合力時需要改成 F = -k * (spring.axis - vec(L0, 0, 0)) - b * block.v
2. 由於木塊不會回到出發點,在判斷木塊是否經過一個週期時,改成用速度來判斷,如果木塊的速度原來向右、後來向左,代表木塊經過一個週期。
```python
vc = block.v.x
if vp > 0 and vc < 0:
i += 1
print(i, t)
vp = vc
```
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/GW9ktDC.png">
<div style="text-align:center">b = 6,過阻尼</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/StiTIYn.png">
<div style="text-align:center">b = 4,臨界阻尼</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/xqNpGAj.png">
<div style="text-align:center">b = 1,次阻尼</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/0OWv3Ti.png">
<div style="text-align:center">b = 0.3,次阻尼</div>
<br />
## 參考資料
1. **canvas**: http://www.glowscript.org/docs/VPythonDocs/canvas.html
2. **box**: http://www.glowscript.org/docs/VPythonDocs/box.html
3. **helix**: http://www.glowscript.org/docs/VPythonDocs/helix.html
4. **arrow**: http://www.glowscript.org/docs/VPythonDocs/arrow.html
5. **graph**: http://www.glowscript.org/docs/VPythonDocs/graph.html
6. **Damped Harmonic Oscillator**: http://hyperphysics.phy-astr.gsu.edu/hbase/oscda.html
---
###### tags:`VPython`